summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/launch.json2
m---------3rd/bee.lua0
m---------3rd/love-api0
m---------3rd/luamake0
-rw-r--r--changelog.md10
-rw-r--r--meta/3rd/love2d/library/love.graphics.lua11
-rw-r--r--script/await.lua8
-rw-r--r--script/brave/work.lua4
-rw-r--r--script/client.lua7
-rw-r--r--script/config/config.lua2
-rw-r--r--script/core/code-action.lua144
-rw-r--r--script/core/command/autoRequire.lua33
-rw-r--r--script/core/command/jsonToLua.lua15
-rw-r--r--script/core/command/removeSpace.lua51
-rw-r--r--script/core/command/solve.lua14
-rw-r--r--script/core/completion.lua484
-rw-r--r--script/core/diagnostics/count-down-loop.lua25
-rw-r--r--script/core/diagnostics/different-requires.lua2
-rw-r--r--script/core/diagnostics/init.lua2
-rw-r--r--script/core/diagnostics/newfield-call.lua5
-rw-r--r--script/core/diagnostics/newline-call.lua17
-rw-r--r--script/core/diagnostics/redundant-value.lua32
-rw-r--r--script/core/diagnostics/trailing-space.lua35
-rw-r--r--script/core/diagnostics/undefined-global.lua2
-rw-r--r--script/core/document-symbol.lua14
-rw-r--r--script/core/find-source.lua6
-rw-r--r--script/core/folding.lua10
-rw-r--r--script/core/highlight.lua40
-rw-r--r--script/core/hover/label.lua3
-rw-r--r--script/core/infer.lua5
-rw-r--r--script/core/keyword.lua36
-rw-r--r--script/core/look-backward.lua2
-rw-r--r--script/core/noder.lua83
-rw-r--r--script/core/reference.lua4
-rw-r--r--script/core/rename.lua25
-rw-r--r--script/core/semantic-tokens.lua14
-rw-r--r--script/core/signature.lua36
-rw-r--r--script/core/type-formatting.lua72
-rw-r--r--script/files.lua252
-rw-r--r--script/parser/compile.lua17
-rw-r--r--script/parser/grammar.lua2
-rw-r--r--script/parser/guide.lua155
-rw-r--r--script/parser/init.lua1
-rw-r--r--script/parser/lines.lua22
-rw-r--r--script/parser/luadoc.lua102
-rw-r--r--script/parser/newparser.lua3623
-rw-r--r--script/parser/parse.lua17
-rw-r--r--script/parser/tokens.lua38
-rw-r--r--script/proto/converter.lua168
-rw-r--r--script/proto/define.lua36
-rw-r--r--script/proto/proto.lua5
-rw-r--r--script/provider/diagnostic.lua33
-rw-r--r--script/provider/provider.lua145
-rw-r--r--script/vm/getDocs.lua80
-rw-r--r--script/vm/getLinks.lua6
-rw-r--r--test/basic/noder.lua24
-rw-r--r--test/catch.lua84
-rw-r--r--test/code_action/init.lua10
-rw-r--r--test/command/auto-require.lua19
-rw-r--r--test/completion/common.lua420
-rw-r--r--test/completion/continue.lua10
-rw-r--r--test/completion/init.lua15
-rw-r--r--test/crossfile/allreferences.lua17
-rw-r--r--test/crossfile/completion.lua58
-rw-r--r--test/crossfile/definition.lua79
-rw-r--r--test/crossfile/diagnostic.lua27
-rw-r--r--test/crossfile/hover.lua22
-rw-r--r--test/crossfile/references.lua66
-rw-r--r--test/definition/init.lua37
-rw-r--r--test/definition/table.lua2
-rw-r--r--test/diagnostics/init.lua63
-rw-r--r--test/document_symbol/init.lua286
-rw-r--r--test/example/guide.txt4
-rw-r--r--test/full/example.lua14
-rw-r--r--test/full/init.lua7
-rw-r--r--test/highlight/init.lua38
-rw-r--r--test/hover/init.lua27
-rw-r--r--test/references/common.lua24
-rw-r--r--test/references/init.lua27
-rw-r--r--test/rename/init.lua17
-rw-r--r--test/signature/init.lua193
-rw-r--r--test/type_formatting/init.lua74
-rw-r--r--test/type_inference/init.lua16
83 files changed, 5603 insertions, 2034 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 73516e26..c4194825 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -24,7 +24,7 @@
"type": "lua",
"request": "attach",
"stopOnEntry": false,
- "address": "127.0.0.1:11427",
+ "address": "127.0.0.1:11413",
"outputCapture": [
],
"sourceMaps": [
diff --git a/3rd/bee.lua b/3rd/bee.lua
-Subproject b025c851433cc698b6e6f6af2043f54cd085f0c
+Subproject ba8291f17c44c1f934a261535e818364a582857
diff --git a/3rd/love-api b/3rd/love-api
-Subproject b913d6e804402d39401b343e3032b30eef0c83b
+Subproject 4fa1de69cb04af2e405b3ac400d4e0cec0e0753
diff --git a/3rd/luamake b/3rd/luamake
-Subproject 077e399e7e04def80ee59f686a4a88bfee0cf3a
+Subproject 59940d9a2c7de34d4c6bfd575dac654719bd144
diff --git a/changelog.md b/changelog.md
index 7d46a108..73fb8878 100644
--- a/changelog.md
+++ b/changelog.md
@@ -8,6 +8,8 @@
+ `Lua.completion.requireSeparator`
* `NEW` diagnostics:
+ `different-requires`
+* `NEW` supports `---@CustomClass<string, number>`
+* `NEW` supports `$/cancelRequest`
* `CHG` hover: improve showing multi defines
* `CHG` hover: improve showing multi comments at enums
* `CHG` hint: `Lua.hint.paramName` now supports `Disable`, `Literal` and `All`
@@ -15,10 +17,18 @@
* `CHG` no longer ignore file names case in Windows
* `CHG` watching library changes
* `CHG` completion: improve misspelling results
+* `CHG` completion: `Lua.completion.displayContext` default to `0`
+* `CHG` completion: `autoRequire` has better inserting position
+* `CHG` diagnostics:
+ + `redundant-parameter` default severity to `Warning`
+ + `redundant-value` default severity to `Warning`
+* `CHG` [#663](https://github.com/sumneko/lua-language-server/issues/663)
* `FIX` runtime errors
* `FIX` hint: may show param-2 as `self`
* `FIX` semantic: may fail when scrolling
+* `FIX` [#647](https://github.com/sumneko/lua-language-server/issues/647)
* `FIX` [#660](https://github.com/sumneko/lua-language-server/issues/660)
+* `FIX` [#673](https://github.com/sumneko/lua-language-server/issues/673)
## 2.3.7
`2021-8-17`
diff --git a/meta/3rd/love2d/library/love.graphics.lua b/meta/3rd/love2d/library/love.graphics.lua
index 9f453304..f70efefd 100644
--- a/meta/3rd/love2d/library/love.graphics.lua
+++ b/meta/3rd/love2d/library/love.graphics.lua
@@ -530,12 +530,13 @@ function love.graphics.newFont(filename) end
---
---Creates a new Image from a filepath, FileData, an ImageData, or a CompressedImageData, and optionally generates or specifies mipmaps for the image.
---
----@overload fun(imageData: love.ImageData):love.Image
----@overload fun(compressedImageData: love.CompressedImageData):love.Image
----@overload fun(filename: string, flags: table):love.Image
+---@overload fun(fileData: love.FileData, flags: table):love.Image
+---@overload fun(imageData: love.ImageData, flags: table):love.Image
+---@overload fun(compressedImageData: love.CompressedImageData, flags: table):love.Image
---@param filename string # The filepath to the image file.
----@return love.Image image # An Image object which can be drawn on screen.
-function love.graphics.newImage(filename) end
+---@param flags {dpiscale: number, linear: boolean, mipmaps: boolean} # A table containing the following fields:
+---@return love.Image image # A new Image object which can be drawn on screen.
+function love.graphics.newImage(filename, flags) end
---
---Creates a new specifically formatted image.
diff --git a/script/await.lua b/script/await.lua
index 2d27ae2c..495f381c 100644
--- a/script/await.lua
+++ b/script/await.lua
@@ -1,11 +1,12 @@
local timer = require 'timer'
-local util = require 'utility'
+
+local wkmt = { __mode = 'k' }
---@class await
local m = {}
m.type = 'await'
-m.coMap = setmetatable({}, { __mode = 'k' })
+m.coMap = setmetatable({}, wkmt)
m.idMap = {}
m.delayQueue = {}
m.delayQueueIndex = 1
@@ -74,7 +75,7 @@ function m.setID(id, co)
return
end
if not m.idMap[id] then
- m.idMap[id] = setmetatable({}, { __mode = 'k' })
+ m.idMap[id] = setmetatable({}, wkmt)
end
m.idMap[id][co] = true
end
@@ -85,6 +86,7 @@ function m.close(id)
if not map then
return
end
+ m.idMap[id] = nil
for co in pairs(map) do
if coroutine.status(co) == 'suspended' then
map[co] = nil
diff --git a/script/brave/work.lua b/script/brave/work.lua
index 907f78cf..7af36fda 100644
--- a/script/brave/work.lua
+++ b/script/brave/work.lua
@@ -26,12 +26,12 @@ brave.on('timer', function (time)
end)
brave.on('compile', function (text)
- local state, err = parser:compile(text, 'lua', 'Lua 5.4')
+ local state, err = parser.compile(text, 'Lua', 'Lua 5.4')
if not state then
log.error(err)
return
end
- local lines = parser:lines(text)
+ local lines = parser.lines(text)
return {
root = state.root,
value = state.value,
diff --git a/script/client.lua b/script/client.lua
index c22b59ab..e77eaaae 100644
--- a/script/client.lua
+++ b/script/client.lua
@@ -4,6 +4,7 @@ local lang = require 'language'
local proto = require 'proto'
local define = require 'proto.define'
local config = require 'config'
+local converter = require 'proto.converter'
local m = {}
@@ -215,15 +216,15 @@ function m.setConfig(changes, onlyMemory)
end
end
----@alias textEdit {start: integer, finish: integer, text: string}
+---@alias textEditor {start: integer, finish: integer, text: string}
---@param uri uri
----@param edits textEdit[]
+---@param edits textEditor[]
function m.editText(uri, edits)
local files = require 'files'
local textEdits = {}
for i, edit in ipairs(edits) do
- textEdits[i] = define.textEdit(files.range(uri, edit.start, edit.finish), edit.text)
+ textEdits[i] = converter.textEdit(converter.packRange(uri, edit.start, edit.finish), edit.text)
end
proto.request('workspace/applyEdit', {
edit = {
diff --git a/script/config/config.lua b/script/config/config.lua
index b44b48dc..52848a44 100644
--- a/script/config/config.lua
+++ b/script/config/config.lua
@@ -180,7 +180,7 @@ local Template = {
['Lua.completion.enable'] = Type.Boolean >> true,
['Lua.completion.callSnippet'] = Type.String >> 'Disable',
['Lua.completion.keywordSnippet'] = Type.String >> 'Replace',
- ['Lua.completion.displayContext'] = Type.Integer >> 6,
+ ['Lua.completion.displayContext'] = Type.Integer >> 0,
['Lua.completion.workspaceWord'] = Type.Boolean >> true,
['Lua.completion.showWord'] = Type.String >> 'Fallback',
['Lua.completion.autoRequire'] = Type.Boolean >> true,
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..b485fcb1 100644
--- a/script/core/command/autoRequire.lua
+++ b/script/core/command/autoRequire.lua
@@ -4,21 +4,32 @@ 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
+ local hasFounded
+ 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
+ if hasFounded then
+ return i, fmt
+ end
+ if not lnText:match '^local%s'
+ and not lnText:match '^%s*$'
+ and not lnText:match '^%-%-' then
+ return i, fmt
+ end
else
+ hasFounded = true
local lpPos = lnText:find '%('
if lpPos then
fmt.pair = true
@@ -33,7 +44,7 @@ local function findInsertOffset(uri)
end
end
end
- return 1, fmt
+ return 0, fmt
end
local function askAutoRequire(visiblePaths)
@@ -70,7 +81,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 +99,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 +132,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/folding.lua b/script/core/folding.lua
index 51d1be6b..bd3ba5f3 100644
--- a/script/core/folding.lua
+++ b/script/core/folding.lua
@@ -146,22 +146,22 @@ local Care = {
}
return function (uri)
- local ast = files.getState(uri)
- local text = files.getText(uri)
- if not ast or not text then
+ local state = files.getState(uri)
+ local text = files.getText(uri)
+ if not state or not text then
return nil
end
local regions = {}
local status = {}
- guide.eachSource(ast.ast, function (source)
+ guide.eachSource(state.ast, function (source)
local tp = source.type
if Care[tp] then
await.delay()
Care[tp](source, text, regions)
end
end)
- for _, source in ipairs(ast.comms) do
+ for _, source in ipairs(state.comms) do
local tp = source.type
if Care[tp] then
await.delay()
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..d1388cb3 100644
--- a/script/core/noder.lua
+++ b/script/core/noder.lua
@@ -883,8 +883,8 @@ compileNodeMap = util.switch()
: call(function (noders, id, source)
if source.bindSources then
for _, src in ipairs(source.bindSources) do
- pushForward(noders, getID(src), id, INFO_REJECT_SET)
- pushForward(noders, id, getID(src))
+ pushForward(noders, getID(src), id)
+ pushBackward(noders, id, getID(src))
end
end
for _, enumUnit in ipairs(source.enums) do
@@ -898,13 +898,16 @@ compileNodeMap = util.switch()
pushForward(noders, id, unitID)
if source.bindSources then
for _, src in ipairs(source.bindSources) do
- pushBackward(noders, unitID, getID(src), INFO_REJECT_SET)
+ pushBackward(noders, unitID, getID(src))
end
end
end
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))
@@ -969,7 +972,7 @@ compileNodeMap = util.switch()
if source.bindSources then
for _, src in ipairs(source.bindSources) do
pushForward(noders, getID(src), id)
- pushForward(noders, id, getID(src), INFO_REJECT_SET)
+ pushForward(noders, id, getID(src))
end
end
for _, field in ipairs(source.fields) do
@@ -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..38b75227 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
@@ -213,15 +214,14 @@ local function buildTokens(uri, results)
end
return function (uri, start, finish)
- local ast = files.getState(uri)
- local lines = files.getLines(uri)
+ local state = files.getState(uri)
local text = files.getText(uri)
- if not ast then
+ if not state then
return nil
end
local results = {}
- guide.eachSourceBetween(ast.ast, start, finish, function (source)
+ guide.eachSourceBetween(state.ast, start, finish, function (source)
local method = Care[source.type]
if not method then
return
@@ -230,7 +230,7 @@ return function (uri, start, finish)
await.delay()
end)
- for _, comm in ipairs(ast.comms) do
+ for _, comm in ipairs(state.comms) do
if comm.type == 'comment.cshort' then
results[#results+1] = {
start = comm.start,
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
diff --git a/script/files.lua b/script/files.lua
index fa5474ef..659a1aa0 100644
--- a/script/files.lua
+++ b/script/files.lua
@@ -7,12 +7,10 @@ local proto = require 'proto'
local lang = require 'language'
local await = require 'await'
local timer = require 'timer'
-local plugin = require 'plugin'
local util = require 'utility'
local guide = require 'parser.guide'
local smerger = require 'string-merger'
local progress = require "progress"
-local client = require 'client'
local unicode
@@ -35,8 +33,6 @@ m.assocMatcher = nil
m.globalVersion = 0
m.fileCount = 0
m.astCount = 0
-m.linesMap = setmetatable({}, { __mode = 'v' })
-m.originLinesMap = setmetatable({}, { __mode = 'v' })
m.astMap = {} -- setmetatable({}, { __mode = 'v' })
--- 打开文件
@@ -100,6 +96,7 @@ function m.exists(uri)
end
local function pluginOnSetText(file, text)
+ local plugin = require 'plugin'
file._diffInfo = nil
local suc, result = plugin.dispatch('OnSetText', file.uri, text)
if not suc then
@@ -115,6 +112,7 @@ local function pluginOnSetText(file, text)
suc, result, diffs = xpcall(smerger.mergeDiff, log.error, text, result)
if suc then
file._diffInfo = diffs
+ file.originLines = parser.lines(text)
return result
else
if DEVELOP and result then
@@ -160,8 +158,6 @@ function m.setText(uri, text, isTrust, instance)
file.trusted = isTrust
file.originText = text
file.words = nil
- m.linesMap[uri] = nil
- m.originLinesMap[uri] = nil
m.astMap[uri] = nil
file.cache = {}
file.cacheActiveTime = math.huge
@@ -206,8 +202,6 @@ function m.setRawText(uri, text)
local file = m.fileMap[uri]
file.text = text
file.originText = text
- m.linesMap[uri] = nil
- m.originLinesMap[uri] = nil
m.astMap[uri] = nil
end
@@ -298,6 +292,17 @@ function m.getOriginText(uri)
return file.originText
end
+--- 获取文件原始行表
+---@param uri uri
+---@return integer[]
+function m.getOriginLines(uri)
+ local file = m.fileMap[uri]
+ if not file then
+ return nil
+ end
+ return file.originLines
+end
+
function m.getChildFiles(uri)
local results = {}
local uris = m.getAllUris()
@@ -321,8 +326,6 @@ function m.remove(uri)
end
m.fileMap[uri] = nil
m.astMap[uri] = nil
- m.linesMap[uri] = nil
- m.originLinesMap[uri] = nil
m._pairsCache = nil
m.flushFileCache(uri)
@@ -346,7 +349,6 @@ function m.removeAll()
m.fileCount = m.fileCount - 1
m.fileMap[uri] = nil
m.astMap[uri] = nil
- m.linesMap[uri] = nil
m.onWatch('remove', uri)
end
end
@@ -366,7 +368,6 @@ function m.removeAllClosed()
m.fileCount = m.fileCount - 1
m.fileMap[uri] = nil
m.astMap[uri] = nil
- m.linesMap[uri] = nil
m.onWatch('remove', uri)
end
end
@@ -419,7 +420,8 @@ function m.eachDll()
end
function m.compileState(uri, text)
- local ws = require 'workspace'
+ local ws = require 'workspace'
+ local client = require 'client'
if not m.isOpen(uri)
and not m.isLibrary(uri)
and #text >= config.get 'Lua.workspace.preloadFileSize' * 1000 then
@@ -446,14 +448,13 @@ function m.compileState(uri, text)
local prog <close> = progress.create(lang.script.WINDOW_COMPILING, 0.5)
prog:setMessage(ws.getRelativePath(uri))
local clock = os.clock()
- local state, err = parser:compile(text
- , 'lua'
+ local state, err = parser.compile(text
+ , 'Lua'
, config.get 'Lua.runtime.version'
, {
special = config.get 'Lua.runtime.special',
unicodeName = config.get 'Lua.runtime.unicodeName',
nonstandardSymbol = config.get 'Lua.runtime.nonstandardSymbol',
- delay = await.delay,
}
)
local passed = os.clock() - clock
@@ -465,7 +466,7 @@ function m.compileState(uri, text)
state.uri = uri
state.ast.uri = uri
local clock = os.clock()
- parser:luadoc(state)
+ parser.luadoc(state)
local passed = os.clock() - clock
if passed > 0.1 then
log.warn(('Parse LuaDoc of [%s] takes [%.3f] sec, size [%.3f] kb.'):format(uri, passed, #text / 1000))
@@ -529,44 +530,16 @@ function m.getVisibles(uri)
end
local visibles = {}
for i, range in ipairs(ranges) do
- local start, finish = m.unrange(uri, range)
+ local startRow = range.start.line
+ local finishRow = range['end'].line
visibles[i] = {
- start = start,
- finish = finish,
+ start = guide.positionOf(startRow, 0),
+ finish = guide.positionOf(finishRow, 0),
}
end
return visibles
end
---- 获取文件行信息
----@param uri uri
----@return table lines
-function m.getLines(uri)
- local file = m.fileMap[uri]
- if not file then
- return nil
- end
- local lines = m.linesMap[uri]
- if not lines then
- lines = parser:lines(file.text)
- m.linesMap[uri] = lines
- end
- return lines
-end
-
-function m.getOriginLines(uri)
- local file = m.fileMap[uri]
- if not file then
- return nil
- end
- local lines = m.originLinesMap[uri]
- if not lines then
- lines = parser:lines(file.originText)
- m.originLinesMap[uri] = lines
- end
- return lines
-end
-
function m.getFile(uri)
return m.fileMap[uri]
or m.dllMap[uri]
@@ -583,111 +556,6 @@ local function isNameChar(text)
return false
end
----@alias position table
-
---- 获取 position 对应的光标位置
----@param uri uri
----@param position position
----@param isFinish? boolean
----@return integer
-function m.offset(uri, position, isFinish)
- local file = m.getFile(uri)
- local lines = m.getLines(uri)
- local text = m.getText(uri)
- if not file then
- return 0
- end
- if file._diffInfo then
- lines = m.getOriginLines(uri)
- text = m.getOriginText(uri)
- end
- local row = position.line + 1
- local start, finish, char
- if row > #lines then
- start, finish = guide.lineRange(lines, #lines)
- start = start + 1
- char = util.utf8Len(text, start, finish)
- else
- start, finish = guide.lineRange(lines, row)
- start = start + 1
- char = position.character
- end
- local utf8Len = util.utf8Len(text, start, finish)
- local offset
- if char <= 0 then
- offset = start
- else
- if char >= utf8Len then
- char = utf8Len
- end
- local left = utf8.offset(text, char, start)
- local right = utf8.offset(text, char + 1, start)
- if isFinish then
- offset = left
- else
- offset = right
- end
- end
- if file._diffInfo then
- local start, finish = smerger.getOffset(file._diffInfo, offset)
- if isFinish then
- offset = finish
- else
- offset = start
- end
- end
- return offset
-end
-
---- 获取 position 对应的光标位置(根据附近的单词)
----@param uri uri
----@param position position
----@return integer
-function m.offsetOfWord(uri, position)
- local file = m.getFile(uri)
- local lines = m.getLines(uri)
- local text = m.getText(uri)
- if not file then
- return 0
- end
- if file._diffInfo then
- lines = m.getOriginLines(uri)
- text = m.getOriginText(uri)
- end
- local row = position.line + 1
- local start, finish, char
- if row > #lines then
- start, finish = guide.lineRange(lines, #lines)
- start = start + 1
- char = util.utf8Len(text, start, finish)
- else
- start, finish = guide.lineRange(lines, row)
- start = start + 1
- char = position.character
- end
- local utf8Len = util.utf8Len(text, start, finish)
- local offset
- if char <= 0 then
- offset = start
- else
- if char >= utf8Len then
- char = utf8Len
- end
- local left = utf8.offset(text, char, start)
- local right = utf8.offset(text, char + 1, start)
- if isNameChar(text:sub(left, right - 1))
- and not isNameChar(text:sub(right, right)) then
- offset = left
- else
- offset = right
- end
- end
- if file._diffInfo then
- offset = smerger.getOffset(file._diffInfo, offset)
- end
- return offset
-end
-
--- 将应用差异前的offset转换为应用差异后的offset
---@param uri uri
---@param offset integer
@@ -720,76 +588,12 @@ function m.diffedOffsetBack(uri, offset)
return smerger.getOffsetBack(file._diffInfo, offset)
end
---- 将光标位置转化为 position
----@param uri uri
----@param offset integer
----@param leftOrRight? '"left"'|'"right"'
----@return position
-function m.position(uri, offset, leftOrRight)
- local file = m.getFile(uri)
- local lines = m.getLines(uri)
- local text = m.getText(uri)
+function m.hasDiffed(uri)
+ local file = m.getFile(uri)
if not file then
- return {
- line = 0,
- character = 0,
- }
- end
- if file._diffInfo then
- local start, finish = smerger.getOffsetBack(file._diffInfo, offset)
- if leftOrRight == 'right' then
- offset = finish
- else
- offset = start
- end
- lines = m.getOriginLines(uri)
- text = m.getOriginText(uri)
- end
- local row, col = guide.positionOf(lines, offset)
- local start, finish = guide.lineRange(lines, row, true)
- start = start + 1
- if col <= finish - start + 1 then
- local ucol = util.utf8Len(text, start, start + col - 1)
- if row < 1 then
- row = 1
- end
- if leftOrRight == 'left' and ucol > 0 then
- ucol = ucol - 1
- end
- return {
- line = row - 1,
- character = ucol,
- }
- else
- return {
- line = row - 1,
- character = util.utf8Len(text, start, finish),
- }
+ return false
end
-end
-
---- 将起点与终点位置转化为 range
----@alias range table
----@param uri uri
----@param offset1 integer
----@param offset2 integer
-function m.range(uri, offset1, offset2)
- local range = {
- start = m.position(uri, offset1, 'left'),
- ['end'] = m.position(uri, offset2, 'right'),
- }
- return range
-end
-
---- convert `range` to `offsetStart` and `offsetFinish`
----@param uri table
----@param range table
----@return integer start
----@return integer finish
-function m.unrange(uri, range)
- local start = m.offset(uri, range.start, true)
- local finish = m.offset(uri, range['end'], false)
- return start, finish
+ return file._diffInfo ~= nil
end
--- 获取文件的自定义缓存信息(在文件内容更新后自动失效)
@@ -922,8 +726,6 @@ end
function m.flushCache()
for uri, file in pairs(m.fileMap) do
file.cacheActiveTime = math.huge
- m.linesMap[uri] = nil
- m.originLinesMap[uri] = nil
m.astMap[uri] = nil
file.cache = {}
end
@@ -935,8 +737,6 @@ function m.flushFileCache(uri)
return
end
file.cacheActiveTime = math.huge
- m.linesMap[uri] = nil
- m.originLinesMap[uri] = nil
m.astMap[uri] = nil
file.cache = {}
end
diff --git a/script/parser/compile.lua b/script/parser/compile.lua
index 207664cc..d6c6a526 100644
--- a/script/parser/compile.lua
+++ b/script/parser/compile.lua
@@ -1,4 +1,6 @@
local guide = require 'parser.guide'
+local parse = require 'parser.parse'
+local newparser = require 'parser.newparser'
local type = type
local tableInsert = table.insert
local pairs = pairs
@@ -566,8 +568,12 @@ local function PostCompile()
end
end
-return function (self, lua, mode, version, options)
- local state, err = self:parse(lua, mode, version, options)
+return function (lua, mode, version, options)
+ do
+ local state, err = newparser(lua, mode, version, options)
+ return state, err
+ end
+ local state, err = parse(lua, mode, version, options)
if not state then
return nil, err
end
@@ -576,11 +582,7 @@ return function (self, lua, mode, version, options)
--end
local clock = os.clock()
pushError = state.pushError
- if version == 'Lua 5.1' or version == 'LuaJIT' then
- ENVMode = '@fenv'
- else
- ENVMode = '_ENV'
- end
+ ENVMode = state.ENVMode
Compiled = {}
GoToTag = {}
LocalCount = 0
@@ -590,7 +592,6 @@ return function (self, lua, mode, version, options)
Root.state = state
end
Options = options
- state.ENVMode = ENVMode
if type(state.ast) == 'table' then
Compile(state.ast)
end
diff --git a/script/parser/grammar.lua b/script/parser/grammar.lua
index 1a3913e0..154bd7ba 100644
--- a/script/parser/grammar.lua
+++ b/script/parser/grammar.lua
@@ -557,7 +557,7 @@ Lua <- Head?
Head <- '#' (!%nl .)*
]]
-return function (self, lua, mode)
+return function (lua, mode)
local gram = compiled[mode] or compiled['Lua']
local r, _, pos = gram:match(lua)
if not r then
diff --git a/script/parser/guide.lua b/script/parser/guide.lua
index 6dcfbf95..b65d9680 100644
--- a/script/parser/guide.lua
+++ b/script/parser/guide.lua
@@ -54,6 +54,7 @@ local type = type
---@field ref parser.guide.object[]
---@field returnIndex integer
---@field docs parser.guide.object[]
+---@field state table
---@field _root parser.guide.object
---@field _noders noders
---@field _mnode parser.guide.object
@@ -85,13 +86,14 @@ local breakBlockTypes = {
['in'] = true,
['loop'] = true,
['repeat'] = true,
+ ['for'] = true,
}
local childMap = {
['main'] = {'#', 'docs'},
['repeat'] = {'#', 'filter'},
['while'] = {'filter', '#'},
- ['in'] = {'keys', '#'},
+ ['in'] = {'keys', 'exps', '#'},
['loop'] = {'loc', 'max', 'step', '#'},
['if'] = {'#'},
['ifblock'] = {'filter', '#'},
@@ -547,24 +549,24 @@ function m.getRange(source)
return start, finish
end
---- 判断source是否包含offset
-function m.isContain(source, offset)
+--- 判断source是否包含position
+function m.isContain(source, position)
local start, finish = m.getStartFinish(source)
if not start then
return false
end
- return start <= offset and finish >= offset
+ return start <= position and finish >= position
end
---- 判断offset在source的影响范围内
+--- 判断position在source的影响范围内
---
--- 主要针对赋值等语句时,key包含value
-function m.isInRange(source, offset)
+function m.isInRange(source, position)
local start, finish = m.getRange(source)
if not start then
return false
end
- return start <= offset and finish >= offset
+ return start <= position and finish >= position
end
function m.isBetween(source, tStart, tFinish)
@@ -596,8 +598,8 @@ local function addChilds(list, obj)
f(obj, list)
end
---- 遍历所有包含offset的source
-function m.eachSourceContain(ast, offset, callback)
+--- 遍历所有包含position的source
+function m.eachSourceContain(ast, position, callback)
local list = { ast }
local mark = {}
while true do
@@ -609,8 +611,8 @@ function m.eachSourceContain(ast, offset, callback)
list[len] = nil
if not mark[obj] then
mark[obj] = true
- if m.isInRange(obj, offset) then
- if m.isContain(obj, offset) then
+ if m.isInRange(obj, position) then
+ if m.isContain(obj, position) then
local res = callback(obj)
if res ~= nil then
return res
@@ -722,15 +724,13 @@ function m.eachSource(ast, callback)
end
--- 获取指定的 special
----@param ast parser.guide.object
----@param name string
----@param callback fun(source: parser.guide.object)
function m.eachSpecialOf(ast, name, callback)
local root = m.getRoot(ast)
- if not root.specials then
+ local state = root.state
+ if not state.specials then
return
end
- local specials = root.specials[name]
+ local specials = state.specials[name]
if not specials then
return
end
@@ -739,92 +739,65 @@ function m.eachSpecialOf(ast, name, callback)
end
end
---- 获取光标偏移对应的坐标。
---- 如果在换行符的右侧,则认为在新的一行。
---- 第一行的行号是1不是0。
----@param lines table
----@return integer {name = 'row'}
----@return integer {name = 'col'}
-function m.positionOf(lines, offset)
- if offset <= 0 then
- return 1, 0
- end
- local lastLine = lines[#lines]
- if offset >= lastLine.finish then
- return #lines, lastLine.finish - lastLine.start
- end
- local min = 1
- local max = #lines
- for _ = 1, 100 do
- if max <= min then
- local line = lines[min]
- return min, offset - line.start
- end
- local row = (max - min) // 2 + min
- local line = lines[row]
- if offset < line.start then
- max = row - 1
- elseif offset >= line.finish then
- min = row + 1
- else
- return row, offset - line.start
- end
- end
- error('Stack overflow!')
+--- 将 position 拆分成行号与列号
+---
+--- 第一行是0
+---@param position integer
+---@return integer row
+---@return integer col
+function m.rowColOf(position)
+ return position // 10000, position % 10000
end
---- 获取坐标对应的光标偏移。
---- 一定会落在当前行的换行符左侧。
---- 第一行的行号是1不是0。
----@param lines table
+--- 将行列合并为 position
+---
+--- 第一行是0
---@param row integer
---@param col integer
----@return integer {name = 'offset'}
-function m.offsetOf(lines, row, col)
- if row < 1 then
- return 0
- end
- if row > #lines then
- local lastLine = lines[#lines]
- return lastLine.finish
- end
- local line = lines[row]
- local len = line.range - line.start
- if col < 0 then
- return line.start
- elseif col > len then
- return line.range
- else
- return line.start + col
- end
+---@return integer
+function m.positionOf(row, col)
+ return row * 10000 + col
end
-function m.lineContent(lines, text, row, ignoreNL)
- local line = lines[row]
- if not line then
- return ''
- end
- if ignoreNL then
- return text:sub(line.start, line.range)
- else
- return text:sub(line.start, line.finish)
- end
+function m.positionToOffsetByLines(lines, position)
+ local row, col = m.rowColOf(position)
+ return lines[row] + col - 1
end
-function m.lineRange(lines, row, ignoreNL)
- local line = lines[row]
- if not line then
- return 0, 0
- end
- if ignoreNL then
- return line.start, line.range
- else
- return line.start, line.finish
+--- 返回全文光标位置
+---@param state any
+---@param position integer
+function m.positionToOffset(state, position)
+ return m.positionToOffsetByLines(state.lines, position)
+end
+
+function m.offsetToPositionByLines(lines, offset)
+ local left = 0
+ local right = #lines
+ local row = 0
+ while true do
+ row = (left + right) // 2
+ if row == left then
+ if right ~= left then
+ if lines[right] <= offset then
+ row = right
+ end
+ end
+ break
+ end
+ local start = lines[row] - 1
+ if start > offset then
+ right = row
+ else
+ left = row
+ end
end
+ local col = offset - lines[row] + 1
+ return m.positionOf(row, col)
end
-function m.lineData(lines, row)
- return lines[row]
+function m.offsetToPosition(state, offset)
+ return m.offsetToPositionByLines(state.lines, offset)
end
local isSetMap = {
diff --git a/script/parser/init.lua b/script/parser/init.lua
index ba40d145..219f8900 100644
--- a/script/parser/init.lua
+++ b/script/parser/init.lua
@@ -7,6 +7,7 @@ local api = {
lines = require 'parser.lines',
guide = require 'parser.guide',
luadoc = require 'parser.luadoc',
+ tokens = require 'parser.tokens',
}
return api
diff --git a/script/parser/lines.lua b/script/parser/lines.lua
index 1aba0ae5..964aabf4 100644
--- a/script/parser/lines.lua
+++ b/script/parser/lines.lua
@@ -2,33 +2,23 @@ local sfind = string.find
local ssub = string.sub
---@param text string
-return function (self, text)
+return function (text)
local current = 1
local lines = {}
- local i = 1
+ lines[0] = 1
+ local i = 0
while true do
local pos = sfind(text,'[\r\n]', current)
if not pos then
break
end
- local line = {
- start = current - 1,
- range = pos - 1,
- }
- lines[i] = line
i = i + 1
if ssub(text, pos, pos + 1) == '\r\n' then
- current = pos + 2
- line.finish = pos + 1
+ current = pos + 2
else
- current = pos + 1
- line.finish = pos
+ current = pos + 1
end
+ lines[i] = current
end
- lines[i] = {
- start = current - 1,
- finish = #text,
- range = #text,
- }
return lines
end
diff --git a/script/parser/luadoc.lua b/script/parser/luadoc.lua
index da612e60..1645487d 100644
--- a/script/parser/luadoc.lua
+++ b/script/parser/luadoc.lua
@@ -1,11 +1,10 @@
local m = require 'lpeglabel'
local re = require 'parser.relabel'
-local lines = require 'parser.lines'
local guide = require 'parser.guide'
-local grammar = require 'parser.grammar'
+local parser = require 'parser.newparser'
local TokenTypes, TokenStarts, TokenFinishs, TokenContents
-local Ci, Offset, pushError, Ct, NextComment, Lines
+local Ci, Offset, pushError, NextComment, Lines
local parseType
local Parser = re.compile([[
Main <- (Token / Sp)*
@@ -121,7 +120,6 @@ local function trim(str)
end
local function parseTokens(text, offset)
- Ct = offset
Ci = 0
Offset = offset
TokenTypes = {}
@@ -162,7 +160,7 @@ local function getFinish()
if Ci == 0 then
return Offset
end
- return TokenFinishs[Ci] + Offset
+ return TokenFinishs[Ci] + Offset + 1
end
local function try(callback)
@@ -257,7 +255,7 @@ local function parseClass(parent)
pushError {
type = 'LUADOC_MISS_EXTENDS_SYMBOL',
start = result.finish + 1,
- finish = getStart() - 1,
+ finish = getStart(),
}
return result
end
@@ -326,7 +324,7 @@ local function parseTypeUnitTable(parent, node)
end
nextSymbolOrError('>')
- node.parent = result;
+ node.parent = result
result.finish = getFinish()
result.tkey = key
result.tvalue = value
@@ -554,7 +552,6 @@ function parseType(parent)
enums = {},
resumes = {},
}
- result.start = getStart()
while true do
local tp, content = peekToken()
if not tp then
@@ -632,10 +629,13 @@ function parseType(parent)
end
nextToken()
end
+ if not result.start then
+ result.start = getFinish()
+ end
result.finish = getFinish()
result.firstFinish = result.finish
- local row = guide.positionOf(Lines, result.finish)
+ local row = guide.rowColOf(result.finish)
local function pushResume()
local comments
@@ -644,8 +644,9 @@ function parseType(parent)
if not nextComm then
return false
end
- local line = Lines[row + i + 1]
- if not line or line.finish < nextComm.start then
+ local nextCommRow = guide.rowColOf(nextComm.start)
+ local currentRow = row + i + 1
+ if currentRow < nextCommRow then
return false
end
if nextComm.text:sub(1, 2) == '-@' then
@@ -655,7 +656,7 @@ function parseType(parent)
NextComment(i)
row = row + i + 1
local finishPos = nextComm.text:find('#', 3) or #nextComm.text
- parseTokens(nextComm.text:sub(3, finishPos), nextComm.start + 1)
+ parseTokens(nextComm.text:sub(3, finishPos), nextComm.start + 3)
local resume = parseResume(result)
if resume then
if comments then
@@ -950,7 +951,7 @@ local function parseVersion()
if not tp then
pushError {
type = 'LUADOC_MISS_VERSION',
- start = getStart(),
+ start = getFinish(),
finish = getFinish(),
}
break
@@ -1120,9 +1121,9 @@ local function trimTailComment(text)
comment = text:sub(3)
end
if comment:find '^%s*[\'"[]' then
- local result = grammar(nil, comment:gsub('^%s+', ''), 'string')
- if result and result[1] then
- comment = result[1][1]
+ local state = parser(comment:gsub('^%s+', ''), 'String')
+ if state and state.ast then
+ comment = state.ast[1]
end
end
return comment
@@ -1143,11 +1144,11 @@ local function buildLuaDoc(comment)
local doc = text:sub(startPos + 1)
- parseTokens(doc, comment.start + startPos - 1)
+ parseTokens(doc, comment.start + startPos + 1)
local result = convertTokens()
if result then
result.range = comment.finish
- local cstart = text:find('%S', (result.firstFinish or result.finish) - comment.start + 2)
+ local cstart = text:find('%S', (result.firstFinish or result.finish) - comment.start)
if cstart and cstart < comment.finish then
result.comment = {
type = 'doc.tailcomment',
@@ -1171,28 +1172,22 @@ local function buildLuaDoc(comment)
}
end
----当前行在注释doc前是否有代码
-local function haveCodeBeforeDocInCurLine(text, lineData, docStart)
- return text:sub(lineData.start + 1, docStart - 1):find '[%w_]'
-end
-
-local function isTailComment(lns, text, binded, doc)
- local lastDoc = binded[#binded]
- local lastDocStartRow = guide.positionOf(lns, lastDoc.originalComment.start)
- local lastDocStartLineData = guide.lineData(lns, lastDocStartRow)
- if haveCodeBeforeDocInCurLine(text, lastDocStartLineData, lastDoc.originalComment.start) then
- return true
- end
- return false
+local function isTailComment(text, binded)
+ local lastDoc = binded[#binded]
+ local left = lastDoc.originalComment.start
+ local row, col = guide.rowColOf(left)
+ local lineStart = Lines[row] or 0
+ local hasCodeBefore = text:sub(lineStart, lineStart + col):find '[%w_]'
+ return hasCodeBefore
end
-local function isNextLine(lns, text, binded, doc)
+local function isNextLine(binded, doc)
if not binded then
return false
end
local lastDoc = binded[#binded]
- local lastRow = guide.positionOf(lns, lastDoc.finish)
- local newRow = guide.positionOf(lns, doc.start)
+ local lastRow = guide.rowColOf(lastDoc.finish)
+ local newRow = guide.rowColOf(doc.start)
return newRow - lastRow == 1
end
@@ -1245,7 +1240,7 @@ local function bindDocsBetween(sources, binded, bindSources, start, finish)
for i = index, max do
local src = sources[i]
if src and src.start >= start then
- if src.start > finish then
+ if src.start >= finish then
break
end
-- 遇到table后中断,处理以下情况:
@@ -1255,8 +1250,16 @@ local function bindDocsBetween(sources, binded, bindSources, start, finish)
break
end
if src.start >= start then
- src.bindDocs = binded
- bindSources[#bindSources+1] = src
+ if src.type == 'local'
+ or src.type == 'setglobal'
+ or src.type == 'tablefield'
+ or src.type == 'tableindex'
+ or src.type == 'setfield'
+ or src.type == 'setindex'
+ or src.type == 'function' then
+ src.bindDocs = binded
+ bindSources[#bindSources+1] = src
+ end
end
end
end
@@ -1318,7 +1321,7 @@ local function bindClassAndFields(binded)
end
end
-local function bindDoc(sources, lns, binded)
+local function bindDoc(sources, binded)
if not binded then
return
end
@@ -1332,12 +1335,10 @@ local function bindDoc(sources, lns, binded)
doc.bindSources = bindSources
end
bindGeneric(binded)
- local row = guide.positionOf(lns, lastDoc.finish)
- local cstart, cfinish = guide.lineRange(lns, row)
- local nstart, nfinish = guide.lineRange(lns, row + 1)
- bindDocsBetween(sources, binded, bindSources, cstart, cfinish)
+ local row = guide.rowColOf(lastDoc.finish)
+ bindDocsBetween(sources, binded, bindSources, guide.positionOf(row, 0), lastDoc.start)
if #bindSources == 0 then
- bindDocsBetween(sources, binded, bindSources, nstart, nfinish)
+ bindDocsBetween(sources, binded, bindSources, guide.positionOf(row + 1, 0), guide.positionOf(row + 2, 0))
end
bindParamAndReturnIndex(binded)
bindClassAndFields(binded)
@@ -1361,21 +1362,21 @@ local function bindDocs(state)
end)
local binded
for _, doc in ipairs(state.ast.docs) do
- if not isNextLine(Lines, text, binded, doc) then
- bindDoc(sources, Lines, binded)
+ if not isNextLine(binded, doc) then
+ bindDoc(sources, binded)
binded = {}
state.ast.docs.groups[#state.ast.docs.groups+1] = binded
end
binded[#binded+1] = doc
- if isTailComment(Lines, text, binded, doc) then
- bindDoc(sources, Lines, binded)
+ if isTailComment(text, binded) then
+ bindDoc(sources, binded)
binded = nil
end
end
- bindDoc(sources, Lines, binded)
+ bindDoc(sources, binded)
end
-return function (_, state)
+return function (state)
local ast = state.ast
local comments = state.comms
table.sort(comments, function (a, b)
@@ -1388,8 +1389,7 @@ return function (_, state)
}
pushError = state.pushError
-
- Lines = lines(nil, state.lua)
+ Lines = state.lines
local ci = 1
NextComment = function (offset, peek)
diff --git a/script/parser/newparser.lua b/script/parser/newparser.lua
new file mode 100644
index 00000000..1b02a5d9
--- /dev/null
+++ b/script/parser/newparser.lua
@@ -0,0 +1,3623 @@
+local tokens = require 'parser.tokens'
+local guide = require 'parser.guide'
+
+local sbyte = string.byte
+local sfind = string.find
+local smatch = string.match
+local sgsub = string.gsub
+local ssub = string.sub
+local schar = string.char
+local supper = string.upper
+local uchar = utf8.char
+local tconcat = table.concat
+local tinsert = table.insert
+local tointeger = math.tointeger
+local mtype = math.type
+local tonumber = tonumber
+local maxinteger = math.maxinteger
+local assert = assert
+local next = next
+
+_ENV = nil
+
+---@alias parser.position integer
+
+---@param str string
+---@return table<integer, boolean>
+local function stringToCharMap(str)
+ local map = {}
+ local pos = 1
+ while pos <= #str do
+ local byte = sbyte(str, pos, pos)
+ map[schar(byte)] = true
+ pos = pos + 1
+ if ssub(str, pos, pos) == '-'
+ and pos < #str then
+ pos = pos + 1
+ local byte2 = sbyte(str, pos, pos)
+ assert(byte < byte2)
+ for b = byte + 1, byte2 do
+ map[schar(b)] = true
+ end
+ pos = pos + 1
+ end
+ end
+ return map
+end
+
+local CharMapNumber = stringToCharMap '0-9'
+local CharMapN16 = stringToCharMap 'xX'
+local CharMapN2 = stringToCharMap 'bB'
+local CharMapE10 = stringToCharMap 'eE'
+local CharMapE16 = stringToCharMap 'pP'
+local CharMapSign = stringToCharMap '+-'
+local CharMapSB = stringToCharMap 'ao|~&=<>.*/%^+-'
+local CharMapSU = stringToCharMap 'n#~!-'
+local CharMapSimple = stringToCharMap '.:([\'"{'
+local CharMapStrSH = stringToCharMap '\'"'
+local CharMapStrLH = stringToCharMap '['
+local CharMapTSep = stringToCharMap ',;'
+local CharMapWord = stringToCharMap '_a-zA-Z\x80-\xff'
+
+local EscMap = {
+ ['a'] = '\a',
+ ['b'] = '\b',
+ ['f'] = '\f',
+ ['n'] = '\n',
+ ['r'] = '\r',
+ ['t'] = '\t',
+ ['v'] = '\v',
+ ['\\'] = '\\',
+ ['\''] = '\'',
+ ['\"'] = '\"',
+}
+
+local NLMap = {
+ ['\n'] = true,
+ ['\r'] = true,
+ ['\r\n'] = true,
+}
+
+local LineMulti = 10000
+
+-- goto 单独处理
+local KeyWord = {
+ ['and'] = true,
+ ['break'] = true,
+ ['do'] = true,
+ ['else'] = true,
+ ['elseif'] = true,
+ ['end'] = true,
+ ['false'] = true,
+ ['for'] = true,
+ ['function'] = true,
+ ['if'] = true,
+ ['in'] = true,
+ ['local'] = true,
+ ['nil'] = true,
+ ['not'] = true,
+ ['or'] = true,
+ ['repeat'] = true,
+ ['return'] = true,
+ ['then'] = true,
+ ['true'] = true,
+ ['until'] = true,
+ ['while'] = true,
+}
+
+local Specials = {
+ ['_G'] = true,
+ ['rawset'] = true,
+ ['rawget'] = true,
+ ['setmetatable'] = true,
+ ['require'] = true,
+ ['dofile'] = true,
+ ['loadfile'] = true,
+ ['pcall'] = true,
+ ['xpcall'] = true,
+ ['pairs'] = true,
+ ['ipairs'] = true,
+}
+
+local UnarySymbol = {
+ ['not'] = 11,
+ ['#'] = 11,
+ ['~'] = 11,
+ ['-'] = 11,
+}
+
+local BinarySymbol = {
+ ['or'] = 1,
+ ['and'] = 2,
+ ['<='] = 3,
+ ['>='] = 3,
+ ['<'] = 3,
+ ['>'] = 3,
+ ['~='] = 3,
+ ['=='] = 3,
+ ['|'] = 4,
+ ['~'] = 5,
+ ['&'] = 6,
+ ['<<'] = 7,
+ ['>>'] = 7,
+ ['..'] = 8,
+ ['+'] = 9,
+ ['-'] = 9,
+ ['*'] = 10,
+ ['//'] = 10,
+ ['/'] = 10,
+ ['%'] = 10,
+ ['^'] = 12,
+}
+
+local BinaryAlias = {
+ ['&&'] = 'and',
+ ['||'] = 'or',
+ ['!='] = '~=',
+}
+
+local BinaryActionAlias = {
+ ['='] = '==',
+}
+
+local UnaryAlias = {
+ ['!'] = 'not',
+}
+
+local SymbolForward = {
+ [01] = true,
+ [02] = true,
+ [03] = true,
+ [04] = true,
+ [05] = true,
+ [06] = true,
+ [07] = true,
+ [08] = false,
+ [09] = true,
+ [10] = true,
+ [11] = true,
+ [12] = false,
+}
+
+local GetToSetMap = {
+ ['getglobal'] = 'setglobal',
+ ['getlocal'] = 'setlocal',
+ ['getfield'] = 'setfield',
+ ['getindex'] = 'setindex',
+ ['getmethod'] = 'setmethod',
+}
+
+local ChunkFinishMap = {
+ ['end'] = true,
+ ['else'] = true,
+ ['elseif'] = true,
+ ['in'] = true,
+ ['then'] = true,
+ ['until'] = true,
+ [';'] = true,
+ [']'] = true,
+ [')'] = true,
+ ['}'] = true,
+}
+
+local ListFinishMap = {
+ ['end'] = true,
+ ['else'] = true,
+ ['elseif'] = true,
+ ['in'] = true,
+ ['then'] = true,
+ ['do'] = true,
+ ['until'] = true,
+ ['for'] = true,
+ ['if'] = true,
+ ['local'] = true,
+ ['repeat'] = true,
+ ['return'] = true,
+ ['while'] = true,
+}
+
+local State, Lua, Line, LineOffset, Chunk, Tokens, Index, LastTokenFinish, Mode, LocalCount
+
+local LocalLimit = 200
+
+local parseExp, parseAction
+
+local pushError
+
+local function addSpecial(name, obj)
+ if not State.specials then
+ State.specials = {}
+ end
+ if not State.specials[name] then
+ State.specials[name] = {}
+ end
+ State.specials[name][#State.specials[name]+1] = obj
+ obj.special = name
+end
+
+---@param offset integer
+---@param leftOrRight '"left"'|'"right"'
+local function getPosition(offset, leftOrRight)
+ if not offset or offset > #Lua then
+ return LineMulti * Line + #Lua - LineOffset + 1
+ end
+ if leftOrRight == 'left' then
+ return LineMulti * Line + offset - LineOffset
+ else
+ return LineMulti * Line + offset - LineOffset + 1
+ end
+end
+
+---@return string word
+---@return parser.position startPosition
+---@return parser.position finishPosition
+---@return integer newOffset
+local function peekWord()
+ local word = Tokens[Index + 1]
+ if not word then
+ return nil
+ end
+ if not CharMapWord[ssub(word, 1, 1)] then
+ return nil
+ end
+ local startPos = getPosition(Tokens[Index] , 'left')
+ local finishPos = getPosition(Tokens[Index] + #word - 1, 'right')
+ return word, startPos, finishPos
+end
+
+local function lastRightPosition()
+ if Index < 2 then
+ return 0
+ end
+ local token = Tokens[Index - 1]
+ if NLMap[token] then
+ return LastTokenFinish
+ elseif token then
+ return getPosition(Tokens[Index - 2] + #token - 1, 'right')
+ else
+ return getPosition(#Lua, 'right')
+ end
+end
+
+local function missSymbol(symbol, start, finish)
+ pushError {
+ type = 'MISS_SYMBOL',
+ start = start or lastRightPosition(),
+ finish = finish or start or lastRightPosition(),
+ info = {
+ symbol = symbol,
+ }
+ }
+end
+
+local function missExp()
+ pushError {
+ type = 'MISS_EXP',
+ start = lastRightPosition(),
+ finish = lastRightPosition(),
+ }
+end
+
+local function missName(pos)
+ pushError {
+ type = 'MISS_NAME',
+ start = pos or lastRightPosition(),
+ finish = pos or lastRightPosition(),
+ }
+end
+
+local function missEnd(relatedStart, relatedFinish)
+ pushError {
+ type = 'MISS_SYMBOL',
+ start = lastRightPosition(),
+ finish = lastRightPosition(),
+ info = {
+ symbol = 'end',
+ related = {
+ {
+ start = relatedStart,
+ finish = relatedFinish,
+ }
+ }
+ }
+ }
+ pushError {
+ type = 'MISS_END',
+ start = relatedStart,
+ finish = relatedFinish,
+ }
+end
+
+local function unknownSymbol(start, finish, word)
+ local token = word or Tokens[Index + 1]
+ if not token then
+ return false
+ end
+ pushError {
+ type = 'UNKNOWN_SYMBOL',
+ start = start or getPosition(Tokens[Index], 'left'),
+ finish = finish or getPosition(Tokens[Index] + #token - 1, 'right'),
+ info = {
+ symbol = token,
+ }
+ }
+ return true
+end
+
+local function skipUnknownSymbol(stopSymbol)
+ if unknownSymbol() then
+ Index = Index + 2
+ return true
+ end
+ return false
+end
+
+local function skipNL()
+ local token = Tokens[Index + 1]
+ if NLMap[token] then
+ if Index >= 2 and not NLMap[Tokens[Index - 1]] then
+ LastTokenFinish = getPosition(Tokens[Index - 2] + #Tokens[Index - 1] - 1, 'right')
+ end
+ Line = Line + 1
+ LineOffset = Tokens[Index] + #token
+ Index = Index + 2
+ State.lines[Line] = LineOffset
+ return true
+ end
+ return false
+end
+
+local function fastForwardToken(offset)
+ while true do
+ local myOffset = Tokens[Index]
+ if not myOffset
+ or myOffset >= offset then
+ break
+ end
+ local token = Tokens[Index + 1]
+ if NLMap[token] then
+ Line = Line + 1
+ LineOffset = Tokens[Index] + #token
+ State.lines[Line] = LineOffset
+ end
+ Index = Index + 2
+ end
+end
+
+local function resolveLongString(finishMark)
+ skipNL()
+ local miss
+ local start = Tokens[Index]
+ local finishOffset = sfind(Lua, finishMark, start, true)
+ if not finishOffset then
+ finishOffset = #Lua + 1
+ miss = true
+ end
+ local stringResult = ssub(Lua, start, finishOffset - 1)
+ local lastLN = stringResult:find '[\r\n][^\r\n]*$'
+ if lastLN then
+ local result = stringResult
+ : gsub('\r\n?', '\n')
+ stringResult = result
+ end
+ fastForwardToken(finishOffset + #finishMark)
+ if miss then
+ local pos = getPosition(finishOffset - 1, 'right')
+ pushError {
+ type = 'MISS_SYMBOL',
+ start = pos,
+ finish = pos,
+ info = {
+ symbol = finishMark,
+ },
+ fix = {
+ title = 'ADD_LSTRING_END',
+ {
+ start = pos,
+ finish = pos,
+ text = finishMark,
+ }
+ },
+ }
+ end
+ return stringResult, getPosition(finishOffset + #finishMark - 1, 'right')
+end
+
+local function parseLongString()
+ local start, finish, mark = sfind(Lua, '^(%[%=*%[)', Tokens[Index])
+ if not mark then
+ return nil
+ end
+ fastForwardToken(finish + 1)
+ local startPos = getPosition(start, 'left')
+ local finishMark = sgsub(mark, '%[', ']')
+ local stringResult, finishPos = resolveLongString(finishMark)
+ return {
+ type = 'string',
+ start = startPos,
+ finish = finishPos,
+ [1] = stringResult,
+ [2] = mark,
+ }
+end
+
+local function pushCommentHeadError(left)
+ if State.options.nonstandardSymbol and State.options.nonstandardSymbol['//'] then
+ return
+ end
+ pushError {
+ type = 'ERR_COMMENT_PREFIX',
+ start = left,
+ finish = left + 2,
+ fix = {
+ title = 'FIX_COMMENT_PREFIX',
+ {
+ start = left,
+ finish = left + 2,
+ text = '--',
+ },
+ }
+ }
+end
+
+local function pushLongCommentError(left, right)
+ if State.options.nonstandardSymbol and State.options.nonstandardSymbol['/**/'] then
+ return
+ end
+ pushError {
+ type = 'ERR_C_LONG_COMMENT',
+ start = left,
+ finish = right,
+ fix = {
+ title = 'FIX_C_LONG_COMMENT',
+ {
+ start = left,
+ finish = left + 2,
+ text = '--[[',
+ },
+ {
+ start = right - 2,
+ finish = right,
+ text = '--]]'
+ },
+ }
+ }
+end
+
+local function skipComment(isAction)
+ local token = Tokens[Index + 1]
+ if token == '--'
+ or (token == '//' and isAction) then
+ local start = Tokens[Index]
+ local left = getPosition(start, 'left')
+ local chead = false
+ if token == '//' then
+ chead = true
+ pushCommentHeadError(left)
+ end
+ Index = Index + 2
+ local longComment = parseLongString()
+ if longComment then
+ longComment.type = 'comment.long'
+ longComment.text = longComment[1]
+ longComment[1] = nil
+ longComment[2] = nil
+ State.comms[#State.comms+1] = longComment
+ return true
+ end
+ while true do
+ local nl = Tokens[Index + 1]
+ if not nl or NLMap[nl] then
+ break
+ end
+ Index = Index + 2
+ end
+ State.comms[#State.comms+1] = {
+ type = chead and 'comment.cshort' or 'comment.short',
+ start = left,
+ finish = getPosition(Tokens[Index], 'right'),
+ text = ssub(Lua, start + 2, Tokens[Index] and (Tokens[Index] - 1) or #Lua),
+ }
+ return true
+ end
+ if token == '/*' then
+ local start = Tokens[Index]
+ local left = getPosition(start, 'left')
+ Index = Index + 2
+ local result, right = resolveLongString '*/'
+ pushLongCommentError(left, right)
+ State.comms[#State.comms+1] = {
+ type = 'comment.long',
+ start = left,
+ finish = right,
+ text = result,
+ }
+ return true
+ end
+ return false
+end
+
+local function skipSpace(isAction)
+ repeat until not skipNL()
+ and not skipComment(isAction)
+end
+
+local function expectAssign()
+ local token = Tokens[Index + 1]
+ if token == '=' then
+ Index = Index + 2
+ return true
+ end
+ if token == '==' then
+ local left = getPosition(Tokens[Index], 'left')
+ local right = getPosition(Tokens[Index] + #token - 1, 'right')
+ pushError {
+ type = 'ERR_ASSIGN_AS_EQ',
+ start = left,
+ finish = right,
+ fix = {
+ title = 'FIX_ASSIGN_AS_EQ',
+ {
+ start = left,
+ finish = right,
+ text = '=',
+ }
+ }
+ }
+ Index = Index + 2
+ return true
+ end
+ return false
+end
+
+local function parseLocalAttrs()
+ local attrs
+ while true do
+ skipSpace()
+ local token = Tokens[Index + 1]
+ if token ~= '<' then
+ break
+ end
+ if not attrs then
+ attrs = {}
+ end
+ local attr = {
+ type = 'localattr',
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index], 'right'),
+ }
+ attrs[#attrs+1] = attr
+ Index = Index + 2
+ skipSpace()
+ local word, wstart, wfinish = peekWord()
+ if word then
+ attr[1] = word
+ attr.finish = wfinish
+ Index = Index + 2
+ else
+ missName()
+ end
+ attr.finish = lastRightPosition()
+ skipSpace()
+ if Tokens[Index + 1] == '>' then
+ attr.finish = getPosition(Tokens[Index], 'right')
+ Index = Index + 2
+ elseif Tokens[Index + 1] == '>=' then
+ attr.finish = getPosition(Tokens[Index], 'right')
+ pushError {
+ type = 'MISS_SPACE_BETWEEN',
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index] + 1, 'right'),
+ }
+ Index = Index + 2
+ else
+ missSymbol '>'
+ end
+ if State.version ~= 'Lua 5.4' then
+ pushError {
+ type = 'UNSUPPORT_SYMBOL',
+ start = attr.start,
+ finish = attr.finish,
+ version = 'Lua 5.4',
+ info = {
+ version = State.version
+ }
+ }
+ end
+ end
+ return attrs
+end
+
+local function createLocal(obj, attrs)
+ if not obj then
+ return nil
+ end
+ obj.type = 'local'
+ obj.effect = obj.finish
+
+ if attrs then
+ obj.attrs = attrs
+ for i = 1, #attrs do
+ local attr = attrs[i]
+ attr.parent = obj
+ end
+ end
+
+ local chunk = Chunk[#Chunk]
+ if chunk then
+ local locals = chunk.locals
+ if not locals then
+ locals = {}
+ chunk.locals = locals
+ end
+ locals[#locals+1] = obj
+ LocalCount = LocalCount + 1
+ if LocalCount > LocalLimit then
+ pushError {
+ type = 'LOCAL_LIMIT',
+ start = obj.start,
+ finish = obj.finish,
+ }
+ end
+ end
+ return obj
+end
+
+local function pushChunk(chunk)
+ Chunk[#Chunk+1] = chunk
+end
+
+local function resolveLable(label, obj)
+ if not label.ref then
+ label.ref = {}
+ end
+ label.ref[#label.ref+1] = obj
+ obj.node = label
+
+ -- 如果有局部变量在 goto 与 label 之间声明,
+ -- 并在 label 之后使用,则算作语法错误
+
+ -- 如果 label 在 goto 之前声明,那么不会有中间声明的局部变量
+ if obj.start > label.start then
+ return
+ end
+
+ local block = guide.getBlock(obj)
+ local locals = block and block.locals
+ if not locals then
+ return
+ end
+
+ for i = 1, #locals do
+ local loc = locals[i]
+ -- 检查局部变量声明位置为 goto 与 label 之间
+ if loc.start < obj.start or loc.finish > label.finish then
+ goto CONTINUE
+ end
+ -- 检查局部变量的使用位置在 label 之后
+ local refs = loc.ref
+ if not refs then
+ goto CONTINUE
+ end
+ for j = 1, #refs do
+ local ref = refs[j]
+ if ref.finish > label.finish then
+ pushError {
+ type = 'JUMP_LOCAL_SCOPE',
+ start = obj.start,
+ finish = obj.finish,
+ info = {
+ loc = loc[1],
+ },
+ relative = {
+ {
+ start = label.start,
+ finish = label.finish,
+ },
+ {
+ start = loc.start,
+ finish = loc.finish,
+ }
+ },
+ }
+ return
+ end
+ end
+ ::CONTINUE::
+ end
+end
+
+local function resolveGoTo(gotos)
+ for i = 1, #gotos do
+ local action = gotos[i]
+ local label = guide.getLabel(action, action[1])
+ if label then
+ resolveLable(label, action)
+ else
+ pushError {
+ type = 'NO_VISIBLE_LABEL',
+ start = action.start,
+ finish = action.finish,
+ info = {
+ label = action[1],
+ }
+ }
+ end
+ end
+end
+
+local function popChunk()
+ local chunk = Chunk[#Chunk]
+ if chunk.gotos then
+ resolveGoTo(chunk.gotos)
+ chunk.gotos = nil
+ end
+ local lastAction = chunk[#chunk]
+ if lastAction then
+ chunk.finish = lastAction.finish
+ end
+ Chunk[#Chunk] = nil
+end
+
+local function parseNil()
+ if Tokens[Index + 1] ~= 'nil' then
+ return nil
+ end
+ local offset = Tokens[Index]
+ Index = Index + 2
+ return {
+ type = 'nil',
+ start = getPosition(offset, 'left'),
+ finish = getPosition(offset + 2, 'right'),
+ }
+end
+
+local function parseBoolean()
+ local word = Tokens[Index+1]
+ if word ~= 'true'
+ and word ~= 'false' then
+ return nil
+ end
+ local start = getPosition(Tokens[Index], 'left')
+ local finish = getPosition(Tokens[Index] + #word - 1, 'right')
+ Index = Index + 2
+ return {
+ type = 'boolean',
+ start = start,
+ finish = finish,
+ [1] = word == 'true' and true or false,
+ }
+end
+
+local function parseStringUnicode()
+ local offset = Tokens[Index] + 1
+ if ssub(Lua, offset, offset) ~= '{' then
+ local pos = getPosition(offset, 'left')
+ missSymbol('{', pos)
+ return nil, offset
+ end
+ local leftPos = getPosition(offset, 'left')
+ local x16 = smatch(Lua, '^%w*', offset + 1)
+ local rightPos = getPosition(offset + #x16, 'right')
+ offset = offset + #x16 + 1
+ if ssub(Lua, offset, offset) == '}' then
+ offset = offset + 1
+ rightPos = rightPos + 1
+ else
+ missSymbol('}', rightPos)
+ end
+ offset = offset + 1
+ if #x16 == 0 then
+ pushError {
+ type = 'UTF8_SMALL',
+ start = leftPos,
+ finish = rightPos,
+ }
+ return '', offset
+ end
+ if State.version ~= 'Lua 5.3'
+ and State.version ~= 'Lua 5.4'
+ and State.version ~= 'LuaJIT'
+ then
+ pushError {
+ type = 'ERR_ESC',
+ start = leftPos - 2,
+ finish = rightPos,
+ version = {'Lua 5.3', 'Lua 5.4', 'LuaJIT'},
+ info = {
+ version = State.version,
+ }
+ }
+ return nil, offset
+ end
+ local byte = tonumber(x16, 16)
+ if not byte then
+ for i = 1, #x16 do
+ if not tonumber(ssub(x16, i, i), 16) then
+ pushError {
+ type = 'MUST_X16',
+ start = leftPos + i,
+ finish = leftPos + i + 1,
+ }
+ end
+ end
+ return nil, offset
+ end
+ if State.version == 'Lua 5.4' then
+ if byte < 0 or byte > 0x7FFFFFFF then
+ pushError {
+ type = 'UTF8_MAX',
+ start = leftPos,
+ finish = rightPos,
+ info = {
+ min = '00000000',
+ max = '7FFFFFFF',
+ }
+ }
+ return nil, offset
+ end
+ else
+ if byte < 0 or byte > 0x10FFFF then
+ pushError {
+ type = 'UTF8_MAX',
+ start = leftPos,
+ finish = rightPos,
+ version = byte <= 0x7FFFFFFF and 'Lua 5.4' or nil,
+ info = {
+ min = '000000',
+ max = '10FFFF',
+ }
+ }
+ end
+ end
+ if byte >= 0 and byte <= 0x10FFFF then
+ return uchar(byte), offset
+ end
+ return '', offset
+end
+
+local stringPool = {}
+local function parseShortString()
+ local mark = Tokens[Index+1]
+ local startOffset = Tokens[Index]
+ local startPos = getPosition(startOffset, 'left')
+ Index = Index + 2
+ -- empty string
+ if Tokens[Index+1] == mark then
+ local finishPos = getPosition(Tokens[Index], 'right')
+ Index = Index + 2
+ return {
+ type = 'string',
+ start = startPos,
+ finish = finishPos,
+ [1] = '',
+ [2] = mark,
+ }
+ end
+ local stringIndex = 0
+ local currentOffset = startOffset + 1
+ while true do
+ local token = Tokens[Index + 1]
+ if token == mark then
+ stringIndex = stringIndex + 1
+ stringPool[stringIndex] = ssub(Lua, currentOffset, Tokens[Index] - 1)
+ Index = Index + 2
+ break
+ end
+ if NLMap[token] then
+ stringIndex = stringIndex + 1
+ stringPool[stringIndex] = ssub(Lua, currentOffset, Tokens[Index] - 1)
+ missSymbol(mark)
+ break
+ end
+ if not token then
+ stringIndex = stringIndex + 1
+ stringPool[stringIndex] = ssub(Lua, currentOffset)
+ missSymbol(mark)
+ break
+ end
+ if token == '\\' then
+ stringIndex = stringIndex + 1
+ stringPool[stringIndex] = ssub(Lua, currentOffset, Tokens[Index] - 1)
+ currentOffset = Tokens[Index]
+ Index = Index + 2
+ -- has space?
+ if Tokens[Index] - currentOffset > 1 then
+ pushError {
+ type = 'ERR_ESC',
+ start = getPosition(currentOffset, 'left'),
+ finish = getPosition(currentOffset + 1, 'right'),
+ }
+ goto CONTINUE
+ end
+ local nextToken = ssub(Tokens[Index + 1], 1, 1)
+ if EscMap[nextToken] then
+ stringIndex = stringIndex + 1
+ stringPool[stringIndex] = EscMap[nextToken]
+ currentOffset = Tokens[Index] + #nextToken
+ Index = Index + 2
+ goto CONTINUE
+ end
+ if nextToken == mark then
+ stringIndex = stringIndex + 1
+ stringPool[stringIndex] = mark
+ currentOffset = Tokens[Index] + #nextToken
+ Index = Index + 2
+ goto CONTINUE
+ end
+ if nextToken == 'z' then
+ Index = Index + 2
+ repeat until not skipNL()
+ currentOffset = Tokens[Index]
+ goto CONTINUE
+ end
+ if CharMapNumber[nextToken] then
+ local numbers = smatch(Tokens[Index + 1], '^%d+')
+ if #numbers > 3 then
+ numbers = ssub(numbers, 1, 3)
+ end
+ currentOffset = Tokens[Index] + #numbers
+ fastForwardToken(currentOffset)
+ local byte = tointeger(numbers)
+ if byte <= 255 then
+ stringIndex = stringIndex + 1
+ stringPool[stringIndex] = schar(byte)
+ else
+ -- TODO pushError
+ end
+ goto CONTINUE
+ end
+ if nextToken == 'x' then
+ local left = getPosition(Tokens[Index] - 1, 'left')
+ local x16 = ssub(Tokens[Index + 1], 2, 3)
+ local byte = tonumber(x16, 16)
+ if byte then
+ currentOffset = Tokens[Index] + 3
+ stringIndex = stringIndex + 1
+ stringPool[stringIndex] = schar(byte)
+ else
+ currentOffset = Tokens[Index] + 1
+ pushError {
+ type = 'MISS_ESC_X',
+ start = getPosition(currentOffset, 'left'),
+ finish = getPosition(currentOffset + 1, 'right'),
+ }
+ end
+ if State.version == 'Lua 5.1' then
+ pushError {
+ type = 'ERR_ESC',
+ start = left,
+ finish = left + 4,
+ version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'},
+ info = {
+ version = State.version,
+ }
+ }
+ end
+ Index = Index + 2
+ goto CONTINUE
+ end
+ if nextToken == 'u' then
+ local str, newOffset = parseStringUnicode()
+ if str then
+ stringIndex = stringIndex + 1
+ stringPool[stringIndex] = str
+ end
+ currentOffset = newOffset
+ fastForwardToken(currentOffset - 1)
+ goto CONTINUE
+ end
+ if NLMap[nextToken] then
+ stringIndex = stringIndex + 1
+ stringPool[stringIndex] = '\n'
+ currentOffset = Tokens[Index] + #nextToken
+ skipNL()
+ goto CONTINUE
+ end
+ pushError {
+ type = 'ERR_ESC',
+ start = getPosition(currentOffset, 'left'),
+ finish = getPosition(currentOffset + 1, 'right'),
+ }
+ end
+ Index = Index + 2
+ ::CONTINUE::
+ end
+ local stringResult = tconcat(stringPool, '', 1, stringIndex)
+ return {
+ type = 'string',
+ start = startPos,
+ finish = lastRightPosition(),
+ [1] = stringResult,
+ [2] = mark,
+ }
+end
+
+local function parseString()
+ local c = Tokens[Index + 1]
+ if CharMapStrSH[c] then
+ return parseShortString()
+ end
+ if CharMapStrLH[c] then
+ return parseLongString()
+ end
+ return nil
+end
+
+local function parseNumber10(start)
+ local integer = true
+ local integerPart = smatch(Lua, '^%d*', start)
+ local offset = start + #integerPart
+ -- float part
+ if ssub(Lua, offset, offset) == '.' then
+ local floatPart = smatch(Lua, '^%d*', offset + 1)
+ integer = false
+ offset = offset + #floatPart + 1
+ end
+ -- exp part
+ local echar = ssub(Lua, offset, offset)
+ if CharMapE10[echar] then
+ integer = false
+ offset = offset + 1
+ local nextChar = ssub(Lua, offset, offset)
+ if CharMapSign[nextChar] then
+ offset = offset + 1
+ end
+ local exp = smatch(Lua, '^%d*', offset)
+ offset = offset + #exp
+ if #exp == 0 then
+ pushError {
+ type = 'MISS_EXPONENT',
+ start = getPosition(offset - 1, 'right'),
+ finish = getPosition(offset - 1, 'right'),
+ }
+ end
+ end
+ return tonumber(ssub(Lua, start, offset - 1)), offset, integer
+end
+
+local function parseNumber16(start)
+ local integerPart = smatch(Lua, '^[%da-fA-F]*', start)
+ local offset = start + #integerPart
+ local integer = true
+ -- float part
+ if ssub(Lua, offset, offset) == '.' then
+ local floatPart = smatch(Lua, '^[%da-fA-F]*', offset + 1)
+ integer = false
+ offset = offset + #floatPart + 1
+ if #integerPart == 0 and #floatPart == 0 then
+ pushError {
+ type = 'MUST_X16',
+ start = getPosition(offset - 1, 'right'),
+ finish = getPosition(offset - 1, 'right'),
+ }
+ end
+ else
+ if #integerPart == 0 then
+ pushError {
+ type = 'MUST_X16',
+ start = getPosition(offset - 1, 'right'),
+ finish = getPosition(offset - 1, 'right'),
+ }
+ return 0, offset
+ end
+ end
+ -- exp part
+ local echar = ssub(Lua, offset, offset)
+ if CharMapE16[echar] then
+ integer = false
+ offset = offset + 1
+ local nextChar = ssub(Lua, offset, offset)
+ if CharMapSign[nextChar] then
+ offset = offset + 1
+ end
+ local exp = smatch(Lua, '^%d*', offset)
+ offset = offset + #exp
+ end
+ local n = tonumber(ssub(Lua, start - 2, offset - 1))
+ return n, offset, integer
+end
+
+local function parseNumber2(start)
+ local bins = smatch(Lua, '^[01]*', start)
+ local offset = start + #bins
+ if State.version ~= 'LuaJIT' then
+ pushError {
+ type = 'UNSUPPORT_SYMBOL',
+ start = getPosition(start - 2, 'left'),
+ finish = getPosition(offset - 1, 'right'),
+ version = 'LuaJIT',
+ info = {
+ version = 'Lua 5.4',
+ }
+ }
+ end
+ return tonumber(bins, 2), offset
+end
+
+local function dropNumberTail(offset, integer)
+ local _, finish, word = sfind(Lua, '^([%.%w_\x80-\xff]+)', offset)
+ if not finish then
+ return offset
+ end
+ if integer then
+ if supper(ssub(word, 1, 2)) == 'LL' then
+ if State.version ~= 'LuaJIT' then
+ pushError {
+ type = 'UNSUPPORT_SYMBOL',
+ start = getPosition(offset, 'left'),
+ finish = getPosition(offset + 1, 'right'),
+ version = 'LuaJIT',
+ info = {
+ version = State.version,
+ }
+ }
+ end
+ offset = offset + 2
+ word = ssub(word, offset)
+ elseif supper(ssub(word, 1, 3)) == 'ULL' then
+ if State.version ~= 'LuaJIT' then
+ pushError {
+ type = 'UNSUPPORT_SYMBOL',
+ start = getPosition(offset, 'left'),
+ finish = getPosition(offset + 2, 'right'),
+ version = 'LuaJIT',
+ info = {
+ version = State.version,
+ }
+ }
+ end
+ offset = offset + 3
+ word = ssub(word, offset)
+ end
+ end
+ if supper(ssub(word, 1, 1)) == 'I' then
+ if State.version ~= 'LuaJIT' then
+ pushError {
+ type = 'UNSUPPORT_SYMBOL',
+ start = getPosition(offset, 'left'),
+ finish = getPosition(offset, 'right'),
+ version = 'LuaJIT',
+ info = {
+ version = State.version,
+ }
+ }
+ end
+ offset = offset + 1
+ word = ssub(word, offset)
+ end
+ if #word > 0 then
+ pushError {
+ type = 'MALFORMED_NUMBER',
+ start = getPosition(offset, 'left'),
+ finish = getPosition(finish, 'right'),
+ }
+ end
+ return finish + 1
+end
+
+local function parseNumber()
+ local offset = Tokens[Index]
+ if not offset then
+ return nil
+ end
+ local startPos = getPosition(offset, 'left')
+ local neg
+ if ssub(Lua, offset, offset) == '-' then
+ neg = true
+ offset = offset + 1
+ end
+ local number, integer
+ local firstChar = ssub(Lua, offset, offset)
+ if firstChar == '.' then
+ number, offset = parseNumber10(offset)
+ integer = false
+ elseif firstChar == '0' then
+ local nextChar = ssub(Lua, offset + 1, offset + 1)
+ if CharMapN16[nextChar] then
+ number, offset, integer = parseNumber16(offset + 2)
+ elseif CharMapN2[nextChar] then
+ number, offset = parseNumber2(offset + 2)
+ integer = true
+ else
+ number, offset, integer = parseNumber10(offset)
+ end
+ elseif CharMapNumber[firstChar] then
+ number, offset, integer = parseNumber10(offset)
+ else
+ return nil
+ end
+ if not number then
+ number = 0
+ end
+ if neg then
+ number = - number
+ end
+ local result = {
+ type = integer and 'integer' or 'number',
+ start = startPos,
+ finish = getPosition(offset - 1, 'right'),
+ [1] = number,
+ }
+ offset = dropNumberTail(offset, integer)
+ fastForwardToken(offset)
+ return result
+end
+
+local function isKeyWord(word)
+ if KeyWord[word] then
+ return true
+ end
+ if word == 'goto' then
+ return State.version ~= 'Lua 5.1'
+ end
+ return false
+end
+
+local function parseName()
+ local word = peekWord()
+ if not word then
+ return nil
+ end
+ if ChunkFinishMap[word] then
+ return nil
+ end
+ local startPos = getPosition(Tokens[Index], 'left')
+ local finishPos = getPosition(Tokens[Index] + #word - 1, 'right')
+ Index = Index + 2
+ if not State.options.unicodeName and word:find '[\x80-\xff]' then
+ pushError {
+ type = 'UNICODE_NAME',
+ start = startPos,
+ finish = finishPos,
+ }
+ end
+ if isKeyWord(word) then
+ pushError {
+ type = 'KEYWORD',
+ start = startPos,
+ finish = finishPos,
+ }
+ end
+ return {
+ type = 'name',
+ start = startPos,
+ finish = finishPos,
+ [1] = word,
+ }
+end
+
+local function parseNameOrList()
+ local first = parseName()
+ if not first then
+ return nil
+ end
+ skipSpace()
+ local list
+ while true do
+ if Tokens[Index + 1] ~= ',' then
+ break
+ end
+ Index = Index + 2
+ skipSpace()
+ local name = parseName()
+ if not name then
+ missName()
+ break
+ end
+ if not list then
+ list = {
+ type = 'list',
+ start = first.start,
+ finish = first.finish,
+ [1] = first
+ }
+ end
+ list[#list+1] = name
+ list.finish = name.finish
+ end
+ return list or first
+end
+
+local function dropTail()
+ local token = Tokens[Index + 1]
+ if token ~= '?'
+ and token ~= ':' then
+ return
+ end
+ local pl, pt, pp = 0, 0, 0
+ while true do
+ local token = Tokens[Index + 1]
+ if not token then
+ break
+ end
+ if NLMap[token] then
+ break
+ end
+ if token == ',' then
+ if pl > 0
+ or pt > 0
+ or pp > 0 then
+ goto CONTINUE
+ else
+ break
+ end
+ end
+ if token == '<' then
+ pl = pl + 1
+ goto CONTINUE
+ end
+ if token == '{' then
+ pt = pt + 1
+ goto CONTINUE
+ end
+ if token == '(' then
+ pp = pp + 1
+ goto CONTINUE
+ end
+ if token == '>' then
+ if pl <= 0 then
+ break
+ end
+ pl = pl - 1
+ goto CONTINUE
+ end
+ if token == '}' then
+ if pt <= 0 then
+ break
+ end
+ pt = pt - 1
+ goto CONTINUE
+ end
+ if token == ')' then
+ if pp <= 0 then
+ break
+ end
+ pp = pp - 1
+ goto CONTINUE
+ end
+ ::CONTINUE::
+ Index = Index + 2
+ end
+end
+
+local function parseExpList(mini)
+ local list
+ local wantSep = false
+ while true do
+ skipSpace()
+ local token = Tokens[Index + 1]
+ if not token then
+ break
+ end
+ if ListFinishMap[token] then
+ break
+ end
+ if token == ',' then
+ local sepPos = getPosition(Tokens[Index], 'right')
+ if not wantSep then
+ pushError {
+ type = 'UNEXPECT_SYMBOL',
+ start = getPosition(Tokens[Index], 'left'),
+ finish = sepPos,
+ info = {
+ symbol = ',',
+ }
+ }
+ end
+ wantSep = false
+ Index = Index + 2
+ goto CONTINUE
+ else
+ if mini then
+ if wantSep then
+ break
+ end
+ local nextToken = peekWord()
+ if isKeyWord(nextToken)
+ and nextToken ~= 'function'
+ and nextToken ~= 'true'
+ and nextToken ~= 'false'
+ and nextToken ~= 'nil'
+ and nextToken ~= 'not' then
+ break
+ end
+ end
+ local exp = parseExp()
+ if not exp then
+ break
+ end
+ dropTail()
+ if wantSep then
+ missSymbol(',', list[#list].finish, exp.start)
+ end
+ wantSep = true
+ if not list then
+ list = {
+ type = 'list',
+ start = exp.start,
+ }
+ end
+ list[#list+1] = exp
+ list.finish = exp.finish
+ exp.parent = list
+ end
+ ::CONTINUE::
+ end
+ if not list then
+ return nil
+ end
+ if not wantSep then
+ missExp()
+ end
+ return list
+end
+
+local function parseIndex()
+ local start = getPosition(Tokens[Index], 'left')
+ Index = Index + 2
+ skipSpace()
+ local exp = parseExp()
+ local index = {
+ type = 'index',
+ start = start,
+ finish = exp and exp.finish or (start + 1),
+ index = exp
+ }
+ if exp then
+ exp.parent = index
+ else
+ missExp()
+ end
+ skipSpace()
+ if Tokens[Index + 1] == ']' then
+ index.finish = getPosition(Tokens[Index], 'right')
+ Index = Index + 2
+ else
+ missSymbol ']'
+ end
+ return index
+end
+
+local function parseTable()
+ local tbl = {
+ type = 'table',
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index], 'right'),
+ }
+ Index = Index + 2
+ local index = 0
+ local wantSep = false
+ while true do
+ skipSpace()
+ local token = Tokens[Index + 1]
+ if token == '}' then
+ Index = Index + 2
+ break
+ end
+ if CharMapTSep[token] then
+ if not wantSep then
+ missExp()
+ end
+ wantSep = false
+ Index = Index + 2
+ goto CONTINUE
+ end
+ local lastRight = lastRightPosition()
+
+ local exp = parseExp(true)
+ if exp then
+ if wantSep then
+ pushError {
+ type = 'MISS_SEP_IN_TABLE',
+ start = lastRight,
+ finish = exp.start,
+ }
+ end
+ wantSep = true
+ index = index + 1
+ if exp.type == 'varargs' then
+ tbl[index] = exp
+ exp.parent = tbl
+ goto CONTINUE
+ end
+ if exp.type == 'getlocal'
+ or exp.type == 'getglobal' then
+ skipSpace()
+ if expectAssign() then
+ local eqRight = lastRightPosition()
+ skipSpace()
+ local fvalue = parseExp()
+ local tfield = {
+ type = 'tablefield',
+ start = exp.start,
+ finish = fvalue and fvalue.finish or eqRight,
+ parent = tbl,
+ field = exp,
+ value = fvalue,
+ }
+ exp.type = 'field'
+ exp.parent = tfield
+ if fvalue then
+ fvalue.parent = tfield
+ else
+ missExp()
+ end
+ tbl[index] = tfield
+ goto CONTINUE
+ end
+ end
+ local texp = {
+ type = 'tableexp',
+ start = exp.start,
+ finish = exp.finish,
+ tindex = index,
+ parent = tbl,
+ value = exp,
+ }
+ exp.parent = texp
+ tbl[index] = texp
+ goto CONTINUE
+ end
+
+ if token == '[' then
+ if wantSep then
+ pushError {
+ type = 'MISS_SEP_IN_TABLE',
+ start = lastRight,
+ finish = getPosition(Tokens[Index], 'left'),
+ }
+ end
+ wantSep = true
+ index = index + 1
+ local tindex = parseIndex()
+ skipSpace()
+ if expectAssign() then
+ skipSpace()
+ local ivalue = parseExp()
+ tindex.type = 'tableindex'
+ tindex.parent = tbl
+ if ivalue then
+ ivalue.parent = tindex
+ tindex.finish = ivalue.finish
+ tindex.value = ivalue
+ else
+ missExp()
+ end
+ tbl[index] = tindex
+ else
+ missSymbol '='
+ end
+ goto CONTINUE
+ end
+
+ missSymbol '}'
+ break
+ ::CONTINUE::
+ end
+ tbl.finish = getPosition(Tokens[Index - 2], 'right')
+ return tbl
+end
+
+local function parseSimple(node, funcName)
+ local lastMethod
+ while true do
+ if lastMethod and node.node == lastMethod then
+ if node.type ~= 'call' then
+ missSymbol('(', node.node.finish, node.node.finish)
+ end
+ lastMethod = nil
+ end
+ skipSpace()
+ local token = Tokens[Index + 1]
+ if token == '.' then
+ local dot = {
+ type = token,
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index], 'right'),
+ }
+ Index = Index + 2
+ skipSpace()
+ local field = parseName()
+ local getfield = {
+ type = 'getfield',
+ start = node.start,
+ finish = lastRightPosition(),
+ node = node,
+ dot = dot,
+ field = field
+ }
+ if field then
+ field.parent = getfield
+ field.type = 'field'
+ else
+ pushError {
+ type = 'MISS_FIELD',
+ start = lastRightPosition(),
+ finish = lastRightPosition(),
+ }
+ end
+ node.parent = getfield
+ node.next = getfield
+ node = getfield
+ elseif token == ':' then
+ local colon = {
+ type = token,
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index], 'right'),
+ }
+ Index = Index + 2
+ skipSpace()
+ local method = parseName()
+ local getmethod = {
+ type = 'getmethod',
+ start = node.start,
+ finish = lastRightPosition(),
+ node = node,
+ colon = colon,
+ method = method
+ }
+ if method then
+ method.parent = getmethod
+ method.type = 'method'
+ else
+ pushError {
+ type = 'MISS_METHOD',
+ start = lastRightPosition(),
+ finish = lastRightPosition(),
+ }
+ end
+ node.parent = getmethod
+ node.next = getmethod
+ node = getmethod
+ if lastMethod then
+ missSymbol('(', node.node.finish, node.node.finish)
+ end
+ lastMethod = getmethod
+ elseif token == '(' then
+ if funcName then
+ break
+ end
+ local startPos = getPosition(Tokens[Index], 'left')
+ local call = {
+ type = 'call',
+ start = node.start,
+ node = node,
+ }
+ Index = Index + 2
+ local args = parseExpList()
+ if Tokens[Index + 1] == ')' then
+ call.finish = getPosition(Tokens[Index], 'right')
+ Index = Index + 2
+ else
+ call.finish = lastRightPosition()
+ missSymbol ')'
+ end
+ if args then
+ args.type = 'callargs'
+ args.start = startPos
+ args.finish = call.finish
+ args.parent = call
+ call.args = args
+ end
+ if node.type == 'getmethod' then
+ -- dummy param `self`
+ if not call.args then
+ call.args = {
+ type = 'callargs',
+ start = call.start,
+ finish = call.finish,
+ parent = call,
+ }
+ end
+ local newNode = {}
+ for k, v in next, call.node.node do
+ newNode[k] = v
+ end
+ newNode.mirror = call.node.node
+ newNode.dummy = true
+ newNode.parent = call.args
+ call.node.node.mirror = newNode
+ tinsert(call.args, 1, newNode)
+ end
+ node.parent = call
+ node = call
+ elseif token == '{' then
+ if funcName then
+ break
+ end
+ local tbl = parseTable()
+ local call = {
+ type = 'call',
+ start = node.start,
+ finish = tbl.finish,
+ node = node,
+ }
+ local args = {
+ type = 'callargs',
+ start = tbl.start,
+ finish = tbl.finish,
+ parent = call,
+ [1] = tbl,
+ }
+ call.args = args
+ tbl.parent = args
+ node.parent = call
+ node = call
+ elseif CharMapStrSH[token] then
+ if funcName then
+ break
+ end
+ local str = parseShortString()
+ local call = {
+ type = 'call',
+ start = node.start,
+ finish = str.finish,
+ node = node,
+ }
+ local args = {
+ type = 'callargs',
+ start = str.start,
+ finish = str.finish,
+ parent = call,
+ [1] = str,
+ }
+ call.args = args
+ str.parent = args
+ node.parent = call
+ node = call
+ elseif CharMapStrLH[token] then
+ local str = parseLongString()
+ if str then
+ if funcName then
+ break
+ end
+ local call = {
+ type = 'call',
+ start = node.start,
+ finish = str.finish,
+ node = node,
+ }
+ local args = {
+ type = 'callargs',
+ start = str.start,
+ finish = str.finish,
+ parent = call,
+ [1] = str,
+ }
+ call.args = args
+ str.parent = args
+ node.parent = call
+ node = call
+ else
+ local index = parseIndex()
+ local bstart = index.start
+ index.type = 'getindex'
+ index.start = node.start
+ index.node = node
+ node.next = index
+ node.parent = index
+ node = index
+ if funcName then
+ pushError {
+ type = 'INDEX_IN_FUNC_NAME',
+ start = bstart,
+ finish = index.finish,
+ }
+ end
+ end
+ else
+ break
+ end
+ end
+ if node.type == 'call'
+ and node.node == lastMethod then
+ lastMethod = nil
+ end
+ if node == lastMethod then
+ if funcName then
+ lastMethod = nil
+ end
+ end
+ if lastMethod then
+ missSymbol('(', lastMethod.finish)
+ end
+ return node
+end
+
+local function parseVarargs()
+ local varargs = {
+ type = 'varargs',
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index] + 2, 'right'),
+ }
+ Index = Index + 2
+ for i = #Chunk, 1, -1 do
+ local chunk = Chunk[i]
+ if chunk.vararg then
+ if not chunk.vararg.ref then
+ chunk.vararg.ref = {}
+ end
+ chunk.vararg.ref[#chunk.vararg.ref+1] = varargs
+ varargs.node = chunk.vararg
+ break
+ end
+ if chunk.type == 'function' then
+ break
+ end
+ end
+ if not varargs.node
+ and Mode == 'Lua'
+ and Chunk[#Chunk].type ~= 'main' then
+ pushError {
+ type = 'UNEXPECT_DOTS',
+ start = varargs.start,
+ finish = varargs.finish,
+ }
+ end
+ return varargs
+end
+
+local function parseParen()
+ local pl = Tokens[Index]
+ local paren = {
+ type = 'paren',
+ start = getPosition(pl, 'left'),
+ finish = getPosition(pl, 'right')
+ }
+ Index = Index + 2
+ skipSpace()
+ local exp = parseExp()
+ if exp then
+ paren.exp = exp
+ paren.finish = exp.finish
+ exp.parent = paren
+ else
+ missExp()
+ end
+ skipSpace()
+ if Tokens[Index + 1] == ')' then
+ paren.finish = getPosition(Tokens[Index], 'right')
+ Index = Index + 2
+ else
+ missSymbol ')'
+ end
+ return paren
+end
+
+local function getLocal(name, pos)
+ for i = #Chunk, 1, -1 do
+ local chunk = Chunk[i]
+ local locals = chunk.locals
+ if locals then
+ local res
+ for n = 1, #locals do
+ local loc = locals[n]
+ if loc.effect > pos then
+ break
+ end
+ if loc[1] == name then
+ if not res or res.effect < loc.effect then
+ res = loc
+ end
+ end
+ end
+ if res then
+ return res
+ end
+ end
+ end
+end
+
+local function resolveName(node)
+ if not node then
+ return nil
+ end
+ local loc = getLocal(node[1], node.start)
+ if loc then
+ node.type = 'getlocal'
+ node.node = loc
+ if not loc.ref then
+ loc.ref = {}
+ end
+ loc.ref[#loc.ref+1] = node
+ if loc.special then
+ addSpecial(loc.special, node)
+ end
+ else
+ node.type = 'getglobal'
+ local env = getLocal(State.ENVMode, node.start)
+ if env then
+ node.node = env
+ if not env.ref then
+ env.ref = {}
+ end
+ env.ref[#env.ref+1] = node
+ end
+ end
+ local name = node[1]
+ if Specials[name] then
+ addSpecial(name, node)
+ else
+ local ospeicals = State.options.special
+ if ospeicals and ospeicals[name] then
+ addSpecial(name, node)
+ end
+ end
+ return node
+end
+
+local function isChunkFinishToken(token)
+ local currentChunk = Chunk[#Chunk]
+ if not currentChunk then
+ return false
+ end
+ local tp = currentChunk.type
+ if tp == 'main' then
+ return false
+ end
+ if tp == 'for'
+ or tp == 'in'
+ or tp == 'loop' then
+ return token == 'end'
+ end
+ if tp == 'if'
+ or tp == 'ifblock'
+ or tp == 'elseifblock'
+ or tp == 'elseblock' then
+ return token == 'then'
+ or token == 'end'
+ or token == 'else'
+ or token == 'elseif'
+ end
+ return true
+end
+
+local function parseActions()
+ local rtn, last
+ while true do
+ skipSpace(true)
+ local token = Tokens[Index + 1]
+ if token == ';' then
+ Index = Index + 2
+ goto CONTINUE
+ end
+ if ChunkFinishMap[token]
+ and isChunkFinishToken(token) then
+ break
+ end
+ local action, failed = parseAction()
+ if failed then
+ break
+ end
+ if action then
+ if action.type == 'return' then
+ rtn = action
+ end
+ last = action
+ end
+ ::CONTINUE::
+ end
+ if rtn and rtn ~= last then
+ pushError {
+ type = 'ACTION_AFTER_RETURN',
+ start = rtn.start,
+ finish = rtn.finish,
+ }
+ end
+end
+
+local function parseParams(params)
+ local lastSep
+ local hasDots
+ while true do
+ skipSpace()
+ local token = Tokens[Index + 1]
+ if not token or token == ')' then
+ if lastSep then
+ missName()
+ end
+ break
+ end
+ if token == ',' then
+ if lastSep or lastSep == nil then
+ missName()
+ else
+ lastSep = true
+ end
+ Index = Index + 2
+ goto CONTINUE
+ end
+ if token == '...' then
+ if lastSep == false then
+ missSymbol ','
+ end
+ lastSep = false
+ if not params then
+ params = {}
+ end
+ local vararg = {
+ type = '...',
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index] + 2, 'right'),
+ parent = params,
+ }
+ local chunk = Chunk[#Chunk]
+ chunk.vararg = vararg
+ params[#params+1] = vararg
+ if hasDots then
+ pushError {
+ type = 'ARGS_AFTER_DOTS',
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index] + 2, 'right'),
+ }
+ end
+ hasDots = true
+ Index = Index + 2
+ goto CONTINUE
+ end
+ if CharMapWord[ssub(token, 1, 1)] then
+ if lastSep == false then
+ missSymbol ','
+ end
+ lastSep = false
+ if not params then
+ params = {}
+ end
+ params[#params+1] = createLocal {
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index] + #token - 1, 'right'),
+ parent = params,
+ [1] = token,
+ }
+ if hasDots then
+ pushError {
+ type = 'ARGS_AFTER_DOTS',
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index] + #token - 1, 'right'),
+ }
+ end
+ Index = Index + 2
+ goto CONTINUE
+ end
+ skipUnknownSymbol '%,%)%.'
+ ::CONTINUE::
+ end
+ return params
+end
+
+local function parseFunction(isLocal, isAction)
+ local funcLeft = getPosition(Tokens[Index], 'left')
+ local funcRight = getPosition(Tokens[Index] + 7, 'right')
+ local func = {
+ type = 'function',
+ start = funcLeft,
+ finish = funcRight,
+ keyword = {
+ [1] = funcLeft,
+ [2] = funcRight,
+ },
+ }
+ Index = Index + 2
+ local LastLocalCount = LocalCount
+ LocalCount = 0
+ skipSpace()
+ local hasLeftParen = Tokens[Index + 1] == '('
+ if not hasLeftParen then
+ local name = parseName()
+ if name then
+ local simple = parseSimple(name, true)
+ if isLocal then
+ if simple == name then
+ createLocal(name)
+ else
+ resolveName(name)
+ pushError {
+ type = 'UNEXPECT_LFUNC_NAME',
+ start = simple.start,
+ finish = simple.finish,
+ }
+ end
+ else
+ resolveName(name)
+ end
+ func.name = simple
+ func.finish = simple.finish
+ if not isAction then
+ pushError {
+ type = 'UNEXPECT_EFUNC_NAME',
+ start = simple.start,
+ finish = simple.finish,
+ }
+ end
+ skipSpace()
+ hasLeftParen = Tokens[Index + 1] == '('
+ end
+ end
+ pushChunk(func)
+ local params
+ if func.name and func.name.type == 'getmethod' then
+ if func.name.type == 'getmethod' then
+ params = {}
+ params[1] = createLocal {
+ start = funcRight,
+ finish = funcRight,
+ method = func.name,
+ parent = params,
+ tag = 'self',
+ dummy = true,
+ [1] = 'self',
+ }
+ end
+ end
+ if hasLeftParen then
+ local parenLeft = getPosition(Tokens[Index], 'left')
+ Index = Index + 2
+ params = parseParams(params)
+ if params then
+ params.type = 'funcargs'
+ params.start = parenLeft
+ params.finish = lastRightPosition()
+ params.parent = func
+ func.args = params
+ end
+ skipSpace()
+ if Tokens[Index + 1] == ')' then
+ local parenRight = getPosition(Tokens[Index], 'right')
+ func.finish = parenRight
+ if params then
+ params.finish = parenRight
+ end
+ Index = Index + 2
+ skipSpace()
+ else
+ func.finish = lastRightPosition()
+ if params then
+ params.finish = func.finish
+ end
+ missSymbol ')'
+ end
+ else
+ missSymbol '('
+ end
+ parseActions()
+ popChunk()
+ if Tokens[Index + 1] == 'end' then
+ local endLeft = getPosition(Tokens[Index], 'left')
+ local endRight = getPosition(Tokens[Index] + 2, 'right')
+ func.keyword[3] = endLeft
+ func.keyword[4] = endRight
+ func.finish = endRight
+ Index = Index + 2
+ else
+ missEnd(funcLeft, funcRight)
+ end
+ LocalCount = LastLocalCount
+ return func
+end
+
+local function parseExpUnit()
+ local token = Tokens[Index + 1]
+ if token == '(' then
+ local paren = parseParen()
+ return parseSimple(paren, false)
+ end
+
+ if token == '...' then
+ local varargs = parseVarargs()
+ return varargs
+ end
+
+ if token == '{' then
+ local table = parseTable()
+ return table
+ end
+
+ if CharMapStrSH[token] then
+ local string = parseShortString()
+ return string
+ end
+
+ if CharMapStrLH[token] then
+ local string = parseLongString()
+ return string
+ end
+
+ local number = parseNumber()
+ if number then
+ return number
+ end
+
+ if ChunkFinishMap[token] then
+ return nil
+ end
+
+ if token == 'nil' then
+ return parseNil()
+ end
+
+ if token == 'true'
+ or token == 'false' then
+ return parseBoolean()
+ end
+
+ if token == 'function' then
+ return parseFunction()
+ end
+
+ local node = parseName()
+ if node then
+ return parseSimple(resolveName(node), false)
+ end
+
+ return nil
+end
+
+local function parseUnaryOP()
+ local token = Tokens[Index + 1]
+ local symbol = UnarySymbol[token] and token or UnaryAlias[token]
+ if not symbol then
+ return nil
+ end
+ local myLevel = UnarySymbol[symbol]
+ local op = {
+ type = symbol,
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index] + #symbol - 1, 'right'),
+ }
+ Index = Index + 2
+ return op, myLevel
+end
+
+---@param level integer # op level must greater than this level
+local function parseBinaryOP(asAction, level)
+ local token = Tokens[Index + 1]
+ local symbol = (BinarySymbol[token] and token)
+ or BinaryAlias[token]
+ or (not asAction and BinaryActionAlias[token])
+ if not symbol then
+ return nil
+ end
+ local myLevel = BinarySymbol[symbol]
+ if level and myLevel < level then
+ return nil
+ end
+ local op = {
+ type = symbol,
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index] + #token - 1, 'right'),
+ }
+ if not asAction then
+ if token == '=' then
+ pushError {
+ type = 'ERR_EQ_AS_ASSIGN',
+ start = op.start,
+ finish = op.finish,
+ fix = {
+ title = 'FIX_EQ_AS_ASSIGN',
+ {
+ start = op.start,
+ finish = op.finish,
+ text = '==',
+ }
+ }
+ }
+ end
+ end
+ if BinaryAlias[token] then
+ if State.options.nonstandardSymbol and State.options.nonstandardSymbol[token] then
+ else
+ pushError {
+ type = 'ERR_NONSTANDARD_SYMBOL',
+ start = op.start,
+ finish = op.finish,
+ info = {
+ symbol = symbol,
+ },
+ fix = {
+ title = 'FIX_NONSTANDARD_SYMBOL',
+ symbol = symbol,
+ {
+ start = op.start,
+ finish = op.finish,
+ text = symbol,
+ },
+ }
+ }
+ end
+ end
+ if token == '//'
+ or token == '<<'
+ or token == '>>' then
+ if State.version ~= 'Lua 5.3'
+ and State.version ~= 'Lua 5.4' then
+ pushError {
+ type = 'UNSUPPORT_SYMBOL',
+ version = {'Lua 5.3', 'Lua 5.4'},
+ start = op.start,
+ finish = op.finish,
+ info = {
+ version = State.version,
+ }
+ }
+ end
+ end
+ Index = Index + 2
+ return op, myLevel
+end
+
+function parseExp(asAction, level)
+ local exp
+ local uop, uopLevel = parseUnaryOP()
+ if uop then
+ skipSpace()
+ local child = parseExp(asAction, uopLevel)
+ -- 预计算负数
+ if uop.type == '-'
+ and child
+ and (child.type == 'number' or child.type == 'integer') then
+ child.start = uop.start
+ child[1] = - child[1]
+ exp = child
+ else
+ exp = {
+ type = 'unary',
+ op = uop,
+ start = uop.start,
+ finish = child and child.finish or uop.finish,
+ [1] = child,
+ }
+ if child then
+ child.parent = exp
+ else
+ missExp()
+ end
+ end
+ else
+ exp = parseExpUnit()
+ if not exp then
+ return nil
+ end
+ end
+
+ while true do
+ skipSpace()
+ local bop, bopLevel = parseBinaryOP(asAction, level)
+ if not bop then
+ break
+ end
+
+ ::AGAIN::
+ skipSpace()
+ local isForward = SymbolForward[bopLevel]
+ local child = parseExp(asAction, isForward and (bopLevel + 0.5) or bopLevel)
+ if not child then
+ if skipUnknownSymbol() then
+ goto AGAIN
+ else
+ missExp()
+ end
+ end
+ local bin = {
+ type = 'binary',
+ start = exp.start,
+ finish = child and child.finish or bop.finish,
+ op = bop,
+ [1] = exp,
+ [2] = child
+ }
+ exp.parent = bin
+ if child then
+ child.parent = bin
+ end
+ exp = bin
+ end
+
+ return exp
+end
+
+local function skipSeps()
+ while true do
+ skipSpace()
+ if Tokens[Index + 1] == ',' then
+ missExp()
+ Index = Index + 2
+ else
+ break
+ end
+ end
+end
+
+---@return parser.guide.object first
+---@return parser.guide.object second
+---@return parser.guide.object[] rest
+local function parseSetValues()
+ skipSpace()
+ local first = parseExp()
+ if not first then
+ return nil
+ end
+ skipSpace()
+ if Tokens[Index + 1] ~= ',' then
+ return first
+ end
+ Index = Index + 2
+ skipSeps()
+ local second = parseExp()
+ if not second then
+ missExp()
+ return first
+ end
+ skipSpace()
+ if Tokens[Index + 1] ~= ',' then
+ return first, second
+ end
+ Index = Index + 2
+ skipSeps()
+ local third = parseExp()
+ if not third then
+ missExp()
+ return first, second
+ end
+
+ local rest = { third }
+ while true do
+ skipSpace()
+ if Tokens[Index + 1] ~= ',' then
+ return first, second, rest
+ end
+ Index = Index + 2
+ skipSeps()
+ local exp = parseExp()
+ if not exp then
+ missExp()
+ return first, second, rest
+ end
+ rest[#rest+1] = exp
+ end
+end
+
+local function pushActionIntoCurrentChunk(action)
+ local chunk = Chunk[#Chunk]
+ if chunk then
+ chunk[#chunk+1] = action
+ action.parent = chunk
+ end
+end
+
+---@return parser.guide.object second
+---@return parser.guide.object[] rest
+local function parseVarTails(parser, isLocal)
+ if Tokens[Index + 1] ~= ',' then
+ return
+ end
+ Index = Index + 2
+ skipSpace()
+ local second = parser(true)
+ if not second then
+ missName()
+ return
+ end
+ if isLocal then
+ createLocal(second, parseLocalAttrs())
+ second.effect = maxinteger
+ end
+ skipSpace()
+ if Tokens[Index + 1] ~= ',' then
+ return second
+ end
+ Index = Index + 2
+ skipSeps()
+ local third = parser(true)
+ if not third then
+ missName()
+ return second
+ end
+ if isLocal then
+ createLocal(third, parseLocalAttrs())
+ third.effect = maxinteger
+ end
+ local rest = { third }
+ while true do
+ skipSpace()
+ if Tokens[Index + 1] ~= ',' then
+ return second, rest
+ end
+ Index = Index + 2
+ skipSeps()
+ local name = parser(true)
+ if not name then
+ missName()
+ return second, rest
+ end
+ if isLocal then
+ createLocal(name, parseLocalAttrs())
+ name.effect = maxinteger
+ end
+ rest[#rest+1] = name
+ end
+end
+
+local function bindValue(n, v, index, lastValue, isLocal, isSet)
+ if isLocal then
+ n.effect = lastRightPosition()
+ if v and v.special then
+ addSpecial(v.special, n)
+ end
+ elseif isSet then
+ n.type = GetToSetMap[n.type] or n.type
+ if n.type == 'setlocal' then
+ local loc = n.node
+ if loc.attrs then
+ pushError {
+ type = 'SET_CONST',
+ start = n.start,
+ finish = n.finish,
+ }
+ end
+ end
+ end
+ if not v and lastValue then
+ if lastValue.type == 'call'
+ or lastValue.type == 'varargs' then
+ v = lastValue
+ if not v.extParent then
+ v.extParent = {}
+ end
+ end
+ end
+ if v then
+ if v.type == 'call'
+ or v.type == 'varargs' then
+ local select = {
+ type = 'select',
+ sindex = index,
+ start = v.start,
+ finish = v.finish,
+ vararg = v
+ }
+ if v.parent then
+ v.extParent[#v.extParent+1] = select
+ else
+ v.parent = select
+ end
+ v = select
+ end
+ n.value = v
+ n.range = v.finish
+ v.parent = n
+ if isLocal then
+ n.effect = lastRightPosition()
+ end
+ end
+end
+
+local function parseMultiVars(n1, parser, isLocal)
+ local n2, nrest = parseVarTails(parser, isLocal)
+ skipSpace()
+ local v1, v2, vrest
+ local isSet
+ local max = 1
+ if expectAssign() then
+ v1, v2, vrest = parseSetValues()
+ isSet = true
+ if not v1 then
+ missExp()
+ end
+ end
+ bindValue(n1, v1, 1, nil, isLocal, isSet)
+ local lastValue = v1
+ if n2 then
+ max = 2
+ bindValue(n2, v2, 2, lastValue, isLocal, isSet)
+ lastValue = v2 or lastValue
+ pushActionIntoCurrentChunk(n2)
+ end
+ if nrest then
+ for i = 1, #nrest do
+ local n = nrest[i]
+ local v = vrest and vrest[i]
+ max = i + 2
+ bindValue(n, v, max, lastValue, isLocal, isSet)
+ lastValue = v or lastValue
+ pushActionIntoCurrentChunk(n)
+ end
+ end
+
+ if v2 and not n2 then
+ v2.redundant = {
+ max = max,
+ passed = 2,
+ }
+ pushActionIntoCurrentChunk(v2)
+ end
+ if vrest then
+ for i = 1, #vrest do
+ local v = vrest[i]
+ if not nrest or not nrest[i] then
+ v.redundant = {
+ max = max,
+ passed = i + 2,
+ }
+ pushActionIntoCurrentChunk(v)
+ end
+ end
+ end
+
+ return n1, isSet
+end
+
+local function compileExpAsAction(exp)
+ pushActionIntoCurrentChunk(exp)
+ if GetToSetMap[exp.type] then
+ skipSpace()
+ local action, isSet = parseMultiVars(exp, parseExp)
+ if isSet
+ or action.type == 'getmethod' then
+ return action
+ end
+ end
+
+ if exp.type == 'call' then
+ return exp
+ end
+
+ if exp.type == 'binary' then
+ if GetToSetMap[exp[1].type] then
+ local op = exp.op
+ if op.type == '==' then
+ pushError {
+ type = 'ERR_ASSIGN_AS_EQ',
+ start = op.start,
+ finish = op.finish,
+ fix = {
+ title = 'FIX_ASSIGN_AS_EQ',
+ {
+ start = op.start,
+ finish = op.finish,
+ text = '=',
+ }
+ }
+ }
+ return
+ end
+ end
+ end
+
+ pushError {
+ type = 'EXP_IN_ACTION',
+ start = exp.start,
+ finish = exp.finish,
+ }
+end
+
+local function parseLocal()
+ Index = Index + 2
+ skipSpace()
+ local word = peekWord()
+ if not word then
+ missName()
+ return nil
+ end
+
+ if word == 'function' then
+ local func = parseFunction(true, true)
+ local name = func.name
+ if name then
+ func.name = nil
+ name.value = func
+ name.vstart = func.start
+ name.range = func.finish
+ func.parent = name
+ pushActionIntoCurrentChunk(name)
+ return name
+ else
+ missName(func.keyword[2])
+ pushActionIntoCurrentChunk(func)
+ return func
+ end
+ end
+
+ local name = parseName()
+ if not name then
+ missName()
+ return nil
+ end
+ local loc = createLocal(name, parseLocalAttrs())
+ loc.effect = maxinteger
+ pushActionIntoCurrentChunk(loc)
+ skipSpace()
+ parseMultiVars(loc, parseName, true)
+ loc.effect = lastRightPosition()
+
+ return loc
+end
+
+local function parseDo()
+ local doLeft = getPosition(Tokens[Index], 'left')
+ local doRight = getPosition(Tokens[Index] + 1, 'right')
+ local obj = {
+ type = 'do',
+ start = doLeft,
+ finish = doRight,
+ keyword = {
+ [1] = doLeft,
+ [2] = doRight,
+ },
+ }
+ Index = Index + 2
+ pushActionIntoCurrentChunk(obj)
+ pushChunk(obj)
+ parseActions()
+ popChunk()
+ if Tokens[Index + 1] == 'end' then
+ obj.finish = getPosition(Tokens[Index] + 2, 'right')
+ obj.keyword[3] = getPosition(Tokens[Index], 'left')
+ obj.keyword[4] = getPosition(Tokens[Index] + 2, 'right')
+ Index = Index + 2
+ else
+ missEnd(doLeft, doRight)
+ end
+ if obj.locals then
+ LocalCount = LocalCount - #obj.locals
+ end
+
+ return obj
+end
+
+local function parseReturn()
+ local returnLeft = getPosition(Tokens[Index], 'left')
+ local returnRight = getPosition(Tokens[Index] + 5, 'right')
+ Index = Index + 2
+ skipSpace()
+ local rtn = parseExpList(true)
+ if rtn then
+ rtn.type = 'return'
+ rtn.start = returnLeft
+ else
+ rtn = {
+ type = 'return',
+ start = returnLeft,
+ finish = returnRight,
+ }
+ end
+ pushActionIntoCurrentChunk(rtn)
+ for i = #Chunk, 1, -1 do
+ local func = Chunk[i]
+ if func.type == 'function'
+ or func.type == 'main' then
+ if not func.returns then
+ func.returns = {}
+ end
+ func.returns[#func.returns+1] = rtn
+ break
+ end
+ end
+
+ return rtn
+end
+
+local function parseLabel()
+ local left = getPosition(Tokens[Index], 'left')
+ Index = Index + 2
+ skipSpace()
+ local label = parseName()
+ skipSpace()
+
+ if not label then
+ missName()
+ end
+
+ if Tokens[Index + 1] == '::' then
+ Index = Index + 2
+ else
+ if label then
+ missSymbol '::'
+ end
+ end
+
+ if not label then
+ return nil
+ end
+
+ label.type = 'label'
+ pushActionIntoCurrentChunk(label)
+
+ local block = guide.getBlock(label)
+ if block then
+ if not block.labels then
+ block.labels = {}
+ end
+ local name = label[1]
+ local olabel = guide.getLabel(block, name)
+ if olabel then
+ if State.version == 'Lua 5.4'
+ or block == guide.getBlock(olabel) then
+ pushError {
+ type = 'REDEFINED_LABEL',
+ start = label.start,
+ finish = label.finish,
+ relative = {
+ {
+ olabel.start,
+ olabel.finish,
+ }
+ }
+ }
+ end
+ end
+ block.labels[name] = label
+ end
+
+ if State.version == 'Lua 5.1' then
+ pushError {
+ type = 'UNSUPPORT_SYMBOL',
+ start = left,
+ finish = lastRightPosition(),
+ version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'},
+ info = {
+ version = State.version,
+ }
+ }
+ return
+ end
+ return label
+end
+
+local function parseGoTo()
+ Index = Index + 2
+ skipSpace()
+
+ local action = parseName()
+ if not action then
+ missName()
+ return nil
+ end
+
+ action.type = 'goto'
+
+ for i = #Chunk, 1, -1 do
+ local chunk = Chunk[i]
+ if chunk.type == 'function'
+ or chunk.type == 'main' then
+ if not chunk.gotos then
+ chunk.gotos = {}
+ end
+ chunk.gotos[#chunk.gotos+1] = action
+ break
+ end
+ end
+
+ pushActionIntoCurrentChunk(action)
+ return action
+end
+
+local function parseIfBlock(parent)
+ local ifLeft = getPosition(Tokens[Index], 'left')
+ local ifRight = getPosition(Tokens[Index] + 1, 'right')
+ Index = Index + 2
+ local ifblock = {
+ type = 'ifblock',
+ parent = parent,
+ start = ifLeft,
+ finish = ifRight,
+ keyword = {
+ [1] = ifLeft,
+ [2] = ifRight,
+ }
+ }
+ skipSpace()
+ local filter = parseExp()
+ if filter then
+ ifblock.filter = filter
+ ifblock.finish = filter.finish
+ filter.parent = ifblock
+ else
+ missExp()
+ end
+ skipSpace()
+ local thenToken = Tokens[Index + 1]
+ if thenToken == 'then'
+ or thenToken == 'do' then
+ ifblock.finish = getPosition(Tokens[Index] + #thenToken - 1, 'right')
+ ifblock.keyword[3] = getPosition(Tokens[Index], 'left')
+ ifblock.keyword[4] = ifblock.finish
+ if thenToken == 'do' then
+ pushError {
+ type = 'ERR_THEN_AS_DO',
+ start = ifblock.keyword[3],
+ finish = ifblock.keyword[4],
+ fix = {
+ title = 'FIX_THEN_AS_DO',
+ {
+ start = ifblock.keyword[3],
+ finish = ifblock.keyword[4],
+ text = 'then',
+ }
+ }
+ }
+ end
+ Index = Index + 2
+ else
+ missSymbol 'then'
+ end
+ pushChunk(ifblock)
+ parseActions()
+ popChunk()
+ if ifblock.locals then
+ LocalCount = LocalCount - #ifblock.locals
+ end
+ return ifblock
+end
+
+local function parseElseIfBlock(parent)
+ local ifLeft = getPosition(Tokens[Index], 'left')
+ local ifRight = getPosition(Tokens[Index] + 5, 'right')
+ local elseifblock = {
+ type = 'elseifblock',
+ parent = parent,
+ start = ifLeft,
+ finish = ifRight,
+ keyword = {
+ [1] = ifLeft,
+ [2] = ifRight,
+ }
+ }
+ Index = Index + 2
+ skipSpace()
+ local filter = parseExp()
+ if filter then
+ elseifblock.filter = filter
+ elseifblock.finish = filter.finish
+ filter.parent = elseifblock
+ else
+ missExp()
+ end
+ skipSpace()
+ local thenToken = Tokens[Index + 1]
+ if thenToken == 'then'
+ or thenToken == 'do' then
+ elseifblock.finish = getPosition(Tokens[Index] + #thenToken - 1, 'right')
+ elseifblock.keyword[3] = getPosition(Tokens[Index], 'left')
+ elseifblock.keyword[4] = elseifblock.finish
+ if thenToken == 'do' then
+ pushError {
+ type = 'ERR_THEN_AS_DO',
+ start = elseifblock.keyword[3],
+ finish = elseifblock.keyword[4],
+ fix = {
+ title = 'FIX_THEN_AS_DO',
+ {
+ start = elseifblock.keyword[3],
+ finish = elseifblock.keyword[4],
+ text = 'then',
+ }
+ }
+ }
+ end
+ Index = Index + 2
+ else
+ missSymbol 'then'
+ end
+ pushChunk(elseifblock)
+ parseActions()
+ popChunk()
+ if elseifblock.locals then
+ LocalCount = LocalCount - #elseifblock.locals
+ end
+ return elseifblock
+end
+
+local function parseElseBlock(parent)
+ local ifLeft = getPosition(Tokens[Index], 'left')
+ local ifRight = getPosition(Tokens[Index] + 3, 'right')
+ local elseblock = {
+ type = 'elseblock',
+ parent = parent,
+ start = ifLeft,
+ finish = ifRight,
+ keyword = {
+ [1] = ifLeft,
+ [2] = ifRight,
+ }
+ }
+ Index = Index + 2
+ skipSpace()
+ pushChunk(elseblock)
+ parseActions()
+ popChunk()
+ if elseblock.locals then
+ LocalCount = LocalCount - #elseblock.locals
+ end
+ return elseblock
+end
+
+local function parseIf()
+ local token = Tokens[Index + 1]
+ local left = getPosition(Tokens[Index], 'left')
+ local action = {
+ type = 'if',
+ start = left,
+ finish = getPosition(Tokens[Index] + #token - 1, 'right'),
+ }
+ pushActionIntoCurrentChunk(action)
+ if token ~= 'if' then
+ missSymbol('if', left, left)
+ end
+ local hasElse
+ while true do
+ local word = Tokens[Index + 1]
+ local child
+ if word == 'if' then
+ child = parseIfBlock(action)
+ elseif word == 'elseif' then
+ child = parseElseIfBlock(action)
+ elseif word == 'else' then
+ child = parseElseBlock(action)
+ end
+ if not child then
+ break
+ end
+ if hasElse then
+ pushError {
+ type = 'BLOCK_AFTER_ELSE',
+ start = child.start,
+ finish = child.finish,
+ }
+ end
+ if word == 'else' then
+ hasElse = true
+ end
+ action[#action+1] = child
+ action.finish = child.finish
+ skipSpace()
+ end
+
+ if Tokens[Index + 1] == 'end' then
+ action.finish = getPosition(Tokens[Index] + 2, 'right')
+ Index = Index + 2
+ else
+ missEnd(action[1].keyword[1], action[1].keyword[2])
+ end
+
+ return action
+end
+
+local function parseFor()
+ local action = {
+ type = 'for',
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index] + 2, 'right'),
+ keyword = {},
+ }
+ action.keyword[1] = action.start
+ action.keyword[2] = action.finish
+ Index = Index + 2
+ pushActionIntoCurrentChunk(action)
+ pushChunk(action)
+ skipSpace()
+ local nameOrList = parseNameOrList()
+ if not nameOrList then
+ missName()
+ end
+ skipSpace()
+ -- for i =
+ if expectAssign() then
+ action.type = 'loop'
+
+ skipSpace()
+ local expList = parseExpList()
+ local name
+ if nameOrList then
+ if nameOrList.type == 'name' then
+ name = nameOrList
+ else
+ name = nameOrList[1]
+ end
+ end
+ if name then
+ local loc = createLocal(name)
+ loc.parent = action
+ action.finish = name.finish
+ action.loc = loc
+ end
+ if expList then
+ local value = expList[1]
+ if value then
+ value.parent = action
+ action.init = value
+ action.finish = expList[#expList].finish
+ end
+ local max = expList[2]
+ if max then
+ max.parent = action
+ action.max = max
+ action.finish = max.finish
+ else
+ pushError {
+ type = 'MISS_LOOP_MAX',
+ start = lastRightPosition(),
+ finish = lastRightPosition(),
+ }
+ end
+ local step = expList[3]
+ if step then
+ step.parent = action
+ action.step = step
+ action.finish = step.finish
+ end
+ else
+ pushError {
+ type = 'MISS_LOOP_MIN',
+ start = lastRightPosition(),
+ finish = lastRightPosition(),
+ }
+ end
+
+ if action.loc then
+ action.loc.effect = action.finish
+ end
+ elseif Tokens[Index + 1] == 'in' then
+ action.type = 'in'
+ local inLeft = getPosition(Tokens[Index], 'left')
+ local inRight = getPosition(Tokens[Index] + 1, 'right')
+ Index = Index + 2
+ skipSpace()
+
+ local exps = parseExpList()
+
+ action.finish = inRight
+ action.keyword[3] = inLeft
+ action.keyword[4] = inRight
+
+ local list
+ if nameOrList and nameOrList.type == 'name' then
+ list = {
+ type = 'list',
+ start = nameOrList.start,
+ finish = nameOrList.finish,
+ [1] = nameOrList,
+ }
+ else
+ list = nameOrList
+ end
+
+ if exps then
+ local lastExp = exps[#exps]
+ if lastExp then
+ action.finish = lastExp.finish
+ end
+
+ action.exps = exps
+ for i = 1, #exps do
+ local exp = exps[i]
+ exp.parent = action
+ end
+ else
+ missExp()
+ end
+
+ if list then
+ local lastName = list[#list]
+ list.range = lastName and lastName.range or inRight
+ action.keys = list
+ for i = 1, #list do
+ local loc = createLocal(list[i])
+ loc.parent = action
+ loc.effect = action.finish
+ end
+ end
+ else
+ missSymbol 'in'
+ end
+
+ skipSpace()
+ local doToken = Tokens[Index + 1]
+ if doToken == 'do'
+ or doToken == 'then' then
+ local left = getPosition(Tokens[Index], 'left')
+ local right = getPosition(Tokens[Index] + #doToken - 1, 'right')
+ action.finish = left
+ action.keyword[#action.keyword+1] = left
+ action.keyword[#action.keyword+1] = right
+ if doToken == 'then' then
+ pushError {
+ type = 'ERR_DO_AS_THEN',
+ start = left,
+ finish = right,
+ fix = {
+ title = 'FIX_DO_AS_THEN',
+ {
+ start = left,
+ finish = right,
+ text = 'do',
+ }
+ }
+ }
+ end
+ Index = Index + 2
+ else
+ missSymbol 'do'
+ end
+
+ skipSpace()
+ parseActions()
+ popChunk()
+
+ skipSpace()
+ if Tokens[Index + 1] == 'end' then
+ action.finish = getPosition(Tokens[Index] + 2, 'right')
+ action.keyword[#action.keyword+1] = getPosition(Tokens[Index], 'left')
+ action.keyword[#action.keyword+1] = action.finish
+ Index = Index + 2
+ else
+ missEnd(action.keyword[1], action.keyword[2])
+ end
+
+ if action.locals then
+ LocalCount = LocalCount - #action.locals
+ end
+
+ return action
+end
+
+local function parseWhile()
+ local action = {
+ type = 'while',
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index] + 4, 'right'),
+ keyword = {},
+ }
+ action.keyword[1] = action.start
+ action.keyword[2] = action.finish
+ Index = Index + 2
+
+ skipSpace()
+ local nextToken = Tokens[Index + 1]
+ local filter = nextToken ~= 'do'
+ and nextToken ~= 'then'
+ and parseExp()
+ if filter then
+ action.filter = filter
+ action.finish = filter.finish
+ filter.parent = action
+ else
+ missExp()
+ end
+
+ skipSpace()
+ local doToken = Tokens[Index + 1]
+ if doToken == 'do'
+ or doToken == 'then' then
+ local left = getPosition(Tokens[Index], 'left')
+ local right = getPosition(Tokens[Index] + #doToken - 1, 'right')
+ action.finish = left
+ action.keyword[#action.keyword+1] = left
+ action.keyword[#action.keyword+1] = right
+ if doToken == 'then' then
+ pushError {
+ type = 'ERR_DO_AS_THEN',
+ start = left,
+ finish = right,
+ fix = {
+ title = 'FIX_DO_AS_THEN',
+ {
+ start = left,
+ finish = right,
+ text = 'do',
+ }
+ }
+ }
+ end
+ Index = Index + 2
+ else
+ missSymbol 'do'
+ end
+
+ pushActionIntoCurrentChunk(action)
+ pushChunk(action)
+ skipSpace()
+ parseActions()
+ popChunk()
+
+ skipSpace()
+ if Tokens[Index + 1] == 'end' then
+ action.finish = getPosition(Tokens[Index] + 2, 'right')
+ action.keyword[#action.keyword+1] = getPosition(Tokens[Index], 'left')
+ action.keyword[#action.keyword+1] = action.finish
+ Index = Index + 2
+ else
+ missEnd(action.keyword[1], action.keyword[2])
+ end
+
+ if action.locals then
+ LocalCount = LocalCount - #action.locals
+ end
+
+ return action
+end
+
+local function parseRepeat()
+ local action = {
+ type = 'repeat',
+ start = getPosition(Tokens[Index], 'left'),
+ finish = getPosition(Tokens[Index] + 5, 'right'),
+ keyword = {},
+ }
+ action.keyword[1] = action.start
+ action.keyword[2] = action.finish
+ Index = Index + 2
+
+ pushActionIntoCurrentChunk(action)
+ pushChunk(action)
+ skipSpace()
+ parseActions()
+
+ skipSpace()
+ if Tokens[Index + 1] == 'until' then
+ action.finish = getPosition(Tokens[Index] + 4, 'right')
+ action.keyword[#action.keyword+1] = getPosition(Tokens[Index], 'left')
+ action.keyword[#action.keyword+1] = action.finish
+ Index = Index + 2
+
+ skipSpace()
+ local filter = parseExp()
+ if filter then
+ action.filter = filter
+ filter.parent = action
+ else
+ missExp()
+ end
+
+ else
+ missSymbol 'until'
+ end
+
+ popChunk()
+ if action.filter then
+ action.finish = action.filter.finish
+ end
+
+ if action.locals then
+ LocalCount = LocalCount - #action.locals
+ end
+
+ return action
+end
+
+local function parseBreak()
+ local returnLeft = getPosition(Tokens[Index], 'left')
+ local returnRight = getPosition(Tokens[Index] + 4, 'right')
+ Index = Index + 2
+ skipSpace()
+ local action = {
+ type = 'break',
+ start = returnLeft,
+ finish = returnRight,
+ }
+
+ local ok
+ for i = #Chunk, 1, -1 do
+ local chunk = Chunk[i]
+ if chunk.type == 'function' then
+ break
+ end
+ if chunk.type == 'while'
+ or chunk.type == 'in'
+ or chunk.type == 'loop'
+ or chunk.type == 'repeat'
+ or chunk.type == 'for' then
+ if not chunk.breaks then
+ chunk.breaks = {}
+ end
+ chunk.breaks[#chunk.breaks+1] = action
+ ok = true
+ break
+ end
+ end
+ if not ok and Mode == 'Lua' then
+ pushError {
+ type = 'BREAK_OUTSIDE',
+ start = action.start,
+ finish = action.finish,
+ }
+ end
+
+ pushActionIntoCurrentChunk(action)
+ return action
+end
+
+function parseAction()
+ local token = Tokens[Index + 1]
+
+ if token == '::' then
+ return parseLabel()
+ end
+
+ if token == 'local' then
+ return parseLocal()
+ end
+
+ if token == 'if'
+ or token == 'elseif'
+ or token == 'else' then
+ return parseIf()
+ end
+
+ if token == 'for' then
+ return parseFor()
+ end
+
+ if token == 'do' then
+ return parseDo()
+ end
+
+ if token == 'return' then
+ return parseReturn()
+ end
+
+ if token == 'break' then
+ return parseBreak()
+ end
+
+ if token == 'while' then
+ return parseWhile()
+ end
+
+ if token == 'repeat' then
+ return parseRepeat()
+ end
+
+ if token == 'goto' then
+ return parseGoTo()
+ end
+
+ if token == 'function' then
+ local exp = parseFunction(false, true)
+ local name = exp.name
+ if name then
+ exp.name = nil
+ name.type = GetToSetMap[name.type]
+ name.value = exp
+ name.vstart = exp.start
+ name.range = exp.finish
+ exp.parent = name
+ pushActionIntoCurrentChunk(name)
+ return name
+ else
+ pushActionIntoCurrentChunk(exp)
+ missName(exp.keyword[2])
+ return exp
+ end
+ end
+
+ local exp = parseExp(true)
+ if exp then
+ local action = compileExpAsAction(exp)
+ if action then
+ return action
+ end
+ end
+ return nil, true
+end
+
+local function skipFirstComment()
+ if Tokens[Index + 1] ~= '#' then
+ return
+ end
+ while true do
+ Index = Index + 2
+ local token = Tokens[Index + 1]
+ if not token then
+ break
+ end
+ if NLMap[token] then
+ skipNL()
+ break
+ end
+ end
+end
+
+local function parseLua()
+ local main = {
+ type = 'main',
+ start = 0,
+ finish = 0,
+ }
+ pushChunk(main)
+ createLocal{
+ type = 'local',
+ start = -1,
+ finish = -1,
+ effect = -1,
+ parent = main,
+ tag = '_ENV',
+ special= '_G',
+ [1] = State.ENVMode,
+ }
+ LocalCount = 0
+ skipFirstComment()
+ while true do
+ parseActions()
+ if Index <= #Tokens then
+ unknownSymbol()
+ Index = Index + 2
+ else
+ break
+ end
+ end
+ popChunk()
+ main.finish = getPosition(#Lua, 'right')
+
+ return main
+end
+
+local function initState(lua, version, options)
+ Lua = lua
+ Line = 0
+ LineOffset = 1
+ LastTokenFinish = 0
+ LocalCount = 0
+ Chunk = {}
+ Tokens = tokens(lua)
+ Index = 1
+ local state = {
+ version = version,
+ lua = lua,
+ ast = {},
+ errs = {},
+ comms = {},
+ lines = {
+ [0] = 1,
+ },
+ options = options or {},
+ }
+ State = state
+ if version == 'Lua 5.1' or version == 'LuaJIT' then
+ state.ENVMode = '@fenv'
+ else
+ state.ENVMode = '_ENV'
+ end
+
+ pushError = function (err)
+ local errs = state.errs
+ if err.finish < err.start then
+ err.finish = err.start
+ end
+ local last = errs[#errs]
+ if last then
+ if last.start <= err.start and last.finish >= err.finish then
+ return
+ end
+ end
+ err.level = err.level or 'error'
+ errs[#errs+1] = err
+ return err
+ end
+
+ state.pushError = pushError
+end
+
+return function (lua, mode, version, options)
+ Mode = mode
+ initState(lua, version, options)
+ skipSpace()
+ if mode == 'Lua' then
+ State.ast = parseLua()
+ elseif mode == 'Nil' then
+ State.ast = parseNil()
+ elseif mode == 'Boolean' then
+ State.ast = parseBoolean()
+ elseif mode == 'String' then
+ State.ast = parseString()
+ elseif mode == 'Number' then
+ State.ast = parseNumber()
+ elseif mode == 'Exp' then
+ State.ast = parseExp()
+ elseif mode == 'Action' then
+ State.ast = parseAction()
+ end
+ State.ast.state = State
+
+ return State
+end
diff --git a/script/parser/parse.lua b/script/parser/parse.lua
index 9b8d5496..e7c7d177 100644
--- a/script/parser/parse.lua
+++ b/script/parser/parse.lua
@@ -1,6 +1,7 @@
-local ast = require 'parser.ast'
+local ast = require 'parser.ast'
+local grammar = require 'parser.grammar'
-return function (self, lua, mode, version, options)
+local function buildState(lua, version, options)
local errs = {}
local diags = {}
local comms = {}
@@ -36,9 +37,19 @@ return function (self, lua, mode, version, options)
comms[#comms+1] = comment
end
}
+ if version == 'Lua 5.1' or version == 'LuaJIT' then
+ state.ENVMode = '@fenv'
+ else
+ state.ENVMode = '_ENV'
+ end
+ return state
+end
+
+return function (lua, mode, version, options)
+ local state = buildState(lua, version, options)
local clock = os.clock()
ast.init(state)
- local suc, res, err = xpcall(self.grammar, debug.traceback, self, lua, mode)
+ local suc, res, err = xpcall(grammar, debug.traceback, lua, mode)
ast.close()
if not suc then
return nil, res
diff --git a/script/parser/tokens.lua b/script/parser/tokens.lua
new file mode 100644
index 00000000..958f292e
--- /dev/null
+++ b/script/parser/tokens.lua
@@ -0,0 +1,38 @@
+local m = require 'lpeglabel'
+
+local Sp = m.S' \t'
+local Nl = m.P'\r\n' + m.S'\r\n'
+local Number = m.R'09'^1
+local Word = m.R('AZ', 'az', '__', '\x80\xff') * m.R('AZ', 'az', '09', '__', '\x80\xff')^0
+local Symbol = m.P'=='
+ + m.P'~='
+ + m.P'--'
+ + m.P'<<'
+ + m.P'>>'
+ + m.P'<='
+ + m.P'>='
+ + m.P'//'
+ + m.P'...'
+ + m.P'..'
+ + m.P'::'
+ -- incorrect
+ + m.P'!='
+ + m.P'&&'
+ + m.P'||'
+ + m.P'/*'
+ + m.P'*/'
+ + m.P'+='
+ + m.P'-='
+ + m.P'*='
+ + m.P'/='
+ -- singles
+ + m.S'+-*/!#%^&()={}[]|\\\'":;<>,.?~`'
+local Unknown = (1 - Number - Word - Symbol - Sp - Nl)^1
+local Token = m.Cp() * m.C(Nl + Number + Word + Symbol + Unknown)
+
+local Parser = m.Ct((Sp^1 + Token)^0)
+
+return function (lua)
+ local results = Parser:match(lua)
+ return results
+end
diff --git a/script/proto/converter.lua b/script/proto/converter.lua
new file mode 100644
index 00000000..19d592fe
--- /dev/null
+++ b/script/proto/converter.lua
@@ -0,0 +1,168 @@
+local guide = require 'parser.guide'
+local files = require 'files'
+
+local m = {}
+
+---@alias position {line: integer, character: integer}
+
+local function rawPackPosition(uri, pos)
+ local row, col = guide.rowColOf(pos)
+ if col > 0 then
+ local state = files.getState(uri)
+ local text = files.getText(uri)
+ if text then
+ local lineOffset = state.lines[row]
+ col = utf8.len(text, lineOffset, lineOffset + col - 1, true)
+ end
+ end
+ return {
+ line = row,
+ character = col,
+ }
+end
+
+local function diffedPackPosition(uri, pos)
+ local state = files.getState(uri)
+ local offset = guide.positionToOffset(state, pos)
+ local originOffset = files.diffedOffsetBack(uri, offset)
+ local originLines = files.getOriginLines(uri)
+ local originPos = guide.offsetToPositionByLines(originLines, originOffset)
+ local row, col = guide.rowColOf(originPos)
+ if col > 0 then
+ local text = files.getOriginText(uri)
+ if text then
+ local lineOffset = originLines[row]
+ col = utf8.len(text, lineOffset, lineOffset + col - 1, true)
+ end
+ end
+ return {
+ line = row,
+ character = col,
+ }
+end
+
+---@param uri uri
+---@param pos integer
+---@return position
+function m.packPosition(uri, pos)
+ if files.hasDiffed(uri) then
+ return diffedPackPosition(uri, pos)
+ else
+ return rawPackPosition(uri, pos)
+ end
+end
+
+local function rawUnpackPosition(uri, position)
+ local row, col = position.line, position.character
+ if col > 0 then
+ local state = files.getState(uri)
+ local text = files.getText(uri)
+ if text then
+ local lineOffset = state.lines[row]
+ local textOffset = utf8.offset(text, col + 1, lineOffset)
+ if textOffset then
+ col = textOffset - lineOffset
+ end
+ end
+ end
+ local pos = guide.positionOf(row, col)
+ return pos
+end
+
+local function diffedUnpackPosition(uri, position)
+ local row, col = position.line, position.character
+ local originLines = files.getOriginLines(uri)
+ if col > 0 then
+ local text = files.getOriginText(uri)
+ if text then
+ local lineOffset = originLines[row]
+ local textOffset = utf8.offset(text, col + 1, lineOffset)
+ if textOffset then
+ col = textOffset - lineOffset
+ end
+ end
+ end
+ local state = files.getState(uri)
+ local originPos = guide.positionOf(row, col)
+ local originOffset = guide.positionToOffsetByLines(originLines, originPos)
+ local offset = files.diffedOffset(uri, originOffset)
+ local pos = guide.offsetToPosition(state, offset)
+ return pos
+end
+
+---@param uri uri
+---@param position position
+---@return integer
+function m.unpackPosition(uri, position)
+ if files.hasDiffed(uri) then
+ return diffedUnpackPosition(uri, position)
+ else
+ return rawUnpackPosition(uri, position)
+ end
+end
+
+---@alias range {start: position, end: position}
+
+---@param uri uri
+---@param start integer
+---@param finish integer
+---@return range
+function m.packRange(uri, start, finish)
+ local range = {
+ start = m.packPosition(uri, start),
+ ['end'] = m.packPosition(uri, finish),
+ }
+ return range
+end
+
+---@param uri uri
+---@param range range
+---@return integer start
+---@return integer finish
+function m.unpackRange(uri, range)
+ local start = m.unpackPosition(uri, range.start)
+ local finish = m.unpackPosition(uri, range['end'])
+ return start, finish
+end
+
+---@alias location {uri: uri, range: range}
+
+---@param uri string
+---@param range range
+---@return location
+function m.location(uri, range)
+ return {
+ uri = uri,
+ range = range,
+ }
+end
+
+---@alias locationLink {targetUri:uri, targetRange: range, targetSelectionRange: range, originSelectionRange: range}
+
+---@param uri string
+---@param range range
+---@param selection range
+---@param origin range
+---@return locationLink
+function m.locationLink(uri, range, selection, origin)
+ return {
+ targetUri = uri,
+ targetRange = range,
+ targetSelectionRange = selection,
+ originSelectionRange = origin,
+ }
+end
+
+---@alias textEdit {range: range, newText: string}
+
+---@param range range
+---@param newtext string
+---@return textEdit
+function m.textEdit(range, newtext)
+ return {
+ range = range,
+ newText = newtext,
+ }
+end
+
+return m
diff --git a/script/proto/define.lua b/script/proto/define.lua
index 2e4116fd..fe7d171b 100644
--- a/script/proto/define.lua
+++ b/script/proto/define.lua
@@ -1,37 +1,5 @@
local m = {}
----@alias location table
----@param uri string
----@param range range
----@return location
-function m.location(uri, range)
- return {
- uri = uri,
- range = range,
- }
-end
-
----@alias locationLink table
----@param uri string
----@param range range
----@param selection range
----@param origin range
-function m.locationLink(uri, range, selection, origin)
- return {
- targetUri = uri,
- targetRange = range,
- targetSelectionRange = selection,
- originSelectionRange = origin,
- }
-end
-
-function m.textEdit(range, newtext)
- return {
- range = range,
- newText = newtext,
- }
-end
-
--- 诊断等级
m.DiagnosticSeverity = {
Error = 1,
@@ -61,14 +29,14 @@ m.DiagnosticDefaultSeverity = {
['redefined-local'] = 'Hint',
['newline-call'] = 'Information',
['newfield-call'] = 'Warning',
- ['redundant-parameter'] = 'Hint',
+ ['redundant-parameter'] = 'Warning',
['ambiguity-1'] = 'Warning',
['lowercase-global'] = 'Information',
['undefined-env-child'] = 'Information',
['duplicate-index'] = 'Warning',
['duplicate-set-field'] = 'Warning',
['empty-block'] = 'Hint',
- ['redundant-value'] = 'Hint',
+ ['redundant-value'] = 'Warning',
['code-after-break'] = 'Hint',
['unbalanced-assignments'] = 'Warning',
['close-non-object'] = 'Warning',
diff --git a/script/proto/proto.lua b/script/proto/proto.lua
index a0880011..61306b9f 100644
--- a/script/proto/proto.lua
+++ b/script/proto/proto.lua
@@ -4,7 +4,6 @@ local await = require 'await'
local pub = require 'pub'
local jsonrpc = require 'jsonrpc'
local define = require 'proto.define'
-local timer = require 'timer'
local json = require 'json'
local reqCounter = util.counter()
@@ -121,6 +120,9 @@ function m.doMethod(proto)
end
await.call(function ()
--log.debug('Start method:', method)
+ if proto.id then
+ await.setID('proto:' .. proto.id)
+ end
local clock = os.clock()
local ok = true
local res
@@ -134,6 +136,7 @@ function m.doMethod(proto)
if not proto.id then
return
end
+ await.close('proto:' .. proto.id)
if ok then
m.response(proto.id, res)
else
diff --git a/script/provider/diagnostic.lua b/script/provider/diagnostic.lua
index db377ea0..382b73d9 100644
--- a/script/provider/diagnostic.lua
+++ b/script/provider/diagnostic.lua
@@ -1,14 +1,15 @@
-local await = require 'await'
-local proto = require 'proto.proto'
-local define = require 'proto.define'
-local lang = require 'language'
-local files = require 'files'
-local config = require 'config'
-local core = require 'core.diagnostics'
-local util = require 'utility'
-local ws = require 'workspace'
-local progress = require "progress"
-local client = require 'client'
+local await = require 'await'
+local proto = require 'proto.proto'
+local define = require 'proto.define'
+local lang = require 'language'
+local files = require 'files'
+local config = require 'config'
+local core = require 'core.diagnostics'
+local util = require 'utility'
+local ws = require 'workspace'
+local progress = require "progress"
+local client = require 'client'
+local converter = require 'proto.converter'
local m = {}
m._start = false
@@ -45,17 +46,17 @@ local function buildSyntaxError(uri, err)
else
rmessage = text:sub(rel.start, rel.finish)
end
- local relUri = rel.uri
+ local relUri = rel.uri or uri
relatedInformation[#relatedInformation+1] = {
message = rmessage,
- location = define.location(relUri, files.range(relUri, rel.start, rel.finish)),
+ location = converter.location(relUri, converter.packRange(relUri, rel.start, rel.finish)),
}
end
end
return {
code = err.type:lower():gsub('_', '-'),
- range = files.range(uri, err.start, err.finish),
+ range = converter.packRange(uri, err.start, err.finish),
severity = define.DiagnosticSeverity.Error,
source = lang.script.DIAG_SYNTAX_CHECK,
message = message,
@@ -75,13 +76,13 @@ local function buildDiagnostic(uri, diag)
local rtext = files.getText(rel.uri)
relatedInformation[#relatedInformation+1] = {
message = rel.message or rtext:sub(rel.start, rel.finish),
- location = define.location(rel.uri, files.range(rel.uri, rel.start, rel.finish))
+ location = converter.location(rel.uri, converter.packRange(rel.uri, rel.start, rel.finish))
}
end
end
return {
- range = files.range(uri, diag.start, diag.finish),
+ range = converter.packRange(uri, diag.start, diag.finish),
source = lang.script.DIAG_DIAGNOSTICS,
severity = diag.level,
message = diag.message,
diff --git a/script/provider/provider.lua b/script/provider/provider.lua
index e1bd0f4f..070652ef 100644
--- a/script/provider/provider.lua
+++ b/script/provider/provider.lua
@@ -13,6 +13,7 @@ local lang = require 'language'
local progress = require 'progress'
local tm = require 'text-merger'
local cfgLoader = require 'config.loader'
+local converter = require 'proto.converter'
local function updateConfig()
local new
@@ -165,8 +166,11 @@ proto.on('textDocument/didChange', function (params)
end)
proto.on('textDocument/hover', function (params)
+ local doc = params.textDocument
+ local uri = doc.uri
await.close 'hover'
await.setID 'hover'
+ await.setID('update:' .. uri)
if not workspace.isReady() then
local count, max = workspace.getLoadProcess()
return {
@@ -178,13 +182,11 @@ proto.on('textDocument/hover', function (params)
end
local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_HOVER, 0.5)
local core = require 'core.hover'
- local doc = params.textDocument
- local uri = doc.uri
if not files.exists(uri) then
return nil
end
- local offset = files.offsetOfWord(uri, params.position)
- local hover, source = core.byUri(uri, offset)
+ local pos = converter.unpackPosition(uri, params.position)
+ local hover, source = core.byUri(uri, pos)
if not hover then
return nil
end
@@ -193,7 +195,7 @@ proto.on('textDocument/hover', function (params)
value = tostring(hover),
kind = 'markdown',
},
- range = files.range(uri, source.start, source.finish),
+ range = converter.packRange(uri, source.start, source.finish),
}
end)
@@ -205,8 +207,8 @@ proto.on('textDocument/definition', function (params)
if not files.exists(uri) then
return nil
end
- local offset = files.offsetOfWord(uri, params.position)
- local result = core(uri, offset)
+ local pos = converter.unpackPosition(uri, params.position)
+ local result = core(uri, pos)
if not result then
return nil
end
@@ -216,14 +218,14 @@ proto.on('textDocument/definition', function (params)
if targetUri then
if files.exists(targetUri) then
if client.getAbility 'textDocument.definition.linkSupport' then
- response[i] = define.locationLink(targetUri
- , files.range(targetUri, info.target.start, info.target.finish)
- , files.range(targetUri, info.target.start, info.target.finish)
- , files.range(uri, info.source.start, info.source.finish)
+ response[i] = converter.locationLink(targetUri
+ , converter.packRange(targetUri, info.target.start, info.target.finish)
+ , converter.packRange(targetUri, info.target.start, info.target.finish)
+ , converter.packRange(uri, info.source.start, info.source.finish)
)
else
- response[i] = define.location(targetUri
- , files.range(targetUri, info.target.start, info.target.finish)
+ response[i] = converter.location(targetUri
+ , converter.packRange(targetUri, info.target.start, info.target.finish)
)
end
end
@@ -240,8 +242,8 @@ proto.on('textDocument/typeDefinition', function (params)
if not files.exists(uri) then
return nil
end
- local offset = files.offsetOfWord(uri, params.position)
- local result = core(uri, offset)
+ local pos = converter.unpackPosition(uri, params.position)
+ local result = core(uri, pos)
if not result then
return nil
end
@@ -251,14 +253,14 @@ proto.on('textDocument/typeDefinition', function (params)
if targetUri then
if files.exists(targetUri) then
if client.getAbility 'textDocument.typeDefinition.linkSupport' then
- response[i] = define.locationLink(targetUri
- , files.range(targetUri, info.target.start, info.target.finish)
- , files.range(targetUri, info.target.start, info.target.finish)
- , files.range(uri, info.source.start, info.source.finish)
+ response[i] = converter.locationLink(targetUri
+ , converter.packRange(targetUri, info.target.start, info.target.finish)
+ , converter.packRange(targetUri, info.target.start, info.target.finish)
+ , converter.packRange(uri, info.source.start, info.source.finish)
)
else
- response[i] = define.location(targetUri
- , files.range(targetUri, info.target.start, info.target.finish)
+ response[i] = converter.location(targetUri
+ , converter.packRange(targetUri, info.target.start, info.target.finish)
)
end
end
@@ -275,16 +277,16 @@ proto.on('textDocument/references', function (params)
if not files.exists(uri) then
return nil
end
- local offset = files.offsetOfWord(uri, params.position)
- local result = core(uri, offset)
+ local pos = converter.unpackPosition(uri, params.position)
+ local result = core(uri, pos)
if not result then
return nil
end
local response = {}
for i, info in ipairs(result) do
local targetUri = info.uri
- response[i] = define.location(targetUri
- , files.range(targetUri, info.target.start, info.target.finish)
+ response[i] = converter.location(targetUri
+ , converter.packRange(targetUri, info.target.start, info.target.finish)
)
end
return response
@@ -296,15 +298,15 @@ proto.on('textDocument/documentHighlight', function (params)
if not files.exists(uri) then
return nil
end
- local offset = files.offsetOfWord(uri, params.position)
- local result = core(uri, offset)
+ local pos = converter.unpackPosition(uri, params.position)
+ local result = core(uri, pos)
if not result then
return nil
end
local response = {}
for _, info in ipairs(result) do
response[#response+1] = {
- range = files.range(uri, info.start, info.finish),
+ range = converter.packRange(uri, info.start, info.finish),
kind = info.kind,
}
end
@@ -319,8 +321,8 @@ proto.on('textDocument/rename', function (params)
if not files.exists(uri) then
return nil
end
- local offset = files.offsetOfWord(uri, params.position)
- local result = core.rename(uri, offset, params.newName)
+ local pos = converter.unpackPosition(uri, params.position)
+ local result = core.rename(uri, pos, params.newName)
if not result then
return nil
end
@@ -332,7 +334,7 @@ proto.on('textDocument/rename', function (params)
if not workspaceEdit.changes[ruri] then
workspaceEdit.changes[ruri] = {}
end
- local textEdit = define.textEdit(files.range(ruri, info.start, info.finish), info.text)
+ local textEdit = converter.textEdit(converter.packRange(ruri, info.start, info.finish), info.text)
workspaceEdit.changes[ruri][#workspaceEdit.changes[ruri]+1] = textEdit
end
return workspaceEdit
@@ -344,20 +346,22 @@ proto.on('textDocument/prepareRename', function (params)
if not files.exists(uri) then
return nil
end
- local offset = files.offsetOfWord(uri, params.position)
- local result = core.prepareRename(uri, offset)
+ local pos = converter.unpackPosition(uri, params.position)
+ local result = core.prepareRename(uri, pos)
if not result then
return nil
end
return {
- range = files.range(uri, result.start, result.finish),
+ range = converter.packRange(uri, result.start, result.finish),
placeholder = result.text,
}
end)
proto.on('textDocument/completion', function (params)
+ local uri = params.textDocument.uri
await.close 'completion'
await.setID 'completion'
+ await.setID('update:' .. uri)
if not workspace.isReady() then
local count, max = workspace.getLoadProcess()
return {
@@ -377,7 +381,6 @@ proto.on('textDocument/completion', function (params)
local core = require 'core.completion'
--log.debug('textDocument/completion')
--log.debug('completion:', params.context and params.context.triggerKind, params.context and params.context.triggerCharacter)
- local uri = params.textDocument.uri
if not files.exists(uri) then
return nil
end
@@ -391,8 +394,8 @@ proto.on('textDocument/completion', function (params)
end
await.setPriority(1000)
local clock = os.clock()
- local offset = files.offset(uri, params.position)
- local result = core.completion(uri, offset - 1, triggerCharacter)
+ local pos = converter.unpackPosition(uri, params.position)
+ local result = core.completion(uri, pos, triggerCharacter)
local passed = os.clock() - clock
if passed > 0.1 then
log.warn(('Completion takes %.3f sec.'):format(passed))
@@ -417,7 +420,7 @@ proto.on('textDocument/completion', function (params)
commitCharacters = res.commitCharacters,
command = res.command,
textEdit = res.textEdit and {
- range = files.range(
+ range = converter.packRange(
uri,
res.textEdit.start,
res.textEdit.finish
@@ -428,7 +431,7 @@ proto.on('textDocument/completion', function (params)
local t = {}
for j, edit in ipairs(res.additionalTextEdits) do
t[j] = {
- range = files.range(
+ range = converter.packRange(
uri,
edit.start,
edit.finish
@@ -474,10 +477,11 @@ proto.on('completionItem/resolve', function (item)
if not item.data then
return item
end
- await.close 'completion.resolve'
- await.setID 'completion.resolve'
local id = item.data.id
local uri = item.data.uri
+ await.close 'completion.resolve'
+ await.setID 'completion.resolve'
+ await.setID('update:' .. uri)
--await.setPriority(1000)
local resolved = core.resolve(id)
if not resolved then
@@ -492,7 +496,7 @@ proto.on('completionItem/resolve', function (item)
local t = {}
for j, edit in ipairs(resolved.additionalTextEdits) do
t[j] = {
- range = files.range(
+ range = converter.packRange(
uri,
edit.start,
edit.finish
@@ -517,9 +521,10 @@ proto.on('textDocument/signatureHelp', function (params)
end
await.close('signatureHelp')
await.setID('signatureHelp')
- local offset = files.offset(uri, params.position)
+ await.setID('update:' .. uri)
+ local pos = converter.unpackPosition(uri, params.position)
local core = require 'core.signature'
- local results = core(uri, offset - 1)
+ local results = core(uri, pos)
if not results then
return nil
end
@@ -529,7 +534,7 @@ proto.on('textDocument/signatureHelp', function (params)
for j, param in ipairs(result.params) do
parameters[j] = {
label = {
- param.label[1] - 1,
+ param.label[1],
param.label[2],
}
}
@@ -562,12 +567,12 @@ proto.on('textDocument/documentSymbol', function (params)
local function convert(symbol)
await.delay()
- symbol.range = files.range(
+ symbol.range = converter.packRange(
uri,
symbol.range[1],
symbol.range[2]
)
- symbol.selectionRange = files.range(
+ symbol.selectionRange = converter.packRange(
uri,
symbol.selectionRange[1],
symbol.selectionRange[2]
@@ -599,7 +604,7 @@ proto.on('textDocument/codeAction', function (params)
return nil
end
- local start, finish = files.unrange(uri, range)
+ local start, finish = converter.unpackRange(uri, range)
local results = core(uri, start, finish, diagnostics)
if not results or #results == 0 then
@@ -610,7 +615,7 @@ proto.on('textDocument/codeAction', function (params)
if res.edit then
for turi, changes in pairs(res.edit.changes) do
for _, change in ipairs(changes) do
- change.range = files.range(turi, change.start, change.finish)
+ change.range = converter.packRange(turi, change.start, change.finish)
change.start = nil
change.finish = nil
end
@@ -648,6 +653,7 @@ proto.on('workspace/symbol', function (params)
await.close('workspace/symbol')
await.setID('workspace/symbol')
+ await.setID('files.version')
local symbols = core(params.query)
if not symbols or #symbols == 0 then
@@ -655,9 +661,9 @@ proto.on('workspace/symbol', function (params)
end
local function convert(symbol)
- symbol.location = define.location(
+ symbol.location = converter.location(
symbol.uri,
- files.range(
+ converter.packRange(
symbol.uri,
symbol.range[1],
symbol.range[2]
@@ -675,12 +681,13 @@ end)
proto.on('textDocument/semanticTokens/full', function (params)
- workspace.awaitReady()
+ local uri = params.textDocument.uri
await.close('textDocument/semanticTokens/full')
await.setID('textDocument/semanticTokens/full')
+ await.setID('update:' .. uri)
+ workspace.awaitReady()
local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SEMANTIC_FULL, 0.5)
local core = require 'core.semantic-tokens'
- local uri = params.textDocument.uri
local text = files.getText(uri)
if not text then
return nil
@@ -692,12 +699,13 @@ proto.on('textDocument/semanticTokens/full', function (params)
end)
proto.on('textDocument/semanticTokens/range', function (params)
+ local uri = params.textDocument.uri
+ await.close('textDocument/semanticTokens/range')
+ await.setID('textDocument/semanticTokens/range')
+ await.setID('update:' .. uri)
workspace.awaitReady()
- --await.close('textDocument/semanticTokens/range')
- --await.setID('textDocument/semanticTokens/range')
local _ <close> = progress.create(lang.script.WINDOW_PROCESSING_SEMANTIC_RANGE, 0.5)
local core = require 'core.semantic-tokens'
- local uri = params.textDocument.uri
local cache = files.getOpenedCache(uri)
local start, finish
if cache and not cache['firstSemantic'] then
@@ -705,8 +713,7 @@ proto.on('textDocument/semanticTokens/range', function (params)
start = 0
finish = #files.getText(uri)
else
- start = files.offsetOfWord(uri, params.range.start)
- finish = files.offsetOfWord(uri, params.range['end'])
+ start, finish = converter.unpackRange(uri, params.range)
end
local results = core(uri, start, finish)
return {
@@ -722,6 +729,7 @@ proto.on('textDocument/foldingRange', function (params)
end
await.close('textDocument/foldingRange')
await.setID('textDocument/foldingRange')
+ await.setID('update:' .. uri)
local regions = core(uri)
if not regions then
return nil
@@ -729,8 +737,8 @@ proto.on('textDocument/foldingRange', function (params)
local results = {}
for _, region in ipairs(regions) do
- local startLine = files.position(uri, region.start, 'left').line
- local endLine = files.position(uri, region.finish, 'right').line
+ local startLine = converter.packPosition(uri, region.start).line
+ local endLine = converter.packPosition(uri, region.finish).line
if not region.hideLastLine then
endLine = endLine - 1
end
@@ -778,8 +786,8 @@ proto.on('textDocument/onTypeFormatting', function (params)
return nil
end
local core = require 'core.type-formatting'
- local offset = files.offset(uri, params.position)
- local edits = core(uri, offset - 1, ch)
+ local pos = converter.unpackPosition(uri, params.position)
+ local edits = core(uri, pos, ch)
if not edits or #edits == 0 then
return nil
end
@@ -790,13 +798,17 @@ proto.on('textDocument/onTypeFormatting', function (params)
local results = {}
for i, edit in ipairs(edits) do
results[i] = {
- range = files.range(uri, edit.start, edit.finish),
+ range = converter.packRange(uri, edit.start, edit.finish),
newText = edit.text:gsub('\t', tab),
}
end
return results
end)
+proto.on('$/cancelRequest', function (params)
+ await.close('proto:' .. params.id)
+end)
+
proto.on('$/requestHint', function (params)
local core = require 'core.hint'
if not config.get 'Lua.hint.enable' then
@@ -804,13 +816,13 @@ proto.on('$/requestHint', function (params)
end
workspace.awaitReady()
local uri = params.textDocument.uri
- local start, finish = files.unrange(uri, params.range)
+ local start, finish = converter.unpackRange(uri, params.range)
local results = core(uri, start, finish)
local hintResults = {}
for i, res in ipairs(results) do
hintResults[i] = {
text = res.text,
- pos = files.position(uri, res.offset, res.where),
+ pos = converter.packPosition(uri, res.offset),
kind = res.kind,
}
end
@@ -826,6 +838,7 @@ do
return
end
await.setID(awaitID)
+ await.setID('update:' .. uri)
workspace.awaitReady()
local visibles = files.getVisibles(uri)
if not visibles then
@@ -840,7 +853,7 @@ do
for _, edit in ipairs(piece) do
edits[#edits+1] = {
text = edit.text,
- pos = files.position(uri, edit.offset, edit.where),
+ pos = converter.packPosition(uri, edit.offset),
}
end
end
diff --git a/script/vm/getDocs.lua b/script/vm/getDocs.lua
index c0205654..0c6b1695 100644
--- a/script/vm/getDocs.lua
+++ b/script/vm/getDocs.lua
@@ -177,7 +177,6 @@ function vm.isDeprecated(value, deep)
end
local function makeDiagRange(uri, doc, results)
- local lines = files.getLines(uri)
local names
if doc.names then
names = {}
@@ -186,57 +185,51 @@ local function makeDiagRange(uri, doc, results)
names[name] = true
end
end
- local row = guide.positionOf(lines, doc.start)
+ local row = guide.rowColOf(doc.start)
if doc.mode == 'disable-next-line' then
- if lines[row+1] then
- results[#results+1] = {
- mode = 'disable',
- names = names,
- offset = lines[row+1].start,
- source = doc,
- }
- results[#results+1] = {
- mode = 'enable',
- names = names,
- offset = lines[row+1].finish,
- source = doc,
- }
- end
+ results[#results+1] = {
+ mode = 'disable',
+ names = names,
+ row = row + 1,
+ source = doc,
+ }
+ results[#results+1] = {
+ mode = 'enable',
+ names = names,
+ row = row + 2,
+ source = doc,
+ }
elseif doc.mode == 'disable-line' then
results[#results+1] = {
mode = 'disable',
names = names,
- offset = lines[row].start,
+ row = row,
source = doc,
}
results[#results+1] = {
mode = 'enable',
names = names,
- offset = lines[row].finish,
+ row = row + 1,
source = doc,
}
elseif doc.mode == 'disable' then
- if lines[row+1] then
- results[#results+1] = {
- mode = 'disable',
- names = names,
- offset = lines[row+1].start,
- source = doc,
- }
- end
+ results[#results+1] = {
+ mode = 'disable',
+ names = names,
+ row = row + 1,
+ source = doc,
+ }
elseif doc.mode == 'enable' then
- if lines[row+1] then
- results[#results+1] = {
- mode = 'enable',
- names = names,
- offset = lines[row+1].start,
- source = doc,
- }
- end
+ results[#results+1] = {
+ mode = 'enable',
+ names = names,
+ row = row + 1,
+ source = doc,
+ }
end
end
-function vm.isDiagDisabledAt(uri, offset, name)
+function vm.isDiagDisabledAt(uri, position, name)
local status = files.getState(uri)
if not status then
return false
@@ -253,29 +246,26 @@ function vm.isDiagDisabledAt(uri, offset, name)
end
end
table.sort(cache.diagnosticRanges, function (a, b)
- return a.offset < b.offset
+ return a.row < b.row
end)
end
if #cache.diagnosticRanges == 0 then
return false
end
- local stack = {}
+ local myRow = guide.rowColOf(position)
+ local count = 0
for _, range in ipairs(cache.diagnosticRanges) do
- if range.offset <= offset then
+ if range.row <= myRow then
if not range.names or range.names[name] then
if range.mode == 'disable' then
- stack[#stack+1] = range
+ count = count + 1
elseif range.mode == 'enable' then
- stack[#stack] = nil
+ count = count - 1
end
end
else
break
end
end
- local current = stack[#stack]
- if not current then
- return false
- end
- return true
+ return count > 0
end
diff --git a/script/vm/getLinks.lua b/script/vm/getLinks.lua
index 161396f6..d2332504 100644
--- a/script/vm/getLinks.lua
+++ b/script/vm/getLinks.lua
@@ -6,12 +6,12 @@ local files = require 'files'
local function getFileLinks(uri)
local ws = require 'workspace'
local links = {}
- local ast = files.getState(uri)
- if not ast then
+ local state = files.getState(uri)
+ if not state then
return links
end
tracy.ZoneBeginN('getFileLinks')
- guide.eachSpecialOf(ast.ast, 'require', function (source)
+ guide.eachSpecialOf(state.ast, 'require', function (source)
local call = source.parent
if not call or call.type ~= 'call' then
return
diff --git a/test/basic/noder.lua b/test/basic/noder.lua
index bc572a4c..87b34e12 100644
--- a/test/basic/noder.lua
+++ b/test/basic/noder.lua
@@ -2,6 +2,7 @@ local noder = require 'core.noder'
local files = require 'files'
local util = require 'utility'
local guide = require 'parser.guide'
+local catch = require 'catch'
local function getSource(pos)
local ast = files.getState('')
@@ -30,12 +31,9 @@ local CARE = {}
local function TEST(script)
return function (expect)
files.removeAll()
- local start = script:find('<?', 1, true)
- local finish = script:find('?>', 1, true)
- local pos = (start + finish) // 2 + 1
- local newScript = script:gsub('<[!?]', ' '):gsub('[!?]>', ' ')
+ local newScript, catched = catch(script, '?')
files.setText('', newScript)
- local source = getSource(pos)
+ local source = getSource(catched['?'][1][1])
assert(source)
local result = {
id = noder.getID(source),
@@ -53,21 +51,21 @@ CARE['id'] = true
TEST [[
local <?x?>
]] {
- id = 'l:9',
+ id = 'l:6',
}
TEST [[
local x
print(<?x?>)
]] {
- id = 'l:7',
+ id = 'l:6',
}
TEST [[
local x
<?x?> = 1
]] {
- id = 'l:7',
+ id = 'l:6',
}
TEST [[
@@ -86,14 +84,14 @@ TEST [[
local x
print(x.y.<?z?>)
]] {
- id = 'l:7|.y|.z',
+ id = 'l:6|.y|.z',
}
TEST [[
local x
function x:<?f?>() end
]] {
- id = 'l:7|.f',
+ id = 'l:6|.f',
}
TEST [[
@@ -113,7 +111,7 @@ TEST [[
<?x?> = 1,
}
]] {
- id = 't:1|.x',
+ id = 't:0|.x',
}
TEST [[
@@ -134,12 +132,12 @@ TEST [[
::<?label?>::
goto label
]] {
- id = 'l:5',
+ id = 'l:2',
}
TEST [[
::label::
goto <?label?>
]] {
- id = 'l:3',
+ id = 'l:2',
}
diff --git a/test/catch.lua b/test/catch.lua
new file mode 100644
index 00000000..849be09d
--- /dev/null
+++ b/test/catch.lua
@@ -0,0 +1,84 @@
+local m = require 'lpeglabel'
+
+local mt = {}
+
+local function catchedTable()
+ return setmetatable({}, mt)
+end
+
+function mt.__add(a, b)
+ if not a or not b then
+ return a or b
+ end
+ local t = catchedTable()
+ for _, v in ipairs(a) do
+ t[#t+1] = v
+ end
+ for _, v in ipairs(b) do
+ t[#t+1] = v
+ end
+ return t
+end
+
+local function parseTokens(script, seps)
+ local parser = m.P {
+ m.Ct(m.V 'Token'^0),
+ Token = m.Cp() * (m.V 'Mark' + m.V 'Nl' + m.V 'Text'),
+ Mark = m.Cc 'ML' * m.P '<' * m.C(m.S(seps))
+ + m.Cc 'MR' * m.C(m.S(seps)) * m.P '>',
+ Nl = m.Cc 'NL' * m.C(m.P '\r\n' + m.S '\r\n'),
+ Text = m.Cc 'TX' * m.C((1 - m.V 'Nl' - m.V 'Mark')^1),
+ }
+ local results = parser:match(script)
+ return results
+end
+
+---@param script string
+---@param seps string
+return function (script, seps)
+ local tokens = parseTokens(script, seps)
+ local newBuf = {}
+ local result = {}
+ local marks = {}
+
+ local lineOffset = 1
+ local line = 0
+ local skipOffset = 0
+ for i = 1, #tokens, 3 do
+ local offset = tokens[i + 0]
+ local mode = tokens[i + 1]
+ local text = tokens[i + 2]
+ if mode == 'TX' then
+ newBuf[#newBuf+1] = text
+ end
+ if mode == 'NL' then
+ newBuf[#newBuf+1] = text
+ line = line + 1
+ lineOffset = offset + #text - skipOffset
+ end
+ if mode == 'ML' then
+ marks[#marks+1] = {
+ char = text,
+ position = line * 10000 + offset - skipOffset - lineOffset,
+ }
+ skipOffset = skipOffset + 1 + #text
+ end
+ if mode == 'MR' then
+ for j = #marks, 1, -1 do
+ local mark = marks[j]
+ if mark.char == text then
+ local position = line * 10000 + offset - skipOffset - lineOffset
+ if not result[text] then
+ result[text] = catchedTable()
+ end
+ result[text][#result[text]+1] = { mark.position, position }
+ table.remove(marks, j)
+ break
+ end
+ end
+ skipOffset = skipOffset + 1 + #text
+ end
+ end
+
+ return table.concat(newBuf), result
+end
diff --git a/test/code_action/init.lua b/test/code_action/init.lua
index 11ce6ec7..70d8c9ec 100644
--- a/test/code_action/init.lua
+++ b/test/code_action/init.lua
@@ -1,6 +1,7 @@
local core = require 'core.code-action'
local files = require 'files'
local lang = require 'language'
+local catch = require 'catch'
rawset(_G, 'TEST', true)
@@ -38,11 +39,10 @@ end
function TEST(script)
return function (expect)
files.removeAll()
- local start = script:find('<?', 1, true)
- local finish = script:find('?>', 1, true)
- local new_script = script:gsub('<[!?]', ' '):gsub('[!?]>', ' ')
- files.setText('', new_script)
- local results = core('', start, finish)
+
+ local newScript, catched = catch(script, '?')
+ files.setText('', newScript)
+ local results = core('', catched['?'][1][1], catched['?'][1][2])
assert(results)
assert(eq(expect, results))
end
diff --git a/test/command/auto-require.lua b/test/command/auto-require.lua
index 94bb5069..a52662fb 100644
--- a/test/command/auto-require.lua
+++ b/test/command/auto-require.lua
@@ -3,7 +3,7 @@ local files = require 'files'
local autoRequire = require 'core.command.autoRequire'
local client = require 'client'
-local findInsertOffset = util.getUpvalue(autoRequire, 'findInsertOffset')
+local findInsertRow = util.getUpvalue(autoRequire, 'findInsertRow')
local applyAutoRequire = util.getUpvalue(autoRequire, 'applyAutoRequire')
local originEditText = client.editText
@@ -19,18 +19,25 @@ function TEST(text)
files.removeAll()
files.setText('', text)
EditResult = nil
- local offset, fmt = findInsertOffset('')
- applyAutoRequire('', offset, name, name, fmt)
+ local row, fmt = findInsertRow('')
+ applyAutoRequire('', row, name, name, fmt)
assert(util.equal(EditResult, expect))
end
end
end
--- TODO change to position
TEST '' 'test' {
start = 0,
- finish = -1,
- text = '\nlocal test = require "test"\n'
+ finish = 0,
+ text = 'local test = require "test"\n'
+}
+
+TEST [[
+local aaaaaa = require 'aaa'
+]] 'test' {
+ start = 10000,
+ finish = 10000,
+ text = 'local test = require \'test\'\n'
}
client.editText = originEditText
diff --git a/test/completion/common.lua b/test/completion/common.lua
index 2efa63ad..33cbdf4d 100644
--- a/test/completion/common.lua
+++ b/test/completion/common.lua
@@ -8,7 +8,7 @@ config.set('Lua.completion.showWord', 'Enable')
TEST [[
local zabcde
-za$
+za<??>
]]
{
{
@@ -20,7 +20,7 @@ za$
TEST [[
local zabcdefg
local zabcde
-zabcde$
+zabcde<??>
]]
{
{
@@ -35,7 +35,7 @@ zabcde$
TEST [[
local zabcdefg
-za$
+za<??>
local zabcde
]]
{
@@ -51,7 +51,7 @@ local zabcde
TEST [[
local zabcde
-zace$
+zace<??>
]]
{
{
@@ -63,7 +63,7 @@ zace$
TEST [[
ZABC = x
local zabc
-zac$
+zac<??>
]]
{
{
@@ -77,7 +77,7 @@ zac$
}
TEST [[
-ass$
+ass<??>
]]
{
{
@@ -92,7 +92,7 @@ ass$
TEST [[
local assert = 1
-ass$
+ass<??>
]]
{
{
@@ -103,7 +103,7 @@ ass$
TEST [[
local assert = 1
-_G.ass$
+_G.ass<??>
]]
{
{
@@ -119,7 +119,7 @@ _G.ass$
TEST [[
local function ffff(a, b)
end
-ff$
+ff<??>
]]
{
{
@@ -134,7 +134,7 @@ ff$
TEST [[
local zabc = 1
-z$
+z<??>
]]
{
{
@@ -145,7 +145,7 @@ z$
TEST [[
local zabc = 1.0
-z$
+z<??>
]]
{
{
@@ -158,7 +158,7 @@ TEST [[
local t = {
abc = 1,
}
-t.ab$
+t.ab<??>
]]
{
{
@@ -172,7 +172,7 @@ local t = {
abc = 1,
}
local n = t.abc
-t.ab$
+t.ab<??>
]]
{
{
@@ -187,7 +187,7 @@ mt.ggg = 1
function mt:get(a, b)
return 1
end
-mt:g$
+mt:g<??>
]]
{
{
@@ -205,7 +205,7 @@ mt:g$
}
TEST [[
-loc$
+loc<??>
]]
{
{
@@ -220,7 +220,7 @@ loc$
IgnoreFunction = true
TEST [[
-do$
+do<??>
]]
{
{
@@ -234,7 +234,7 @@ do$
}
TEST [[
-while true d$
+while true d<??>
]]
{
{
@@ -248,18 +248,18 @@ while true d$
}
TEST [[
-results$
+results<??>
]]
(nil)
TEST [[
-result$
+result<??>
local results
]]
(EXISTS)
TEST [[
-local a$
+local a<??>
local function f(fff)
fff = ast
@@ -280,7 +280,7 @@ end
TEST [[
t.a = {}
t.b = {}
-t.$
+t.<??>
]]
{
{
@@ -296,7 +296,7 @@ t.$
TEST [[
t.a = {}
t.b = {}
-t. $
+t. <??>
]]
{
{
@@ -314,7 +314,7 @@ TEST [[
t.a = {}
function t:b()
end
-t:$
+t:<??>
]]
{
{
@@ -331,7 +331,7 @@ TEST [[
local t = {
a = {},
}
-t.$
+t.<??>
xxx()
]]
{
@@ -342,14 +342,14 @@ xxx()
}
TEST [[
-(''):$
+(''):<??>
]]
(EXISTS)
TEST [[
local zzz
-return 'aa' .. zz$
+return 'aa' .. zz<??>
]]
{
{
@@ -358,9 +358,9 @@ return 'aa' .. zz$
},
}
-TEST 'local s = "a:$"' (nil)
+TEST 'local s = "a:<??>"' (nil)
-TEST 'debug.$'
+TEST 'debug.<??>'
(EXISTS)
IgnoreFunction = true
@@ -371,7 +371,7 @@ local xxxx = {
}
local t = {
- x$
+ x<??>
}
]]
{
@@ -392,7 +392,7 @@ local t = {
TEST [[
print(ff2)
local faa
-local f$
+local f<??>
print(fff)
]]
{
@@ -419,7 +419,7 @@ print(fff)
}
TEST [[
-local function f(ff$)
+local function f(ff<??>)
print(fff)
end
]]
@@ -431,19 +431,19 @@ end
}
TEST [[
-collectgarbage($)
+collectgarbage(<??>)
]]
(EXISTS)
TEST [[
-collectgarbage('$')
+collectgarbage('<??>')
]]
{
{
label = "'collect'",
kind = define.CompletionItemKind.EnumMember,
textEdit = {
- start = 16,
+ start = 15,
finish = 17,
newText = "'collect'",
},
@@ -452,7 +452,7 @@ collectgarbage('$')
label = "'stop'",
kind = define.CompletionItemKind.EnumMember,
textEdit = {
- start = 16,
+ start = 15,
finish = 17,
newText = "'stop'",
},
@@ -461,7 +461,7 @@ collectgarbage('$')
label = "'restart'",
kind = define.CompletionItemKind.EnumMember,
textEdit = {
- start = 16,
+ start = 15,
finish = 17,
newText = "'restart'",
},
@@ -470,7 +470,7 @@ collectgarbage('$')
label = "'count'",
kind = define.CompletionItemKind.EnumMember,
textEdit = {
- start = 16,
+ start = 15,
finish = 17,
newText = "'count'",
},
@@ -479,7 +479,7 @@ collectgarbage('$')
label = "'step'",
kind = define.CompletionItemKind.EnumMember,
textEdit = {
- start = 16,
+ start = 15,
finish = 17,
newText = "'step'",
},
@@ -488,7 +488,7 @@ collectgarbage('$')
label = "'isrunning'",
kind = define.CompletionItemKind.EnumMember,
textEdit = {
- start = 16,
+ start = 15,
finish = 17,
newText = "'isrunning'",
},
@@ -497,7 +497,7 @@ collectgarbage('$')
label = "'incremental'",
kind = define.CompletionItemKind.EnumMember,
textEdit = {
- start = 16,
+ start = 15,
finish = 17,
newText = "'incremental'",
},
@@ -506,7 +506,7 @@ collectgarbage('$')
label = "'generational'",
kind = define.CompletionItemKind.EnumMember,
textEdit = {
- start = 16,
+ start = 15,
finish = 17,
newText = "'generational'",
},
@@ -514,7 +514,7 @@ collectgarbage('$')
}
TEST [[
-io.read($)
+io.read(<??>)
]]
{
{
@@ -536,25 +536,25 @@ io.read($)
}
TEST [[
-io.open('', $)
+io.open('', <??>)
]]
(EXISTS)
TEST [[
-local function f(a, $)
+local function f(a, <??>)
end
]]
(nil)
TEST [[
-self.results.list[#$]
+self.results.list[#<??>]
]]
{
{
label = '#self.results.list+1',
kind = define.CompletionItemKind.Snippet,
textEdit = {
- start = 19,
+ start = 18,
finish = 20,
newText = '#self.results.list+1] = ',
},
@@ -562,7 +562,7 @@ self.results.list[#$]
}
TEST [[
-self.results.list[#$]
+self.results.list[#<??>]
local n = 1
]]
{
@@ -570,7 +570,7 @@ local n = 1
label = '#self.results.list+1',
kind = define.CompletionItemKind.Snippet,
textEdit = {
- start = 19,
+ start = 18,
finish = 20,
newText = '#self.results.list+1] = ',
},
@@ -578,14 +578,14 @@ local n = 1
}
TEST [[
-self.results.list[#$] = 1
+self.results.list[#<??>] = 1
]]
{
{
label = '#self.results.list+1',
kind = define.CompletionItemKind.Snippet,
textEdit = {
- start = 19,
+ start = 18,
finish = 20,
newText = '#self.results.list+1]',
},
@@ -593,14 +593,14 @@ self.results.list[#$] = 1
}
TEST [[
-self.results.list[#self.re$]
+self.results.list[#self.re<??>]
]]
{
{
label = '#self.results.list+1',
kind = define.CompletionItemKind.Snippet,
textEdit = {
- start = 19,
+ start = 18,
finish = 27,
newText = '#self.results.list+1] = ',
},
@@ -612,14 +612,14 @@ self.results.list[#self.re$]
}
TEST [[
-fff[#ff$]
+fff[#ff<??>]
]]
{
{
label = '#fff+1',
kind = define.CompletionItemKind.Snippet,
textEdit = {
- start = 5,
+ start = 4,
finish = 8,
newText = '#fff+1] = ',
},
@@ -631,14 +631,14 @@ fff[#ff$]
}
TEST [[
-local _ = fff.kkk[#$]
+local _ = fff.kkk[#<??>]
]]
{
{
label = '#fff.kkk',
kind = define.CompletionItemKind.Snippet,
textEdit = {
- start = 19,
+ start = 18,
finish = 20,
newText = '#fff.kkk]',
},
@@ -646,14 +646,14 @@ local _ = fff.kkk[#$]
}
TEST [[
-fff.kkk[#$].yy
+fff.kkk[#<??>].yy
]]
{
{
label = '#fff.kkk',
kind = define.CompletionItemKind.Snippet,
textEdit = {
- start = 9,
+ start = 8,
finish = 10,
newText = '#fff.kkk]',
},
@@ -665,7 +665,7 @@ local t = {
a = 1,
}
-t . $
+t . <??>
]]
(EXISTS)
@@ -674,7 +674,7 @@ local t = {
a = 1,
}
-t . $ b
+t . <??> b
]]
(EXISTS)
@@ -683,7 +683,7 @@ local t = {
a = 1,
}
-t $
+t <??>
]]
(nil)
@@ -692,13 +692,13 @@ local t = {
a = 1,
}
-t $.
+t <??>.
]]
(nil)
TEST [[
local xxxx
-xxxx$
+xxxx<??>
]]
{
{
@@ -710,7 +710,7 @@ xxxx$
TEST [[
local xxxx
local XXXX
-xxxx$
+xxxx<??>
]]
{
{
@@ -727,7 +727,7 @@ TEST [[
local t = {
xxxxx = 1,
}
-xx$
+xx<??>
]]
{
{
@@ -738,7 +738,7 @@ xx$
TEST [[
local index
-tbl[inde$]
+tbl[inde<??>]
]]
{
{
@@ -753,7 +753,7 @@ return function ()
a = {},
b = {},
}
- t.$
+ t.<??>
end
]]
{
@@ -769,7 +769,7 @@ end
TEST [[
local ast = 1
-local t = 'as$'
+local t = 'as<??>'
local ask = 1
]]
(EXISTS)
@@ -777,7 +777,7 @@ local ask = 1
TEST [[
local add
-function f(ad$)
+function f(ad<??>)
local _ = add
end
]]
@@ -789,25 +789,25 @@ end
}
TEST [[
-function table.i$
+function table.i<??>
]]
(EXISTS)
TEST [[
do
- xx.$
+ xx.<??>
end
]]
(nil)
TEST [[
-print(io.$)
+print(io.<??>)
]]
(EXISTS)
require 'config'.set('Lua.runtime.version', 'Lua 5.4')
--TEST [[
---local $
+--local <??>
--]]
--{
-- {
@@ -821,7 +821,7 @@ require 'config'.set('Lua.runtime.version', 'Lua 5.4')
--}
--
--TEST [[
---local <toc$
+--local <toc<??>
--]]
--{
-- {
@@ -835,7 +835,7 @@ local mt = {}
mt.__index = mt
local t = setmetatable({}, mt)
-t.$
+t.<??>
]]
{
{
@@ -848,7 +848,7 @@ TEST [[
local elseaaa
ELSE = 1
if a then
-else$
+else<??>
]]
{
{
@@ -877,7 +877,7 @@ Cared['insertText'] = true
IgnoreFunction = false
TEST [[
local xpcal
-xpcal$
+xpcal<??>
]]
{
{
@@ -900,7 +900,7 @@ TEST [[
function mt:f(a, b, c)
end
-mt:f$
+mt:f<??>
]]
{
{
@@ -916,7 +916,7 @@ mt:f$
}
TEST [[
-function$
+function<??>
]]
{
{
@@ -934,7 +934,7 @@ end",
}
TEST [[
-local t = function$
+local t = function<??>
]]
{
{
@@ -956,7 +956,7 @@ IgnoreFunction = true
TEST [[
local function f()
if a then
- else$
+ else<??>
end
]]
{
@@ -979,21 +979,21 @@ local t = {
['a.b.c'] = {}
}
-t.$
+t.<??>
]]
{
{
label = 'a.b.c',
kind = define.CompletionItemKind.Field,
textEdit = {
- start = 37,
- finish = 36,
+ start = 40002,
+ finish = 40002,
newText = '["a.b.c"]',
},
additionalTextEdits = {
{
- start = 36,
- finish = 36,
+ start = 40001,
+ finish = 40002,
newText = '',
},
},
@@ -1005,21 +1005,21 @@ local t = {
['a.b.c'] = {}
}
-t. $
+t. <??>
]]
{
{
label = 'a.b.c',
kind = define.CompletionItemKind.Field,
textEdit = {
- start = 40,
- finish = 39,
+ start = 40005,
+ finish = 40005,
newText = '["a.b.c"]',
},
additionalTextEdits = {
{
- start = 36,
- finish = 36,
+ start = 40001,
+ finish = 40002,
newText = '',
},
},
@@ -1031,15 +1031,15 @@ local t = {
['a.b.c'] = {}
}
-t['$']
+t['<??>']
]]
{
{
label = 'a.b.c',
kind = define.CompletionItemKind.Field,
textEdit = {
- start = 38,
- finish = 37,
+ start = 40003,
+ finish = 40003,
newText = 'a.b.c',
}
}
@@ -1048,33 +1048,33 @@ t['$']
TEST [[
_ENV['z.b.c'] = {}
-z$
+z<??>
]]
{
{
label = 'z.b.c',
kind = define.CompletionItemKind.Field,
textEdit = {
- start = 21,
- finish = 21,
+ start = 20000,
+ finish = 20001,
newText = '_ENV["z.b.c"]',
},
},
}
TEST [[
-io.close(1, $)
+io.close(1, <??>)
]]
(nil)
TEST [[
-io$
+io<??>
]]
(EXISTS)
IgnoreFunction = false
TEST [[
-loadstring$
+loadstring<??>
]]
{
{
@@ -1090,7 +1090,7 @@ loadstring$
}
--TEST [[
---bit32$
+--bit32<??>
--]]
--{
-- {
@@ -1103,7 +1103,7 @@ loadstring$
TEST [[
function loadstring()
end
-loadstring$
+loadstring<??>
]]
{
{
@@ -1127,7 +1127,7 @@ loadstring$
}
TEST [[
-debug.setcsta$
+debug.setcsta<??>
]]
{
{
@@ -1143,12 +1143,12 @@ debug.setcsta$
}
TEST [[
----@$
+---@<??>
]]
(EXISTS)
TEST [[
----@cl$
+---@cl<??>
]]
{
{
@@ -1159,7 +1159,7 @@ TEST [[
TEST [[
---@class ZABC
----@class ZBBC : Z$
+---@class ZBBC : Z<??>
]]
{
{
@@ -1170,14 +1170,14 @@ TEST [[
TEST [[
---@class ZABC
----@class ZBBC : $
+---@class ZBBC : <??>
]]
(EXISTS)
TEST [[
---@class zabc
local abcd
----@type za$
+---@type za<??>
]]
{
{
@@ -1189,14 +1189,14 @@ local abcd
TEST [[
---@class abc
local abcd
----@type $
+---@type <??>
]]
(EXISTS)
TEST [[
---@class zabc
local abcd
----@type zxxx|z$
+---@type zxxx|z<??>
]]
{
{
@@ -1207,7 +1207,7 @@ local abcd
TEST [[
---@alias zabc zabb
----@type za$
+---@type za<??>
]]
{
{
@@ -1218,7 +1218,7 @@ TEST [[
TEST [[
---@class ZClass
----@param x ZC$
+---@param x ZC<??>
]]
{
{
@@ -1229,7 +1229,7 @@ TEST [[
Cared['insertText'] = true
TEST [[
----@param $
+---@param <??>
function f(a, b, c)
end
]]
@@ -1257,7 +1257,7 @@ a ${1:any}
}
TEST [[
----@param $
+---@param <??>
function f(a, b, c) end
function f2(a) end
@@ -1286,7 +1286,7 @@ a ${1:any}
}
TEST [[
----@param aa$
+---@param aa<??>
function f(aaa, bbb, ccc)
end
]]
@@ -1299,7 +1299,7 @@ end
TEST [[
local function f()
- ---@param $
+ ---@param <??>
function f(a, b, c)
end
end
@@ -1328,7 +1328,7 @@ a ${1:any}
}
TEST [[
----@param $
+---@param <??>
function mt:f(a, b, c, ...)
end
]]
@@ -1360,7 +1360,7 @@ a ${1:any}
}
TEST [[
----@param aaa $
+---@param aaa <??>
function f(aaa, bbb, ccc)
end
]]
@@ -1369,7 +1369,7 @@ end
TEST [[
---@param xyz Class
---@param xxx Class
-function f(x$)
+function f(x<??>)
]]
{
{
@@ -1389,7 +1389,7 @@ function f(x$)
TEST [[
---@param xyz Class
---@param xxx Class
-function f($
+function f(<??>
]]
{
{
@@ -1409,7 +1409,7 @@ function f($
TEST [[
---@param xyz Class
---@param xxx Class
-function f($)
+function f(<??>)
]]
{
{
@@ -1428,7 +1428,7 @@ function f($)
TEST [[
local function f()
- ---@t$
+ ---@t<??>
end
]]
{
@@ -1443,7 +1443,7 @@ TEST [[
---@field name string
---@field id integer
local mt = {}
-mt.$
+mt.<??>
]]
{
{
@@ -1461,7 +1461,7 @@ TEST [[
function f(y, x)
end
-f(1, $)
+f(1, <??>)
]]
{
{
@@ -1483,7 +1483,7 @@ TEST [[
function f(y, x)
end
-f(1,$)
+f(1,<??>)
]]
{
{
@@ -1505,7 +1505,7 @@ TEST [[
function f(x)
end
-f($)
+f(<??>)
]]
{
{
@@ -1528,7 +1528,7 @@ TEST [[
function f(x)
end
-f($)
+f(<??>)
]]
{
{
@@ -1550,7 +1550,7 @@ TEST [[
function f(x)
end
-f('$')
+f('<??>')
]]
{
{
@@ -1586,7 +1586,7 @@ TEST [[
local function f(x)
end
-f($)
+f(<??>)
]]
{
{
@@ -1619,7 +1619,7 @@ end
---comment 3
---| '3'
-f($)
+f(<??>)
]]
{
{
@@ -1638,7 +1638,7 @@ function f(x)
end
f(function ()
- $
+ <??>
end)
]]
(nil)
@@ -1652,7 +1652,7 @@ TEST [[
---@return string
local function zzzzz(list, sep, i, j) end
-zzz$
+zzz<??>
]]
{
{
@@ -1672,7 +1672,7 @@ Cared['description'] = true
TEST [[
--- abc
zzz = 1
-zz$
+zz<??>
]]
{
{
@@ -1695,7 +1695,7 @@ TEST [[
---| "'选项2'" # 注释2
function f(x) end
-f($)
+f(<??>)
]]
{
{
@@ -1711,7 +1711,7 @@ f($)
}
TEST [[
-utf8.charpatter$
+utf8.charpatter<??>
]]
{
{
@@ -1726,7 +1726,7 @@ TEST [[
---@type "'a'"|"'b'"|"'c'"
local x
-print(x == $)
+print(x == <??>)
]]
{
{
@@ -1747,7 +1747,7 @@ TEST [[
---@type "'a'"|"'b'"|"'c'"
local x
-x = $
+x = <??>
]]
{
{
@@ -1768,7 +1768,7 @@ TEST [[
---@type "'a'"|"'b'"|"'c'"
local x
-print(x == '$')
+print(x == '<??>')
]]
{
{
@@ -1792,7 +1792,7 @@ TEST [[
---@type "'a'"|"'b'"|"'c'"
local x
-x = '$'
+x = '<??>'
]]
{
{
@@ -1815,24 +1815,24 @@ x = '$'
TEST [[
local t = type()
-print(t == $)
+print(t == <??>)
]]
(EXISTS)
TEST [[
-if type(arg) == '$'
+if type(arg) == '<??>'
]]
(EXISTS)
TEST [[
-if type(arg) == $
+if type(arg) == <??>
]]
(EXISTS)
TEST [[
---@type string
local s
-s.$
+s.<??>
]]
(EXISTS)
@@ -1841,7 +1841,7 @@ TEST [[
local t
local vvv = assert(t)
-vvv$
+vvv<??>
]]
{
{
@@ -1857,7 +1857,7 @@ TEST [[
---@param callback fun(x: number, y: number):string
local function f(callback) end
-f($)
+f(<??>)
]]
{
{
@@ -1871,7 +1871,7 @@ end",
}
TEST [[
----$
+---<??>
local function f(a, b, c)
return a + 1, b .. '', c[1]
end
@@ -1894,7 +1894,7 @@ ${1:comment}\
Cared['insertText'] = nil
TEST [[
---$
+--<??>
]]
{
{
@@ -1918,7 +1918,7 @@ TEST [[
local function f(x) end
f({
- $
+ <??>
})
]]
{
@@ -1942,7 +1942,7 @@ local function f(x) end
f({
aaa = 1,
- $
+ <??>
})
]]
{
@@ -1960,7 +1960,7 @@ TEST [[
---@param x cc
local function f(x) end
-f({aaa = 1,$})
+f({aaa = 1,<??>})
]]
{
{
@@ -1977,7 +1977,7 @@ TEST [[
---@param x cc
local function f(x) end
-f({aaa $})
+f({aaa <??>})
]]
(nil)
@@ -1988,7 +1988,7 @@ TEST [[
---@param x cc
local function f(x) end
-f({if$})
+f({if<??>})
]]
{
include = true,
@@ -2008,7 +2008,7 @@ local function f(x) end
f({
{
- $
+ <??>
}
})
]]
@@ -2020,7 +2020,7 @@ local function f() end
local s = f()
-s.$
+s.<??>
]]
(EXISTS)
@@ -2032,7 +2032,7 @@ TEST [[
---@type cc
local t
-print(t.aa$)
+print(t.aa<??>)
]]
{
{
@@ -2050,7 +2050,7 @@ TEST [[
---@type table<string, "'a'"|"'b'"|"'c'">
local x
-x.a = $
+x.a = <??>
]]
{
{
@@ -2071,7 +2071,7 @@ TEST [[
---@type table<string, "'a'"|"'b'"|"'c'">
local x
-x['a'] = $
+x['a'] = <??>
]]
{
{
@@ -2091,7 +2091,7 @@ x['a'] = $
TEST [[
---@type table<string, "'a'"|"'b'"|"'c'">
local x = {
- a = $
+ a = <??>
}
]]
{
@@ -2112,7 +2112,7 @@ local x = {
TEST [[
---@type table<string, "'a'"|"'b'"|"'c'">
local x = {
- ['a'] = $
+ ['a'] = <??>
}
]]
{
@@ -2138,7 +2138,7 @@ local m
function m.f()
end
-m.f$
+m.f<??>
]]{
{
label = "f()",
@@ -2154,7 +2154,7 @@ m.f$
Cared['insertText'] = nil
TEST [[
-if true then$
+if true then<??>
]]
{
{
@@ -2168,7 +2168,7 @@ if true then$
}
TEST [[
-if true then$
+if true then<??>
end
]]
{
@@ -2179,7 +2179,7 @@ end
}
TEST [[
-if true then$
+if true then<??>
else
]]
{
@@ -2190,7 +2190,7 @@ else
}
TEST [[
-if true then$
+if true then<??>
elseif
]]
{
@@ -2202,7 +2202,7 @@ elseif
TEST [[
do
- if true then$
+ if true then<??>
end
]]
{
@@ -2226,7 +2226,7 @@ local function f(x, ...)
end
f(1, {
- $
+ <??>
})
]]
{
@@ -2250,7 +2250,7 @@ local function f(x, ...)
end
f(1, {}, {}, {
- $
+ <??>
})
]]
{
@@ -2271,7 +2271,7 @@ TEST [[
---@type C
local t = {
- $
+ <??>
}
]]
@@ -2293,7 +2293,7 @@ TEST [[
---@type C
local t = {
- x$
+ x<??>
}
]]
@@ -2306,19 +2306,19 @@ local t = {
}
TEST [[
-if $ then
+if <??> then
]]
(nil)
TEST [[
-elseif $ then
+elseif <??> then
]]
(nil)
TEST [[
---@type iolib
local t = {
- $
+ <??>
]]
(EXISTS)
@@ -2329,7 +2329,7 @@ TEST [[
---@param t A
function api(t) end
-api({$})
+api({<??>})
]]
(EXISTS)
@@ -2340,22 +2340,22 @@ TEST [[
---@param t A
function m:api(t) end
-m:api({$})
+m:api({<??>})
]]
(EXISTS)
TEST [[
---@class AAA.BBB
----@type AAA.$
+---@type AAA.<??>
]]
{
{
label = 'AAA.BBB',
kind = define.CompletionItemKind.Class,
textEdit = {
- start = 29,
- finish = 32,
+ start = 20009,
+ finish = 20013,
newText = 'AAA.BBB',
},
}
@@ -2365,7 +2365,7 @@ Cared['insertText'] = true
TEST [[
---@overload fun(a: any, b: any)
local function zzzz(a) end
-zzzz$
+zzzz<??>
]]
{
{
@@ -2406,13 +2406,13 @@ local tarray
local b = tdirect -- type . here, shows "world"
-- Inferred by index
-local c = tarray[1].$ -- type . here, no auto completion
+local c = tarray[1].<??> -- type . here, no auto completion
]]
(EXISTS)
TEST [[
local function f()
- if type() == '$' then
+ if type() == '<??>' then
end
end
]]
@@ -2425,7 +2425,7 @@ GGG = 1
GGG = function ()
end
-GGG$
+GGG<??>
]]
{
{
@@ -2446,7 +2446,7 @@ local t = {}
t.GGG = function ()
end
-t.GGG$
+t.GGG<??>
]]
{
{
@@ -2463,7 +2463,7 @@ TEST [[
---@param f fun(a: any, b: any):boolean
local function f(f) end
-f(fun$)
+f(fun<??>)
]]
{
{
@@ -2471,8 +2471,8 @@ f(fun$)
kind = define.CompletionItemKind.Function,
textEdit = {
newText = 'function (${1:a}, ${2:b})\n\t$0\nend',
- start = 68,
- finish = 70,
+ start = 30002,
+ finish = 30005,
}
},
{
@@ -2489,7 +2489,7 @@ TEST [[
---@type {[1]: number}
local t
-t.$
+t.<??>
]]
{
{
@@ -2497,13 +2497,13 @@ t.$
kind = define.CompletionItemKind.Field,
textEdit = {
newText = '[1]',
- start = 35,
- finish = 34,
+ start = 30002,
+ finish = 30002,
},
additionalTextEdits = {
{
- start = 34,
- finish = 34,
+ start = 30001,
+ finish = 30002,
newText = '',
},
},
@@ -2514,7 +2514,7 @@ TEST [[
---@type {[1]: number}
local t
-t.$
+t.<??>
]]
{
{
@@ -2522,13 +2522,13 @@ t.$
kind = define.CompletionItemKind.Field,
textEdit = {
newText = '[1]',
- start = 35,
- finish = 34,
+ start = 30002,
+ finish = 30002,
},
additionalTextEdits = {
{
- start = 34,
- finish = 34,
+ start = 30001,
+ finish = 30002,
newText = '',
},
},
@@ -2543,7 +2543,7 @@ TEST [[
local function f(x)
end
-local r = f('$')
+local r = f('<??>')
]]
{
{
@@ -2551,8 +2551,8 @@ local r = f('$')
kind = define.CompletionItemKind.EnumMember,
textEdit = {
newText = "'aaa'",
- start = 103,
- finish = 104,
+ start = 70012,
+ finish = 70014,
},
},
{
@@ -2560,8 +2560,8 @@ local r = f('$')
kind = define.CompletionItemKind.EnumMember,
textEdit = {
newText = "'bbb'",
- start = 103,
- finish = 104,
+ start = 70012,
+ finish = 70014,
},
},
}
@@ -2570,7 +2570,7 @@ TEST [[
---@type fun(x: "'aaa'"|"'bbb'")
local f
-f('$')
+f('<??>')
]]
{
{
@@ -2578,8 +2578,8 @@ f('$')
kind = define.CompletionItemKind.EnumMember,
textEdit = {
newText = "'aaa'",
- start = 45,
- finish = 46,
+ start = 30002,
+ finish = 30004,
},
},
{
@@ -2587,8 +2587,8 @@ f('$')
kind = define.CompletionItemKind.EnumMember,
textEdit = {
newText = "'bbb'",
- start = 45,
- finish = 46,
+ start = 30002,
+ finish = 30004,
},
},
}
@@ -2598,7 +2598,7 @@ TEST [[
---@field on fun()
local c
-c:$
+c:<??>
]]
{
{
@@ -2612,7 +2612,7 @@ TEST [[
---@field on fun(x: "'aaa'"|"'bbb'")
local c
-c:on($)
+c:on(<??>)
]]
(EXISTS)
@@ -2621,7 +2621,7 @@ TEST [[
---@field on fun(x: "'aaa'"|"'bbb'")
local c
-c:on('$')
+c:on('<??>')
]]
(EXISTS)
@@ -2632,7 +2632,7 @@ function m.f()
end
m.f()
-m.$
+m.<??>
]]
{
[1] = EXISTS,
@@ -2647,7 +2647,7 @@ function class1:method1() end
---@class class2 : class1
class2 = {}
-class2:$
+class2:<??>
]]
{
diff --git a/test/completion/continue.lua b/test/completion/continue.lua
index 4159ea7a..63f970e9 100644
--- a/test/completion/continue.lua
+++ b/test/completion/continue.lua
@@ -9,7 +9,7 @@ ContinueTyping = true
TEST [[
local zabcde
-za$
+za<??>
]]
{
{
@@ -20,7 +20,7 @@ za$
TEST [[
-- zabcde
-io.z$
+io.z<??>
]]
{
{
@@ -32,7 +32,7 @@ io.z$
TEST [[
-- provider
-pro$
+pro<??>
]]
{
{
@@ -45,7 +45,7 @@ TEST [[
---@param n '"abcdefg"'
local function f(n) end
-f 'abc$'
+f 'abc<??>'
]]
{
{
@@ -59,7 +59,7 @@ TEST [[
---@type '"abcdefg"'
local t
-if t == 'abc$'
+if t == 'abc<??>'
]]
{
{
diff --git a/test/completion/init.lua b/test/completion/init.lua
index 24c8932f..051c288c 100644
--- a/test/completion/init.lua
+++ b/test/completion/init.lua
@@ -1,5 +1,6 @@
local core = require 'core.completion'
local files = require 'files'
+local catch = require 'catch'
EXISTS = {'EXISTS'}
@@ -64,17 +65,17 @@ ContinueTyping = false
function TEST(script)
return function (expect)
files.removeAll()
- local pos = script:find('$', 1, true) - 1
- local new_script = script:gsub('%$', '')
+ local newScript, catched = catch(script, '?')
- files.setText('', new_script)
+ files.setText('', newScript)
core.clearCache()
+ local inputPos = catched['?'][1][1]
if ContinueTyping then
- local triggerCharacter = script:sub(pos - 1, pos - 1)
- core.completion('', pos - 1, triggerCharacter)
+ local triggerCharacter = script:sub(inputPos - 1, inputPos - 1)
+ core.completion('', inputPos, triggerCharacter)
end
- local triggerCharacter = script:sub(pos, pos)
- local result = core.completion('', pos, triggerCharacter)
+ local triggerCharacter = script:sub(inputPos, inputPos)
+ local result = core.completion('', inputPos, triggerCharacter)
if not expect then
assert(result == nil)
return
diff --git a/test/crossfile/allreferences.lua b/test/crossfile/allreferences.lua
index 056fa416..bddc159d 100644
--- a/test/crossfile/allreferences.lua
+++ b/test/crossfile/allreferences.lua
@@ -9,10 +9,9 @@ TEST {
{
path = 'lib.lua',
content = [[
- return <~function~> ()
- end
+ return <!<?function?> ()
+ end!>
]],
- target = {22, 50},
},
}
@@ -21,7 +20,7 @@ TEST {
path = 'a.lua',
content = [[
local m = {}
- function m.<?func?>()
+ function m.<~func~>()
end
return m
]],
@@ -39,7 +38,7 @@ TEST {
{
path = 'a.lua',
content = [[
- return <?function () end?>
+ return <~function () end~>
]],
},
{
@@ -72,7 +71,7 @@ TEST {
{
path = 'a.lua',
content = [[
- local function <?f?>()
+ local function <~f~>()
end
return {
@@ -126,7 +125,7 @@ TEST {
local t = require 'a'
local <!f!> = t.<!f!>
- <?f?>()
+ <~f~>()
return {
<!f!> = <!f!>,
@@ -139,7 +138,7 @@ TEST {
{
path = 'a.lua',
content = [[
- local function <?f?>()
+ local function <~f~>()
end
return {
@@ -202,7 +201,7 @@ TEST {
{
path = 'a.lua',
content = [[
- local <?t?> = require 'b'
+ local <~t~> = require 'b'
return <!t!>
]]
},
diff --git a/test/crossfile/completion.lua b/test/crossfile/completion.lua
index d26398ca..9c5ed96e 100644
--- a/test/crossfile/completion.lua
+++ b/test/crossfile/completion.lua
@@ -4,6 +4,7 @@ local furi = require 'file-uri'
local platform = require 'bee.platform'
local util = require 'utility'
local config = require 'config'
+local catch = require 'catch'
rawset(_G, 'TEST', true)
@@ -88,8 +89,9 @@ function TEST(data)
local uri = furi.encode(info.path)
local script = info.content
if info.main then
- pos = script:find('$', 1, true) - 1
- script = script:gsub('%$', '')
+ local newScript, catched = catch(script, '?')
+ pos = catched['?'][1][1]
+ script = newScript
mainUri = uri
end
files.setText(uri, script)
@@ -139,7 +141,7 @@ TEST {
},
{
path = 'test.lua',
- content = 'require "a$"',
+ content = 'require "a<??>"',
main = true,
},
completion = {
@@ -178,7 +180,7 @@ TEST {
},
{
path = 'test.lua',
- content = 'require "A$"',
+ content = 'require "A<??>"',
main = true,
},
completion = {
@@ -201,7 +203,7 @@ TEST {
},
{
path = 'test.lua',
- content = 'require "a$"',
+ content = 'require "a<??>"',
main = true,
},
completion = {
@@ -229,7 +231,7 @@ TEST {
},
{
path = 'test.lua',
- content = 'require "abc$"',
+ content = 'require "abc<??>"',
main = true,
},
completion = {
@@ -257,7 +259,7 @@ TEST {
},
{
path = 'test.lua',
- content = 'require "abc$"',
+ content = 'require "abc<??>"',
main = true,
},
completion = {
@@ -290,7 +292,7 @@ TEST {
},
{
path = 'test.lua',
- content = 'require "abc.i$"',
+ content = 'require "abc.i<??>"',
main = true,
},
completion = {
@@ -315,7 +317,7 @@ TEST {
},
{
path = 'test.lua',
- content = 'require "abc/i$"',
+ content = 'require "abc/i<??>"',
main = true,
},
completion = {
@@ -339,7 +341,7 @@ TEST {
},
{
path = 'test.lua',
- content = 'require "core.co$"',
+ content = 'require "core.co<??>"',
main = true,
},
completion = {
@@ -362,7 +364,7 @@ TEST {
},
{
path = 'abc/test.lua',
- content = 'require "x$"',
+ content = 'require "x<??>"',
main = true,
},
completion = {
@@ -397,7 +399,7 @@ TEST {
},
{
path = 'main.lua',
- content = 'require "x$"',
+ content = 'require "x<??>"',
main = true,
},
completion = {
@@ -428,7 +430,7 @@ TEST {
},
{
path = 'main.lua',
- content = 'require "x$"',
+ content = 'require "x<??>"',
main = true,
},
completion = {
@@ -457,7 +459,7 @@ TEST {
path = 'b.lua',
content = [[
local t = require 'a'
- t.$
+ t.<??>
]],
main = true,
},
@@ -499,7 +501,7 @@ TEST {
{
path = 'b.lua',
content = [[
- zab$
+ zab<??>
]],
main = true,
},
@@ -533,7 +535,7 @@ TEST {
{
path = 'b.lua',
content = [[
- zab$
+ zab<??>
]],
main = true,
},
@@ -557,7 +559,7 @@ TEST {
path = 'a.lua',
content = [[
local japi = require 'jass.japi'
- japi.xxxaaaax$
+ japi.xxxaaaax<??>
]],
main = true,
},
@@ -571,7 +573,7 @@ TEST {
{
path = 'xxxx.lua',
content = [[
- require 'xx$'
+ require 'xx<??>'
]],
main = true,
},
@@ -592,7 +594,7 @@ TEST {
{
path = 'main.lua',
content = [[
- require 'xx$'
+ require 'xx<??>'
]],
main = true,
},
@@ -613,7 +615,7 @@ TEST {
{
path = 'main.lua',
content = [[
- require [=[xx$]=]'
+ require [=[xx<??>]=]'
]],
main = true,
},
@@ -635,7 +637,7 @@ TEST {
{
path = 'main.lua',
content = [[
- dofile 'ab$'
+ dofile 'ab<??>'
]],
main = true,
},
@@ -656,7 +658,7 @@ TEST {
{
path = 'main.lua',
content = [[
- dofile 'ab$'
+ dofile 'ab<??>'
]],
main = true,
},
@@ -686,7 +688,7 @@ TEST {
content = [[
local t = require 'a'
local v = setmetatable({}, {__index = t})
- v.$
+ v.<??>
]]
},
completion = {
@@ -712,7 +714,7 @@ TEST {
content = [[
local z = require 'a'
- z$
+ z<??>
]],
main = true,
},
@@ -743,7 +745,7 @@ TEST {
path = 'main.lua',
main = true,
content = [[
- myfun$
+ myfun<??>
]],
},
completion = {
@@ -774,7 +776,7 @@ TEST {
path = 'main.lua',
main = true,
content = [[
- myfun$
+ myfun<??>
]],
},
completion = {
@@ -807,7 +809,7 @@ TEST {
path = 'main.lua',
main = true,
content = [[
- A.$
+ A.<??>
]],
},
completion = EXISTS,
@@ -819,7 +821,7 @@ TEST {
path = 'main.lua',
main = true,
content = [[
- require'$
+ require'<??>
]]
},
completion = EXISTS
diff --git a/test/crossfile/definition.lua b/test/crossfile/definition.lua
index 66e9b269..01bf0202 100644
--- a/test/crossfile/definition.lua
+++ b/test/crossfile/definition.lua
@@ -3,26 +3,10 @@ local furi = require 'file-uri'
local core = require 'core.definition'
local config = require 'config'
local platform = require 'bee.platform'
+local catch = require 'catch'
rawset(_G, 'TEST', true)
-local function catch_target(script, sep)
- local list = {}
- local cur = 1
- local cut = 0
- while true do
- local start, finish = script:find(('<%%%s.-%%%s>'):format(sep, sep), cur)
- if not start then
- break
- end
- list[#list+1] = { start - cut, finish - 4 - cut }
- cur = finish + 1
- cut = cut + 4
- end
- local new_script = script:gsub(('<%%%s(.-)%%%s>'):format(sep, sep), '%1')
- return new_script, list
-end
-
local function founded(targets, results)
if #targets ~= #results then
return false
@@ -50,32 +34,26 @@ function TEST(datas)
local sourceUri
for i, data in ipairs(datas) do
local uri = furi.encode(data.path)
- local new, list = catch_target(data.content, '!')
- if new ~= data.content or data.target then
- if data.target then
- targetList[#targetList+1] = {
- data.target[1],
- data.target[2],
- uri,
- }
- else
- for _, position in ipairs(list) do
- targetList[#targetList+1] = {
- position[1],
- position[2],
- uri,
- }
- end
- end
- data.content = new
+ local newScript, catched = catch(data.content, '!?~')
+ for _, position in ipairs(catched['!'] or {}) do
+ targetList[#targetList+1] = {
+ position[1],
+ position[2],
+ uri,
+ }
+ end
+ for _, position in ipairs(catched['~'] or {}) do
+ targetList[#targetList+1] = {
+ position[1],
+ position[2],
+ uri,
+ }
end
- new, list = catch_target(data.content, '?')
- if new ~= data.content then
- sourceList = list
+ if catched['?'] or catched['~'] then
+ sourceList = catched['?'] or catched['~']
sourceUri = uri
- data.content = new
end
- files.setText(uri, data.content)
+ files.setText(uri, newScript)
end
local sourcePos = (sourceList[1][1] + sourceList[1][2]) // 2
@@ -98,8 +76,7 @@ end
TEST {
{
path = 'a.lua',
- content = '',
- target = {0, 0},
+ content = '<!!>',
},
{
path = 'b.lua',
@@ -110,8 +87,7 @@ TEST {
TEST {
{
path = 'aaa/bbb.lua',
- content = '',
- target = {0, 0},
+ content = '<!!>',
},
{
path = 'b.lua',
@@ -122,8 +98,7 @@ TEST {
TEST {
{
path = '@bbb.lua',
- content = '',
- target = {0, 0},
+ content = '<!!>',
},
{
path = 'b.lua',
@@ -134,8 +109,7 @@ TEST {
TEST {
{
path = 'aaa/bbb.lua',
- content = '',
- target = {0, 0},
+ content = '<!!>',
},
{
path = 'b.lua',
@@ -150,8 +124,7 @@ TEST {
},
{
path = 'b.lua',
- content = 'local <?t?> = require "a"',
- target = {7, 7},
+ content = 'local <~t~> = require "a"',
},
}
@@ -176,8 +149,7 @@ TEST {
},
{
path = 'b.lua',
- content = 'local <?t?> = require "a"',
- target = {7, 7},
+ content = 'local <~t~> = require "a"',
},
}
@@ -245,9 +217,8 @@ TEST {
{
path = 'b.lua',
content = [[
- local <?t?> = require 'a'
+ local <~t~> = require 'a'
]],
- target = {19, 19},
},
}
diff --git a/test/crossfile/diagnostic.lua b/test/crossfile/diagnostic.lua
index a430cb4e..adc2961f 100644
--- a/test/crossfile/diagnostic.lua
+++ b/test/crossfile/diagnostic.lua
@@ -3,28 +3,12 @@ local furi = require 'file-uri'
local core = require 'core.diagnostics'
local config = require 'config'
local platform = require 'bee.platform'
+local catch = require 'catch'
config.get 'Lua.diagnostics.neededFileStatus'['deprecated'] = 'Any'
rawset(_G, 'TEST', true)
-local function catch_target(script, sep)
- local list = {}
- local cur = 1
- local cut = 0
- while true do
- local start, finish = script:find(('<%%%s.-%%%s>'):format(sep, sep), cur)
- if not start then
- break
- end
- list[#list+1] = { start - cut, finish - 4 - cut }
- cur = finish + 1
- cut = cut + 4
- end
- local new_script = script:gsub(('<%%%s(.-)%%%s>'):format(sep, sep), '%1')
- return new_script, list
-end
-
local function founded(targets, results)
if #targets ~= #results then
return false
@@ -48,19 +32,18 @@ function TEST(datas)
files.removeAll()
local targetList = {}
- local sourceUri
for _, data in ipairs(datas) do
local uri = furi.encode(data.path)
- local new, list = catch_target(data.content, '!')
- for _, position in ipairs(list) do
+ local newScript, catched = catch(data.content, '!')
+ for _, position in ipairs(catched['!'] or {}) do
targetList[#targetList+1] = {
position[1],
position[2],
uri,
}
end
- data.content = new
- files.setText(uri, new)
+ data.content = newScript
+ files.setText(uri, newScript)
end
local result = {}
diff --git a/test/crossfile/hover.lua b/test/crossfile/hover.lua
index a877d226..1c46214c 100644
--- a/test/crossfile/hover.lua
+++ b/test/crossfile/hover.lua
@@ -2,6 +2,7 @@ local files = require 'files'
local furi = require 'file-uri'
local core = require 'core.hover'
local config = require 'config'
+local catch = require 'catch'
rawset(_G, 'TEST', true)
@@ -36,36 +37,19 @@ local function eq(a, b)
return a == b
end
-local function catch_target(script, sep)
- local list = {}
- local cur = 1
- local cut = 0
- while true do
- local start, finish = script:find(('<%%%s.-%%%s>'):format(sep, sep), cur)
- if not start then
- break
- end
- list[#list+1] = { start - cut, finish - 4 - cut }
- cur = finish + 1
- cut = cut + 4
- end
- local new_script = script:gsub(('<%%%s(.-)%%%s>'):format(sep, sep), '%1')
- return new_script, list
-end
-
function TEST(expect)
files.removeAll()
local targetScript = expect[1].content
local targetUri = furi.encode(expect[1].path)
- local sourceScript, sourceList = catch_target(expect[2].content, '?')
+ local sourceScript, sourceList = catch(expect[2].content, '?')
local sourceUri = furi.encode(expect[2].path)
files.setText(targetUri, targetScript)
files.setText(sourceUri, sourceScript)
- local sourcePos = (sourceList[1][1] + sourceList[1][2]) // 2
+ local sourcePos = (sourceList['?'][1][1] + sourceList['?'][1][2]) // 2
local hover = core.byUri(sourceUri, sourcePos)
assert(hover)
hover = tostring(hover):gsub('\r\n', '\n')
diff --git a/test/crossfile/references.lua b/test/crossfile/references.lua
index 6c28b34b..145792b5 100644
--- a/test/crossfile/references.lua
+++ b/test/crossfile/references.lua
@@ -1,6 +1,7 @@
local files = require 'files'
local furi = require 'file-uri'
local core = require 'core.reference'
+local catch = require 'catch'
rawset(_G, 'TEST', true)
@@ -32,21 +33,6 @@ local function eq(a, b)
return a == b
end
-local function catch_target(script, sep)
- local list = {}
- local cur = 1
- while true do
- local start, finish = script:find(('<%%%s.-%%%s>'):format(sep, sep), cur)
- if not start then
- break
- end
- list[#list+1] = { start + 2, finish - 2 }
- cur = finish + 1
- end
- local new_script = script:gsub(('<%%%s(.-)%%%s>'):format(sep, sep), ' %1 ')
- return new_script, list
-end
-
local function founded(targets, results)
if #targets ~= #results then
return false
@@ -74,37 +60,9 @@ function TEST(datas)
local sourceUri
for i, data in ipairs(datas) do
local uri = furi.encode(data.path)
- local new, list = catch_target(data.content, '!')
- if new ~= data.content or data.target then
- if data.target then
- targetList[#targetList+1] = {
- data.target[1],
- data.target[2],
- uri,
- }
- else
- for _, position in ipairs(list) do
- targetList[#targetList+1] = {
- position[1],
- position[2],
- uri,
- }
- end
- end
- data.content = new
- end
- new, list = catch_target(data.content, '~')
- if new ~= data.content then
- sourceList = list
- sourceUri = uri
- data.content = new
- end
- new, list = catch_target(data.content, '?')
- if new ~= data.content then
- sourceList = list
- sourceUri = uri
- data.content = new
- for _, position in ipairs(list) do
+ local newScript, catched = catch(data.content, '!?~')
+ if catched['!'] or catched['~'] then
+ for _, position in ipairs(catched['!'] + catched['~']) do
targetList[#targetList+1] = {
position[1],
position[2],
@@ -112,7 +70,11 @@ function TEST(datas)
}
end
end
- files.setText(uri, data.content)
+ if catched['?'] or catched['~'] then
+ sourceList = catched['?'] + catched['~']
+ sourceUri = uri
+ end
+ files.setText(uri, newScript)
end
local sourcePos = (sourceList[1][1] + sourceList[1][2]) // 2
@@ -143,7 +105,7 @@ TEST {
{
path = 'a.lua',
content = [[
- local <?f?> = require 'lib'
+ local <~f~> = require 'lib'
]],
},
}
@@ -158,7 +120,7 @@ TEST {
{
path = 'b.lua',
content = [[
- print(<?ROOT?>)
+ print(<~ROOT~>)
]],
},
}
@@ -167,7 +129,7 @@ TEST {
{
path = 'a.lua',
content = [[
- <?ROOT?> = 1
+ <~ROOT~> = 1
]],
},
{
@@ -183,7 +145,7 @@ TEST {
path = 'a.lua',
content = [[
local f = require 'lib'
- local <?o?> = f()
+ local <~o~> = f()
]],
},
{
@@ -212,7 +174,7 @@ TEST {
---@class A
local mt
- function mt.<?f?>()
+ function mt.<~f~>()
end
]]
}
diff --git a/test/definition/init.lua b/test/definition/init.lua
index 85bcd5d5..2e87b0c2 100644
--- a/test/definition/init.lua
+++ b/test/definition/init.lua
@@ -1,23 +1,10 @@
local core = require 'core.definition'
local files = require 'files'
local vm = require 'vm'
+local catch = require 'catch'
rawset(_G, 'TEST', true)
-local function catch_target(script)
- local list = {}
- local cur = 1
- while true do
- local start, finish = script:find('<!.-!>', cur)
- if not start then
- break
- end
- list[#list+1] = { start + 2, finish - 2 }
- cur = finish + 1
- end
- return list
-end
-
local function founded(targets, results)
if #targets ~= #results then
return false
@@ -36,15 +23,11 @@ end
function TEST(script)
files.removeAll()
- script = script:gsub('\n', '\r\n')
- local target = catch_target(script)
- local start = script:find('<?', 1, true)
- local finish = script:find('?>', 1, true)
- local pos = (start + finish) // 2 + 1
- local new_script = script:gsub('<[!?]', ' '):gsub('[!?]>', ' ')
- files.setText('', new_script)
+ local newScript, catched = catch(script, '!?')
- local results = core('', pos)
+ files.setText('', newScript)
+
+ local results = core('', catched['?'][1][1])
if results then
local positions = {}
for i, result in ipairs(results) do
@@ -52,15 +35,9 @@ function TEST(script)
positions[i] = { result.target.start, result.target.finish }
end
end
- if not founded(target, positions) then
- core('', pos)
- end
- assert(founded(target, positions))
+ assert(founded(catched['!'] or {}, positions))
else
- if #target ~= 0 then
- core('', pos)
- end
- assert(#target == 0)
+ assert(catched['!'] == nil)
end
end
diff --git a/test/definition/table.lua b/test/definition/table.lua
index d63cc655..61e8746d 100644
--- a/test/definition/table.lua
+++ b/test/definition/table.lua
@@ -54,7 +54,7 @@ t[<?"method"?>]()
TEST [[
local t
t[<!"longString"!>] = 1
-t[<?[==[longString]==]?>]()
+t[ <?[==[longString]==]?> ]()
]]
TEST [[
diff --git a/test/diagnostics/init.lua b/test/diagnostics/init.lua
index 79b267c3..e3bf9516 100644
--- a/test/diagnostics/init.lua
+++ b/test/diagnostics/init.lua
@@ -2,36 +2,12 @@ local core = require 'core.diagnostics'
local files = require 'files'
local config = require 'config'
local util = require 'utility'
+local catch = require 'catch'
config.get 'Lua.diagnostics.neededFileStatus'['deprecated'] = 'Any'
rawset(_G, 'TEST', true)
-local function catch_target(script, ...)
- local list = {}
- local function catch(buf)
- local cur = 1
- local cut = 0
- while true do
- local start, finish = buf:find('<!.-!>', cur)
- if not start then
- break
- end
- list[#list+1] = { start - cut, finish - 4 - cut }
- cur = finish + 1
- cut = cut + 4
- end
- end
- catch(script)
- if ... then
- for _, buf in ipairs {...} do
- catch(buf)
- end
- end
- local new_script = script:gsub('<!(.-)!>', '%1')
- return new_script, list
-end
-
local function founded(targets, results)
if #targets ~= #results then
return false
@@ -50,8 +26,8 @@ end
function TEST(script, ...)
files.removeAll()
- local new_script, target = catch_target(script, ...)
- files.setText('', new_script)
+ local newScript, catched = catch(script, '!')
+ files.setText('', newScript)
files.open('')
local datas = {}
core('', function (results)
@@ -65,11 +41,11 @@ function TEST(script, ...)
end
if results[1] then
- if not founded(target, results) then
- error(('%s\n%s'):format(util.dump(target), util.dump(results)))
+ if not founded(catched['!'] or {}, results) then
+ error(('%s\n%s'):format(util.dump(catched['!']), util.dump(results)))
end
else
- assert(#target == 0)
+ assert(catched['!'] == nil)
end
end
@@ -99,15 +75,11 @@ local <!t!> = {}
<!t!>.a = 1
]]
-TEST([[
-local <!function x()
+TEST [[
+local <!function <!x!>()
end!>
-]],
-[[
-local function <!x!>()
-end
]]
-)
+
TEST [[
local <!x!> = <!function () end!>
@@ -118,21 +90,13 @@ local <!x!>
<!x!> = <!function () end!>
]]
-TEST([[
+TEST [[
local <!function x()
end!>
-local <!function y()
+local <!function <!y!>()
x()
end!>
-]],
-[[
-local function x()
-end
-local function <!y!>()
- x()
-end
]]
-)
TEST [[
local print, _G
@@ -155,6 +119,11 @@ TEST [[
]]
TEST [[
+
+<! !>
+]]
+
+TEST [[
X = 1<! !>
]]
diff --git a/test/document_symbol/init.lua b/test/document_symbol/init.lua
index d745d53f..d3168197 100644
--- a/test/document_symbol/init.lua
+++ b/test/document_symbol/init.lua
@@ -63,8 +63,8 @@ A = 1
name = 'A',
detail = 'global number = 1',
kind = define.SymbolKind.Class,
- range = {1, 5},
- selectionRange = {1, 1},
+ range = {0, 5},
+ selectionRange = {0, 1},
}
}
@@ -77,9 +77,9 @@ end
name = 'f',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {7, 22},
- selectionRange = {16, 16},
- valueRange = {7, 22},
+ range = {6, 10003},
+ selectionRange = {15, 16},
+ valueRange = {6, 10003},
}
}
@@ -92,9 +92,9 @@ end
name = 'f',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {1, 16},
- selectionRange = {10, 10},
- valueRange = {1, 16},
+ range = {0, 10003},
+ selectionRange = {9, 10},
+ valueRange = {0, 10003},
}
}
@@ -107,9 +107,9 @@ end
name = '',
detail = 'return function ()',
kind = define.SymbolKind.Function,
- range = {8, 22},
- selectionRange = {8, 8},
- valueRange = {8, 22},
+ range = {7, 10003},
+ selectionRange = {7, 15},
+ valueRange = {7, 10003},
}
}
@@ -122,9 +122,9 @@ end
name = 'f',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {1, 19},
- selectionRange = {1, 1},
- valueRange = {5, 19},
+ range = {0, 10003},
+ selectionRange = {0, 1},
+ valueRange = {4, 10003},
}
}
@@ -137,9 +137,9 @@ end
name = 'f',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {7, 25},
- selectionRange = {7, 7},
- valueRange = {11, 25},
+ range = {6, 10003},
+ selectionRange = {6, 7},
+ valueRange = {10, 10003},
}
}
@@ -152,9 +152,9 @@ end
name = 'mt:add',
detail = 'function ()',
kind = define.SymbolKind.Method,
- range = {1, 21},
- selectionRange = {10, 15},
- valueRange = {1, 21},
+ range = {0, 10003},
+ selectionRange = {9, 15},
+ valueRange = {0, 10003},
}
}
@@ -173,25 +173,25 @@ end
name = 'A',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {1, 68},
- selectionRange = {10, 10},
- valueRange = {1, 68},
+ range = {0, 50003},
+ selectionRange = {9, 10},
+ valueRange = {0, 50003},
children = {
[1] = {
name = 'A1',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {18, 38},
- selectionRange = {27, 28},
- valueRange = {18, 38},
+ range = {10004, 20007},
+ selectionRange = {10013, 10015},
+ valueRange = {10004, 20007},
},
[2] = {
name = 'A2',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {44, 64},
- selectionRange = {53, 54},
- valueRange = {44, 64},
+ range = {30004, 40007},
+ selectionRange = {30013, 30015},
+ valueRange = {30004, 40007},
},
},
},
@@ -199,9 +199,9 @@ end
name = 'B',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {70, 85},
- selectionRange = {79, 79},
- valueRange = {70, 85},
+ range = {60000, 70003},
+ selectionRange = {60009, 60010},
+ valueRange = {60000, 70003},
},
}
@@ -220,31 +220,31 @@ local z
name = 'x',
detail = 'local number = 1',
kind = define.SymbolKind.Variable,
- range = {7, 11},
- selectionRange = {7, 7},
+ range = {6, 11},
+ selectionRange = {6, 7},
},
[2] = {
name = 'f',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {19, 79},
- selectionRange = {28, 28},
- valueRange = {19, 79},
+ range = {10006, 50003},
+ selectionRange = {10015, 10016},
+ valueRange = {10006, 50003},
children = {
[1] = {
name = 'x',
detail = 'local string = "x"',
kind = define.SymbolKind.Variable,
- range = {42, 48},
- selectionRange = {42, 42},
+ range = {20010, 20017},
+ selectionRange = {20010, 20011},
},
[2] = {
name = 'y',
detail = 'local {}',
kind = define.SymbolKind.Variable,
- range = {60, 65},
- selectionRange = {60, 60},
- valueRange = {64, 65},
+ range = {30010, 30016},
+ selectionRange = {30010, 30011},
+ valueRange = {30014, 30016},
},
--[3] = {
-- name = 'z',
@@ -260,15 +260,15 @@ local z
name = 'y',
detail = 'local boolean = true',
kind = define.SymbolKind.Variable,
- range = {87, 94},
- selectionRange = {87, 87},
+ range = {60006, 60014},
+ selectionRange = {60006, 60007},
},
[4] = {
name = 'z',
detail = 'local',
kind = define.SymbolKind.Variable,
- range = {102, 102},
- selectionRange = {102, 102},
+ range = {70006, 70007},
+ selectionRange = {70006, 70007},
},
}
@@ -284,30 +284,30 @@ local t = {
name = 't',
detail = 'local {a, b, c}',
kind = define.SymbolKind.Variable,
- range = {7, 46},
- selectionRange = {7, 7},
- valueRange = {11, 46},
+ range = {6, 40001},
+ selectionRange = {6, 7},
+ valueRange = {10, 40001},
children = {
[1] = {
name = 'a',
detail = 'field number = 1',
kind = define.SymbolKind.Property,
- range = {17, 21},
- selectionRange = {17, 17},
+ range = {10004, 10009},
+ selectionRange = {10004, 10005},
},
[2] = {
name = 'b',
detail = 'field number = 2',
kind = define.SymbolKind.Property,
- range = {28, 32},
- selectionRange = {28, 28},
+ range = {20004, 20009},
+ selectionRange = {20004, 20005},
},
[3] = {
name = 'c',
detail = 'field number = 3',
kind = define.SymbolKind.Property,
- range = {39, 43},
- selectionRange = {39, 39},
+ range = {30004, 30009},
+ selectionRange = {30004, 30005},
},
}
}
@@ -325,24 +325,24 @@ local t = {
name = 't',
detail = 'local {a}',
kind = define.SymbolKind.Variable,
- range = {7, 44},
- selectionRange = {7, 7},
- valueRange = {11, 44},
+ range = {6, 40001},
+ selectionRange = {6, 7},
+ valueRange = {10, 40001},
children = {
[1] = {
name = 'a',
detail = 'field {b}',
kind = define.SymbolKind.Property,
- range = {17, 42},
- selectionRange = {17, 17},
- valueRange = {21, 42},
+ range = {10004, 30005},
+ selectionRange = {10004, 10005},
+ valueRange = {10008, 30005},
children = {
[1] = {
name = 'b',
detail = EXISTS,
kind = define.SymbolKind.Property,
- range = {31, 35},
- selectionRange = {31, 31},
+ range = {20008, 20013},
+ selectionRange = {20008, 20009},
}
}
},
@@ -360,16 +360,16 @@ g = 1
name = 'g',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {7, 22},
- selectionRange = {16, 16},
- valueRange = {7, 22},
+ range = {6, 10003},
+ selectionRange = {15, 16},
+ valueRange = {6, 10003},
},
[2] = {
name = 'g',
detail = 'setlocal number = 1',
kind = define.SymbolKind.Variable,
- range = {25, 29},
- selectionRange = {25, 25},
+ range = {30000, 30005},
+ selectionRange = {30000, 30001},
}
}
@@ -383,30 +383,30 @@ end
name = 'f',
detail = 'function (a, b, ...)',
kind = define.SymbolKind.Function,
- range = {1, 58},
- selectionRange = {10, 10},
- valueRange = {1, 58},
+ range = {0, 30003},
+ selectionRange = {9, 10},
+ valueRange = {0, 30003},
children = {
[1] = {
name = 'a',
detail = 'param',
kind = define.SymbolKind.Constant,
- range = {12, 12},
- selectionRange = {12, 12},
+ range = {11, 12},
+ selectionRange = {11, 12},
},
[2] = {
name = 'b',
detail = 'param',
kind = define.SymbolKind.Constant,
- range = {15, 15},
- selectionRange = {15, 15},
+ range = {14, 15},
+ selectionRange = {14, 15},
},
[3] = {
name = 'x',
detail = 'local',
kind = define.SymbolKind.Variable,
- range = {33, 39},
- selectionRange = {33, 33},
+ range = {10010, 10017},
+ selectionRange = {10010, 10011},
}
}
},
@@ -424,17 +424,17 @@ local v = t
name = 't',
detail = 'local {a, b}',
kind = define.SymbolKind.Variable,
- range = {7, 35},
- selectionRange = {7, 7},
- valueRange = {11, 35},
+ range = {6, 30001},
+ selectionRange = {6, 7},
+ valueRange = {10, 30001},
children = EXISTS,
},
[2] = {
name = 'v',
detail = 'local',
kind = define.SymbolKind.Variable,
- range = {44, 48},
- selectionRange = {44, 44},
+ range = {50006, 50011},
+ selectionRange = {50006, 50007},
},
}
@@ -446,47 +446,47 @@ local function
name = 'x',
detail = 'local',
kind = define.SymbolKind.Variable,
- range = {7, 7},
- selectionRange = {7, 7},
+ range = {6, 7},
+ selectionRange = {6, 7},
},
[2] = {
name = "",
detail = "function ()",
kind = 12,
- range = {15, 22},
- selectionRange = {15, 15},
- valueRange = {15, 22},
+ range = {10006, 10014},
+ selectionRange = {10006, 10014},
+ valueRange = {10006, 10014},
},
}
TEST [[
local a, b = {
- x = 1,
- y = 1,
- z = 1,
+ x1 = 1,
+ y1 = 1,
+ z1 = 1,
}, {
- x = 1,
- y = 1,
- z = 1,
+ x2 = 1,
+ y2= 1,
+ z2 = 1,
}
]]{
[1] = {
name = 'a',
- detail = 'local {x, y, z}',
+ detail = 'local {x1, y1, z1}',
kind = define.SymbolKind.Variable,
- range = {7, 49},
- selectionRange = {7, 7},
- valueRange = {14, 49},
+ range = {6, 40001},
+ selectionRange = {6, 7},
+ valueRange = {13, 40001},
children = EXISTS,
},
[2] = {
name = 'b',
- detail = 'local {x, y, z}',
+ detail = 'local {x2, y2, z2}',
kind = define.SymbolKind.Variable,
- range = {10, 87},
- selectionRange = {10, 10},
- valueRange = {52, 87},
+ range = {9, 80001},
+ selectionRange = {9, 10},
+ valueRange = {40003, 80001},
children = EXISTS,
}
}
@@ -504,24 +504,24 @@ end
name = 'x',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {7, 22},
- selectionRange = {16, 16},
- valueRange = {7, 22},
+ range = {6, 10003},
+ selectionRange = {15, 16},
+ valueRange = {6, 10003},
},
[2] = {
name = 'f',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {31, 58},
- selectionRange = {40, 40},
- valueRange = {31, 58},
+ range = {30006, 50003},
+ selectionRange = {30015, 30016},
+ valueRange = {30006, 50003},
children = {
[1] = {
name = 'c',
detail = 'local',
kind = define.SymbolKind.Variable,
- range = {54, 54},
- selectionRange = {54, 54},
+ range = {40010, 40011},
+ selectionRange = {40010, 40011},
},
},
}
@@ -537,16 +537,16 @@ local t = f({
name = 't',
detail = 'local',
kind = define.SymbolKind.Variable,
- range = {7, 26},
- selectionRange = {7, 7},
- valueRange = {11, 26},
+ range = {6, 20002},
+ selectionRange = {6, 7},
+ valueRange = {10, 20002},
children = {
[1] = {
name = 'k',
detail = 'field number = 1',
kind = define.SymbolKind.Property,
- range = {19, 23},
- selectionRange = {19, 19},
+ range = {10004, 10009},
+ selectionRange = {10004, 10005},
}
}
}
@@ -563,31 +563,31 @@ end
name = 't',
detail = 'local {}',
kind = define.SymbolKind.Variable,
- range = {7, 12},
- selectionRange = {7, 7},
- valueRange = {11, 12},
+ range = {6, 12},
+ selectionRange = {6, 7},
+ valueRange = {10, 12},
},
[2] = {
name = 'f',
detail = 'function (a, b)',
kind = define.SymbolKind.Function,
- range = {21, 40},
- selectionRange = {30, 30},
- valueRange = {21, 40},
+ range = {20006, 30003},
+ selectionRange = {20015, 20016},
+ valueRange = {20006, 30003},
children = {
[1] = {
name = 'a',
detail = 'param',
kind = define.SymbolKind.Constant,
- range = {32, 32},
- selectionRange = {32, 32},
+ range = {20017, 20018},
+ selectionRange = {20017, 20018},
},
[2] = {
name = 'b',
detail = 'param',
kind = define.SymbolKind.Constant,
- range = {35, 35},
- selectionRange = {35, 35},
+ range = {20020, 20021},
+ selectionRange = {20020, 20021},
}
}
}
@@ -604,17 +604,17 @@ local a = f {
name = 'a',
detail = 'local',
kind = define.SymbolKind.Variable,
- range = {7, 43},
- selectionRange = {7, 7},
- valueRange = {11, 43},
+ range = {6, 30001},
+ selectionRange = {6, 7},
+ valueRange = {10, 30001},
children = {
[1] = {
name = 'x',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {19, 41},
- selectionRange = {19, 19},
- valueRange = {23, 41},
+ range = {10004, 20007},
+ selectionRange = {10004, 10005},
+ valueRange = {10008, 20007},
}
}
}
@@ -630,9 +630,9 @@ end)
name = '',
detail = 'table.sort -> function (a, b)',
kind = define.SymbolKind.Function,
- range = {15, 50},
- selectionRange = {15, 15},
- valueRange = {15, 50},
+ range = {14, 20003},
+ selectionRange = {14, 22},
+ valueRange = {14, 20003},
children = EXISTS,
}
}
@@ -643,32 +643,32 @@ local root = {
local function function_inside_function()
end
end
- }
+}
]]
{
[1] = {
name = 'root',
detail = 'local {inner_function}',
kind = define.SymbolKind.Variable,
- range = {7, 123},
- selectionRange = {7, 10},
- valueRange = {14, 123},
+ range = {6, 50001},
+ selectionRange = {6, 10},
+ valueRange = {13, 50001},
children = {
[1] = {
name = 'inner_function',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {20, 117},
- selectionRange = {20, 33},
- valueRange = {37, 117},
+ range = {10004, 40007},
+ selectionRange = {10004, 10018},
+ valueRange = {10021, 40007},
children = {
[1] = {
name = 'function_inside_function',
detail = 'function ()',
kind = define.SymbolKind.Function,
- range = {63, 109},
- selectionRange = {72, 95},
- valueRange = {63, 109},
+ range = {20014, 30011},
+ selectionRange = {20023, 20047},
+ valueRange = {20014, 30011},
},
},
},
diff --git a/test/example/guide.txt b/test/example/guide.txt
index da8d5c32..cff05faf 100644
--- a/test/example/guide.txt
+++ b/test/example/guide.txt
@@ -586,7 +586,7 @@ end
---@param lines table
---@return integer {name = 'row'}
---@return integer {name = 'col'}
-function m.positionOf(lines, offset)
+function m.rowColOf(lines, offset)
if offset < 1 then
return 0, 0
end
@@ -619,7 +619,7 @@ end
---@param row integer
---@param col integer
---@return integer {name = 'offset'}
-function m.offsetOf(lines, row, col)
+function m.positionOf(lines, row, col)
if row < 1 then
return 0
end
diff --git a/test/full/example.lua b/test/full/example.lua
index 5596233e..20de5528 100644
--- a/test/full/example.lua
+++ b/test/full/example.lua
@@ -12,26 +12,24 @@ local function testIfExit(path)
config.set('Lua.workspace.preloadFileSize', 1000000000)
local buf = util.loadFile(path:string())
if buf then
- local vm
+ local state
local clock = os.clock()
local max = 1
local need
- local parseClock = 0
local compileClock = 0
local luadocClock = 0
local noderClock = 0
local total
for i = 1, max do
- vm = TEST(buf)
+ state = TEST(buf)
local luadocStart = os.clock()
- luadoc(nil, vm)
+ luadoc(state)
local luadocPassed = os.clock() - luadocStart
local passed = os.clock() - clock
local noderStart = os.clock()
local noderPassed = os.clock() - noderStart
- parseClock = parseClock + vm.parseClock
- compileClock = compileClock + vm.compileClock
+ compileClock = compileClock + state.compileClock
luadocClock = luadocClock + luadocPassed
noderClock = noderClock + noderPassed
if passed >= 1.0 or i == max then
@@ -40,10 +38,9 @@ local function testIfExit(path)
break
end
end
- print(('基准编译测试[%s]单次耗时:%.10f(解析:%.10f, 编译:%.10f, LuaDoc: %.10f, Noder: %.10f)'):format(
+ print(('基准编译测试[%s]单次耗时:%.10f(解析:%.10f, LuaDoc: %.10f, Noder: %.10f)'):format(
path:filename():string(),
need,
- parseClock / total,
compileClock / total,
luadocClock / total,
noderClock / total
@@ -52,7 +49,6 @@ local function testIfExit(path)
local clock = os.clock()
local max = 100
local need
- local lines = parser:lines(buf)
for i = 1, max do
files.removeAll()
files.open('')
diff --git a/test/full/init.lua b/test/full/init.lua
index 89d9b8b3..9584db6f 100644
--- a/test/full/init.lua
+++ b/test/full/init.lua
@@ -5,9 +5,10 @@ local util = require 'utility'
rawset(_G, 'TEST', true)
function TEST(script)
- local ast = parser:compile(script, 'lua', 'Lua 5.3')
- assert(ast)
- return ast
+ local clock = os.clock()
+ local state = parser.compile(script, 'Lua', 'Lua 5.3')
+ state.compileClock = os.clock() - clock
+ return state
end
local function startCollectDiagTimes()
diff --git a/test/highlight/init.lua b/test/highlight/init.lua
index 2bf639fa..7faa9e08 100644
--- a/test/highlight/init.lua
+++ b/test/highlight/init.lua
@@ -1,22 +1,6 @@
local core = require 'core.highlight'
local files = require 'files'
-
-local function catch_target(script)
- local list = {}
- local cur = 1
- while true do
- local start, finish = script:find('<[!?].-[!?]>', cur)
- if not start then
- break
- end
- list[#list+1] = {
- start = start + 2,
- finish = finish - 2,
- }
- cur = finish + 1
- end
- return list
-end
+local catch = require 'catch'
local function founded(targets, results)
if #targets ~= #results then
@@ -35,20 +19,18 @@ local function founded(targets, results)
end
function TEST(script)
- local target = catch_target(script)
- for _, enter in ipairs(target) do
- local start, finish = enter.start, enter.finish
- files.removeAll()
+ files.removeAll()
+ local newScript, catched = catch(script, '!')
+ files.setText('', newScript)
+ for _, enter in ipairs(catched['!']) do
+ local start, finish = enter[1], enter[2]
local pos = (start + finish) // 2
- local new_script = script:gsub('<[!?~]', ' '):gsub('[!?~]>', ' ')
- files.setText('', new_script)
-
local positions = core('', pos)
- if positions then
- assert(founded(target, positions))
- else
- assert(#target == 0)
+ local results = {}
+ for _, position in ipairs(positions) do
+ results[#results+1] = { position.start, position.finish }
end
+ assert(founded(catched['!'], results))
end
end
diff --git a/test/hover/init.lua b/test/hover/init.lua
index 3f07ea30..8058317d 100644
--- a/test/hover/init.lua
+++ b/test/hover/init.lua
@@ -1,7 +1,6 @@
local core = require 'core.hover'
-local findSource = require 'core.find-source'
-local getLabel = require 'core.hover.label'
local files = require 'files'
+local catch = require 'catch'
rawset(_G, 'TEST', true)
@@ -23,12 +22,9 @@ local accept = {
function TEST(script)
return function (expect)
files.removeAll()
- local start = script:find('<?', 1, true)
- local finish = script:find('?>', 1, true)
- local pos = (start + finish) // 2 + 1
- local new_script = script:gsub('<[!?]', ' '):gsub('[!?]>', ' ')
- files.setText('', new_script)
- local hover = core.byUri('', pos)
+ local newScript, catched = catch(script, '?')
+ files.setText('', newScript)
+ local hover = core.byUri('', catched['?'][1][1])
assert(hover)
expect = expect:gsub('^[\r\n]*(.-)[\r\n]*$', '%1'):gsub('\r\n', '\n')
local label = tostring(hover):match('```lua[\r\n]*(.-)[\r\n]*```'):gsub('\r\n', '\n')
@@ -1703,3 +1699,18 @@ print(b.<?x?>)
[[
field A.x: any
]]
+
+TEST [[
+---@class A
+---@field x number
+---@field y number
+
+---@type A<string, number>
+local <?t?>
+]]
+[[
+local t: A<string, number> {
+ x: number,
+ y: number,
+}
+]]
diff --git a/test/references/common.lua b/test/references/common.lua
index 3669486b..4245a79d 100644
--- a/test/references/common.lua
+++ b/test/references/common.lua
@@ -221,30 +221,12 @@ TEST [[
---@class A
local a
-a.<!xxx!> = 1
-
---@type A
local b
-b.xxx = 1 -- Dont search this
-
----@class C: A
-local c
-print(c.<?xxx?>)
-]]
-
-TEST [[
----@class A
-local <?a?>
-
----@class B
-local b
-
-b.xx = 1 -- Dont search this
-
----@type B
+---@type A
local c
----@type A
-c.<!xx!> = 1
+b.<?x?> = 1
+c.<!x!> = 1
]]
diff --git a/test/references/init.lua b/test/references/init.lua
index 2fba92e5..351c80b6 100644
--- a/test/references/init.lua
+++ b/test/references/init.lua
@@ -1,19 +1,6 @@
local core = require 'core.reference'
local files = require 'files'
-
-local function catch_target(script)
- local list = {}
- local cur = 1
- while true do
- local start, finish = script:find('<[!?].-[!?]>', cur)
- if not start then
- break
- end
- list[#list+1] = { start + 2, finish - 2 }
- cur = finish + 1
- end
- return list
-end
+local catch = require 'catch'
local function founded(targets, results)
if #targets ~= #results then
@@ -33,14 +20,12 @@ end
function TEST(script)
files.removeAll()
- local expect = catch_target(script)
- local start = script:find('<[?~]')
- local finish = script:find('[?~]>')
- local pos = (start + finish) // 2 + 1
- local new_script = script:gsub('<[!?~]', ' '):gsub('[!?~]>', ' ')
- files.setText('', new_script)
+ local newScript, catched = catch(script, '!?~')
+ files.setText('', newScript)
- local results = core('', pos)
+ local input = catched['?'] + catched['~']
+ local expect = catched['!'] + catched['?']
+ local results = core('', input[1][1])
if results then
local positions = {}
for i, result in ipairs(results) do
diff --git a/test/rename/init.lua b/test/rename/init.lua
index 4b10756e..b20c0279 100644
--- a/test/rename/init.lua
+++ b/test/rename/init.lua
@@ -1,16 +1,21 @@
local core = require 'core.rename'
local files = require 'files'
+local catch = require 'catch'
+local guide = require 'parser.guide'
local function replace(text, positions)
+ local state = files.getState('')
local buf = {}
table.sort(positions, function (a, b)
return a.start < b.start
end)
local lastPos = 1
for _, info in ipairs(positions) do
- buf[#buf+1] = text:sub(lastPos, info.start - 1)
+ local start = guide.positionToOffset(state, info.start)
+ local finish = guide.positionToOffset(state, info.finish)
+ buf[#buf+1] = text:sub(lastPos, start)
buf[#buf+1] = info.text
- lastPos = info.finish + 1
+ lastPos = finish + 1
end
buf[#buf+1] = text:sub(lastPos)
return table.concat(buf)
@@ -21,10 +26,12 @@ function TEST(oldName, newName)
return function (expectScript)
files.removeAll()
files.setText('', oldScript)
- local pos = oldScript:find('[^%w_]'..oldName..'[^%w_]')
- assert(pos)
+ local state = files.getState('')
+ local offset = oldScript:find('[^%w_]'..oldName..'[^%w_]')
+ assert(offset)
+ local position = guide.offsetToPosition(state, offset)
- local positions = core.rename('', pos+1, newName)
+ local positions = core.rename('', position, newName)
local script = oldScript
if positions then
script = replace(script, positions)
diff --git a/test/signature/init.lua b/test/signature/init.lua
index 27051310..43bce29e 100644
--- a/test/signature/init.lua
+++ b/test/signature/init.lua
@@ -1,26 +1,25 @@
-local core = require 'core.signature'
+local core = require 'core.signature'
local files = require 'files'
+local catch = require 'catch'
rawset(_G, 'TEST', true)
function TEST(script)
return function (expect)
- local pos = script:find('$', 1, true) - 1
- local new_script = script:gsub('%$', '')
+ local newScript, catched1 = catch(script, '?')
+ local newExpect, catched2 = catch(expect or '', '!')
files.removeAll()
- files.setText('', new_script)
- local hovers = core('', pos)
+ files.setText('', newScript)
+ local hovers = core('', catched1['?'][1][1])
if hovers then
assert(expect)
local hover = hovers[#hovers]
- local label = hover.label:gsub('^[\r\n]*(.-)[\r\n]*$', '%1'):gsub('\r\n', '\n')
- expect.label = expect.label:gsub('^[\r\n]*(.-)[\r\n]*$', '%1'):gsub('\r\n', '\n')
local arg = hover.params[hover.index].label
- assert(expect.label == label)
- assert(expect.arg[1] == arg[1])
- assert(expect.arg[2] == arg[2])
+ assert(newExpect == hover.label)
+ assert(catched2['!'][1][1] == arg[1])
+ assert(catched2['!'][1][2] == arg[2])
else
assert(expect == nil)
end
@@ -31,93 +30,67 @@ TEST [[
local function x(a, b)
end
-x($
+x(<??>
]]
-{
- label = "function x(a: any, b: any)",
- arg = {12, 17},
-}
+'function x(<!a: any!>, b: any)'
TEST [[
local function x(a, b)
end
-x($)
+x(<??>)
]]
-{
- label = "function x(a: any, b: any)",
- arg = {12, 17},
-}
+'function x(<!a: any!>, b: any)'
TEST [[
local function x(a, b)
end
-x(xxx$)
+x(xxx<??>)
]]
-{
- label = "function x(a: any, b: any)",
- arg = {12, 17},
-}
+'function x(<!a: any!>, b: any)'
TEST [[
local function x(a, b)
end
-x(xxx, $)
+x(xxx, <??>)
]]
-{
- label = "function x(a: any, b: any)",
- arg = {20, 25},
-}
+'function x(a: any, <!b: any!>)'
TEST [[
function mt:f(a)
end
-mt:f($
+mt:f(<??>
]]
-{
- label = 'function mt:f(a: any)',
- arg = {15, 20},
-}
+'function mt:f(<!a: any!>)'
TEST [[
local function x(a, b)
return 1
end
-x($
+x(<??>
]]
-{
- label = "function x(a: any, b: any)",
- arg = {12, 17},
-}
+'function x(<!a: any!>, b: any)'
TEST [[
local function x(a, ...)
return 1
end
-x(1, 2, 3, $
+x(1, 2, 3, <??>
]]
-{
- label = "function x(a: any, ...)",
- arg = {20, 22},
-}
+'function x(a: any, <!...!>)'
TEST [[
-(''):sub($
+(''):sub(<??>
]]
-{
- label = [[
-function string:sub(i: integer, j?: integer)
-]],
- arg = {21, 30},
-}
+'function string:sub(<!i: integer!>, j?: integer)'
TEST [[
-(''):sub(1)$
+(''):sub(1)<??>
]]
(nil)
@@ -125,39 +98,29 @@ TEST [[
local function f(a, b, c)
end
-f(1, 'string$')
+f(1, 'string<??>')
]]
-{
- label = [[
-function f(a: any, b: any, c: any)
-]],
- arg = {20, 25},
-}
+'function f(a: any, <!b: any!>, c: any)'
TEST [[
-pcall(function () $ end)
+pcall(function () <??> end)
]]
(nil)
TEST [[
-table.unpack {$}
+table.unpack {<??>}
]]
(nil)
TEST [[
---@type fun(x: number, y: number):boolean
local zzzz
-zzzz($)
+zzzz(<??>)
]]
-{
- label = [[
-function zzzz(x: number, y: number)
-]],
- arg = {15, 23},
-}
+'function zzzz(<!x: number!>, y: number)'
TEST [[
-('abc'):format(f($))
+('abc'):format(f(<??>))
]]
(nil)
@@ -166,14 +129,9 @@ function Foo(param01, param02)
end
-Foo($)
+Foo(<??>)
]]
-{
- label = [[
-function Foo(param01: any, param02: any)
-]],
- arg = {14, 25},
-}
+'function Foo(<!param01: any!>, param02: any)'
TEST [[
function f1(a, b)
@@ -182,51 +140,31 @@ end
function f2(c, d)
end
-f2(f1(),$)
+f2(f1(),<??>)
]]
-{
- label = [[
-function f2(c: any, d: any)
-]],
- arg = {21, 26},
-}
+'function f2(c: any, <!d: any!>)'
TEST [[
local function f(a, b, c)
end
-f({},$)
+f({},<??>)
]]
-{
- label = [[
-function f(a: any, b: any, c: any)
-]],
- arg = {20, 25},
-}
+'function f(a: any, <!b: any!>, c: any)'
TEST [[
-for _ in pairs($) do
+for _ in pairs(<??>) do
end
]]
-{
- label = [[
-function pairs(t: <T>)
-]],
- arg = {16, 21},
-}
+'function pairs(<!t: <T>!>)'
TEST [[
function m:f()
end
-m.f($)
+m.f(<??>)
]]
-{
- label = [[
-function m.f(self: table)
-]],
- arg = {14, 24},
-}
+'function m.f(<!self: table!>)'
TEST [[
---@alias nnn table<number, string>
@@ -234,59 +172,41 @@ TEST [[
---@param x nnn
local function f(x, y, z) end
-f($)
+f(<??>)
]]
-{
- label = [[
-function f(x: table<number, string>, y: any, z: any)
-]],
- arg = {12, 35},
-}
-
+'function f(<!x: table<number, string>!>, y: any, z: any)'
TEST [[
local function x(a, b)
end
-x( aaaa $, 2)
+x( aaaa <??>, 2)
]]
-{
- label = "function x(a: any, b: any)",
- arg = {12, 17},
-}
+"function x(<!a: any!>, b: any)"
TEST [[
local function x(a, b)
end
-x($ aaaa , 2)
+x(<??> aaaa , 2)
]]
-{
- label = "function x(a: any, b: any)",
- arg = {12, 17},
-}
+'function x(<!a: any!>, b: any)'
TEST [[
local function x(a, b)
end
-x(aaaa ,$ 2)
+x(aaaa ,<??> 2)
]]
-{
- label = "function x(a: any, b: any)",
- arg = {20, 25},
-}
+'function x(a: any, <!b: any!>)'
TEST [[
local function x(a, b)
end
-x(aaaa , 2 $)
+x(aaaa , 2 <??>)
]]
-{
- label = "function x(a: any, b: any)",
- arg = {20, 25},
-}
+'function x(a: any, <!b: any!>)'
TEST [[
local fooC
@@ -298,9 +218,6 @@ function fooC(callback, par) end
fooC(function (x, s)
-end,$)
+end,<??>)
]]
-{
- label = 'function fooC(callback: fun(x: number, s: string):nil, par: number)',
- arg = {56, 66},
-}
+'function fooC(callback: fun(x: number, s: string):nil, <!par: number!>)'
diff --git a/test/type_formatting/init.lua b/test/type_formatting/init.lua
index 9178ea4f..46b8223d 100644
--- a/test/type_formatting/init.lua
+++ b/test/type_formatting/init.lua
@@ -1,16 +1,16 @@
local core = require 'core.type-formatting'
local files = require 'files'
local util = require 'utility'
+local catch = require 'catch'
rawset(_G, 'TEST', true)
function TEST(script)
return function (expect)
- local pos = script:find('$', 1, true) - 1
- local new_script = script:gsub('%$', '')
files.removeAll()
- files.setText('', new_script)
- local edits = core('', pos, expect.ch)
+ local newScript, catched = catch(script, '?')
+ files.setText('', newScript)
+ local edits = core('', catched['?'][1][1], expect.ch)
if edits then
assert(expect.edits)
assert(util.equal(edits, expect.edits))
@@ -21,80 +21,104 @@ function TEST(script)
end
TEST [[
-if true then $ end
+if true then <??> end
]]
{
ch = '\n',
edits = {
{
- start = 13,
+ start = 12,
finish = 13,
text = '\n\t',
},
{
- start = 14,
- finish = 17,
+ start = 13,
+ finish = 15,
text = '',
},
{
- start = 18,
- finish = 17,
- text = '\nend',
+ start = 15,
+ finish = 15,
+ text = '\ne',
},
}
}
TEST [[
-if true then $end
+if true then <??>end
]]
{
ch = '\n',
edits = {
{
- start = 13,
+ start = 12,
finish = 13,
text = '\n\t',
},
{
- start = 14,
- finish = 16,
+ start = 13,
+ finish = 14,
text = '',
},
{
- start = 17,
- finish = 16,
- text = '\nend',
+ start = 14,
+ finish = 14,
+ text = '\ne',
},
}
}
TEST [[
-if true then$end
+if true then<??>end
]]
{
ch = '\n',
edits = {
{
- start = 13,
+ start = 12,
finish = 12,
text = '\n\t',
},
{
- start = 13,
- finish = 15,
+ start = 12,
+ finish = 13,
text = '',
},
{
+ start = 13,
+ finish = 13,
+ text = '\ne',
+ },
+ }
+}
+
+TEST [[
+ if true then<??>end
+]]
+{
+ ch = '\n',
+ edits = {
+ {
start = 16,
- finish = 15,
- text = '\nend',
+ finish = 16,
+ text = '\n \t',
+ },
+ {
+ start = 16,
+ finish = 17,
+ text = '',
+ },
+ {
+ start = 17,
+ finish = 17,
+ text = '\n e',
},
}
}
TEST [[
if true then
- $
+ <??>
end
]]
{
diff --git a/test/type_inference/init.lua b/test/type_inference/init.lua
index a20a96e9..23ebf54e 100644
--- a/test/type_inference/init.lua
+++ b/test/type_inference/init.lua
@@ -1,8 +1,8 @@
local files = require 'files'
-local vm = require 'vm'
local guide = require 'parser.guide'
local infer = require 'core.infer'
local config = require 'config'
+local catch = require 'catch'
rawset(_G, 'TEST', true)
@@ -24,12 +24,9 @@ end
function TEST(wanted)
return function (script)
files.removeAll()
- local start = script:find('<?', 1, true)
- local finish = script:find('?>', 1, true)
- local pos = (start + finish) // 2 + 1
- local newScript = script:gsub('<[!?]', ' '):gsub('[!?]>', ' ')
+ local newScript, catched = catch(script, '?')
files.setText('', newScript)
- local source = getSource(pos)
+ local source = getSource(catched['?'][1][1])
assert(source)
local result = infer.searchAndViewInfers(source)
if wanted ~= result then
@@ -356,6 +353,13 @@ TEST 'table<string, number>' [[
local <?x?>
]]
+TEST 'A<string, number>' [[
+---@class A
+
+---@type A<string, number>
+local <?x?>
+]]
+
TEST 'table' [[
self.<?t?>[#self.t+1] = {}
]]