summaryrefslogtreecommitdiff
path: root/script
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2020-11-20 21:55:41 +0800
committer最萌小汐 <sumneko@hotmail.com>2020-11-20 21:55:41 +0800
commitc63b2e404d8d2bb984afe3678a5ba2b2836380cc (patch)
treea70661effacc7a29caa8d49583673ac4be2faaf5 /script
parent85c5a4210e4447422cd5677369ae740ed65725a0 (diff)
downloadlua-language-server-c63b2e404d8d2bb984afe3678a5ba2b2836380cc.zip
remove the old version
Diffstat (limited to 'script')
-rw-r--r--script/async/async.lua127
-rw-r--r--script/async/init.lua1
-rw-r--r--script/async/loadfile.lua13
-rw-r--r--script/async/proto.lua62
-rw-r--r--script/async/scanfiles.lua55
-rw-r--r--script/build_package.lua329
-rw-r--r--script/capability/completion.lua68
-rw-r--r--script/capability/init.lua4
-rw-r--r--script/capability/semantic.lua64
-rw-r--r--script/client.lua16
-rw-r--r--script/config.lua218
-rw-r--r--script/constant/DiagnosticDefaultSeverity.lua23
-rw-r--r--script/constant/DiagnosticSeverity.lua6
-rw-r--r--script/constant/DiagnosticTag.lua4
-rw-r--r--script/constant/TokenModifiers.lua8
-rw-r--r--script/constant/TokenTypes.lua21
-rw-r--r--script/core/code_action.lua416
-rw-r--r--script/core/completion.lua1118
-rw-r--r--script/core/definition.lua296
-rw-r--r--script/core/diagnostics.lua1082
-rw-r--r--script/core/document_symbol.lua260
-rw-r--r--script/core/find_lib.lua65
-rw-r--r--script/core/find_source.lua59
-rw-r--r--script/core/folding_range.lua73
-rw-r--r--script/core/global.lua49
-rw-r--r--script/core/highlight.lua54
-rw-r--r--script/core/hover/emmy_function.lua143
-rw-r--r--script/core/hover/function.lua243
-rw-r--r--script/core/hover/hover.lua416
-rw-r--r--script/core/hover/init.lua1
-rw-r--r--script/core/hover/lib_function.lua258
-rw-r--r--script/core/hover/name.lua38
-rw-r--r--script/core/implementation.lua204
-rw-r--r--script/core/init.lua19
-rw-r--r--script/core/library.lua296
-rw-r--r--script/core/matchKey.lua30
-rw-r--r--script/core/name.lua70
-rw-r--r--script/core/references.lua91
-rw-r--r--script/core/rename.lua72
-rw-r--r--script/core/signature.lua133
-rw-r--r--script/core/snippet.lua64
-rw-r--r--script/emmy/alias.lua42
-rw-r--r--script/emmy/arrayType.lua37
-rw-r--r--script/emmy/class.lua61
-rw-r--r--script/emmy/field.lua45
-rw-r--r--script/emmy/funcType.lua64
-rw-r--r--script/emmy/generic.lua27
-rw-r--r--script/emmy/init.lua1
-rw-r--r--script/emmy/manager.lua221
-rw-r--r--script/emmy/param.lua70
-rw-r--r--script/emmy/return.lua36
-rw-r--r--script/emmy/tableType.lua40
-rw-r--r--script/emmy/type.lua59
-rw-r--r--script/emmy/typeUnit.lua51
-rw-r--r--script/file-uri.lua89
-rw-r--r--script/filename.lua65
-rw-r--r--script/files/file.lua158
-rw-r--r--script/files/files.lua115
-rw-r--r--script/files/init.lua1
-rw-r--r--script/glob/gitignore.lua226
-rw-r--r--script/glob/glob.lua124
-rw-r--r--script/glob/init.lua4
-rw-r--r--script/glob/matcher.lua151
-rw-r--r--script/json-beautify.lua120
-rw-r--r--script/json.lua450
-rw-r--r--script/language.lua136
-rw-r--r--script/log.lua116
-rw-r--r--script/meta/type.lua4
-rw-r--r--script/method/completionItem/resolve.lua34
-rw-r--r--script/method/exit.lua4
-rw-r--r--script/method/init.lua35
-rw-r--r--script/method/initialize.lua76
-rw-r--r--script/method/initialized.lua81
-rw-r--r--script/method/shutdown.lua4
-rw-r--r--script/method/textDocument/codeAction.lua26
-rw-r--r--script/method/textDocument/completion.lua133
-rw-r--r--script/method/textDocument/definition.lua88
-rw-r--r--script/method/textDocument/didChange.lua22
-rw-r--r--script/method/textDocument/didClose.lua8
-rw-r--r--script/method/textDocument/didOpen.lua13
-rw-r--r--script/method/textDocument/documentHighlight.lua40
-rw-r--r--script/method/textDocument/documentSymbol.lua75
-rw-r--r--script/method/textDocument/foldingRange.lua60
-rw-r--r--script/method/textDocument/hover.lua69
-rw-r--r--script/method/textDocument/implementation.lua111
-rw-r--r--script/method/textDocument/onTypeFormatting.lua17
-rw-r--r--script/method/textDocument/publishDiagnostics.lua166
-rw-r--r--script/method/textDocument/references.lua89
-rw-r--r--script/method/textDocument/rename.lua53
-rw-r--r--script/method/textDocument/semanticTokens/full.lua206
-rw-r--r--script/method/textDocument/signatureHelp.lua58
-rw-r--r--script/method/workspace/didChangeConfiguration.lua30
-rw-r--r--script/method/workspace/didChangeWatchedFiles.lua52
-rw-r--r--script/method/workspace/didChangeWorkspaceFolders.lua43
-rw-r--r--script/method/workspace/executeCommand.lua182
-rw-r--r--script/method/workspace/symbol.lua103
-rw-r--r--script/parser/ast.lua1913
-rw-r--r--script/parser/calcline.lua93
-rw-r--r--script/parser/grammar.lua630
-rw-r--r--script/parser/init.lua9
-rw-r--r--script/parser/lines.lua190
-rw-r--r--script/parser/parse.lua41
-rw-r--r--script/parser/relabel.lua361
-rw-r--r--script/parser/split.lua9
-rw-r--r--script/plugin.lua98
-rw-r--r--script/publish.lua189
-rw-r--r--script/rpc.lua91
-rw-r--r--script/sandbox.lua167
-rw-r--r--script/service.lua1107
-rw-r--r--script/task.lua64
-rw-r--r--script/timer.lua276
-rw-r--r--script/uri.lua15
-rw-r--r--script/utility.lua234
-rw-r--r--script/vm/chain.lua65
-rw-r--r--script/vm/emmy.lua372
-rw-r--r--script/vm/function.lua553
-rw-r--r--script/vm/global.lua25
-rw-r--r--script/vm/init.lua1
-rw-r--r--script/vm/ipairs.lua51
-rw-r--r--script/vm/label.lua75
-rw-r--r--script/vm/library.lua112
-rw-r--r--script/vm/list.lua30
-rw-r--r--script/vm/local.lua191
-rw-r--r--script/vm/manager.lua17
-rw-r--r--script/vm/module.lua56
-rw-r--r--script/vm/multi.lua83
-rw-r--r--script/vm/pcall.lua50
-rw-r--r--script/vm/raw.lua30
-rw-r--r--script/vm/source.lua183
-rw-r--r--script/vm/special.lua130
-rw-r--r--script/vm/value.lua634
-rw-r--r--script/vm/vm.lua1341
-rw-r--r--script/without-check-nil.lua126
-rw-r--r--script/workspace.lua468
134 files changed, 0 insertions, 21040 deletions
diff --git a/script/async/async.lua b/script/async/async.lua
deleted file mode 100644
index 3db1162c..00000000
--- a/script/async/async.lua
+++ /dev/null
@@ -1,127 +0,0 @@
-local thread = require 'bee.thread'
-local errlog = thread.channel 'errlog'
-
-local TaskId = 0
-local IdlePool = {}
-local RunningList = {}
-local GCInfo = {}
-
-thread.newchannel 'gc'
-
-local function createTask(name)
- TaskId = TaskId + 1
- GCInfo[TaskId] = false
- local id = TaskId
- local requestName = 'request' .. tostring(id)
- local responseName = 'response' .. tostring(id)
- thread.newchannel(requestName)
- thread.newchannel(responseName)
- local buf = ([[
-ID = %d
-package.cpath = %q
-package.path = %q
-local thread = require 'bee.thread'
-local request = thread.channel(%q)
-local response = thread.channel(%q)
-local errlog = thread.channel 'errlog'
-local gc = thread.channel 'gc'
-
-local function task()
- local dump, path, arg = request:bpop()
- local env = setmetatable({
- IN = request,
- OUT = response,
- ERR = errlog,
- GC = gc,
- }, { __index = _ENV })
- local f, err = load(dump, '@'..path, 't', env)
- if not f then
- errlog:push(err .. '\n' .. dump)
- return
- end
- local result = f(arg)
- response:push(result)
-end
-
-while true do
- local ok, result = xpcall(task, debug.traceback)
- if not ok then
- errlog:push(result)
- end
- collectgarbage()
- gc:push(ID, collectgarbage 'count')
-end
-]]):format(id, package.cpath, package.path, requestName, responseName)
- log.debug('Create thread, id: ', id, 'task: ', name)
- return {
- id = id,
- thread = thread.thread(buf),
- request = thread.channel(requestName),
- response = thread.channel(responseName),
- }
-end
-
-local function run(name, arg, callback)
- local path = ROOT / 'script' / 'async' / (name .. '.lua')
- local dump = io.load(path)
- if not dump then
- error(('找不到[%s]'):format(name))
- end
- local task = table.remove(IdlePool)
- if not task then
- task = createTask(name)
- end
- RunningList[task.id] = {
- task = task,
- callback = callback,
- }
- task.request:push(dump, path:string(), arg)
- -- TODO 线程回收后禁止外部再使用通道
- return task.request, task.response
-end
-
-local function callback(id, running)
- if running.callback then
- while true do
- local results = table.pack(running.task.response:pop())
- if not results[1] then
- break
- end
- -- TODO 封装成对象
- local suc, destroy = xpcall(running.callback, log.error, table.unpack(results, 2))
- if not suc or destroy then
- RunningList[id] = nil
- IdlePool[#IdlePool+1] = running.task
- break
- end
- end
- end
-end
-
-local function checkGC()
- local gc = thread.channel 'gc'
- while true do
- local ok, id, count = gc:pop()
- if not ok then
- break
- end
- GCInfo[id] = count
- end
-end
-
-local function onTick()
- local ok, msg = errlog:pop()
- if ok then
- log.error(msg)
- end
- for id, running in pairs(RunningList) do
- callback(id, running)
- end
- checkGC()
-end
-
-return {
- onTick = onTick,
- run = run,
- info = GCInfo,
-}
diff --git a/script/async/init.lua b/script/async/init.lua
deleted file mode 100644
index 3d75a720..00000000
--- a/script/async/init.lua
+++ /dev/null
@@ -1 +0,0 @@
-return require 'async.async'
diff --git a/script/async/loadfile.lua b/script/async/loadfile.lua
deleted file mode 100644
index b9d0f41c..00000000
--- a/script/async/loadfile.lua
+++ /dev/null
@@ -1,13 +0,0 @@
-require 'utility'
-local fs = require 'bee.filesystem'
-
-while true do
- local filename, mode = IN:bpop()
- local buf = io.load(fs.path(filename))
- if buf then
- OUT:push(filename, mode, buf)
- else
- ERR:push('Load failed:' .. filename)
- end
- GC:push(ID, collectgarbage 'count')
-end
diff --git a/script/async/proto.lua b/script/async/proto.lua
deleted file mode 100644
index c8126c63..00000000
--- a/script/async/proto.lua
+++ /dev/null
@@ -1,62 +0,0 @@
-local json = require 'json'
-
-local function pushError(...)
- local t = table.pack(...)
- for i = 1, t.n do
- t[i] = tostring(t[i])
- end
- local buf = table.concat(t, '\t')
- ERR:push(buf)
-end
-
-local function readProtoHead(reader)
- local head = {}
- while true do
- local line = reader 'L'
- if line == '\r\n' then
- break
- else
- local k, v = line:match '^([^:]+)%s*%:%s*(.+)\r\n$'
- if k then
- if k == 'Content-Length' then
- v = tonumber(v)
- end
- head[k] = v
- else
- pushError('Proto header error:', head)
- break
- end
- end
- end
- return head
-end
-
-local function readProtoContent(head)
- local len = head['Content-Length']
- if not len then
- pushError('Proto header error:', head)
- return nil
- end
- local buf = io.read(len)
- if not buf then
- return nil
- end
- return buf
-end
-
-local function readProto()
- local head = readProtoHead(io.read)
- if not head then
- return
- end
- local data = readProtoContent(head)
- if not data then
- return
- end
- OUT:push(data)
-end
-
-while true do
- readProto()
- GC:push(ID, collectgarbage 'count')
-end
diff --git a/script/async/scanfiles.lua b/script/async/scanfiles.lua
deleted file mode 100644
index f5249c8e..00000000
--- a/script/async/scanfiles.lua
+++ /dev/null
@@ -1,55 +0,0 @@
-local args = ...
-
-require 'utility'
-local fs = require 'bee.filesystem'
-local glob = require 'glob'
-
-local function scan(mode, root, pattern, options)
- OUT:push('log', 'Scanning:', root:string())
- OUT:push('log', 'Scan pattern:', table.dump(pattern))
- OUT:push('log', 'Scan options:', table.dump(options))
- local session = glob.gitignore(pattern, options)
-
- session:setInterface('type', function (path)
- local fullpath = root / path
- if not fs.exists(fullpath) then
- return nil
- end
- if fs.is_directory(fullpath) then
- return 'directory'
- else
- return 'file'
- end
- return nil
- end)
- session:setInterface('list', function (path)
- local fullpath = root / path
- if not fs.exists(fullpath) then
- return nil
- end
- local list = {}
- for child in fullpath:list_directory() do
- list[#list+1] = child:string()
- end
- return list
- end)
-
- session:scan(function (path)
- local ok, msg = IN:pop()
- if ok and msg == 'stop' then
- OUT:push 'stop'
- return
- end
- OUT:push(mode, fs.absolute(root / path):string())
- end)
-end
-
-for _, data in ipairs(args) do
- local root = fs.path(data.root)
- local suc, err = xpcall(scan, debug.traceback, data.mode, root, data.pattern, data.options)
- if not suc then
- ERR:push(err)
- end
-end
-
-OUT:push 'ok'
diff --git a/script/build_package.lua b/script/build_package.lua
deleted file mode 100644
index 402cea14..00000000
--- a/script/build_package.lua
+++ /dev/null
@@ -1,329 +0,0 @@
-local json = require 'json'
-local diagDefault = require 'constant.DiagnosticDefaultSeverity'
-
-local VERSION = "0.14.2"
-
-local package = {
- name = "lua",
- displayName = "Lua",
- description = "Lua Language Server coded by Lua",
- author = "sumneko",
- icon = "images/logo.png",
- license = "MIT",
- repository = {
- type = "git",
- url = "https://github.com/sumneko/lua-language-server"
- },
- publisher = "sumneko",
- categories = {
- "Linters",
- "Programming Languages",
- "Snippets"
- },
- keywords = {
- "Lua",
- "LSP",
- "GoTo Definition",
- "IntelliSense"
- },
- engines = {
- vscode = "^1.23.0"
- },
- activationEvents = {
- "onLanguage:lua"
- },
- main = "./client/out/extension",
- contributes = {
- configuration = {
- type = "object",
- title = "Lua",
- properties = {
- ["Lua.runtime.version"] = {
- scope = "resource",
- type = "string",
- default = "Lua 5.3",
- enum = {
- "Lua 5.1",
- "Lua 5.2",
- "Lua 5.3",
- "Lua 5.4",
- "LuaJIT"
- },
- markdownDescription = "%config.runtime.version%"
- },
- ["Lua.runtime.path"] = {
- scope = "resource",
- type = "array",
- items = {
- type = 'string',
- },
- markdownDescription = "%config.runtime.path%",
- default = {
- "?.lua",
- "?/init.lua",
- "?/?.lua"
- }
- },
- ["Lua.diagnostics.enable"] = {
- scope = 'resource',
- type = 'boolean',
- default = true,
- markdownDescription = "%config.diagnostics.enable%"
- },
- ["Lua.diagnostics.disable"] = {
- scope = "resource",
- type = "array",
- items = {
- type = 'string',
- },
- markdownDescription = "%config.diagnostics.disable%"
- },
- ["Lua.diagnostics.globals"] = {
- scope = "resource",
- type = "array",
- items = {
- type = 'string',
- },
- markdownDescription = "%config.diagnostics.globals%"
- },
- ["Lua.diagnostics.severity"] = {
- scope = "resource",
- type = 'object',
- markdownDescription = "%config.diagnostics.severity%",
- title = "severity",
- properties = {}
- },
- ["Lua.workspace.ignoreDir"] = {
- scope = "resource",
- type = "array",
- items = {
- type = 'string',
- },
- markdownDescription = "%config.workspace.ignoreDir%",
- default = {
- ".vscode",
- },
- },
- ["Lua.workspace.ignoreSubmodules"] = {
- scope = "resource",
- type = "boolean",
- default = true,
- markdownDescription = "%config.workspace.ignoreSubmodules%"
- },
- ["Lua.workspace.useGitIgnore"] = {
- scope = "resource",
- type = "boolean",
- default = true,
- markdownDescription = "%config.workspace.useGitIgnore%"
- },
- ["Lua.workspace.maxPreload"] = {
- scope = "resource",
- type = "integer",
- default = 300,
- markdownDescription = "%config.workspace.maxPreload%"
- },
- ["Lua.workspace.preloadFileSize"] = {
- scope = "resource",
- type = "integer",
- default = 100,
- markdownDescription = "%config.workspace.preloadFileSize%"
- },
- ["Lua.workspace.library"] = {
- scope = 'resource',
- type = 'object',
- markdownDescription = "%config.workspace.library%"
- },
- ["Lua.completion.enable"] = {
- scope = "resource",
- type = "boolean",
- default = true,
- markdownDescription = "%config.completion.enable%"
- },
- ["Lua.completion.callSnippet"] = {
- scope = "resource",
- type = "string",
- default = "Disable",
- enum = {
- "Disable",
- "Both",
- "Replace",
- },
- markdownEnumDescriptions = {
- "%config.completion.callSnippet.Disable%",
- "%config.completion.callSnippet.Both%",
- "%config.completion.callSnippet.Replace%",
- },
- markdownDescription = "%config.completion.callSnippet%"
- },
- ["Lua.completion.keywordSnippet"] = {
- scope = "resource",
- type = "string",
- default = "Replace",
- enum = {
- "Disable",
- "Both",
- "Replace",
- },
- markdownEnumDescriptions = {
- "%config.completion.keywordSnippet.Disable%",
- "%config.completion.keywordSnippet.Both%",
- "%config.completion.keywordSnippet.Replace%",
- },
- markdownDescription = "%config.completion.keywordSnippet%"
- },
- --["Lua.plugin.enable"] = {
- -- scope = "resource",
- -- type = "boolean",
- -- default = false,
- -- markdownDescription = "%config.plugin.enable%"
- --},
- --["Lua.plugin.path"] = {
- -- scope = "resource",
- -- type = "string",
- -- default = ".vscode/lua-plugin/*.lua",
- -- markdownDescription = "%config.plugin.path%"
- --},
- ["Lua.zzzzzz.cat"] = {
- scope = "resource",
- type = "boolean",
- default = false,
- markdownDescription = "%config.zzzzzz.cat%"
- },
- }
- },
- grammars = {
- {
- language = "lua",
- scopeName = "source.lua",
- path = "./syntaxes/lua.tmLanguage.json"
- }
- }
- },
- __metadata = {
- id = "3a15b5a7-be12-47e3-8445-88ee3eabc8b2",
- publisherDisplayName = "sumneko",
- publisherId = "fb626675-24cf-4881-8c13-b465f29bec2f",
- },
-}
-
-local DiagSeverity = package.contributes.configuration.properties["Lua.diagnostics.severity"].properties
-for name, level in pairs(diagDefault) do
- DiagSeverity[name] = {
- scope = 'resource',
- type = 'string',
- default = level,
- enum = {
- 'Error',
- 'Warning',
- 'Information',
- 'Hint',
- }
- }
-end
-
-package.version = VERSION
-
-io.save(ROOT:parent_path() / 'package.json', json.encode(package))
-
-local example = {
- library = [[
-```json
-"Lua.workspace.library": {
- "C:/lua": true,
- "../lib": [
- "temp/*"
- ]
-}
-```
-]],
- disable = [[
-```json
-"Lua.diagnostics.disable" : [
- "unused-local",
- "lowercase-global"
-]
-```
-]],
- globals = [[
-```json
-"Lua.diagnostics.globals" : [
- "GLOBAL1",
- "GLOBAL2"
-]
-```
-]],
- severity = [[
-```json
-"Lua.diagnostics.severity" : {
- "redefined-local" : "Warning",
- "emmy-lua" : "Hint"
-}
-```
-]],
- ignoreDir = [[
-```json
-"Lua.workspace.ignoreDir" : [
- "temp/*.*",
- "!temp/*.lua"
-]
-```
-]]
-}
-
-io.save(ROOT:parent_path() / 'package.nls.json', json.encode {
- ["config.runtime.version"] = "Lua runtime version.",
- ["config.runtime.path"] = "`package.path`",
- ["config.diagnostics.enable"] = "Enable diagnostics.",
- ["config.diagnostics.disable"] = "Disabled diagnostic (Use code in hover brackets).\n" .. example.disable,
- ["config.diagnostics.globals"] = "Defined global variables.\n" .. example.globals,
- ["config.diagnostics.severity"] = "Modified diagnostic severity.\n" .. example.severity,
- ["config.workspace.ignoreDir"] = "Ignored directories (Use `.gitignore` grammar).\n" .. example.ignoreDir,
- ["config.workspace.ignoreSubmodules"] = "Ignore submodules.",
- ["config.workspace.useGitIgnore"] = "Ignore files list in `.gitignore` .",
- ["config.workspace.maxPreload"] = "Max preloaded files.",
- ["config.workspace.preloadFileSize"] = "Skip files larger than this value (KB) when preloading.",
- ["config.workspace.library"] = [[
-Load external library.
-This feature can load external Lua files, which can be used for definition, automatic completion and other functions. Note that the language server does not monitor changes in external files and needs to restart if the external files are modified.
-The following example shows loaded files in `C:/lua` and `../lib` ,exclude `../lib/temp`.
-]] .. example.library,
- ['config.completion.enable'] = 'Enable completion.',
- ['config.completion.callSnippet'] = 'Shows function call snippets.',
- ['config.completion.callSnippet.Disable'] = "Only shows `function name`.",
- ['config.completion.callSnippet.Both'] = "Shows `function name` and `call snippet`.",
- ['config.completion.callSnippet.Replace'] = "Only shows `call snippet.`",
- ['config.completion.keywordSnippet'] = 'Shows keyword syntax snippets.',
- ['config.completion.keywordSnippet.Disable'] = "Only shows `keyword`.",
- ['config.completion.keywordSnippet.Both'] = "Shows `keyword` and `syntax snippet`.",
- ['config.completion.keywordSnippet.Replace'] = "Only shows `syntax snippet`.",
- ['config.zzzzzz.cat'] = 'DO NOT TOUCH ME, LET ME SLEEP >_<\n\n(This will enable beta version, which are still in the early stages of development, and all features will fail after enabling this setting.)',
-})
-
-io.save(ROOT:parent_path() / 'package.nls.zh-cn.json', json.encode {
- ["config.runtime.version"] = "Lua运行版本。",
- ["config.runtime.path"] = "`package.path`",
- ["config.diagnostics.enable"] = "启用诊断。",
- ["config.diagnostics.disable"] = "禁用的诊断(使用浮框括号内的代码)。\n" .. example.disable,
- ["config.diagnostics.globals"] = "已定义的全局变量。\n" .. example.globals,
- ["config.diagnostics.severity"] = "修改诊断等级。\n" .. example.severity,
- ["config.workspace.ignoreDir"] = "忽略的目录(使用 `.gitignore` 语法)。\n" .. example.ignoreDir,
- ["config.workspace.ignoreSubmodules"] = "忽略子模块。",
- ["config.workspace.useGitIgnore"] = "忽略 `.gitignore` 中列举的文件。",
- ["config.workspace.maxPreload"] = "最大预加载文件数。",
- ["config.workspace.preloadFileSize"] = "预加载时跳过大小大于该值(KB)的文件。",
- ["config.workspace.library"] = [[
-加载外部函数库。
-该功能可以加载外部的Lua文件,用于函数定义、自动完成等功能。注意,语言服务不会监视外部文件的变化,如果修改了外部文件需要重启。
-下面这个例子表示加载`C:/lua`与`../lib`中的所有文件,但不加载`../lib/temp`中的文件。
-]] .. example.library,
- ['config.completion.enable'] = '启用自动完成。',
- ['config.completion.callSnippet'] = '显示函数调用片段。',
- ['config.completion.callSnippet.Disable'] = "只显示 `函数名`。",
- ['config.completion.callSnippet.Both'] = "显示 `函数名` 与 `调用片段`。",
- ['config.completion.callSnippet.Replace'] = "只显示 `调用片段`。",
- ['config.completion.keywordSnippet'] = '显示关键字语法片段',
- ['config.completion.keywordSnippet.Disable'] = "只显示 `关键字`。",
- ['config.completion.keywordSnippet.Both'] = "显示 `关键字` 与 `语法片段`。",
- ['config.completion.keywordSnippet.Replace'] = "只显示 `语法片段`。",
- ['config.zzzzzz.cat'] = 'DO NOT TOUCH ME, LET ME SLEEP >_<\n\n(这会启用还处于早期开发阶段的beta版,开启后所有的功能都会失效)',
-})
diff --git a/script/capability/completion.lua b/script/capability/completion.lua
deleted file mode 100644
index e302f30d..00000000
--- a/script/capability/completion.lua
+++ /dev/null
@@ -1,68 +0,0 @@
-local rpc = require 'rpc'
-local nonil = require 'without-check-nil'
-
-local isEnable = false
-
-local function allWords()
- local str = [[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.:('"[,#*@| ]]
- local list = {}
- for c in str:gmatch '.' do
- list[#list+1] = c
- end
- return list
-end
-
-local function enable(lsp)
- if isEnable then
- return
- end
-
- nonil.enable()
- if not lsp.client.capabilities.textDocument.completion.dynamicRegistration then
- return
- end
- nonil.disable()
-
- isEnable = true
- log.debug('Enable completion.')
- rpc:request('client/registerCapability', {
- registrations = {
- {
- id = 'completion',
- method = 'textDocument/completion',
- registerOptions = {
- resolveProvider = true,
- triggerCharacters = allWords(),
- },
- },
- }
- })
-end
-
-local function disable(lsp)
- if not isEnable then
- return
- end
-
- nonil.enable()
- if not lsp.client.capabilities.textDocument.completion.dynamicRegistration then
- return
- end
- nonil.disable()
-
- isEnable = false
- log.debug('Disable completion.')
- rpc:request('client/unregisterCapability', {
- unregisterations = {
- {
- id = 'completion',
- method = 'textDocument/completion',
- },
- }
- })
-end
-
-return {
- enable = enable,
- disable = disable,
-}
diff --git a/script/capability/init.lua b/script/capability/init.lua
deleted file mode 100644
index 2cc9e70b..00000000
--- a/script/capability/init.lua
+++ /dev/null
@@ -1,4 +0,0 @@
-return {
- completion = require 'capability.completion',
- semantic = require 'capability.semantic',
-}
diff --git a/script/capability/semantic.lua b/script/capability/semantic.lua
deleted file mode 100644
index 2be22be3..00000000
--- a/script/capability/semantic.lua
+++ /dev/null
@@ -1,64 +0,0 @@
-local rpc = require 'rpc'
-local TokenTypes = require 'constant.TokenTypes'
-local TokenModifiers = require 'constant.TokenModifiers'
-
-local isEnable = false
-
-local function toArray(map)
- local array = {}
- for k in pairs(map) do
- array[#array+1] = k
- end
- table.sort(array, function (a, b)
- return map[a] < map[b]
- end)
- return array
-end
-
-local function enable(lsp)
- if isEnable then
- return
- end
- if not lsp.client.capabilities.textDocument.semanticTokens then
- return
- end
- isEnable = true
- log.debug('Enable semantic.')
- rpc:request('client/registerCapability', {
- registrations = {
- {
- id = 'semantic',
- method = 'textDocument/semanticTokens',
- registerOptions = {
- legend = {
- tokenTypes = toArray(TokenTypes),
- tokenModifiers = toArray(TokenModifiers),
- },
- range = false,
- full = true,
- },
- },
- }
- })
-end
-
-local function disable()
- if not isEnable then
- return
- end
- isEnable = false
- log.debug('Disable semantic.')
- rpc:request('client/unregisterCapability', {
- unregisterations = {
- {
- id = 'semantic',
- method = 'textDocument/semanticTokens',
- },
- }
- })
-end
-
-return {
- enable = enable,
- disable = disable,
-}
diff --git a/script/client.lua b/script/client.lua
deleted file mode 100644
index a2671597..00000000
--- a/script/client.lua
+++ /dev/null
@@ -1,16 +0,0 @@
-local nonil = require 'without-check-nil'
-
-local m = {}
-
-function m.client()
- nonil.enable()
- local name = m.info.clientInfo.name
- nonil.disable()
- return name
-end
-
-function m.init(t)
- m.info = t
-end
-
-return m
diff --git a/script/config.lua b/script/config.lua
deleted file mode 100644
index 8011b905..00000000
--- a/script/config.lua
+++ /dev/null
@@ -1,218 +0,0 @@
-local DiagnosticDefaultSeverity = require 'constant.DiagnosticDefaultSeverity'
-
-local function Boolean(v)
- if type(v) == 'boolean' then
- return true, v
- end
- return false
-end
-
-local function Integer(v)
- if type(v) == 'number' then
- return true, math.floor(v)
- end
- return false
-end
-
-local function String(v)
- return true, tostring(v)
-end
-
-local function Str2Hash(sep)
- return function (v)
- if type(v) == 'string' then
- local t = {}
- for s in v:gmatch('[^'..sep..']+') do
- t[s] = true
- end
- return true, t
- end
- if type(v) == 'table' then
- local t = {}
- for _, s in ipairs(v) do
- if type(s) == 'string' then
- t[s] = true
- end
- end
- return true, t
- end
- return false
- end
-end
-
-local function Array(checker)
- return function (tbl)
- if type(tbl) ~= 'table' then
- return false
- end
- local t = {}
- for _, v in ipairs(tbl) do
- local ok, result = checker(v)
- if ok then
- t[#t+1] = result
- end
- end
- return true, t
- end
-end
-
-local function Hash(keyChecker, valueChecker)
- return function (tbl)
- if type(tbl) ~= 'table' then
- return false
- end
- local t = {}
- for k, v in pairs(tbl) do
- local ok1, key = keyChecker(k)
- local ok2, value = valueChecker(v)
- if ok1 and ok2 then
- t[key] = value
- end
- end
- if not next(t) then
- return false
- end
- return true, t
- end
-end
-
-local function Or(...)
- local checkers = {...}
- return function (obj)
- for _, checker in ipairs(checkers) do
- local suc, res = checker(obj)
- if suc then
- return true, res
- end
- end
- return false
- end
-end
-
-local ConfigTemplate = {
- runtime = {
- version = {'Lua 5.4', String},
- library = {{}, Str2Hash ';'},
- path = {{
- "?.lua",
- "?/init.lua",
- "?/?.lua"
- }, Array(String)},
- special = {{}, Hash(String, String)},
- },
- diagnostics = {
- enable = {true, Boolean},
- globals = {{}, Str2Hash ';'},
- disable = {{}, Str2Hash ';'},
- severity = {
- table.deepCopy(DiagnosticDefaultSeverity),
- Hash(String, String),
- },
- workspaceDelay = {0, Integer},
- workspaceRate = {100, Integer},
- },
- workspace = {
- ignoreDir = {{}, Str2Hash ';'},
- ignoreSubmodules= {true, Boolean},
- useGitIgnore = {true, Boolean},
- maxPreload = {1000, Integer},
- preloadFileSize = {100, Integer},
- library = {{}, Hash(
- String,
- Or(Boolean, Array(String))
- )}
- },
- completion = {
- enable = {true, Boolean},
- callSnippet = {'Disable', String},
- keywordSnippet = {'Replace', String},
- displayContext = {6, Integer},
- },
- signatureHelp = {
- enable = {true, Boolean},
- },
- hover = {
- enable = {true, Boolean},
- viewString = {true, Boolean},
- viewStringMax = {1000, Integer},
- viewNumber = {true, Boolean},
- fieldInfer = {3000, Integer},
- },
- color = {
- mode = {'Semantic', String},
- },
- luadoc = {
- enable = {true, Boolean},
- },
- plugin = {
- enable = {false, Boolean},
- path = {'.vscode/lua-plugin/*.lua', String},
- },
- intelliSense = {
- searchDepth = {0, Integer},
- fastGlobal = {true, Boolean},
- },
-}
-
-local OtherTemplate = {
- associations = {{}, Hash(String, String)},
- exclude = {{}, Hash(String, Boolean)},
-}
-
-local Config, Other
-
-local function init()
- if Config then
- return
- end
-
- Config = {}
- for c, t in pairs(ConfigTemplate) do
- Config[c] = {}
- for k, info in pairs(t) do
- Config[c][k] = info[1]
- end
- end
-
- Other = {}
- for k, info in pairs(OtherTemplate) do
- Other[k] = info[1]
- end
-end
-
-local function setConfig(self, config, other)
- xpcall(function ()
- for c, t in pairs(config) do
- for k, v in pairs(t) do
- local region = ConfigTemplate[c]
- if region then
- local info = region[k]
- local suc, v = info[2](v)
- if suc then
- Config[c][k] = v
- else
- Config[c][k] = info[1]
- end
- end
- end
- end
- for k, v in pairs(other) do
- local info = OtherTemplate[k]
- local suc, v = info[2](v)
- if suc then
- Other[k] = v
- else
- Other[k] = info[1]
- end
- end
- log.debug('Config update: ', table.dump(Config), table.dump(Other))
- end, log.error)
-end
-
-init()
-
-return {
- setConfig = setConfig,
- config = Config,
- other = Other,
-}
diff --git a/script/constant/DiagnosticDefaultSeverity.lua b/script/constant/DiagnosticDefaultSeverity.lua
deleted file mode 100644
index f5babcdc..00000000
--- a/script/constant/DiagnosticDefaultSeverity.lua
+++ /dev/null
@@ -1,23 +0,0 @@
-return {
- ['unused-local'] = 'Hint',
- ['unused-function'] = 'Hint',
- ['undefined-global'] = 'Warning',
- ['global-in-nil-env'] = 'Warning',
- ['unused-label'] = 'Hint',
- ['unused-vararg'] = 'Hint',
- ['trailing-space'] = 'Hint',
- ['redefined-local'] = 'Hint',
- ['newline-call'] = 'Information',
- ['newfield-call'] = 'Warning',
- ['redundant-parameter'] = 'Hint',
- ['ambiguity-1'] = 'Warning',
- ['lowercase-global'] = 'Information',
- ['undefined-env-child'] = 'Information',
- ['duplicate-index'] = 'Warning',
- ['duplicate-method'] = 'Warning',
- ['empty-block'] = 'Hint',
- ['redundant-value'] = 'Hint',
- ['emmy-lua'] = 'Warning',
- ['set-const'] = 'Error',
- ['set-for-state'] = 'Warning',
-}
diff --git a/script/constant/DiagnosticSeverity.lua b/script/constant/DiagnosticSeverity.lua
deleted file mode 100644
index 05bd3659..00000000
--- a/script/constant/DiagnosticSeverity.lua
+++ /dev/null
@@ -1,6 +0,0 @@
-return {
- Error = 1,
- Warning = 2,
- Information = 3,
- Hint = 4,
-}
diff --git a/script/constant/DiagnosticTag.lua b/script/constant/DiagnosticTag.lua
deleted file mode 100644
index 918b7bad..00000000
--- a/script/constant/DiagnosticTag.lua
+++ /dev/null
@@ -1,4 +0,0 @@
-return {
- Unnecessary = 1,
- Deprecated = 2,
-}
diff --git a/script/constant/TokenModifiers.lua b/script/constant/TokenModifiers.lua
deleted file mode 100644
index b77fd386..00000000
--- a/script/constant/TokenModifiers.lua
+++ /dev/null
@@ -1,8 +0,0 @@
-return {
- ["declaration"] = 1 << 0,
- ["documentation"] = 1 << 1,
- ["static"] = 1 << 2,
- ["abstract"] = 1 << 3,
- ["deprecated"] = 1 << 4,
- ["readonly"] = 1 << 5,
-}
diff --git a/script/constant/TokenTypes.lua b/script/constant/TokenTypes.lua
deleted file mode 100644
index 236a7805..00000000
--- a/script/constant/TokenTypes.lua
+++ /dev/null
@@ -1,21 +0,0 @@
-return {
- ["comment"] = 0,
- ["keyword"] = 1,
- ["number"] = 2,
- ["regexp"] = 3,
- ["operator"] = 4,
- ["namespace"] = 5,
- ["type"] = 6,
- ["struct"] = 7,
- ["class"] = 8,
- ["interface"] = 9,
- ["enum"] = 10,
- ["typeParameter"] = 11,
- ["function"] = 12,
- ["member"] = 13,
- ["macro"] = 14,
- ["variable"] = 15,
- ["parameter"] = 16,
- ["property"] = 17,
- ["label"] = 18,
-}
diff --git a/script/core/code_action.lua b/script/core/code_action.lua
deleted file mode 100644
index 3e8f7582..00000000
--- a/script/core/code_action.lua
+++ /dev/null
@@ -1,416 +0,0 @@
-local lang = require 'language'
-local library = require 'core.library'
-local client = require 'client'
-local sp = require 'bee.subprocess'
-
-local function disableDiagnostic(lsp, uri, data, callback)
- callback {
- title = lang.script('ACTION_DISABLE_DIAG', data.code),
- kind = 'quickfix',
- command = {
- title = lang.script.COMMAND_DISABLE_DIAG,
- command = 'lua.config',
- arguments = {
- {
- key = 'Lua.diagnostics.disable',
- action = 'add',
- value = data.code,
- uri = uri,
- }
- }
- }
- }
-end
-
-local function addGlobal(name, uri, callback)
- callback {
- title = lang.script('ACTION_MARK_GLOBAL', name),
- kind = 'quickfix',
- command = {
- title = lang.script.COMMAND_MARK_GLOBAL,
- command = 'lua.config',
- arguments = {
- {
- key = 'Lua.diagnostics.globals',
- action = 'add',
- value = name,
- uri = uri,
- }
- }
- },
- }
-end
-
-local function changeVersion(version, uri, callback)
- callback {
- title = lang.script('ACTION_RUNTIME_VERSION', version),
- kind = 'quickfix',
- command = {
- title = lang.script.COMMAND_RUNTIME_VERSION,
- command = 'lua.config',
- arguments = {
- {
- key = 'Lua.runtime.version',
- action = 'set',
- value = version,
- uri = uri,
- }
- }
- },
- }
-end
-
-local function openCustomLibrary(libName, uri, callback)
- callback {
- title = lang.script('ACTION_OPEN_LIBRARY', libName),
- kind = 'quickfix',
- command = {
- title = lang.script.COMMAND_OPEN_LIBRARY,
- command = 'lua.config',
- arguments = {
- {
- key = 'Lua.runtime.library',
- action = 'add',
- value = libName,
- uri = uri,
- }
- }
- },
- }
-end
-
-local function solveUndefinedGlobal(lsp, uri, data, callback)
- local vm, lines, text = lsp:getVM(uri)
- if not vm then
- return
- end
- local start = lines:position(data.range.start.line + 1, data.range.start.character + 1)
- local finish = lines:position(data.range['end'].line + 1, data.range['end'].character)
- local name = text:sub(start, finish)
- if #name < 0 or name:find('[^%w_]') then
- return
- end
- addGlobal(name, uri, callback)
- local otherVersion = library.other[name]
- if otherVersion then
- for _, version in ipairs(otherVersion) do
- changeVersion(version, uri, callback)
- end
- end
-
- local customLibrary = library.custom[name]
- if customLibrary then
- for _, libName in ipairs(customLibrary) do
- openCustomLibrary(libName, uri, callback)
- end
- end
-end
-
-local function solveLowercaseGlobal(lsp, uri, data, callback)
- local vm, lines, text = lsp:getVM(uri)
- if not vm then
- return
- end
- local start = lines:position(data.range.start.line + 1, data.range.start.character + 1)
- local finish = lines:position(data.range['end'].line + 1, data.range['end'].character)
- local name = text:sub(start, finish)
- if #name < 0 or name:find('[^%w_]') then
- return
- end
- addGlobal(name, uri, callback)
-end
-
-local function solveTrailingSpace(lsp, uri, data, callback)
- callback {
- title = lang.script.ACTION_REMOVE_SPACE,
- kind = 'quickfix',
- command = {
- title = lang.script.COMMAND_REMOVE_SPACE,
- command = 'lua.removeSpace:' .. sp:get_id(),
- arguments = {
- {
- uri = uri,
- }
- }
- },
- }
-end
-
-local function solveNewlineCall(lsp, uri, data, callback)
- callback {
- title = lang.script.ACTION_ADD_SEMICOLON,
- kind = 'quickfix',
- edit = {
- changes = {
- [uri] = {
- {
- range = {
- start = data.range.start,
- ['end'] = data.range.start,
- },
- newText = ';',
- }
- }
- }
- }
- }
-end
-
-local function solveAmbiguity1(lsp, uri, data, callback)
- callback {
- title = lang.script.ACTION_ADD_BRACKETS,
- kind = 'quickfix',
- command = {
- title = lang.script.COMMAND_ADD_BRACKETS,
- command = 'lua.solve:' .. sp:get_id(),
- arguments = {
- {
- name = 'ambiguity-1',
- uri = uri,
- range = data.range,
- }
- }
- },
- }
-end
-
-local function findSyntax(astErr, lines, data)
- local start = lines:position(data.range.start.line + 1, data.range.start.character + 1)
- local finish = lines:position(data.range['end'].line + 1, data.range['end'].character)
- for _, err in ipairs(astErr) do
- if err.start == start and err.finish == finish then
- return err
- end
- end
- return nil
-end
-
-local function solveSyntaxByChangeVersion(err, uri, callback)
- if type(err.version) == 'table' then
- for _, version in ipairs(err.version) do
- changeVersion(version, uri, callback)
- end
- else
- changeVersion(err.version, uri, callback)
- end
-end
-
-local function solveSyntaxByAddDoEnd(uri, data, callback)
- callback {
- title = lang.script.ACTION_ADD_DO_END,
- kind = 'quickfix',
- edit = {
- changes = {
- [uri] = {
- {
- range = {
- start = data.range.start,
- ['end'] = data.range.start,
- },
- newText = 'do ',
- },
- {
- range = {
- start = data.range['end'],
- ['end'] = data.range['end'],
- },
- newText = ' end',
- }
- }
- }
- }
- }
-end
-
-local function solveSyntaxByFix(uri, err, lines, callback)
- local changes = {}
- for _, e in ipairs(err.fix) do
- local start_row, start_col = lines:rowcol(e.start)
- local finish_row, finish_col = lines:rowcol(e.finish)
- changes[#changes+1] = {
- range = {
- start = {
- line = start_row - 1,
- character = start_col - 1,
- },
- ['end'] = {
- line = finish_row - 1,
- character = finish_col,
- },
- },
- newText = e.text,
- }
- end
- callback {
- title = lang.script['ACTION_' .. err.fix.title],
- kind = 'quickfix',
- edit = {
- changes = {
- [uri] = changes,
- }
- }
- }
-end
-
-local function findEndPosition(lines, row, endrow)
- if endrow == row then
- return {
- newText = ' end',
- range = {
- start = {
- line = row - 1,
- character = 999999,
- },
- ['end'] = {
- line = row - 1,
- character = 999999,
- }
- }
- }
- else
- local l = lines[row]
- return {
- newText = ('\t'):rep(l.tab) .. (' '):rep(l.sp) .. 'end\n',
- range = {
- start = {
- line = endrow,
- character = 0,
- },
- ['end'] = {
- line = endrow,
- character = 0,
- }
- }
- }
- end
-end
-
-local function isIfPart(id, lines, i)
- if id ~= 'if' then
- return false
- end
- local buf = lines:line(i)
- local first = buf:match '^[%s\t]*([%w]+)'
- if first == 'else' or first == 'elseif' then
- return true
- end
- return false
-end
-
-local function solveSyntaxByAddEnd(uri, start, finish, lines, callback)
- local row = lines:rowcol(start)
- local line = lines[row]
- if not line then
- return nil
- end
- local id = lines.buf:sub(start, finish)
- local sp = line.sp + line.tab * 4
- for i = row + 1, #lines do
- local nl = lines[i]
- local lsp = nl.sp + nl.tab * 4
- if lsp <= sp and not isIfPart(id, lines, i) then
- callback {
- title = lang.script['ACTION_ADD_END'],
- kind = 'quickfix',
- edit = {
- changes = {
- [uri] = {
- findEndPosition(lines, row, i - 1)
- }
- }
- }
- }
- return
- end
- end
- return nil
-end
-
----@param lsp LSP
----@param uri uri
----@param data table
----@param callback function
-local function solveSyntax(lsp, uri, data, callback)
- local file = lsp:getFile(uri)
- if not file then
- return
- end
- local astErr, lines = file:getAstErr(), file:getLines()
- if not astErr or not lines then
- return
- end
- local err = findSyntax(astErr, lines, data)
- if not err then
- return nil
- end
- if err.version then
- solveSyntaxByChangeVersion(err, uri, callback)
- end
- if err.type == 'ACTION_AFTER_BREAK' or err.type == 'ACTION_AFTER_RETURN' then
- solveSyntaxByAddDoEnd(uri, data, callback)
- end
- if err.type == 'MISS_END' then
- solveSyntaxByAddEnd(uri, err.start, err.finish, lines, callback)
- end
- if err.type == 'MISS_SYMBOL' and err.info.symbol == 'end' then
- solveSyntaxByAddEnd(uri, err.info.related[1], err.info.related[2], lines, callback)
- end
- if err.fix then
- solveSyntaxByFix(uri, err, lines, callback)
- end
-end
-
-local function solveDiagnostic(lsp, uri, data, callback)
- if data.source == lang.script.DIAG_SYNTAX_CHECK then
- solveSyntax(lsp, uri, data, callback)
- end
- if not data.code then
- return
- end
- if data.code == 'undefined-global' then
- solveUndefinedGlobal(lsp, uri, data, callback)
- end
- if data.code == 'trailing-space' then
- solveTrailingSpace(lsp, uri, data, callback)
- end
- if data.code == 'newline-call' then
- solveNewlineCall(lsp, uri, data, callback)
- end
- if data.code == 'ambiguity-1' then
- solveAmbiguity1(lsp, uri, data, callback)
- end
- if data.code == 'lowercase-global' then
- solveLowercaseGlobal(lsp, uri, data, callback)
- end
- disableDiagnostic(lsp, uri, data, callback)
-end
-
-local function rangeContain(a, b)
- if a.start.line > b.start.line then
- return false
- end
- if a.start.character > b.start.character then
- return false
- end
- if a['end'].line < b['end'].line then
- return false
- end
- if a['end'].character < b['end'].character then
- return false
- end
- return true
-end
-
-return function (lsp, uri, diagnostics, range)
- local results = {}
-
- for _, data in ipairs(diagnostics) do
- if rangeContain(data.range, range) then
- solveDiagnostic(lsp, uri, data, function (result)
- results[#results+1] = result
- end)
- end
- end
-
- return results
-end
diff --git a/script/core/completion.lua b/script/core/completion.lua
deleted file mode 100644
index 5af72de0..00000000
--- a/script/core/completion.lua
+++ /dev/null
@@ -1,1118 +0,0 @@
-local findSource = require 'core.find_source'
-local getFunctionHover = require 'core.hover.function'
-local getFunctionHoverAsLib = require 'core.hover.lib_function'
-local getFunctionHoverAsEmmy = require 'core.hover.emmy_function'
-local sourceMgr = require 'vm.source'
-local config = require 'config'
-local matchKey = require 'core.matchKey'
-local parser = require 'parser'
-local lang = require 'language'
-local snippet = require 'core.snippet'
-local uric = require 'uri'
-local State
-
-local CompletionItemKind = {
- Text = 1,
- Method = 2,
- Function = 3,
- Constructor = 4,
- Field = 5,
- Variable = 6,
- Class = 7,
- Interface = 8,
- Module = 9,
- Property = 10,
- Unit = 11,
- Value = 12,
- Enum = 13,
- Keyword = 14,
- Snippet = 15,
- Color = 16,
- File = 17,
- Reference = 18,
- Folder = 19,
- EnumMember = 20,
- Constant = 21,
- Struct = 22,
- Event = 23,
- Operator = 24,
- TypeParameter = 25,
-}
-
-local KEYS = {'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while'}
-local KEYMAP = {}
-for _, k in ipairs(KEYS) do
- KEYMAP[k] = true
-end
-
-local EMMY_KEYWORD = {'class', 'type', 'alias', 'param', 'return', 'field', 'generic', 'vararg', 'language', 'see', 'overload'}
-
-local function getDucumentation(name, value)
- if value:getType() == 'function' then
- local lib = value:getLib()
- local hover
- if lib then
- hover = getFunctionHoverAsLib(name, lib)
- else
- local emmy = value:getEmmy()
- if emmy and emmy.type == 'emmy.functionType' then
- hover = getFunctionHoverAsEmmy(name, emmy)
- else
- hover = getFunctionHover(name, value:getFunction())
- end
- end
- if not hover then
- return nil
- end
- local text = ([[
-```lua
-%s
-```
-%s
-```lua
-%s
-```
-%s
-]]):format(hover.label or '', hover.description or '', hover.enum or '', hover.doc or '')
- return {
- kind = 'markdown',
- value = text,
- }
- end
- local lib = value:getLib()
- if lib then
- return {
- kind = 'markdown',
- value = lib.description,
- }
- end
- local comment = value:getComment()
- if comment then
- return {
- kind = 'markdown',
- value = comment,
- }
- end
- return nil
-end
-
-local function getDetail(value)
- local literal = value:getLiteral()
- local tp = type(literal)
- local detals = {}
- if value:getType() ~= 'any' then
- detals[#detals+1] = ('(%s)'):format(value:getType())
- end
- if tp == 'boolean' then
- detals[#detals+1] = (' = %q'):format(literal)
- elseif tp == 'string' then
- detals[#detals+1] = (' = %q'):format(literal)
- elseif tp == 'number' then
- if math.type(literal) == 'integer' then
- detals[#detals+1] = (' = %q'):format(literal)
- else
- local str = (' = %.16f'):format(literal)
- local dot = str:find('.', 1, true) or 0
- local suffix = str:find('[0]+$', dot + 2)
- if suffix then
- detals[#detals+1] = str:sub(1, suffix - 1)
- else
- detals[#detals+1] = str
- end
- end
- end
- if value:getType() == 'function' then
- ---@type emmyFunction
- local func = value:getFunction()
- local overLoads = func and func:getEmmyOverLoads()
- if overLoads then
- detals[#detals+1] = lang.script('HOVER_MULTI_PROTOTYPE', #overLoads + 1)
- end
- end
- if #detals == 0 then
- return nil
- end
- return table.concat(detals)
-end
-
-local function getKind(cata, value)
- if value:getType() == 'function' then
- local func = value:getFunction()
- if func and func:getObject() then
- return CompletionItemKind.Method
- else
- return CompletionItemKind.Function
- end
- end
- if cata == 'field' then
- local literal = value:getLiteral()
- local tp = type(literal)
- if tp == 'number' or tp == 'integer' or tp == 'string' then
- return CompletionItemKind.Enum
- end
- end
- return nil
-end
-
-local function buildSnipArgs(args, enums)
- local t = {}
- for i, arg in ipairs(args) do
- local name = arg:match '^[^:]+'
- local enum = enums and enums[name]
- if enum and #enum > 0 then
- t[i] = ('${%d|%s|}'):format(i, table.concat(enum, ','))
- else
- t[i] = ('${%d:%s}'):format(i, arg)
- end
- end
- return table.concat(t, ', ')
-end
-
-local function getFunctionSnip(name, value, source)
- if value:getType() ~= 'function' then
- return
- end
- local lib = value:getLib()
- local object = source:get 'object'
- local hover
- if lib then
- hover = getFunctionHoverAsLib(name, lib, object)
- else
- local emmy = value:getEmmy()
- if emmy and emmy.type == 'emmy.functionType' then
- hover = getFunctionHoverAsEmmy(name, emmy, object)
- else
- hover = getFunctionHover(name, value:getFunction(), object)
- end
- end
- if not hover then
- return ('%s()'):format(name)
- end
- if not hover.args then
- return ('%s()'):format(name)
- end
- return ('%s(%s)'):format(name, buildSnipArgs(hover.args, hover.rawEnum))
-end
-
-local function getValueData(cata, name, value, pos, source)
- local data = {
- documentation = getDucumentation(name, value),
- detail = getDetail(value),
- kind = getKind(cata, value),
- snip = getFunctionSnip(name, value, source),
- }
- if cata == 'field' then
- if not parser:grammar(name, 'Name') then
- if source:get 'simple' and source:get 'simple' [1] ~= source then
- data.textEdit = {
- start = pos + 1,
- finish = pos,
- newText = ('[%q]'):format(name),
- }
- data.additionalTextEdits = {
- {
- start = pos,
- finish = pos,
- newText = '',
- }
- }
- else
- data.textEdit = {
- start = pos + 1,
- finish = pos,
- newText = ('_ENV[%q]'):format(name),
- }
- data.additionalTextEdits = {
- {
- start = pos,
- finish = pos,
- newText = '',
- }
- }
- end
- end
- end
- return data
-end
-
-local function searchLocals(vm, source, word, callback, pos)
- vm:eachSource(function (src)
- local loc = src:bindLocal()
- if not loc then
- return
- end
-
- if src.start <= source.start
- and loc:close() >= source.finish
- and matchKey(word, loc:getName())
- then
- callback(loc:getName(), src, CompletionItemKind.Variable, getValueData('local', loc:getName(), loc:getValue(), pos, source))
- end
- end)
-end
-
-local function sortPairs(t)
- local keys = {}
- for k in pairs(t) do
- keys[#keys+1] = k
- end
- table.sort(keys)
- local i = 0
- return function ()
- i = i + 1
- local k = keys[i]
- return k, t[k]
- end
-end
-
-local function searchFieldsByInfo(parent, word, source, map, srcMap)
- parent:eachInfo(function (info, src)
- local k = info[1]
- if src == source then
- return
- end
- if map[k] then
- return
- end
- if KEYMAP[k] then
- return
- end
- if info.type ~= 'set child' and info.type ~= 'get child' then
- return
- end
- if type(k) ~= 'string' then
- return
- end
- local v = parent:getChild(k)
- if not v then
- return
- end
- if source:get 'object' and v:getType() ~= 'function' then
- return
- end
- if matchKey(word, k) then
- map[k] = v
- srcMap[k] = src
- end
- end)
-end
-
-local function searchFieldsByChild(parent, word, source, map, srcMap)
- parent:eachChild(function (k, v, src)
- if map[k] then
- return
- end
- if KEYMAP[k] then
- return
- end
- if not v:getLib() then
- return
- end
- if type(k) ~= 'string' then
- return
- end
- if source:get 'object' and v:getType() ~= 'function' then
- return
- end
- if matchKey(word, k) then
- map[k] = v
- srcMap[k] = src
- end
- end)
-end
-
----@param vm VM
-local function searchFields(vm, source, word, callback, pos)
- local parent = source:get 'parent' or vm.env:getValue()
- if not parent then
- return
- end
- local map = {}
- local srcMap = {}
- local current = parent
- for _ = 1, 3 do
- searchFieldsByInfo(current, word, source, map, srcMap)
- current = current:getMetaMethod('__index')
- if not current then
- break
- end
- end
- searchFieldsByChild(parent, word, source, map, srcMap)
- for k, v in sortPairs(map) do
- callback(k, srcMap[k], CompletionItemKind.Field, getValueData('field', k, v, pos, source))
- end
-end
-
-local function searchIndex(vm, source, word, callback)
- vm:eachSource(function (src)
- if src:get 'table index' then
- if matchKey(word, src[1]) then
- callback(src[1], src, CompletionItemKind.Property)
- end
- end
- end)
-end
-
-local function searchCloseGlobal(vm, start, finish, word, callback)
- vm:eachSource(function (src)
- if (src:get 'global' or src:bindLocal())
- and src.start >= start
- and src.finish <= finish
- then
- if matchKey(word, src[1]) then
- callback(src[1], src, CompletionItemKind.Variable)
- end
- end
- end)
-end
-
-local function searchParams(vm, source, func, word, callback)
- if not func then
- return
- end
- ---@type emmyFunction
- local emmyParams = func:getEmmyParams()
- if not emmyParams then
- return
- end
- if #emmyParams > 1 then
- if not func.args
- or not func.args[1]
- or func.args[1]:getSource() == source then
- if matchKey(word, source and source[1] or '') then
- local names = {}
- for _, param in ipairs(emmyParams) do
- local name = param:getName()
- names[#names+1] = name
- end
- callback(table.concat(names, ', '), nil, CompletionItemKind.Snippet)
- end
- end
- end
- for _, param in ipairs(emmyParams) do
- local name = param:getName()
- if matchKey(word, name) then
- callback(name, param:getSource(), CompletionItemKind.Interface)
- end
- end
-end
-
-local function searchKeyWords(vm, source, word, callback)
- local snipType = config.config.completion.keywordSnippet
- for _, key in ipairs(KEYS) do
- if matchKey(word, key) then
- if snippet.key[key] then
- if snipType ~= 'Replace'
- or key == 'local'
- or key == 'return'
- or key == 'do' then
- callback(key, nil, CompletionItemKind.Keyword)
- end
- if snipType ~= 'Disable' then
- for _, data in ipairs(snippet.key[key]) do
- callback(data.label, nil, CompletionItemKind.Snippet, {
- insertText = data.text,
- })
- end
- end
- else
- callback(key, nil, CompletionItemKind.Keyword)
- end
- end
- end
-end
-
-local function searchGlobals(vm, source, word, callback, pos)
- local global = vm.env:getValue()
- local map = {}
- local srcMap = {}
- local current = global
- for _ = 1, 3 do
- searchFieldsByInfo(current, word, source, map, srcMap)
- current = current:getMetaMethod('__index')
- if not current then
- break
- end
- end
- searchFieldsByChild(global, word, source, map, srcMap)
- for k, v in sortPairs(map) do
- callback(k, srcMap[k], CompletionItemKind.Field, getValueData('field', k, v, pos, source))
- end
-end
-
-local function searchAsGlobal(vm, source, word, callback, pos)
- if word == '' then
- return
- end
- searchLocals(vm, source, word, callback, pos)
- searchFields(vm, source, word, callback, pos)
- searchKeyWords(vm, source, word, callback)
-end
-
-local function searchAsKeyowrd(vm, source, word, callback, pos)
- searchLocals(vm, source, word, callback, pos)
- searchGlobals(vm, source, word, callback, pos)
- searchKeyWords(vm, source, word, callback)
-end
-
-local function searchAsSuffix(vm, source, word, callback, pos)
- searchFields(vm, source, word, callback, pos)
-end
-
-local function searchAsIndex(vm, source, word, callback, pos)
- searchLocals(vm, source, word, callback, pos)
- searchIndex(vm, source, word, callback)
- searchFields(vm, source, word, callback, pos)
-end
-
-local function searchAsLocal(vm, source, word, callback)
- local loc = source:bindLocal()
- if not loc then
- return
- end
- local close = loc:close()
- -- 因为闭包的关系落在局部变量finish到close范围内的全局变量一定能访问到该局部变量
- searchCloseGlobal(vm, source.finish, close, word, callback)
- -- 特殊支持 local function
- if matchKey(word, 'function') then
- callback('function', nil, CompletionItemKind.Keyword)
- -- TODO 需要有更优美的实现方式
- local data = snippet.key['function'][1]
- callback(data.label, nil, CompletionItemKind.Snippet, {
- insertText = data.text,
- })
- end
-end
-
-local function searchAsArg(vm, source, word, callback)
- searchParams(vm, source, source:get 'arg', word, callback)
-
- local loc = source:bindLocal()
- if loc then
- local close = loc:close()
- -- 因为闭包的关系落在局部变量finish到close范围内的全局变量一定能访问到该局部变量
- searchCloseGlobal(vm, source.finish, close, word, callback)
- return
- end
-end
-
-local function searchFunction(vm, source, word, pos, callback)
- if pos >= source.argStart and pos <= source.argFinish then
- searchParams(vm, nil, source:bindFunction():getFunction(), word, callback)
- searchCloseGlobal(vm, source.argFinish, source.finish, word, callback)
- end
-end
-
-local function searchEmmyKeyword(vm, source, word, callback)
- for _, kw in ipairs(EMMY_KEYWORD) do
- if matchKey(word, kw) then
- callback(kw, nil, CompletionItemKind.Keyword)
- end
- end
-end
-
-local function searchEmmyClass(vm, source, word, callback)
- local classes = {}
- vm.emmyMgr:eachClass(function (class)
- if class.type == 'emmy.class' or class.type == 'emmy.alias' then
- if matchKey(word, class:getName()) then
- classes[#classes+1] = class
- end
- end
- end)
- table.sort(classes, function (a, b)
- return a:getName() < b:getName()
- end)
- for _, class in ipairs(classes) do
- callback(class:getName(), class:getSource(), CompletionItemKind.Class)
- end
-end
-
-local function searchEmmyFunctionParam(vm, source, word, callback)
- local func = source:get 'emmy function'
- if not func.args then
- return
- end
- if #func.args > 1 and matchKey(word, func.args[1].name) then
- local list = {}
- local args = {}
- for i, arg in ipairs(func.args) do
- if func:getObject() and i == 1 then
- goto NEXT
- end
- args[#args+1] = arg.name
- if #list == 0 then
- list[#list+1] = ('%s any'):format(arg.name)
- else
- list[#list+1] = ('---@param %s any'):format(arg.name)
- end
- :: NEXT ::
- end
- callback(('%s'):format(table.concat(args, ', ')), nil, CompletionItemKind.Snippet, {
- insertText = table.concat(list, '\n')
- })
- end
- for i, arg in ipairs(func.args) do
- if func:getObject() and i == 1 then
- goto NEXT
- end
- if matchKey(word, arg.name) then
- callback(arg.name, nil, CompletionItemKind.Interface)
- end
- :: NEXT ::
- end
-end
-
-local function searchSource(vm, source, word, callback, pos)
- if source.type == 'keyword' then
- searchAsKeyowrd(vm, source, word, callback, pos)
- return
- end
- if source:get 'table index' then
- searchAsIndex(vm, source, word, callback, pos)
- return
- end
- if source:get 'arg' then
- searchAsArg(vm, source, word, callback)
- return
- end
- if source:get 'global' then
- searchAsGlobal(vm, source, word, callback, pos)
- return
- end
- if source:action() == 'local' then
- searchAsLocal(vm, source, word, callback)
- return
- end
- if source:bindLocal() then
- searchAsGlobal(vm, source, word, callback, pos)
- return
- end
- if source:get 'simple'
- and (source.type == 'name' or source.type == '.' or source.type == ':') then
- searchAsSuffix(vm, source, word, callback, pos)
- return
- end
- if source:bindFunction() then
- searchFunction(vm, source, word, pos, callback)
- return
- end
- if source.type == 'emmyIncomplete' then
- searchEmmyKeyword(vm, source, word, callback)
- State.ignoreText = true
- return
- end
- if source:get 'emmy class' then
- searchEmmyClass(vm, source, word, callback)
- State.ignoreText = true
- return
- end
- if source:get 'emmy function' then
- searchEmmyFunctionParam(vm, source, word, callback)
- State.ignoreText = true
- return
- end
-end
-
-local function buildTextEdit(start, finish, str, quo)
- local text, lquo, rquo, label, filterText
- if quo == nil then
- local text = str:gsub('\r', '\\r'):gsub('\n', '\\n'):gsub('"', '\\"')
- return {
- label = '"' .. text .. '"'
- }
- end
- if quo == '"' then
- label = str
- filterText = str
- text = str:gsub('\r', '\\r'):gsub('\n', '\\n'):gsub('"', '\\"')
- lquo = quo
- rquo = quo
- elseif quo == "'" then
- label = str
- filterText = str
- text = str:gsub('\r', '\\r'):gsub('\n', '\\n'):gsub("'", "\\'")
- lquo = quo
- rquo = quo
- else
- label = str
- filterText = str
- lquo = quo
- rquo = ']' .. lquo:sub(2, -2) .. ']'
- while str:find(rquo, 1, true) do
- lquo = '[=' .. quo:sub(2)
- rquo = ']' .. lquo:sub(2, -2) .. ']'
- end
- text = str
- end
- return {
- label = label,
- filterText = filterText,
- textEdit = {
- start = start + #quo,
- finish = finish - #quo,
- newText = text,
- },
- additionalTextEdits = {
- {
- start = start,
- finish = start + #quo - 1,
- newText = lquo,
- },
- {
- start = finish - #quo + 1,
- finish = finish,
- newText = rquo,
- },
- }
- }
-end
-
---- @param vm VM
---- @param source table
---- @param callback function
-local function searchInRequire(vm, source, callback)
- if not vm.lsp then
- return
- end
- if source.type ~= 'string' then
- return
- end
- local ws = vm.lsp:findWorkspaceFor(vm.uri)
- if not ws then
- return
- end
- local list, map = ws:matchPath(vm.uri, source[1])
- if not list then
- return
- end
- for _, str in ipairs(list) do
- local data = buildTextEdit(source.start, source.finish, str, source[2])
- data.documentation = {
- value = map[str],
- kind = 'markdown',
- }
- callback(str, nil, CompletionItemKind.Reference, data)
- end
-end
-
-local function searchEnumAsLib(vm, source, word, callback, pos, args, lib)
- local select = #args + 1
- for i, arg in ipairs(args) do
- if arg.start <= pos and arg.finish >= pos then
- select = i
- break
- end
- end
-
- -- 根据参数位置找枚举值
- if lib.args and lib.enums then
- local arg = lib.args[select]
- local name = arg and arg.name
- for _, enum in ipairs(lib.enums) do
- if enum.name and enum.name == name and enum.enum then
- if matchKey(word, enum.enum) then
- local strSource = parser:parse(tostring(enum.enum), 'String')
- if strSource then
- if source.type == 'string' then
- local data = buildTextEdit(source.start, source.finish, strSource[1], source[2])
- data.documentation = {
- kind = 'markdown',
- value = enum.description,
- }
- callback(enum.enum, nil, CompletionItemKind.EnumMember, data)
- else
- callback(enum.enum, nil, CompletionItemKind.EnumMember, {
- documentation = {
- value = enum.description,
- kind = 'markdown',
- }
- })
- end
- end
- else
- callback(enum.enum, nil, CompletionItemKind.EnumMember, {
- documentation = {
- value = enum.description,
- kind = 'markdown',
- }
- })
- end
- end
- end
- end
-
- -- 搜索特殊函数
- if lib.special == 'require' then
- if select == 1 then
- searchInRequire(vm, source, callback)
- end
- end
-end
-
-local function buildEmmyEnumComment(enum, data)
- if not enum.comment then
- return data
- end
- if not data then
- data = {}
- end
- data.documentation = {
- value = tostring(enum.comment),
- kind = 'markdown',
- }
- return data
-end
-
-local function searchEnumAsEmmyParams(vm, source, word, callback, pos, args, func)
- local select = #args + 1
- for i, arg in ipairs(args) do
- if arg.start <= pos and arg.finish >= pos then
- select = i
- break
- end
- end
-
- local param = func:findEmmyParamByIndex(select)
- if not param then
- return
- end
-
- param:eachEnum(function (enum)
- local str = enum[1]
- if matchKey(word, str) then
- local strSource = parser:parse(tostring(str), 'String')
- if strSource then
- if source.type == 'string' then
- local data = buildTextEdit(source.start, source.finish, strSource[1], source[2])
- callback(str, nil, CompletionItemKind.EnumMember, buildEmmyEnumComment(enum, data))
- else
- callback(str, nil, CompletionItemKind.EnumMember, buildEmmyEnumComment(enum))
- end
- else
- callback(str, nil, CompletionItemKind.EnumMember, buildEmmyEnumComment(enum))
- end
- end
- end)
-
- local option = param:getOption()
- if option and option.special == 'require:1' then
- searchInRequire(vm, source, callback)
- end
-end
-
-local function getSelect(args, pos)
- if not args then
- return 1
- end
- for i, arg in ipairs(args) do
- if arg.start <= pos and arg.finish >= pos - 1 then
- return i
- end
- end
- return #args + 1
-end
-
-local function isInFunctionOrTable(call, pos)
- local args = call:bindCall()
- if not args then
- return false
- end
- local select = getSelect(args, pos)
- local arg = args[select]
- if not arg then
- return false
- end
- if arg.type == 'function' or arg.type == 'table' then
- return true
- end
- return false
-end
-
-local function searchCallArg(vm, source, word, callback, pos)
- local results = {}
- vm:eachSource(function (src)
- if src.type == 'call'
- and src.start <= pos
- and src.finish >= pos
- then
- results[#results+1] = src
- end
- end)
- if #results == 0 then
- return nil
- end
- -- 可能处于 'func1(func2(' 的嵌套中,将最近的call放到最前面
- table.sort(results, function (a, b)
- return a.start > b.start
- end)
- local call = results[1]
- if isInFunctionOrTable(call, pos) then
- return
- end
-
- local args = call:bindCall()
- if not args then
- return
- end
-
- local value = call:findCallFunction()
- if not value then
- return
- end
-
- local lib = value:getLib()
- if lib then
- searchEnumAsLib(vm, source, word, callback, pos, args, lib)
- return
- end
-
- ---@type emmyFunction
- local func = value:getFunction()
- if func then
- searchEnumAsEmmyParams(vm, source, word, callback, pos, args, func)
- return
- end
-end
-
-local function searchAllWords(vm, source, word, callback, pos)
- if word == '' then
- return
- end
- if source.type == 'string' then
- return
- end
- vm:eachSource(function (src)
- if src.type == 'name'
- and not (src.start <= pos and src.finish >= pos)
- and matchKey(word, src[1])
- then
- callback(src[1], src, CompletionItemKind.Text)
- end
- end)
-end
-
-local function searchSpecialHashSign(vm, pos, text, callback)
- -- 尝试 XXX[#XXX+1]
- -- 1. 搜索 []
- local index
- vm:eachSource(function (src)
- if src.type == 'index'
- and src.start <= pos
- and src.finish >= pos
- then
- index = src
- return true
- end
- end)
- if not index then
- return nil
- end
- -- 2. [] 内部只能有一个 #
- local inside = index[1]
- if not inside then
- return nil
- end
- if inside.op ~= '#' then
- return nil
- end
- -- 3. [] 左侧必须是 simple ,且index 是 simple 的最后一项
- local simple = index:get 'simple'
- if not simple then
- return nil
- end
- if simple[#simple] ~= index then
- return nil
- end
- local chars = text:sub(simple.start, simple[#simple-1].finish)
- -- 4. 创建代码片段
- if simple:get 'as action' then
- local label = chars .. '+1'
- callback(label, nil, CompletionItemKind.Snippet, {
- textEdit = {
- start = inside.start + 1,
- finish = index.finish,
- newText = ('%s] = '):format(label),
- },
- })
- else
- local label = chars
- callback(label, nil, CompletionItemKind.Snippet, {
- textEdit = {
- start = inside.start + 1,
- finish = index.finish,
- newText = ('%s]'):format(label),
- },
- })
- end
-end
-
-local function searchSpecial(vm, source, word, callback, pos, text)
- searchSpecialHashSign(vm, pos, text, callback)
-end
-
-local function makeList(source, pos, word)
- local list = {}
- local mark = {}
- return function (name, src, kind, data)
- if src == source then
- return
- end
- if word == name then
- if src and src.start <= pos and src.finish >= pos then
- return
- end
- end
- if mark[name] then
- return
- end
- mark[name] = true
- if not data then
- data = {}
- end
- if not data.label then
- data.label = name
- end
- if not data.kind then
- data.kind = kind
- end
- if src then
- data.data = {
- uri = src.uri,
- offset = src.start,
- }
- end
- list[#list+1] = data
- if data.snip then
- local snipType = config.config.completion.callSnippet
- if snipType ~= 'Disable' then
- local snipData = table.deepCopy(data)
- snipData.insertText = data.snip
- snipData.kind = CompletionItemKind.Snippet
- snipData.label = snipData.label .. '()'
- snipData.snip = nil
- if snipType == 'Both' then
- list[#list+1] = snipData
- elseif snipType == 'Replace' then
- list[#list] = snipData
- end
- end
- data.snip = nil
- end
- end, list
-end
-
-local function keywordSource(vm, word, pos)
- if not KEYMAP[word] then
- return nil
- end
- return vm:instantSource {
- type = 'keyword',
- start = pos,
- finish = pos + #word - 1,
- [1] = word,
- }
-end
-
-local function findStartPos(pos, buf)
- local res = nil
- for i = pos, 1, -1 do
- local c = buf:sub(i, i)
- if c:find '[%w_]' then
- res = i
- else
- break
- end
- end
- if not res then
- for i = pos, 1, -1 do
- local c = buf:sub(i, i)
- if c == '.' or c == ':' or c == '|' or c == '(' then
- res = i
- break
- elseif c == '#' or c == '@' then
- res = i + 1
- break
- elseif c:find '[%s%c]' then
- else
- break
- end
- end
- end
- if not res then
- return pos
- end
- return res
-end
-
-local function findWord(position, text)
- local word = text
- for i = position, 1, -1 do
- local c = text:sub(i, i)
- if not c:find '[%w_]' then
- word = text:sub(i+1, position)
- break
- end
- end
- return word:match('^([%w_]*)')
-end
-
-local function getSource(vm, pos, text, filter)
- local word = findWord(pos, text)
- local source = keywordSource(vm, word, pos)
- if source then
- return source, pos, word
- end
- source = findSource(vm, pos, filter)
- if source then
- return source, pos, word
- end
- pos = findStartPos(pos, text)
- source = findSource(vm, pos, filter) or findSource(vm, pos-1, filter)
- return source, pos, word
-end
-
---- @param vm VM
---- @param text string
---- @param pos table
---- @param oldText string
---- @return table
-return function (vm, text, pos, oldText)
- local filter = {
- ['name'] = true,
- ['string'] = true,
- ['.'] = true,
- [':'] = true,
- ['emmyName'] = true,
- ['emmyIncomplete'] = true,
- ['call'] = true,
- ['function'] = true,
- ['localfunction'] = true,
- }
- local source, pos, word = getSource(vm, pos, text, filter)
- if not source then
- return nil
- end
- if oldText then
- local oldWord = oldText:sub(source.start, source.finish)
- if word:sub(1, #oldWord) ~= oldWord then
- return nil
- end
- end
- State = {}
- local callback, list = makeList(source, pos, word)
- searchSpecial(vm, source, word, callback, pos, text)
- searchCallArg(vm, source, word, callback, pos)
- searchSource(vm, source, word, callback, pos)
- if not oldText or #list > 0 then
- if not State.ignoreText then
- searchAllWords(vm, source, word, callback, pos)
- end
- end
-
- if #list == 0 then
- return nil
- end
-
- return list
-end
diff --git a/script/core/definition.lua b/script/core/definition.lua
deleted file mode 100644
index 8680a29b..00000000
--- a/script/core/definition.lua
+++ /dev/null
@@ -1,296 +0,0 @@
-local findSource = require 'core.find_source'
-local Mode
-
-local function parseValueSimily(callback, vm, source)
- local key = source[1]
- if not key then
- return nil
- end
- vm:eachSource(function (other)
- if other == source then
- goto CONTINUE
- end
- if other[1] == key
- and not other:bindLocal()
- and other:bindValue()
- and source:bindValue() ~= other:bindValue()
- then
- if Mode == 'definition' then
- if other:action() == 'set' then
- callback(other)
- end
- elseif Mode == 'reference' then
- if other:action() == 'set' or other:action() == 'get' then
- callback(other)
- end
- end
- end
- :: CONTINUE ::
- end)
-end
-
-local function parseLocal(callback, vm, source)
- ---@type Local
- local loc = source:bindLocal()
- callback(loc:getSource())
- loc:eachInfo(function (info, src)
- if Mode == 'definition' then
- if info.type == 'set' or info.type == 'local' then
- if vm.uri == src:getUri() then
- if source.id >= src.id then
- callback(src)
- end
- end
- end
- elseif Mode == 'reference' then
- if info.type == 'set' or info.type == 'local' or info.type == 'return' or info.type == 'get' then
- callback(src)
- end
- end
- end)
-end
-
-local function parseValueByValue(callback, vm, source, value)
- if not source then
- return
- end
- local mark = { [vm] = true }
- local list = {}
- for _ = 1, 5 do
- value:eachInfo(function (info, src)
- if Mode == 'definition' then
- if info.type == 'local' then
- if vm.uri == src:getUri() then
- if source.id >= src.id then
- callback(src)
- end
- end
- end
- if info.type == 'set' then
- if vm.uri == src:getUri() then
- if source.id >= src.id then
- callback(src)
- end
- else
- callback(src)
- end
- end
- if info.type == 'return' then
- if (src.type ~= 'simple' or src[#src].type == 'call')
- and src.type ~= 'name'
- then
- callback(src)
- end
- if vm.lsp then
- local destVM = vm.lsp:getVM(src:getUri())
- if destVM and not mark[destVM] then
- mark[destVM] = true
- list[#list+1] = { destVM, src }
- end
- end
- end
- elseif Mode == 'reference' then
- if info.type == 'set' or info.type == 'local' or info.type == 'return' or info.type == 'get' then
- callback(src)
- end
- end
- end)
- local nextData = table.remove(list, 1)
- if nextData then
- vm, source = nextData[1], nextData[2]
- end
- end
-end
-
-local function parseValue(callback, vm, source)
- local value = source:bindValue()
- local isGlobal
- if value then
- isGlobal = value:isGlobal()
- parseValueByValue(callback, vm, source, value)
- local emmy = value:getEmmy()
- if emmy and emmy.type == 'emmy.type' then
- ---@type EmmyType
- local emmyType = emmy
- emmyType:eachClass(function (class)
- if class and class:getValue() then
- local emmyVM = vm
- if vm.lsp then
- local destVM = vm.lsp:getVM(class:getSource():getUri())
- if destVM then
- emmyVM = destVM
- end
- end
- parseValueByValue(callback, emmyVM, class:getValue():getSource(), class:getValue())
- end
- end)
- end
- end
- local parent = source:get 'parent'
- for _ = 1, 3 do
- if parent then
- local ok = parent:eachInfo(function (info, src)
- if Mode == 'definition' then
- if info.type == 'set child' and info[1] == source[1] then
- callback(src)
- return true
- end
- elseif Mode == 'reference' then
- if (info.type == 'set child' or info.type == 'get child') and info[1] == source[1] then
- callback(src)
- return true
- end
- end
- end)
- if ok then
- break
- end
- parent = parent:getMetaMethod('__index')
- end
- end
- return isGlobal
-end
-
-local function parseLabel(callback, vm, label)
- label:eachInfo(function (info, src)
- if Mode == 'definition' then
- if info.type == 'set' then
- callback(src)
- end
- elseif Mode == 'reference' then
- if info.type == 'set' or info.type == 'get' then
- callback(src)
- end
- end
- end)
-end
-
-local function jumpUri(callback, vm, source)
- local uri = source:get 'target uri'
- callback {
- start = 0,
- finish = 0,
- uri = uri
- }
-end
-
-local function parseClass(callback, vm, source)
- local className = source:get 'emmy class'
- vm.emmyMgr:eachClass(className, function (class)
- if Mode == 'definition' then
- if class.type == 'emmy.class' or class.type == 'emmy.alias' then
- local src = class:getSource()
- callback(src)
- end
- elseif Mode == 'reference' then
- if class.type == 'emmy.class' or class.type == 'emmy.alias' or class.type == 'emmy.typeUnit' then
- local src = class:getSource()
- callback(src)
- end
- end
- end)
-end
-
-local function parseSee(callback, vm, source)
- local see = source:get 'emmy see'
- local className = see[1][1]
- local childName = see[2][1]
- vm.emmyMgr:eachClass(className, function (class)
- ---@type value
- local value = class:getValue()
- local child = value:getChild(childName)
- parseValueByValue(callback, vm, source, child)
- end)
-end
-
-local function parseFunction(callback, vm, source)
- if Mode == 'definition' then
- callback(source:bindFunction():getSource())
- source:bindFunction():eachInfo(function (info, src)
- if info.type == 'set' or info.type == 'local' then
- if vm.uri == src:getUri() then
- if source.id >= src.id then
- callback(src)
- end
- else
- callback(src)
- end
- end
- end)
- elseif Mode == 'reference' then
- callback(source:bindFunction():getSource())
- source:bindFunction():eachInfo(function (info, src)
- if info.type == 'set' or info.type == 'local' or info.type == 'get' then
- callback(src)
- end
- end)
- end
-end
-
-local function makeList(source)
- local list = {}
- local mark = {}
- return list, function (src)
- if mark[src] then
- return
- end
- mark[src] = true
- list[#list+1] = {
- src.start,
- src.finish,
- src.uri
- }
- end
-end
-
-return function (vm, pos, mode)
- local filter = {
- ['name'] = true,
- ['string'] = true,
- ['number'] = true,
- ['boolean'] = true,
- ['label'] = true,
- ['goto'] = true,
- ['function'] = true,
- ['...'] = true,
- ['emmyName'] = true,
- ['emmyIncomplete'] = true,
- }
- local source = findSource(vm, pos, filter)
- if not source then
- return nil
- end
- Mode = mode
- local list, callback = makeList(source)
- local isGlobal
- if source:bindLocal() then
- parseLocal(callback, vm, source)
- end
- if source:bindValue() then
- isGlobal = parseValue(callback, vm, source)
- end
- if source:bindLabel() then
- parseLabel(callback, vm, source:bindLabel())
- end
- if source:bindFunction() then
- parseFunction(callback, vm, source)
- end
- if source:get 'target uri' then
- jumpUri(callback, vm, source)
- end
- if source:get 'in index' then
- isGlobal = parseValue(callback, vm, source)
- end
- if source:get 'emmy class' then
- parseClass(callback, vm, source)
- end
- if source:get 'emmy see' then
- parseSee(callback, vm, source)
- end
-
- if #list == 0 then
- parseValueSimily(callback, vm, source)
- end
-
- return list, isGlobal
-end
diff --git a/script/core/diagnostics.lua b/script/core/diagnostics.lua
deleted file mode 100644
index 0ec98954..00000000
--- a/script/core/diagnostics.lua
+++ /dev/null
@@ -1,1082 +0,0 @@
-local lang = require 'language'
-local config = require 'config'
-local library = require 'core.library'
-local buildGlobal = require 'vm.global'
-local DiagnosticSeverity = require 'constant.DiagnosticSeverity'
-local DiagnosticDefaultSeverity = require 'constant.DiagnosticDefaultSeverity'
-local DiagnosticTag = require 'constant.DiagnosticTag'
-
-local mt = {}
-mt.__index = mt
-
-local function isContainPos(obj, start, finish)
- if obj.start <= start and obj.finish >= finish then
- return true
- end
- return false
-end
-
-function mt:searchUnusedLocals(callback)
- self.vm:eachSource(function (source)
- local loc = source:bindLocal()
- if not loc then
- return
- end
- if loc:get 'emmy arg' then
- return
- end
- local name = loc:getName()
- if name == '_' or name == '_ENV' or name == '' then
- return
- end
- if source:action() ~= 'local' then
- return
- end
- if loc:get 'hide' then
- return
- end
- if loc.tags then
- for _, tag in ipairs(loc.tags) do
- if tag[1] == 'close' then
- return
- end
- end
- end
- local used = loc:eachInfo(function (info)
- if info.type == 'get' then
- return true
- end
- end)
- if not used then
- callback(source.start, source.finish, name)
- end
- end)
-end
-
-function mt:searchUnusedFunctions(callback)
- self.vm:eachSource(function (source)
- local loc = source:bindLocal()
- if not loc then
- return
- end
- if loc:get 'emmy arg' then
- return
- end
- if source:action() ~= 'local' then
- return
- end
- if loc:get 'hide' then
- return
- end
- local used = loc:eachInfo(function (info)
- if info.type == 'get' then
- return true
- end
- end)
- if used then
- return
- end
- loc:eachInfo(function (info, src)
- if info.type == 'set' or info.type == 'local' then
- local v = src:bindValue()
- local func = v and v:getFunction()
- if func and func:getSource().uri == self.vm.uri then
- callback(func:getSource().start, func:getSource().finish)
- end
- end
- end)
- end)
-end
-
-function mt:searchUndefinedGlobal(callback)
- local definedGlobal = {}
- for name in pairs(config.config.diagnostics.globals) do
- definedGlobal[name] = true
- end
- local envValue = buildGlobal(self.vm.lsp)
- envValue:eachInfo(function (info)
- if info.type == 'set child' then
- local name = info[1]
- definedGlobal[name] = true
- end
- end)
- self.vm:eachSource(function (source)
- if not source:get 'global' then
- return
- end
- local name = source:getName()
- if name == '' then
- return
- end
- local parent = source:get 'parent'
- if not parent then
- return
- end
- if not parent:get 'ENV' and not source:get 'in index' then
- return
- end
- if definedGlobal[name] then
- return
- end
- if type(name) ~= 'string' then
- return
- end
- callback(source.start, source.finish, name)
- end)
-end
-
-function mt:searchUnusedLabel(callback)
- self.vm:eachSource(function (source)
- local label = source:bindLabel()
- if not label then
- return
- end
- if source:action() ~= 'set' then
- return
- end
- local used = label:eachInfo(function (info)
- if info.type == 'get' then
- return true
- end
- end)
- if not used then
- callback(source.start, source.finish, label:getName())
- end
- end)
-end
-
-function mt:searchUnusedVararg(callback)
- self.vm:eachSource(function (source)
- local value = source:bindFunction()
- if not value then
- return
- end
- local func = value:getFunction()
- if not func then
- return
- end
- if func._dotsSource and not func._dotsLoad then
- callback(func._dotsSource.start, func._dotsSource.finish)
- end
- end)
-end
-
-local function isInString(vm, start, finish)
- return vm:eachSource(function (source)
- if source.type == 'string' and isContainPos(source, start, finish) then
- return true
- end
- end)
-end
-
-function mt:searchSpaces(callback)
- local vm = self.vm
- local lines = self.lines
- for i = 1, #lines do
- local line = lines:line(i)
-
- if line:find '^[ \t]+$' then
- local start, finish = lines:range(i)
- if isInString(vm, start, finish) then
- goto NEXT_LINE
- end
- callback(start, finish, lang.script.DIAG_LINE_ONLY_SPACE)
- goto NEXT_LINE
- end
-
- local pos = line:find '[ \t]+$'
- if pos then
- local start, finish = lines:range(i)
- start = start + pos - 1
- if isInString(vm, start, finish) then
- goto NEXT_LINE
- end
- callback(start, finish, lang.script.DIAG_LINE_POST_SPACE)
- goto NEXT_LINE
- end
-
- ::NEXT_LINE::
- end
-end
-
-function mt:searchRedefinition(callback)
- local used = {}
- local uri = self.uri
- self.vm:eachSource(function (source)
- local loc = source:bindLocal()
- if not loc then
- return
- end
- local shadow = loc:shadow()
- if not shadow then
- return
- end
- if used[shadow] then
- return
- end
- used[shadow] = true
- if loc:get 'hide' then
- return
- end
- local name = loc:getName()
- if name == '_' or name == '_ENV' or name == '' then
- return
- end
- local related = {}
- for i = 1, #shadow do
- related[i] = {
- start = shadow[i]:getSource().start,
- finish = shadow[i]:getSource().finish,
- uri = uri,
- }
- end
- for i = 2, #shadow do
- callback(shadow[i]:getSource().start, shadow[i]:getSource().finish, name, related)
- end
- end)
-end
-
-function mt:searchNewLineCall(callback)
- local lines = self.lines
- self.vm:eachSource(function (source)
- if source.type ~= 'simple' then
- return
- end
- for i = 1, #source - 1 do
- local callSource = source[i]
- local funcSource = source[i-1]
- if callSource.type ~= 'call' then
- goto CONTINUE
- end
- local callLine = lines:rowcol(callSource.start)
- local funcLine = lines:rowcol(funcSource.finish)
- if callLine > funcLine then
- callback(callSource.start, callSource.finish)
- end
- :: CONTINUE ::
- end
- end)
-end
-
-function mt:searchNewFieldCall(callback)
- local lines = self.lines
- self.vm:eachSource(function (source)
- if source.type ~= 'table' then
- return
- end
- for i = 1, #source do
- local field = source[i]
- if field.type == 'simple' then
- local callSource = field[#field]
- local funcSource = field[#field-1]
- local callLine = lines:rowcol(callSource.start)
- local funcLine = lines:rowcol(funcSource.finish)
- if callLine > funcLine then
- callback(funcSource.start, callSource.finish
- , lines.buf:sub(funcSource.start, funcSource.finish)
- , lines.buf:sub(callSource.start, callSource.finish)
- )
- end
- end
- end
- end)
-end
-
-function mt:searchRedundantParameters(callback)
- self.vm:eachSource(function (source)
- local args = source:bindCall()
- if not args then
- return
- end
-
- -- 回调函数不检查
- local simple = source:get 'simple'
- if simple and simple[2] == source then
- local loc = simple[1]:bindLocal()
- if loc then
- local source = loc:getSource()
- if source:get 'arg' then
- return
- end
- end
- end
-
- local value = source:findCallFunction()
- if not value then
- return
- end
-
- local func = value:getFunction()
- -- 参数中有 ... ,不用再检查了
- if func:hasDots() then
- return
- end
- local max = #func.args
- local passed = #args
- -- function m.open() end
- -- m:open()
- -- 这种写法不算错
- if passed == 1 and source:get 'has object' then
- return
- end
- for i = max + 1, passed do
- local extra = args[i]
- callback(extra.start, extra.finish, max, passed)
- end
- end)
-end
-
-local opMap = {
- ['+'] = true,
- ['-'] = true,
- ['*'] = true,
- ['/'] = true,
- ['//'] = true,
- ['^'] = true,
- ['<<'] = true,
- ['>>'] = true,
- ['&'] = true,
- ['|'] = true,
- ['~'] = true,
- ['..'] = true,
-}
-
-local literalMap = {
- ['number'] = true,
- ['boolean'] = true,
- ['string'] = true,
- ['table'] = true,
-}
-
-function mt:searchAmbiguity1(callback)
- self.vm:eachSource(function (source)
- if source.op ~= 'or' then
- return
- end
- local first = source[1]
- local second = source[2]
- -- a + (b or 0) --> (a + b) or 0
- do
- if opMap[first.op]
- and first.type ~= 'unary'
- and not second.op
- and literalMap[second.type]
- and not first.brackets
- then
- callback(source.start, source.finish, first.start, first.finish)
- end
- end
- -- (a or 0) + c --> a or (0 + c)
- do
- if opMap[second.op]
- and second.type ~= 'unary'
- and not first.op
- and literalMap[second[1].type]
- and not second.brackets
- then
- callback(source.start, source.finish, second.start, second.finish)
- end
- end
- end)
-end
-
-function mt:searchLowercaseGlobal(callback)
- local definedGlobal = {}
- for name in pairs(config.config.diagnostics.globals) do
- definedGlobal[name] = true
- end
- for name in pairs(library.global) do
- definedGlobal[name] = true
- end
- self.vm:eachSource(function (source)
- if source.type == 'name'
- and source:get 'parent'
- and not source:get 'simple'
- and not source:get 'table index'
- and source:action() == 'set'
- then
- local name = source[1]
- if definedGlobal[name] then
- return
- end
- local first = name:match '%w'
- if not first then
- return
- end
- if first:match '%l' then
- callback(source.start, source.finish)
- end
- end
- end)
-end
-
-function mt:searchDuplicateIndex(callback)
- self.vm:eachSource(function (source)
- if source.type ~= 'table' then
- return
- end
- local mark = {}
- for _, obj in ipairs(source) do
- if obj.type == 'pair' then
- local key = obj[1]
- local name
- if key.type == 'index' then
- if key[1].type == 'string' then
- name = key[1][1]
- end
- elseif key.type == 'name' then
- name = key[1]
- end
- if name then
- if mark[name] then
- mark[name][#mark[name]+1] = obj
- else
- mark[name] = { obj }
- end
- end
- end
- end
- for name, defs in pairs(mark) do
- if #defs > 1 then
- local related = {}
- for i = 1, #defs do
- related[i] = {
- start = defs[i][1].start,
- finish = defs[i][2].finish,
- uri = self.uri,
- }
- end
- for i = 1, #defs - 1 do
- callback(defs[i][1].start, defs[i][2].finish, name, related, 'unused')
- end
- for i = #defs, #defs do
- callback(defs[i][1].start, defs[i][1].finish, name, related, 'duplicate')
- end
- end
- end
- end)
-end
-
-function mt:searchDuplicateMethod(callback)
- local uri = self.uri
- local mark = {}
- local map = {}
- self.vm:eachSource(function (source)
- local parent = source:get 'parent'
- if not parent then
- return
- end
- if mark[parent] then
- return
- end
- mark[parent] = true
- local relates = {}
- parent:eachInfo(function (info, src)
- local k = info[1]
- if info.type ~= 'set child' then
- return
- end
- if type(k) ~= 'string' then
- return
- end
- if src.start == 0 then
- return
- end
- if not src:get 'object' then
- return
- end
- if map[src] then
- return
- end
- if not relates[k] then
- relates[k] = map[src] or {
- name = k,
- }
- end
- map[src] = relates[k]
- relates[k][#relates[k]+1] = {
- start = src.start,
- finish = src.finish,
- uri = src.uri
- }
- end)
- end)
- for src, relate in pairs(map) do
- if #relate > 1 and src.uri == uri then
- callback(src.start, src.finish, relate.name, relate)
- end
- end
-end
-
-function mt:searchEmptyBlock(callback)
- self.vm:eachSource(function (source)
- -- 认为空repeat与空while是合法的
- -- 要去vm中激活source
- if source.type == 'if' then
- for _, block in ipairs(source) do
- if #block > 0 then
- return
- end
- end
- callback(source.start, source.finish)
- return
- end
- if source.type == 'loop'
- or source.type == 'in'
- then
- if #source == 0 then
- callback(source.start, source.finish)
- end
- return
- end
- end)
-end
-
-function mt:searchRedundantValue(callback)
- self.vm:eachSource(function (source)
- if source.type == 'set' or source.type == 'local' then
- local args = source[1]
- local values = source[2]
- if not source[2] then
- return
- end
- local argCount, valueCount
- if args.type == 'list' then
- argCount = #args
- else
- argCount = 1
- end
- if values.type == 'list' then
- valueCount = #values
- else
- valueCount = 1
- end
- for i = argCount + 1, valueCount do
- local value = values[i]
- callback(value.start, value.finish, argCount, valueCount)
- end
- end
- end)
-end
-
-function mt:searchUndefinedEnvChild(callback)
- self.vm:eachSource(function (source)
- if not source:get 'global' then
- return
- end
- local name = source:getName()
- if name == '' then
- return
- end
- if source:get 'in index' then
- return
- end
- local parent = source:get 'parent'
- if parent:get 'ENV' then
- return
- end
- local value = source:bindValue()
- if not value then
- return
- end
- if value:getSource() == source then
- callback(source.start, source.finish, name)
- end
- return
- end)
-end
-
-function mt:searchGlobalInNilEnv(callback)
- self.vm:eachSource(function (source)
- if not source:get 'global' then
- return
- end
- local name = source:getName()
- if name == '' then
- return
- end
- local parentSource = source:get 'parent' :getSource()
- if parentSource and parentSource.type == 'nil' then
- callback(source.start, source.finish, {
- {
- start = parentSource.start,
- finish = parentSource.finish,
- uri = self.uri,
- }
- })
- end
- return
- end)
-end
-
-function mt:checkEmmyClass(source, callback)
- local class = source:get 'emmy.class'
- if not class then
- return
- end
- -- class重复定义
- local name = class:getName()
- local related = {}
- self.vm.emmyMgr:eachClass(name, function (class)
- if class.type ~= 'emmy.class' and class.type ~= 'emmy.alias' then
- return
- end
- local src = class:getSource()
- if src ~= source then
- related[#related+1] = {
- start = src.start,
- finish = src.finish,
- uri = src.uri,
- }
- end
- end)
- if #related > 0 then
- callback(source.start, source.finish, lang.script.DIAG_DUPLICATE_CLASS ,related)
- end
- -- 继承不存在的class
- local extends = class.extends
- if not extends then
- return
- end
- local parent = self.vm.emmyMgr:eachClass(extends, function (parent)
- if parent.type == 'emmy.class' then
- return parent
- end
- end)
- if not parent then
- callback(source[2].start, source[2].finish, lang.script.DIAG_UNDEFINED_CLASS)
- return
- end
-
- -- class循环继承
- local related = {}
- local current = class
- for _ = 1, 10 do
- local extends = current.extends
- if not extends then
- break
- end
- related[#related+1] = {
- start = current:getSource().start,
- finish = current:getSource().finish,
- uri = current:getSource().uri,
- }
- current = self.vm.emmyMgr:eachClass(extends, function (parent)
- if parent.type == 'emmy.class' then
- return parent
- end
- end)
- if not current then
- break
- end
- if current:getName() == class:getName() then
- callback(source.start, source.finish, lang.script.DIAG_CYCLIC_EXTENDS, related)
- break
- end
- end
-end
-
-function mt:checkEmmyType(source, callback)
- for _, tpsource in ipairs(source) do
- -- TODO 临时决绝办法,重构后解决
- local name
- if tpsource.type == 'emmyArrayType' then
- name = tpsource[1][1]
- else
- name = tpsource[1]
- end
- local class = self.vm.emmyMgr:eachClass(name, function (class)
- if class.type == 'emmy.class' or class.type == 'emmy.alias' then
- return class
- end
- end)
- if not class then
- callback(tpsource.start, tpsource.finish, lang.script.DIAG_UNDEFINED_CLASS)
- end
- end
-end
-
-function mt:checkEmmyAlias(source, callback)
- local class = source:get 'emmy.alias'
- if not class then
- return
- end
- -- class重复定义
- local name = class:getName()
- local related = {}
- self.vm.emmyMgr:eachClass(name, function (class)
- if class.type ~= 'emmy.class' and class.type ~= 'emmy.alias' then
- return
- end
- local src = class:getSource()
- if src ~= source then
- related[#related+1] = {
- start = src.start,
- finish = src.finish,
- uri = src.uri,
- }
- end
- end)
- if #related > 0 then
- callback(source.start, source.finish, lang.script.DIAG_DUPLICATE_CLASS ,related)
- end
-end
-
-function mt:checkEmmyParam(source, callback, mark)
- local func = source:get 'emmy function'
- if not func then
- return
- end
- if mark[func] then
- return
- end
- mark[func] = true
-
- -- 检查不存在的参数
- local emmyParams = func:getEmmyParams()
- local funcParams = {}
- if func.args then
- for _, arg in ipairs(func.args) do
- funcParams[arg.name] = true
- end
- end
- for _, param in ipairs(emmyParams) do
- local name = param:getName()
- if not funcParams[name] then
- callback(param:getSource()[1].start, param:getSource()[1].finish, lang.script.DIAG_INEXISTENT_PARAM)
- end
- end
-
- -- 检查重复的param
- local lists = {}
- for _, param in ipairs(emmyParams) do
- local name = param:getName()
- if not lists[name] then
- lists[name] = {}
- end
- lists[name][#lists[name]+1] = param:getSource()[1]
- end
- for _, list in pairs(lists) do
- if #list > 1 then
- local related = {}
- for _, src in ipairs(list) do
- related[#related+1] = {
- src.start,
- src.finish,
- src.uri,
- }
- callback(src.start, src.finish, lang.script.DIAG_DUPLICATE_PARAM)
- end
- end
- end
-end
-
-function mt:checkEmmyField(source, callback, mark)
- ---@type EmmyClass
- local class = source:get 'target class'
- -- 必须写在 class 的后面
- if not class then
- callback(source.start, source.finish, lang.script.DIAG_NEED_CLASS)
- end
-
- -- 检查重复的 field
- if class and not mark[class] then
- mark[class] = true
- local lists = {}
- class:eachField(function (field)
- local name = field:getName()
- if not lists[name] then
- lists[name] = {}
- end
- lists[name][#lists[name]+1] = field:getSource()[2]
- end)
- for _, list in pairs(lists) do
- if #list > 1 then
- local related = {}
- for _, src in ipairs(list) do
- related[#related+1] = {
- src.start,
- src.finish,
- src.uri,
- }
- callback(src.start, src.finish, lang.script.DIAG_DUPLICATE_FIELD)
- end
- end
- end
- end
-end
-
-function mt:searchEmmyLua(callback)
- local mark = {}
- self.vm:eachSource(function (source)
- if source.type == 'emmyClass' then
- self:checkEmmyClass(source, callback)
- elseif source.type == 'emmyType' then
- self:checkEmmyType(source, callback)
- elseif source.type == 'emmyAlias' then
- self:checkEmmyAlias(source, callback)
- elseif source.type == 'emmyParam' then
- self:checkEmmyParam(source, callback, mark)
- elseif source.type == 'emmyField' then
- self:checkEmmyField(source, callback, mark)
- end
- end)
-end
-
-function mt:searchSetConstLocal(callback)
- local mark = {}
- self.vm:eachSource(function (source)
- local loc = source:bindLocal()
- if not loc then
- return
- end
- if mark[loc] then
- return
- end
- mark[loc] = true
- if not loc.tags then
- return
- end
- local const
- for _, tag in ipairs(loc.tags) do
- if tag[1] == 'const' then
- const = true
- break
- end
- end
- if not const then
- return
- end
- loc:eachInfo(function (info, src)
- if info.type == 'set' then
- callback(src.start, src.finish)
- end
- end)
- end)
-end
-
-function mt:searchSetForState(callback)
- local locs = {}
- self.vm:eachSource(function (source)
- if source.type == 'loop' then
- locs[#locs+1] = source.arg:bindLocal()
- elseif source.type == 'in' then
- --self.vm:forList(source.arg, function (arg)
- -- locs[#locs+1] = arg:bindLocal()
- --end)
- end
- end)
- for i = 1, #locs do
- local loc = locs[i]
- loc:eachInfo(function (info, src)
- if info.type == 'set' then
- callback(src.start, src.finish)
- end
- end)
- end
-end
-
-function mt:doDiagnostics(func, code, callback)
- if config.config.diagnostics.disable[code] then
- return
- end
- local level = config.config.diagnostics.severity[code]
- if not DiagnosticSeverity[level] then
- level = DiagnosticDefaultSeverity[code]
- end
- func(self, function (start, finish, ...)
- local data = callback(...)
- data.code = code
- data.start = start
- data.finish = finish
- data.level = data.level or DiagnosticSeverity[level]
- self.datas[#self.datas+1] = data
- end)
- if coroutine.isyieldable() then
- if self.vm:isRemoved() then
- coroutine.yield('stop')
- else
- coroutine.yield()
- end
- end
-end
-
-return function (vm, lines, uri)
- local session = setmetatable({
- vm = vm,
- lines = lines,
- uri = uri,
- datas = {},
- }, mt)
-
- -- 未使用的局部变量
- session:doDiagnostics(session.searchUnusedLocals, 'unused-local', function (key)
- return {
- message = lang.script('DIAG_UNUSED_LOCAL', key),
- tags = {DiagnosticTag.Unnecessary},
- }
- end)
- -- 未使用的函数
- session:doDiagnostics(session.searchUnusedFunctions, 'unused-function', function ()
- return {
- message = lang.script.DIAG_UNUSED_FUNCTION,
- tags = {DiagnosticTag.Unnecessary},
- }
- end)
- -- 读取未定义全局变量
- session:doDiagnostics(session.searchUndefinedGlobal, 'undefined-global', function (key)
- local message = lang.script('DIAG_UNDEF_GLOBAL', key)
- local otherVersion = library.other[key]
- local customLib = library.custom[key]
- if otherVersion then
- message = ('%s(%s)'):format(message, lang.script('DIAG_DEFINED_VERSION', table.concat(otherVersion, '/'), config.config.runtime.version))
- end
- if customLib then
- message = ('%s(%s)'):format(message, lang.script('DIAG_DEFINED_CUSTOM', table.concat(customLib, '/')))
- end
- return {
- message = message,
- }
- end)
- -- 未使用的Label
- session:doDiagnostics(session.searchUnusedLabel, 'unused-label', function (key)
- return {
- message = lang.script('DIAG_UNUSED_LABEL', key),
- tags = {DiagnosticTag.Unnecessary},
- }
- end)
- -- 未使用的不定参数
- session:doDiagnostics(session.searchUnusedVararg, 'unused-vararg', function ()
- return {
- message = lang.script.DIAG_UNUSED_VARARG,
- tags = {DiagnosticTag.Unnecessary},
- }
- end)
- -- 只有空格与制表符的行,以及后置空格
- session:doDiagnostics(session.searchSpaces, 'trailing-space', function (message)
- return {
- message = message,
- }
- end)
- -- 重定义局部变量
- session:doDiagnostics(session.searchRedefinition, 'redefined-local', function (key, related)
- return {
- message = lang.script('DIAG_REDEFINED_LOCAL', key),
- related = related,
- }
- end)
- -- 以括号开始的一行(可能被误解析为了上一行的call)
- session:doDiagnostics(session.searchNewLineCall, 'newline-call', function ()
- return {
- message = lang.script.DIAG_PREVIOUS_CALL,
- }
- end)
- -- 以字符串开始的field(可能被误解析为了上一行的call)
- session:doDiagnostics(session.searchNewFieldCall, 'newfield-call', function (func, call)
- return {
- message = lang.script('DIAG_PREFIELD_CALL', func, call),
- }
- end)
- -- 调用函数时的参数数量是否超过函数的接收数量
- session:doDiagnostics(session.searchRedundantParameters, 'redundant-parameter', function (max, passed)
- return {
- message = lang.script('DIAG_OVER_MAX_ARGS', max, passed),
- tags = {DiagnosticTag.Unnecessary},
- }
- end)
- -- x or 0 + 1
- session:doDiagnostics(session.searchAmbiguity1, 'ambiguity-1', function (start, finish)
- return {
- message = lang.script('DIAG_AMBIGUITY_1', lines.buf:sub(start, finish)),
- }
- end)
- -- 不允许定义首字母小写的全局变量(很可能是拼错或者漏删)
- session:doDiagnostics(session.searchLowercaseGlobal, 'lowercase-global', function ()
- return {
- message = lang.script.DIAG_LOWERCASE_GLOBAL,
- }
- end)
- -- 未定义的变量(重载了 `_ENV`)
- session:doDiagnostics(session.searchUndefinedEnvChild, 'undefined-env-child', function (key)
- if vm.envType == '_ENV' then
- return {
- message = lang.script('DIAG_UNDEF_ENV_CHILD', key),
- }
- else
- return {
- message = lang.script('DIAG_UNDEF_FENV_CHILD', key),
- }
- end
- end)
- -- 全局变量不可用(置空了 `_ENV`)
- session:doDiagnostics(session.searchGlobalInNilEnv, 'global-in-nil-env', function (related)
- if vm.envType == '_ENV' then
- return {
- message = lang.script.DIAG_GLOBAL_IN_NIL_ENV,
- related = related,
- }
- else
- return {
- message = lang.script.DIAG_GLOBAL_IN_NIL_FENV,
- related = related,
- }
- end
- end)
- -- 构建表时重复定义field
- session:doDiagnostics(session.searchDuplicateIndex, 'duplicate-index', function (key, related, type)
- if type == 'unused' then
- return {
- message = lang.script('DIAG_DUPLICATE_INDEX', key),
- related = related,
- level = DiagnosticSeverity.Hint,
- tags = {DiagnosticTag.Unnecessary},
- }
- else
- return {
- message = lang.script('DIAG_DUPLICATE_INDEX', key),
- related = related,
- }
- end
- end)
- -- 往表里面塞重复的method
- --session:doDiagnostics(session.searchDuplicateMethod, 'duplicate-method', function (key, related)
- -- return {
- -- message = lang.script('DIAG_DUPLICATE_METHOD', key),
- -- related = related,
- -- }
- --end)
- -- 空代码块
- session:doDiagnostics(session.searchEmptyBlock, 'empty-block', function ()
- return {
- message = lang.script.DIAG_EMPTY_BLOCK,
- tags = {DiagnosticTag.Unnecessary},
- }
- end)
- -- 多余的赋值
- session:doDiagnostics(session.searchRedundantValue, 'redundant-value', function (max, passed)
- return {
- message = lang.script('DIAG_OVER_MAX_VALUES', max, passed),
- tags = {DiagnosticTag.Unnecessary},
- }
- end)
- -- Emmy相关的检查
- session:doDiagnostics(session.searchEmmyLua, 'emmy-lua', function (message, related)
- return {
- message = message,
- related = related,
- }
- end)
- -- 检查给const变量赋值
- session:doDiagnostics(session.searchSetConstLocal, 'set-const', function ()
- return {
- message = lang.script.DIAG_SET_CONST
- }
- end)
- -- 检查修改for的内置变量
- session:doDiagnostics(session.searchSetForState, 'set-for-state', function ()
- return {
- message = lang.script.DIAG_SET_FOR_STATE,
- }
- end)
- return session.datas
-end
diff --git a/script/core/document_symbol.lua b/script/core/document_symbol.lua
deleted file mode 100644
index 48e01332..00000000
--- a/script/core/document_symbol.lua
+++ /dev/null
@@ -1,260 +0,0 @@
-local hoverFunction = require 'core.hover.function'
-local getName = require 'core.name'
-local hover = require 'core.hover'
-
-local SymbolKind = {
- File = 1,
- Module = 2,
- Namespace = 3,
- Package = 4,
- Class = 5,
- Method = 6,
- Property = 7,
- Field = 8,
- Constructor = 9,
- Enum = 10,
- Interface = 11,
- Function = 12,
- Variable = 13,
- Constant = 14,
- String = 15,
- Number = 16,
- Boolean = 17,
- Array = 18,
- Object = 19,
- Key = 20,
- Null = 21,
- EnumMember = 22,
- Struct = 23,
- Event = 24,
- Operator = 25,
- TypeParameter = 26,
-}
-
-local function buildLocal(vm, source, used, callback)
- local vars = source[1]
- local exps = source[2]
- if vars.type ~= 'list' then
- vars = {vars}
- end
- if not exps or exps.type ~= 'list' then
- exps = {exps}
- end
- for i, var in ipairs(vars) do
- local exp = exps[i]
- local data = {}
- local loc = var:bindLocal()
- data.name = loc:getName()
- data.range = { var.start, var.finish }
- data.selectionRange = { var.start, var.finish }
- if exp then
- local hvr = hover(var)
- if exp.type == 'function' then
- data.kind = SymbolKind.Function
- else
- data.kind = SymbolKind.Variable
- end
- data.detail = hvr.label:gsub('[\r\n]', '')
- data.valueRange = { exp.start, exp.finish }
- used[exp] = true
- else
- data.kind = SymbolKind.Variable
- data.detail = ''
- data.valueRange = { var.start, var.finish }
- end
- callback(data)
- end
-end
-
-local function buildSet(vm, source, used, callback)
- local vars = source[1]
- local exps = source[2]
- if vars.type ~= 'list' then
- vars = {vars}
- end
- if not exps or exps.type ~= 'list' then
- exps = {exps}
- end
- for i, var in ipairs(vars) do
- if var:bindLocal() then
- goto CONTINUE
- end
- local exp = exps[i]
- local data = {}
- data.name = getName(var)
- data.range = { var.start, var.finish }
- data.selectionRange = { var.start, var.finish }
- if exp then
- local hvr = hover(var)
- if not hvr then
- goto CONTINUE
- end
- if exp.type == 'function' then
- data.kind = SymbolKind.Function
- else
- data.kind = SymbolKind.Property
- end
- data.detail = hvr.label:gsub('[\r\n]', '')
- data.valueRange = { exp.start, exp.finish }
- used[exp] = true
- else
- data.kind = SymbolKind.Property
- data.detail = ''
- data.valueRange = { var.start, var.finish }
- end
- callback(data)
- :: CONTINUE ::
- end
-end
-
-local function buildPair(vm, source, used, callback)
- local var = source[1]
- local exp = source[2]
- local data = {}
- data.name = getName(var)
- data.range = { var.start, var.finish }
- data.selectionRange = { var.start, var.finish }
- if exp then
- local hvr = hover(var)
- if not hvr then
- return
- end
- if exp.type == 'function' then
- data.kind = SymbolKind.Function
- else
- data.kind = SymbolKind.Class
- end
- data.detail = hvr.label:gsub('[\r\n]', '')
- data.valueRange = { exp.start, exp.finish }
- used[exp] = true
- else
- data.kind = SymbolKind.Class
- data.detail = ''
- data.valueRange = { var.start, var.finish }
- end
- callback(data)
-end
-
-local function buildLocalFunction(vm, source, used, callback)
- local value = source:bindFunction()
- if not value then
- return
- end
- local name = getName(source.name)
- local hvr = hoverFunction(name, value:getFunction())
- if not hvr then
- return
- end
- local kind = SymbolKind.Function
- callback {
- name = name,
- detail = hvr.label:gsub('[\r\n]', ''),
- kind = kind,
- range = { source.start, source.finish },
- selectionRange = { source.name.start, source.name.finish },
- valueRange = { source.start, source.finish },
- }
-end
-
-
-local function buildFunction(vm, source, used, callback)
- if used[source] then
- return
- end
- local value = source:bindFunction()
- if not value then
- return
- end
- local name = getName(source.name)
- local func = value:getFunction()
- if not func then
- return
- end
- local hvr = hoverFunction(name, func, func:getObject())
- if not hvr then
- return
- end
- local data = {}
- data.name = name
- data.detail = hvr.label:gsub('[\r\n]', '')
- data.range = { source.start, source.finish }
- data.valueRange = { source.start, source.finish }
- if source.name then
- data.selectionRange = { source.name.start, source.name.finish }
- else
- data.selectionRange = { source.start, source.start }
- end
- if func:getObject() then
- data.kind = SymbolKind.Field
- else
- data.kind = SymbolKind.Function
- end
- callback(data)
-end
-
-local function buildSource(vm, source, used, callback)
- if source.type == 'local' then
- buildLocal(vm, source, used, callback)
- return
- end
- if source.type == 'set' then
- buildSet(vm, source, used, callback)
- return
- end
- if source.type == 'pair' then
- buildPair(vm, source, used, callback)
- return
- end
- if source.type == 'localfunction' then
- buildLocalFunction(vm, source, used, callback)
- return
- end
- if source.type == 'function' then
- buildFunction(vm, source, used, callback)
- return
- end
-end
-
-local function packChild(symbols, finish, kind)
- local t
- while true do
- local symbol = symbols[#symbols]
- if not symbol then
- break
- end
- if symbol.valueRange[1] > finish then
- break
- end
- symbols[#symbols] = nil
- symbol.children = packChild(symbols, symbol.valueRange[2], symbol.kind)
- if not t then
- t = {}
- end
- t[#t+1] = symbol
- end
- return t
-end
-
-local function packSymbols(symbols)
- -- 按照start位置反向排序
- table.sort(symbols, function (a, b)
- return a.range[1] > b.range[1]
- end)
- -- 处理嵌套
- return packChild(symbols, math.maxinteger, SymbolKind.Function)
-end
-
-return function (vm)
- local symbols = {}
- local used = {}
-
- vm:eachSource(function (source)
- buildSource(vm, source, used, function (data)
- symbols[#symbols+1] = data
- end)
- end)
-
- local packedSymbols = packSymbols(symbols)
-
- return packedSymbols
-end
diff --git a/script/core/find_lib.lua b/script/core/find_lib.lua
deleted file mode 100644
index e76549a8..00000000
--- a/script/core/find_lib.lua
+++ /dev/null
@@ -1,65 +0,0 @@
-local hoverName = require 'core.hover.name'
-
-local function getParentName(lib, isObject)
- for _, parent in ipairs(lib.parent) do
- if isObject then
- if parent.type == 'object' then
- return parent.nick or parent.name
- end
- else
- if parent.type ~= 'object' then
- return parent.nick or parent.name
- end
- end
- end
- return ''
-end
-
-local function findLib(source)
- local value = source:bindValue()
- local lib = value:getLib()
- if not lib then
- return nil
- end
- if lib.parent then
- if source:get 'object' then
- -- *string:sub
- local fullKey = ('*%s:%s'):format(getParentName(lib, true), lib.name)
- return lib, fullKey
- else
- local parentValue = source:get 'parent'
- if parentValue and parentValue:getType() == 'string' then
- -- *string.sub
- local fullKey = ('*%s.%s'):format(getParentName(lib, false), lib.name)
- return lib, fullKey
- else
- -- string.sub
- local fullKey = ('%s.%s'):format(getParentName(lib, false), lib.name)
- return lib, fullKey
- end
- end
- else
- local name = hoverName(source)
- local libName = lib.nick or lib.name
- if name == libName or not libName then
- return lib, name
- elseif name == '' then
- return lib, libName
- else
- return lib, ('%s<%s>'):format(name, libName)
- end
- end
-end
-
-return function (source)
- if source:bindValue() then
- local lib, fullKey = findLib(source)
- return lib, fullKey
- end
- if source:get 'in index' then
- source = source:get 'in index'
- local lib, fullKey = findLib(source)
- return lib, fullKey
- end
- return nil
-end
diff --git a/script/core/find_source.lua b/script/core/find_source.lua
deleted file mode 100644
index a64a047e..00000000
--- a/script/core/find_source.lua
+++ /dev/null
@@ -1,59 +0,0 @@
-local function isContainPos(obj, pos)
- if obj.start <= pos and obj.finish >= pos then
- return true
- end
- return false
-end
-
-local function isValidSource(source)
- return source.start ~= nil and source.start ~= 0
-end
-
-local function matchFilter(source, filter)
- if not filter then
- return true
- end
- return filter[source.type]
-end
-
-local function findAtPos(vm, pos, filter)
- local res = {}
- vm:eachSource(function (source)
- if isValidSource(source)
- and isContainPos(source, pos)
- and matchFilter(source, filter)
- then
- res[#res+1] = source
- end
- end)
- if #res == 0 then
- return nil
- end
- table.sort(res, function (a, b)
- if a == b then
- return false
- end
- local rangeA = a.finish - a.start
- local rangeB = b.finish - b.start
- -- 特殊处理:func 'str' 的情况下,list与string的范围会完全相同,此时取string
- if rangeA == rangeB then
- if b.type == 'call' and #b == 1 and b[1] == a then
- return true
- elseif a.type == 'call' and #a == 1 and a[1] == b then
- return false
- else
- return a.id < b.id
- end
- end
- return rangeA < rangeB
- end)
- local source = res[1]
- if not source then
- return nil
- end
- return source
-end
-
-return function (vm, pos, filter)
- return findAtPos(vm, pos, filter)
-end
diff --git a/script/core/folding_range.lua b/script/core/folding_range.lua
deleted file mode 100644
index e94d1ffe..00000000
--- a/script/core/folding_range.lua
+++ /dev/null
@@ -1,73 +0,0 @@
-local foldingType = {
- ['function'] = {'region', 'end', },
- ['localfunction'] = {'region', 'end', },
- ['do'] = {'region', 'end', },
- ['if'] = {'region', 'end', },
- ['loop'] = {'region', 'end', },
- ['in'] = {'region', 'end', },
- ['while'] = {'region', 'end', },
- ['repeat'] = {'region', 'until',},
- ['table'] = {'region', '}', },
- ['string'] = {'regtion', ']', },
-}
-
-return function (vm, comments)
- local result = {}
- vm:eachSource(function (source)
- local tp = source.type
- local data = foldingType[tp]
- if not data then
- return
- end
- local start = source.start
- local finish = source.finish
- if tp == 'repeat' then
- if #source > 0 then
- finish = source[#source].finish
- else
- finish = start + #'repeat'
- end
- finish = vm.text:find('until', finish, true) or finish
- result[#result+1] = {
- start = start,
- finish = finish,
- kind = data[1],
- }
- elseif tp == 'if' then
- for i = 1, #source do
- local block = source[i]
- local nblock = source[i+1]
- result[#result+1] = {
- start = block.start,
- finish = nblock and nblock.start or finish,
- kind = data[1],
- }
- end
- elseif tp == 'string' then
- result[#result+1] = {
- start = start,
- finish = finish,
- kind = data[1],
- }
- elseif data[1] == 'region' then
- result[#result+1] = {
- start = start,
- finish = finish,
- kind = data[1],
- }
- end
- end)
- if comments then
- for _, comment in ipairs(comments) do
- result[#result+1] = {
- start = comment.start,
- finish = comment.finish,
- kind = 'comment',
- }
- end
- end
- if #result == 0 then
- return nil
- end
- return result
-end
diff --git a/script/core/global.lua b/script/core/global.lua
deleted file mode 100644
index 961ad304..00000000
--- a/script/core/global.lua
+++ /dev/null
@@ -1,49 +0,0 @@
-local mt = {}
-mt.__index = mt
-
-function mt:markSet(uri)
- if not uri then
- return
- end
- self.set[uri] = true
-end
-
-function mt:markGet(uri)
- if not uri then
- return
- end
- self.get[uri] = true
-end
-
-function mt:clearGlobal(uri)
- self.set[uri] = nil
- self.get[uri] = nil
-end
-
-function mt:getAllUris()
- local uris = {}
- for uri in pairs(self.set) do
- uris[#uris+1] = uri
- end
- for uri in pairs(self.get) do
- if not self.set[uri] then
- uris[#uris+1] = uri
- end
- end
- return uris
-end
-
-function mt:hasSetGlobal(uri)
- return self.set[uri] ~= nil
-end
-
-function mt:remove()
-end
-
-return function (lsp)
- return setmetatable({
- get = {},
- set = {},
- lsp = lsp,
- }, mt)
-end
diff --git a/script/core/highlight.lua b/script/core/highlight.lua
deleted file mode 100644
index 2073573d..00000000
--- a/script/core/highlight.lua
+++ /dev/null
@@ -1,54 +0,0 @@
-local findSource = require 'core.find_source'
-local parser = require 'parser'
-
-local DocumentHighlightKind = {
- Text = 1,
- Read = 2,
- Write = 3,
-}
-
-local function parseResult(source)
- local positions = {}
- if source:bindLabel() then
- source:bindLabel():eachInfo(function (info, src)
- positions[#positions+1] = { src.start, src.finish, DocumentHighlightKind.Text }
- end)
- return positions
- end
- if source:bindLocal() then
- local loc = source:bindLocal()
- local mark = {}
- loc:eachInfo(function (info, src)
- if not mark[src] then
- mark[src] = info
- positions[#positions+1] = { src.start, src.finish, DocumentHighlightKind.Text }
- end
- end)
- return positions
- end
- if source:bindValue() and source:get 'parent' then
- local parent = source:get 'parent'
- local mark = {}
- parent:eachInfo(function (info, src)
- if not mark[src] and source.uri == src.uri then
- mark[src] = info
- if info.type == 'get child' or info.type == 'set child' then
- if info[1] == source[1] then
- positions[#positions+1] = {src.start, src.finish, DocumentHighlightKind.Text}
- end
- end
- end
- end)
- return positions
- end
- return nil
-end
-
-return function (vm, pos)
- local source = findSource(vm, pos)
- if not source then
- return nil
- end
- local positions = parseResult(source)
- return positions
-end
diff --git a/script/core/hover/emmy_function.lua b/script/core/hover/emmy_function.lua
deleted file mode 100644
index 7c87954e..00000000
--- a/script/core/hover/emmy_function.lua
+++ /dev/null
@@ -1,143 +0,0 @@
----@param emmy EmmyFunctionType
-local function buildEmmyArgs(emmy, object, select)
- local start
- if object then
- start = 2
- else
- start = 1
- end
- local strs = {}
- local args = {}
- local i = 0
- emmy:eachParam(function (name, typeObj)
- i = i + 1
- if i < start then
- return
- end
- if i > start then
- strs[#strs+1] = ', '
- end
- if i == select then
- strs[#strs+1] = '@ARG'
- end
- strs[#strs+1] = name .. ': ' .. typeObj:getType()
- args[#args+1] = strs[#strs]
- if i == select then
- strs[#strs+1] = '@ARG'
- end
- end)
- local text = table.concat(strs)
- local argLabel = {}
- for i = 1, 2 do
- local pos = text:find('@ARG', 1, true)
- if pos then
- if i == 1 then
- argLabel[i] = pos
- else
- argLabel[i] = pos - 1
- end
- text = text:sub(1, pos-1) .. text:sub(pos+4)
- end
- end
- if #argLabel == 0 then
- argLabel = nil
- end
- return text, argLabel, args
-end
-
-local function buildEmmyReturns(emmy)
- local rtns = {}
- local i = 0
- emmy:eachReturn(function (rtn)
- i = i + 1
- if i > 1 then
- rtns[#rtns+1] = ('\n% 3d. '):format(i)
- end
- rtns[#rtns+1] = rtn:getType()
- end)
- if #rtns == 0 then
- return '\n -> ' .. 'any'
- else
- return '\n -> ' .. table.concat(rtns)
- end
-end
-
-local function buildEnum(lib)
- if not lib.enums then
- return ''
- end
- local container = table.container()
- for _, enum in ipairs(lib.enums) do
- if not enum.name or (not enum.enum and not enum.code) then
- goto NEXT_ENUM
- end
- if not container[enum.name] then
- container[enum.name] = {}
- if lib.args then
- for _, arg in ipairs(lib.args) do
- if arg.name == enum.name then
- container[enum.name].type = arg.type
- break
- end
- end
- end
- if lib.returns then
- for _, rtn in ipairs(lib.returns) do
- if rtn.name == enum.name then
- container[enum.name].type = rtn.type
- break
- end
- end
- end
- end
- table.insert(container[enum.name], enum)
- ::NEXT_ENUM::
- end
- local strs = {}
- local raw = {}
- for name, enums in pairs(container) do
- local tp
- if type(enums.type) == 'table' then
- tp = table.concat(enums.type, '/')
- else
- tp = enums.type
- end
- raw[name] = {}
- strs[#strs+1] = ('\n%s: %s'):format(name, tp or 'any')
- for _, enum in ipairs(enums) do
- if enum.default then
- strs[#strs+1] = '\n -> '
- else
- strs[#strs+1] = '\n | '
- end
- if enum.code then
- strs[#strs+1] = tostring(enum.code)
- else
- strs[#strs+1] = ('%q'):format(enum.enum)
- end
- raw[name][#raw[name]+1] = strs[#strs]
- if enum.description then
- strs[#strs+1] = ' -- ' .. enum.description
- end
- end
- end
- return table.concat(strs), raw
-end
-
-return function (name, emmy, object, select)
- local argStr, argLabel, args = buildEmmyArgs(emmy, object, select)
- local returns = buildEmmyReturns(emmy)
- local enum, rawEnum = buildEnum(emmy)
- local tip = emmy.description
- return {
- label = ('function %s(%s)%s'):format(name, argStr, returns),
- name = name,
- argStr = argStr,
- returns = returns,
- description = tip,
- enum = enum,
- rawEnum = rawEnum,
- argLabel = argLabel,
- args = args,
- }
-end
diff --git a/script/core/hover/function.lua b/script/core/hover/function.lua
deleted file mode 100644
index 3865f602..00000000
--- a/script/core/hover/function.lua
+++ /dev/null
@@ -1,243 +0,0 @@
-local emmyFunction = require 'core.hover.emmy_function'
-
-local function buildValueArgs(func, object, select)
- if not func then
- return '', nil
- end
- local names = {}
- local values = {}
- local options = {}
- if func.argValues then
- for i, value in ipairs(func.argValues) do
- values[i] = value:getType()
- end
- end
- if func.args then
- for i, arg in ipairs(func.args) do
- names[#names+1] = arg:getName()
- local param = func:findEmmyParamByName(arg:getName())
- if param then
- values[i] = param:getType()
- options[i] = param:getOption()
- end
- end
- end
- local strs = {}
- local start = 1
- if object then
- start = 2
- end
- local max
- if func:getSource() then
- max = #names
- else
- max = math.max(#names, #values)
- end
- local args = {}
- for i = start, max do
- local name = names[i]
- local value = values[i] or 'any'
- local option = options[i]
- if option and option.optional then
- if i > start then
- strs[#strs+1] = ' ['
- else
- strs[#strs+1] = '['
- end
- end
- if i > start then
- strs[#strs+1] = ', '
- end
-
- if i == select then
- strs[#strs+1] = '@ARG'
- end
- if name then
- strs[#strs+1] = name .. ': ' .. value
- else
- strs[#strs+1] = value
- end
- args[#args+1] = strs[#strs]
- if i == select then
- strs[#strs+1] = '@ARG'
- end
-
- if option and option.optional == 'self' then
- strs[#strs+1] = ']'
- end
- end
- if func:hasDots() then
- if max > 0 then
- strs[#strs+1] = ', '
- end
- strs[#strs+1] = '...'
- end
-
- if options then
- for _, option in pairs(options) do
- if option.optional == 'after' then
- strs[#strs+1] = ']'
- end
- end
- end
-
- local text = table.concat(strs)
- local argLabel = {}
- for i = 1, 2 do
- local pos = text:find('@ARG', 1, true)
- if pos then
- if i == 1 then
- argLabel[i] = pos
- else
- argLabel[i] = pos - 1
- end
- text = text:sub(1, pos-1) .. text:sub(pos+4)
- end
- end
- if #argLabel == 0 then
- argLabel = nil
- end
- return text, argLabel, args
-end
-
-local function buildValueReturns(func)
- if not func then
- return '\n -> any'
- end
- if not func:get 'hasReturn' then
- return ''
- end
- local strs = {}
- local emmys = {}
- local n = 0
- func:eachEmmyReturn(function (emmy)
- n = n + 1
- emmys[n] = emmy
- end)
- if func.returns then
- for i, rtn in ipairs(func.returns) do
- local emmy = emmys[i]
- local option = emmy and emmy.option
- if option and option.optional then
- if i > 1 then
- strs[#strs+1] = ' ['
- else
- strs[#strs+1] = '['
- end
- end
- if i > 1 then
- strs[#strs+1] = ('\n% 3d. '):format(i)
- end
- if emmy and emmy.name then
- strs[#strs+1] = ('%s: '):format(emmy.name)
- elseif option and option.name then
- strs[#strs+1] = ('%s: '):format(option.name)
- end
- strs[#strs+1] = rtn:getType()
- if option and option.optional == 'self' then
- strs[#strs+1] = ']'
- end
- end
- for i = 1, #func.returns do
- local emmy = emmys[i]
- if emmy and emmy.option and emmy.option.optional == 'after' then
- strs[#strs+1] = ']'
- end
- end
- end
- if #strs == 0 then
- strs[1] = 'any'
- end
- return '\n -> ' .. table.concat(strs)
-end
-
----@param func emmyFunction
-local function buildEnum(func)
- if not func then
- return nil
- end
- local params = func:getEmmyParams()
- if not params then
- return nil
- end
- local strs = {}
- local raw = {}
- for _, param in ipairs(params) do
- local first = true
- local name = param:getName()
- raw[name] = {}
- param:eachEnum(function (enum)
- if first then
- first = false
- strs[#strs+1] = ('\n%s: %s'):format(param:getName(), param:getType())
- end
- if enum.default then
- strs[#strs+1] = ('\n |>%s'):format(enum[1])
- else
- strs[#strs+1] = ('\n | %s'):format(enum[1])
- end
- if enum.comment then
- strs[#strs+1] = ' -- ' .. enum.comment
- end
- raw[name][#raw[name]+1] = enum[1]
- end)
- end
- if #strs == 0 then
- return nil
- end
- return table.concat(strs), raw
-end
-
-local function getComment(func)
- if not func then
- return nil
- end
- local comments = {}
- local params = func:getEmmyParams()
- if params then
- for _, param in ipairs(params) do
- local option = param:getOption()
- if option and option.comment then
- comments[#comments+1] = ('+ `%s`*(%s)*: %s'):format(param:getName(), param:getType(), option.comment)
- end
- end
- end
- comments[#comments+1] = func:getComment()
- if #comments == 0 then
- return nil
- end
- return table.concat(comments, '\n\n')
-end
-
-local function getOverLoads(name, func, object, select)
- local overloads = func and func:getEmmyOverLoads()
- if not overloads then
- return nil
- end
- local list = {}
- for _, ol in ipairs(overloads) do
- local hover = emmyFunction(name, ol, object, select)
- list[#list+1] = hover.label
- end
- return table.concat(list, '\n')
-end
-
-return function (name, func, object, select)
- local argStr, argLabel, args = buildValueArgs(func, object, select)
- local returns = buildValueReturns(func)
- local enum, rawEnum = buildEnum(func)
- local comment = getComment(func)
- local overloads = getOverLoads(name, func, object, select)
- return {
- label = ('function %s(%s)%s'):format(name, argStr, returns),
- name = name,
- argStr = argStr,
- returns = returns,
- description = comment,
- enum = enum,
- rawEnum = rawEnum,
- argLabel = argLabel,
- overloads = overloads,
- args = args,
- }
-end
diff --git a/script/core/hover/hover.lua b/script/core/hover/hover.lua
deleted file mode 100644
index 7b99f8c0..00000000
--- a/script/core/hover/hover.lua
+++ /dev/null
@@ -1,416 +0,0 @@
-local findLib = require 'core.find_lib'
-local getFunctionHover = require 'core.hover.function'
-local getFunctionHoverAsLib = require 'core.hover.lib_function'
-local getFunctionHoverAsEmmy = require 'core.hover.emmy_function'
-local buildValueName = require 'core.hover.name'
-local lang = require 'language'
-local config = require 'config'
-local uric = require 'uri'
-
-local function utf8Len(str, start, finish)
- local len, pos = utf8.len(str, start, finish, true)
- if len then
- return len
- end
- return 1 + utf8Len(str, start, pos-1) + utf8Len(str, pos+1, finish)
-end
-
-local OriginTypes = {
- ['any'] = true,
- ['nil'] = true,
- ['integer'] = true,
- ['number'] = true,
- ['boolean'] = true,
- ['string'] = true,
- ['thread'] = true,
- ['userdata'] = true,
- ['table'] = true,
- ['function'] = true,
-}
-
-local function longString(str)
- for i = 0, 10 do
- local finish = ']' .. ('='):rep(i) .. ']'
- if not str:find(finish, 1, true) then
- return ('[%s[\n%s%s'):format(('='):rep(i), str, finish)
- end
- end
- return ('%q'):format(str)
-end
-
-local function formatString(str)
- if #str > 1000 then
- str = str:sub(1000)
- end
- if str:find('[\r\n]') then
- str = str:gsub('[\000-\008\011-\012\014-\031\127]', '')
- return longString(str)
- else
- str = str:gsub('[\000-\008\011-\012\014-\031\127]', function (char)
- return ('\\%03d'):format(char:byte())
- end)
- local single = str:find("'", 1, true)
- local double = str:find('"', 1, true)
- if single and double then
- return longString(str)
- elseif double then
- return ("'%s'"):format(str)
- else
- return ('"%s"'):format(str)
- end
- end
-end
-
-local function formatLiteral(v)
- if math.type(v) == 'float' then
- return ('%.10f'):format(v):gsub('[0]*$', ''):gsub('%.$', '.0')
- elseif type(v) == 'string' then
- return formatString(v)
- else
- return ('%q'):format(v)
- end
-end
-
-local function findClass(value)
- -- 检查是否有emmy
- local emmy = value:getEmmy()
- if emmy then
- return emmy:getType()
- end
- -- 检查对象元表
- local metaValue = value:getMetaTable()
- if not metaValue then
- return nil
- end
- -- 检查元表中的 __name
- local metaName = metaValue:rawGet('__name')
- if metaName and type(metaName:getLiteral()) == 'string' then
- return metaName:getLiteral()
- end
- -- 检查元表的 __index
- local indexValue = metaValue:rawGet('__index')
- if not indexValue then
- return nil
- end
- -- 查找index方法中的以下字段: type name class
- -- 允许多重继承
- return indexValue:eachChild(function (k, v)
- -- 键值类型必须均为字符串
- if type(k) ~= 'string' then
- return
- end
- if type(v:getLiteral()) ~= 'string' then
- return
- end
- local lKey = k:lower()
- if lKey == 'type'
- or lKey == 'name'
- or lKey == 'class'
- then
- -- 必须只有过一次赋值
- local hasSet = false
- local ok = v:eachInfo(function (info)
- if info.type == 'set' then
- if hasSet then
- return false
- else
- hasSet = true
- end
- end
- end)
- if ok == false then
- return false
- end
- return v:getLiteral()
- end
- end)
-end
-
-local function formatKey(key)
- local kType = type(key)
- if kType == 'table' then
- key = ('[*%s]'):format(key:getType())
- elseif math.type(key) == 'integer' then
- key = ('[%03d]'):format(key)
- elseif kType == 'string' then
- if key:find '^%d' or key:find '[^%w_]' then
- key = ('[%s]'):format(formatString(key))
- end
- elseif key == '' then
- key = '[*any]'
- else
- key = ('[%s]'):format(key)
- end
- return key
-end
-
-local function unpackTable(value)
- local lines = {}
- value:eachChild(function (key, child)
- key = formatKey(key)
-
- local vType = type(child:getLiteral())
- if vType == 'boolean'
- or vType == 'integer'
- or vType == 'number'
- or vType == 'string'
- then
- lines[#lines+1] = ('%s: %s = %s'):format(key, child:getType(), formatLiteral(child:getLiteral()))
- else
- lines[#lines+1] = ('%s: %s'):format(key, child:getType())
- end
- end)
- local emmy = value:getEmmy()
- if emmy then
- if emmy.type == 'emmy.arrayType' then
- lines[#lines+1] = ('[*integer]: %s'):format(emmy:getName())
- elseif emmy.type == 'emmy.tableType' then
- lines[#lines+1] = ('[*%s]: %s'):format(emmy:getKeyType():getType(), emmy:getValueType():getType())
- end
- end
- if #lines == 0 then
- return '{}'
- end
-
- -- 整理一下表
- local cleaned = {}
- local used = {}
- for _, line in ipairs(lines) do
- if used[line] then
- goto CONTINUE
- end
- used[line] = true
- if line == '[*any]: any' then
- goto CONTINUE
- end
- cleaned[#cleaned+1] = ' ' .. line .. ','
- :: CONTINUE ::
- end
-
- table.sort(cleaned)
- table.insert(cleaned, 1, '{')
- cleaned[#cleaned+1] = '}'
- return table.concat(cleaned, '\r\n')
-end
-
-local function getValueHover(source, name, value, lib)
- local valueType = value:getType()
- local class = findClass(value)
-
- if class then
- valueType = class
- lib = nil
- end
-
- if not OriginTypes[valueType] then
- valueType = '*' .. valueType
- end
-
- local tips = {}
- local literal
- if lib then
- literal = lib.code or (lib.value and formatLiteral(lib.value))
- tips[#tips+1] = lib.description
- else
- literal = value:getLiteral() and formatLiteral(value:getLiteral())
- end
-
- tips[#tips+1] = value:getComment()
-
- local tp
- if source:bindLocal() then
- tp = 'local'
- local loc = source:bindLocal()
- if loc.tags then
- local mark = {}
- local tagBufs = {}
- for _, tag in ipairs(loc.tags) do
- local tagName = tag[1]
- if not mark[tagName] then
- mark[tagName] = true
- tagBufs[#tagBufs+1] = ('<%s>'):format(tagName)
- end
- end
- name = name .. ' ' .. table.concat(tagBufs, ' ')
- end
- tips[#tips+1] = loc:getComment()
- elseif source:get 'global' then
- tp = 'global'
- elseif source:get 'simple' then
- local simple = source:get 'simple'
- if simple[1]:get 'global' then
- tp = 'global'
- else
- tp = 'field'
- end
- else
- tp = 'field'
- end
-
- local text
- if valueType == 'table' then
- text = ('%s %s: %s'):format(tp, name, unpackTable(value))
- else
- if literal == nil then
- if class and not OriginTypes[class] then
- text = ('%s %s: %s %s'):format(tp, name, valueType, unpackTable(value))
- else
- text = ('%s %s: %s'):format(tp, name, valueType)
- end
- else
- text = ('%s %s: %s = %s'):format(tp, name, valueType, literal)
- end
- end
-
- local tip
- if #tips > 0 then
- tip = table.concat(tips, '\n\n-------------\n\n')
- end
- return {
- label = text,
- description = tip,
- }
-end
-
-local function hoverAsValue(source, lsp, select)
- local lib, fullkey = findLib(source)
- ---@type value
- local value = source:findValue()
- local name = fullkey or buildValueName(source)
-
- local hover
- if value:getType() == 'function' then
- local object = source:get 'object'
- if lib then
- hover = getFunctionHoverAsLib(name, lib, object, select)
- else
- local emmy = value:getEmmy()
- if emmy and emmy.type == 'emmy.functionType' then
- hover = getFunctionHoverAsEmmy(name, emmy, object, select)
- else
- local func = value:getFunction()
- hover = getFunctionHover(name, func, object, select)
- end
- end
- else
- hover = getValueHover(source, name, value, lib)
- end
-
- if not hover then
- return nil
- end
- hover.name = name
- return hover
-end
-
-local function hoverAsTargetUri(source, lsp)
- local uri = source:get 'target uri'
- if not lsp then
- return nil
- end
- local ws = lsp:findWorkspaceFor(uri)
- if ws then
- local path = ws:relativePathByUri(uri)
- if not path then
- return nil
- end
- return {
- description = ('[%s](%s)'):format(path:string(), uri),
- }
- else
- return {
- description = ('[%s](%s)'):format(uric.decode(uri):string(), uri),
- }
- end
-end
-
-local function hoverAsString(source)
- local str = source[1]
- if type(str) ~= 'string' then
- return nil
- end
- local len = #str
- local charLen = utf8Len(str, 1, -1)
- local lines = {}
- if len == charLen then
- lines[#lines+1] = lang.script('HOVER_STRING_BYTES', len)
- else
- lines[#lines+1] = lang.script('HOVER_STRING_CHARACTERS', len, charLen)
- end
- -- 内部包含转义符?
- local rawLen = source.finish - source.start - 2 * #source[2] + 1
- if config.config.hover.viewString
- and (source[2] == '"' or source[2] == "'")
- and rawLen > #str then
- local view = str
- local max = config.config.hover.viewStringMax
- if #view > max then
- view = view:sub(1, max) .. '...'
- end
- lines[#lines+1] = ([[
-
-------------------
-```txt
-%s
-```]]):format(view)
- end
- return {
- description = table.concat(lines, '\n'),
- range = {
- start = source.start,
- finish = source.finish,
- },
- }
-end
-
-local function formatNumber(n)
- local str = ('%.10f'):format(n)
- str = str:gsub('%.?0*$', '')
- return str
-end
-
-local function hoverAsNumber(source)
- if not config.config.hover.viewNumber then
- return nil
- end
- local num = source[1]
- if type(num) ~= 'number' then
- return nil
- end
- local raw = source[2]
- if not raw or not raw:find '[^%-%d%.]' then
- return nil
- end
- return {
- description = formatNumber(num),
- range = {
- start = source.start,
- finish = source.finish,
- },
- }
-end
-
-return function (source, lsp, select)
- if not source then
- return nil
- end
- if source:get 'target uri' then
- return hoverAsTargetUri(source, lsp)
- end
- if source.type == 'name' and source:bindValue() then
- return hoverAsValue(source, lsp, select)
- end
- if source.type == 'simple' then
- source = source[#source]
- if source.type == 'name' and source:bindValue() then
- return hoverAsValue(source, lsp, select)
- end
- end
- if source.type == 'string' then
- return hoverAsString(source)
- end
- if source.type == 'number' then
- return hoverAsNumber(source)
- end
- return nil
-end
diff --git a/script/core/hover/init.lua b/script/core/hover/init.lua
deleted file mode 100644
index be5b5632..00000000
--- a/script/core/hover/init.lua
+++ /dev/null
@@ -1 +0,0 @@
-return require 'core.hover.hover'
diff --git a/script/core/hover/lib_function.lua b/script/core/hover/lib_function.lua
deleted file mode 100644
index 162b6b75..00000000
--- a/script/core/hover/lib_function.lua
+++ /dev/null
@@ -1,258 +0,0 @@
-local lang = require 'language'
-local config = require 'config'
-local client = require 'client'
-
-local function buildLibArgs(lib, object, select)
- if not lib.args then
- return ''
- end
- local start
- if object then
- start = 2
- else
- start = 1
- end
- local strs = {}
- local args = {}
- for i = start, #lib.args do
- local arg = lib.args[i]
- if arg.optional then
- if i > start then
- strs[#strs+1] = ' ['
- else
- strs[#strs+1] = '['
- end
- end
- if i > start then
- strs[#strs+1] = ', '
- end
-
- local argStr = {}
- if i == select then
- argStr[#argStr+1] = '@ARG'
- end
- local name = ''
- if arg.name then
- name = ('%s: '):format(arg.name)
- end
- if type(arg.type) == 'table' then
- name = name .. table.concat(arg.type, '/')
- else
- name = name .. (arg.type or 'any')
- end
- argStr[#argStr+1] = name
- args[#args+1] = name
- if arg.default then
- argStr[#argStr+1] = ('(%q)'):format(arg.default)
- end
- if i == select then
- argStr[#argStr+1] = '@ARG'
- end
-
- for _, str in ipairs(argStr) do
- strs[#strs+1] = str
- end
- if arg.optional == 'self' then
- strs[#strs+1] = ']'
- end
- end
- for _, arg in ipairs(lib.args) do
- if arg.optional == 'after' then
- strs[#strs+1] = ']'
- end
- end
- local text = table.concat(strs)
- local argLabel = {}
- for i = 1, 2 do
- local pos = text:find('@ARG', 1, true)
- if pos then
- if i == 1 then
- argLabel[i] = pos
- else
- argLabel[i] = pos - 1
- end
- text = text:sub(1, pos-1) .. text:sub(pos+4)
- end
- end
- if #argLabel == 0 then
- argLabel = nil
- end
- return text, argLabel, args
-end
-
-local function buildLibReturns(lib)
- if not lib.returns then
- return ''
- end
- local strs = {}
- for i, rtn in ipairs(lib.returns) do
- if rtn.optional then
- if i > 1 then
- strs[#strs+1] = ' ['
- else
- strs[#strs+1] = '['
- end
- end
- if i > 1 then
- strs[#strs+1] = ('\n% 3d. '):format(i)
- end
- if rtn.name then
- strs[#strs+1] = ('%s: '):format(rtn.name)
- end
- if type(rtn.type) == 'table' then
- strs[#strs+1] = table.concat(rtn.type, '/')
- else
- strs[#strs+1] = rtn.type or 'any'
- end
- if rtn.default then
- strs[#strs+1] = ('(%q)'):format(rtn.default)
- end
- if rtn.optional == 'self' then
- strs[#strs+1] = ']'
- end
- end
- for _, rtn in ipairs(lib.returns) do
- if rtn.optional == 'after' then
- strs[#strs+1] = ']'
- end
- end
- return '\n -> ' .. table.concat(strs)
-end
-
-local function buildEnum(lib)
- if not lib.enums then
- return ''
- end
- local container = table.container()
- for _, enum in ipairs(lib.enums) do
- if not enum.name or (not enum.enum and not enum.code) then
- goto NEXT_ENUM
- end
- if not container[enum.name] then
- container[enum.name] = {}
- if lib.args then
- for _, arg in ipairs(lib.args) do
- if arg.name == enum.name then
- container[enum.name].type = arg.type
- break
- end
- end
- end
- if lib.returns then
- for _, rtn in ipairs(lib.returns) do
- if rtn.name == enum.name then
- container[enum.name].type = rtn.type
- break
- end
- end
- end
- end
- table.insert(container[enum.name], enum)
- ::NEXT_ENUM::
- end
- local strs = {}
- local raw = {}
- for name, enums in pairs(container) do
- local tp
- if type(enums.type) == 'table' then
- tp = table.concat(enums.type, '/')
- else
- tp = enums.type
- end
- strs[#strs+1] = ('\n%s: %s'):format(name, tp or 'any')
- raw[name] = {}
- for _, enum in ipairs(enums) do
- if enum.default then
- strs[#strs+1] = '\n -> '
- else
- strs[#strs+1] = '\n | '
- end
- if enum.code then
- strs[#strs+1] = tostring(enum.code)
- else
- strs[#strs+1] = tostring(enum.enum)
- end
- raw[name][#raw[name]+1] = strs[#strs]
- if enum.description then
- strs[#strs+1] = ' -- ' .. enum.description
- end
- end
- end
- return table.concat(strs), raw
-end
-
-local function getDocFormater()
- local version = config.config.runtime.version
- if client.client() == 'vscode' then
- if version == 'Lua 5.1' then
- return 'HOVER_NATIVE_DOCUMENT_LUA51'
- elseif version == 'Lua 5.2' then
- return 'HOVER_NATIVE_DOCUMENT_LUA52'
- elseif version == 'Lua 5.3' then
- return 'HOVER_NATIVE_DOCUMENT_LUA53'
- elseif version == 'Lua 5.4' then
- return 'HOVER_NATIVE_DOCUMENT_LUA54'
- elseif version == 'LuaJIT' then
- return 'HOVER_NATIVE_DOCUMENT_LUAJIT'
- end
- else
- if version == 'Lua 5.1' then
- return 'HOVER_DOCUMENT_LUA51'
- elseif version == 'Lua 5.2' then
- return 'HOVER_DOCUMENT_LUA52'
- elseif version == 'Lua 5.3' then
- return 'HOVER_DOCUMENT_LUA53'
- elseif version == 'Lua 5.4' then
- return 'HOVER_DOCUMENT_LUA54'
- elseif version == 'LuaJIT' then
- return 'HOVER_DOCUMENT_LUAJIT'
- end
- end
-end
-
-local function buildDescription(lib)
- local desc = lib.description
- if not desc then
- return
- end
- return desc:gsub('%(doc%:(.-)%)', function (tag)
- local fmt = getDocFormater()
- if fmt then
- return '(' .. lang.script(fmt, tag) .. ')'
- end
- end)
-end
-
-local function buildDoc(lib)
- local doc = lib.doc
- if not doc then
- return
- end
- if lib.web then
- return lang.script(lib.web, doc)
- end
- local fmt = getDocFormater()
- if fmt then
- return ('[%s](%s)'):format(lang.script.HOVER_VIEW_DOCUMENTS, lang.script(fmt, 'pdf-' .. doc))
- end
-end
-
-return function (name, lib, object, select)
- local argStr, argLabel, args = buildLibArgs(lib, object, select)
- local returns = buildLibReturns(lib)
- local enum, rawEnum = buildEnum(lib)
- local tip = buildDescription(lib)
- local doc = buildDoc(lib)
- return {
- label = ('function %s(%s)%s'):format(name, argStr, returns),
- name = name,
- argStr = argStr,
- returns = returns,
- description = tip,
- enum = enum,
- rawEnum = rawEnum,
- argLabel = argLabel,
- doc = doc,
- args = args,
- }
-end
diff --git a/script/core/hover/name.lua b/script/core/hover/name.lua
deleted file mode 100644
index 763083b9..00000000
--- a/script/core/hover/name.lua
+++ /dev/null
@@ -1,38 +0,0 @@
-local getName = require 'core.name'
-
-return function (source)
- if not source then
- return ''
- end
- local value = source:bindValue()
- if not value then
- return ''
- end
- local func = value:getFunction()
- local declarat
- if func and func:getSource() then
- declarat = func:getSource().name
- else
- declarat = source
- end
- if not declarat then
- -- 如果声明者没有给名字,则找一个合适的名字
- local names = {}
- value:eachInfo(function (info, src)
- if info.type == 'local' or info.type == 'set' or info.type == 'return' then
- if src.type == 'name' and src.uri == value.uri then
- names[#names+1] = src
- end
- end
- end)
- if #names == 0 then
- return ''
- end
- table.sort(names, function (a, b)
- return a.id < b.id
- end)
- return names[1][1] or ''
- end
-
- return getName(declarat, source)
-end
diff --git a/script/core/implementation.lua b/script/core/implementation.lua
deleted file mode 100644
index f51a97ca..00000000
--- a/script/core/implementation.lua
+++ /dev/null
@@ -1,204 +0,0 @@
-local function parseValueSimily(vm, source, lsp)
- local key = source[1]
- if not key then
- return nil
- end
- local positions = {}
- vm:eachSource(function (other)
- if other == source then
- return
- end
- if other[1] == key
- and not other:bindLocal()
- and other:bindValue()
- and other:action() == 'set'
- and source:bindValue() ~= other:bindValue()
- then
- positions[#positions+1] = {
- other.start,
- other.finish,
- }
- end
- end)
- if #positions == 0 then
- return nil
- end
- return positions
-end
-
-local function parseValueCrossFile(vm, source, lsp)
- local value = source:bindValue()
- local positions = {}
- value:eachInfo(function (info, src)
- if info.type == 'local' and src.uri == value.uri then
- positions[#positions+1] = {
- src.start,
- src.finish,
- value.uri,
- }
- return true
- end
- end)
- if #positions > 0 then
- return positions
- end
-
- value:eachInfo(function (info, src)
- if info.type == 'set' and src.uri == value.uri then
- positions[#positions+1] = {
- src.start,
- src.finish,
- value.uri,
- }
- end
- end)
- if #positions > 0 then
- return positions
- end
-
- value:eachInfo(function (info, src)
- if info.type == 'return' and src.uri == value.uri then
- positions[#positions+1] = {
- src.start,
- src.finish,
- value.uri,
- }
- end
- end)
- if #positions > 0 then
- return positions
- end
-
- local destVM = lsp:getVM(value.uri)
- if not destVM then
- positions[#positions+1] = {
- 0, 0, value.uri,
- }
- return positions
- end
-
- local result = parseValueSimily(destVM, source, lsp)
- if result then
- for _, position in ipairs(result) do
- positions[#positions+1] = position
- position[3] = value.uri
- end
- end
- if #positions > 0 then
- return positions
- end
-
- return positions
-end
-
-local function parseValue(vm, source, lsp)
- local positions = {}
- local mark = {}
-
- local function callback(src)
- if source == src then
- return
- end
- if mark[src] then
- return
- end
- mark[src] = true
- if src.start == 0 then
- return
- end
- local uri = src.uri
- if uri == '' then
- uri = nil
- end
- positions[#positions+1] = {
- src.start,
- src.finish,
- uri,
- }
- end
-
- if source:bindValue() then
- source:bindValue():eachInfo(function (info, src)
- if info.type == 'set' or info.type == 'local' or info.type == 'return' then
- callback(src)
- return true
- end
- end)
- end
- local parent = source:get 'parent'
- if parent then
- parent:eachInfo(function (info, src)
- if info[1] == source[1] then
- if info.type == 'set child' then
- callback(src)
- end
- end
- end)
- end
- if #positions == 0 then
- return nil
- end
- return positions
-end
-
-local function parseLabel(vm, label, lsp)
- local positions = {}
- label:eachInfo(function (info, src)
- if info.type == 'set' then
- positions[#positions+1] = {
- src.start,
- src.finish,
- }
- end
- end)
- if #positions == 0 then
- return nil
- end
- return positions
-end
-
-local function jumpUri(vm, source, lsp)
- local uri = source:get 'target uri'
- local positions = {}
- positions[#positions+1] = {
- 0, 0, uri,
- }
- return positions
-end
-
-local function parseClass(vm, source)
- local className = source:get 'emmy class'
- local positions = {}
- vm.emmyMgr:eachClass(className, function (class)
- local src = class:getSource()
- positions[#positions+1] = {
- src.start,
- src.finish,
- src.uri,
- }
- end)
- return positions
-end
-
-return function (vm, source, lsp)
- if not source then
- return nil
- end
- if source:bindValue() then
- return parseValue(vm, source, lsp)
- or parseValueSimily(vm, source, lsp)
- end
- if source:bindLabel() then
- return parseLabel(vm, source:bindLabel(), lsp)
- end
- if source:get 'target uri' then
- return jumpUri(vm, source, lsp)
- end
- if source:get 'in index' then
- return parseValue(vm, source, lsp)
- or parseValueSimily(vm, source, lsp)
- end
- if source:get 'emmy class' then
- return parseClass(vm, source)
- end
-end
diff --git a/script/core/init.lua b/script/core/init.lua
deleted file mode 100644
index 213dbaca..00000000
--- a/script/core/init.lua
+++ /dev/null
@@ -1,19 +0,0 @@
-local api = {
- definition = require 'core.definition',
- implementation = require 'core.implementation',
- references = require 'core.references',
- rename = require 'core.rename',
- hover = require 'core.hover',
- diagnostics = require 'core.diagnostics',
- findSource = require 'core.find_source',
- findLib = require 'core.find_lib',
- completion = require 'core.completion',
- signature = require 'core.signature',
- documentSymbol = require 'core.document_symbol',
- global = require 'core.global',
- highlight = require 'core.highlight',
- codeAction = require 'core.code_action',
- foldingRange = require 'core.folding_range',
-}
-
-return api
diff --git a/script/core/library.lua b/script/core/library.lua
deleted file mode 100644
index d5edad66..00000000
--- a/script/core/library.lua
+++ /dev/null
@@ -1,296 +0,0 @@
-local lni = require 'lni'
-local fs = require 'bee.filesystem'
-local config = require 'config'
-
-local Library = {}
-
-local function mergeEnum(lib, locale)
- if not lib or not locale then
- return
- end
- local pack = {}
- for _, enum in ipairs(lib) do
- if enum.enum then
- pack[enum.enum] = enum
- end
- if enum.code then
- pack[enum.code] = enum
- end
- end
- for _, enum in ipairs(locale) do
- if pack[enum.enum] then
- if enum.description then
- pack[enum.enum].description = enum.description
- end
- end
- if pack[enum.code] then
- if enum.description then
- pack[enum.code].description = enum.description
- end
- end
- end
-end
-
-local function mergeField(lib, locale)
- if not lib or not locale then
- return
- end
- local pack = {}
- for _, field in ipairs(lib) do
- if field.field then
- pack[field.field] = field
- end
- end
- for _, field in ipairs(locale) do
- if pack[field.field] then
- if field.description then
- pack[field.field].description = field.description
- end
- end
- end
-end
-
-local function mergeLocale(libs, locale)
- if not libs or not locale then
- return
- end
- for name in pairs(locale) do
- if libs[name] then
- if locale[name].description then
- libs[name].description = locale[name].description
- end
- mergeEnum(libs[name].enums, locale[name].enums)
- mergeField(libs[name].fields, locale[name].fields)
- end
- end
-end
-
-local function isMatchVersion(version)
- if not version then
- return true
- end
- local runtimeVersion = config.config.runtime.version
- if type(version) == 'table' then
- for i = 1, #version do
- if version[i] == runtimeVersion then
- return true
- end
- end
- else
- if version == runtimeVersion then
- return true
- end
- end
- return false
-end
-
-local function insertGlobal(tbl, key, value)
- if not isMatchVersion(value.version) then
- return false
- end
- if not value.doc then
- value.doc = key
- end
- tbl[key] = value
- return true
-end
-
-local function insertOther(tbl, key, value)
- if not value.version then
- return
- end
- if not tbl[key] then
- tbl[key] = {}
- end
- if type(value.version) == 'string' then
- tbl[key][#tbl[key]+1] = value.version
- elseif type(value.version) == 'table' then
- for _, version in ipairs(value.version) do
- if type(version) == 'string' then
- tbl[key][#tbl[key]+1] = version
- end
- end
- end
- table.sort(tbl[key])
-end
-
-local function insertCustom(tbl, key, value, libName)
- if not tbl[key] then
- tbl[key] = {}
- end
- tbl[key][#tbl[key]+1] = libName
- table.sort(tbl[key])
-end
-
-local function isEnableGlobal(libName)
- if config.config.runtime.library[libName] then
- return true
- end
- if libName:sub(1, 1) == '@' then
- return true
- end
- return false
-end
-
-local function mergeSource(alllibs, name, lib, libName)
- if not lib.source then
- if isEnableGlobal(libName) then
- local suc = insertGlobal(alllibs.global, name, lib)
- if not suc then
- insertOther(alllibs.other, name, lib)
- end
- else
- insertCustom(alllibs.custom, name, lib, libName)
- end
- return
- end
- for _, source in ipairs(lib.source) do
- local sourceName = source.name or name
- if source.type == 'global' then
- if isEnableGlobal(libName) then
- local suc = insertGlobal(alllibs.global, sourceName, lib)
- if not suc then
- insertOther(alllibs.other, sourceName, lib)
- end
- else
- insertCustom(alllibs.custom, sourceName, lib, libName)
- end
- elseif source.type == 'library' then
- insertGlobal(alllibs.library, sourceName, lib)
- elseif source.type == 'object' then
- insertGlobal(alllibs.object, sourceName, lib)
- end
- end
-end
-
-local function copy(t)
- local new = {}
- for k, v in pairs(t) do
- new[k] = v
- end
- return new
-end
-
-local function insertChild(tbl, name, key, value)
- if not name or not key then
- return
- end
- if not isMatchVersion(value.version) then
- return
- end
- if not value.doc then
- value.doc = ('%s.%s'):format(name, key)
- end
- if not tbl[name] then
- tbl[name] = {
- type = name,
- name = name,
- child = {},
- }
- end
- tbl[name].child[key] = copy(value)
-end
-
-local function mergeParent(alllibs, name, lib, libName)
- for _, parent in ipairs(lib.parent) do
- if parent.type == 'global' then
- if isEnableGlobal(libName) then
- insertChild(alllibs.global, parent.name, name, lib)
- end
- elseif parent.type == 'library' then
- insertChild(alllibs.library, parent.name, name, lib)
- elseif parent.type == 'object' then
- insertChild(alllibs.object, parent.name, name, lib)
- end
- end
-end
-
-local function mergeLibs(alllibs, libs, libName)
- if not libs then
- return
- end
- for _, lib in pairs(libs) do
- if lib.parent then
- mergeParent(alllibs, lib.name, lib, libName)
- else
- mergeSource(alllibs, lib.name, lib, libName)
- end
- end
-end
-
-local function loadLocale(language, relative)
- local localePath = ROOT / 'locale' / language / relative
- local localeBuf = io.load(localePath)
- if localeBuf then
- local locale = table.container()
- xpcall(lni, log.error, localeBuf, localePath:string(), {locale})
- return locale
- end
- return nil
-end
-
-local function fix(libs)
- for name, lib in pairs(libs) do
- lib.name = lib.name or name
- lib.child = {}
- end
-end
-
-local function scan(path)
- local result = {path}
- local i = 0
- return function ()
- i = i + 1
- local current = result[i]
- if not current then
- return nil
- end
- if fs.is_directory(current) then
- for path in current:list_directory() do
- result[#result+1] = path
- end
- end
- return current
- end
-end
-
-local function init()
- local lang = require 'language'
- local id = lang.id
- Library.global = table.container()
- Library.library = table.container()
- Library.object = table.container()
- Library.other = table.container()
- Library.custom = table.container()
-
- for libPath in (ROOT / 'libs'):list_directory() do
- local enableGlobal
- local libName = libPath:filename():string()
- for path in scan(libPath) do
- local libs
- local buf = io.load(path)
- if buf then
- libs = table.container()
- xpcall(lni, log.error, buf, path:string(), {libs})
- fix(libs)
- end
- local relative = fs.relative(path, ROOT)
-
- local locale = loadLocale('en-US', relative)
- mergeLocale(libs, locale)
- if id ~= 'en-US' then
- locale = loadLocale(id, relative)
- mergeLocale(libs, locale)
- end
- mergeLibs(Library, libs, libName)
- end
- end
-end
-
-function Library.reload()
- init()
-end
-
-init()
-
-return Library
diff --git a/script/core/matchKey.lua b/script/core/matchKey.lua
deleted file mode 100644
index b46250cb..00000000
--- a/script/core/matchKey.lua
+++ /dev/null
@@ -1,30 +0,0 @@
-return function (me, other)
- if me == other then
- return true
- end
- if me == '' then
- return true
- end
- if #me > #other then
- return false
- end
- local lMe = me:lower()
- local lOther = other:lower()
- if lMe == lOther:sub(1, #lMe) then
- return true
- end
- local chars = {}
- for i = 1, #lOther do
- local c = lOther:sub(i, i)
- chars[c] = (chars[c] or 0) + 1
- end
- for i = 1, #lMe do
- local c = lMe:sub(i, i)
- if chars[c] and chars[c] > 0 then
- chars[c] = chars[c] - 1
- else
- return false
- end
- end
- return true
-end
diff --git a/script/core/name.lua b/script/core/name.lua
deleted file mode 100644
index 54947974..00000000
--- a/script/core/name.lua
+++ /dev/null
@@ -1,70 +0,0 @@
-return function (source, caller)
- if not source then
- return ''
- end
- local key
- if source:get 'simple' then
- local simple = source:get 'simple'
- local chars = {}
- for i, obj in ipairs(simple) do
- if obj.type == 'name' then
- chars[i] = obj[1]
- elseif obj.type == 'index' then
- chars[i] = '[?]'
- elseif obj.type == 'call' then
- chars[i] = '(?)'
- elseif obj.type == ':' then
- chars[i] = ':'
- elseif obj.type == '.' then
- chars[i] = '.'
- else
- chars[i] = '*' .. obj.type
- end
- if obj == source then
- break
- end
- end
- key = table.concat(chars)
- elseif source.type == 'name' then
- key = source[1]
- elseif source.type == 'string' then
- key = ('%q'):format(source[1])
- elseif source.type == 'number' or source.type == 'boolean' then
- key = tostring(source[1])
- elseif source.type == 'simple' then
- local chars = {}
- for i, obj in ipairs(source) do
- if obj.type == 'name' then
- chars[i] = obj[1]
- elseif obj.type == 'index' then
- chars[i] = '[?]'
- elseif obj.type == 'call' then
- chars[i] = '(?)'
- elseif obj.type == ':' then
- chars[i] = ':'
- elseif obj.type == '.' then
- chars[i] = '.'
- else
- chars[i] = '*' .. obj.type
- end
- end
- -- 这里有个特殊处理
- -- function mt:func() 以 mt.func 的形式调用时
- -- hover 显示为 mt.func(self)
- if caller then
- if chars[#chars-1] == ':' then
- if not caller:get 'object' then
- chars[#chars-1] = '.'
- end
- elseif chars[#chars-1] == '.' then
- if caller:get 'object' then
- chars[#chars-1] = ':'
- end
- end
- end
- key = table.concat(chars)
- else
- key = ''
- end
- return key
-end
diff --git a/script/core/references.lua b/script/core/references.lua
deleted file mode 100644
index 33b38fec..00000000
--- a/script/core/references.lua
+++ /dev/null
@@ -1,91 +0,0 @@
-local findSource = require 'core.find_source'
-
-local function parseResult(vm, source, declarat, callback)
- local isGlobal
- if source:bindLabel() then
- source:bindLabel():eachInfo(function (info, src)
- if (declarat and info.type == 'set') or info.type == 'get' then
- callback(src)
- end
- end)
- end
- if source:bindLocal() then
- local loc = source:bindLocal()
- callback(loc:getSource())
- loc:eachInfo(function (info, src)
- if (declarat and info.type == 'set') or info.type == 'get' then
- callback(src)
- end
- end)
- loc:getValue():eachInfo(function (info, src)
- if (declarat and (info.type == 'set' or info.type == 'local' or info.type == 'return')) or info.type == 'get' then
- callback(src)
- end
- end)
- end
- if source:bindFunction() then
- if declarat then
- callback(source:bindFunction():getSource())
- end
- source:bindFunction():eachInfo(function (info, src)
- if (declarat and (info.type == 'set' or info.type == 'local')) or info.type == 'get' then
- callback(src)
- end
- end)
- end
- if source:bindValue() then
- source:bindValue():eachInfo(function (info, src)
- if (declarat and (info.type == 'set' or info.type == 'local')) or info.type == 'get' then
- callback(src)
- end
- end)
- if source:bindValue():isGlobal() then
- isGlobal = true
- end
- end
- local parent = source:get 'parent'
- if parent then
- parent:eachInfo(function (info, src)
- if info[1] == source[1] then
- if (declarat and info.type == 'set child') or info.type == 'get child' then
- callback(src)
- end
- end
- end)
- end
- --local emmy = source:getEmmy()
- --if emmy then
- -- if emmy.type == 'emmy.class' or emmy.type == 'emmy.type' --then
---
- -- end
- --end
- return isGlobal
-end
-
-return function (vm, pos, declarat)
- local source = findSource(vm, pos)
- if not source then
- return nil
- end
- local positions = {}
- local mark = {}
- local isGlobal = parseResult(vm, source, declarat, function (src)
- if mark[src] then
- return
- end
- mark[src] = true
- if src.start == 0 then
- return
- end
- local uri = src.uri
- if uri == '' then
- uri = nil
- end
- positions[#positions+1] = {
- src.start,
- src.finish,
- uri,
- }
- end)
- return positions, isGlobal
-end
diff --git a/script/core/rename.lua b/script/core/rename.lua
deleted file mode 100644
index 3a2e8532..00000000
--- a/script/core/rename.lua
+++ /dev/null
@@ -1,72 +0,0 @@
-local findSource = require 'core.find_source'
-local parser = require 'parser'
-
-local function parseResult(source, newName)
- local positions = {}
- if source:bindLabel() then
- if not parser:grammar(newName, 'Name') then
- return nil
- end
- source:bindLabel():eachInfo(function (info, src)
- positions[#positions+1] = { src.start, src.finish, src:getUri() }
- end)
- return positions
- end
- if source:bindLocal() then
- local loc = source:bindLocal()
- if loc:get 'hide' then
- return nil
- end
- if source:get 'in index' then
- if not parser:grammar(newName, 'Exp') then
- return positions
- end
- else
- if not parser:grammar(newName, 'Name') then
- return positions
- end
- end
- local mark = {}
- loc:eachInfo(function (info, src)
- if not mark[src] then
- mark[src] = info
- positions[#positions+1] = { src.start, src.finish, src:getUri() }
- end
- end)
- return positions
- end
- if source:bindValue() and source:get 'parent' then
- if source:get 'in index' then
- if not parser:grammar(newName, 'Exp') then
- return positions
- end
- else
- if not parser:grammar(newName, 'Name') then
- return positions
- end
- end
- local parent = source:get 'parent'
- local mark = {}
- parent:eachInfo(function (info, src)
- if not mark[src] then
- mark[src] = info
- if info.type == 'get child' or info.type == 'set child' then
- if info[1] == source[1] then
- positions[#positions+1] = {src.start, src.finish, src:getUri()}
- end
- end
- end
- end)
- return positions
- end
- return nil
-end
-
-return function (vm, pos, newName)
- local source = findSource(vm, pos)
- if not source then
- return nil
- end
- local positions = parseResult(source, newName)
- return positions
-end
diff --git a/script/core/signature.lua b/script/core/signature.lua
deleted file mode 100644
index bbe35ffa..00000000
--- a/script/core/signature.lua
+++ /dev/null
@@ -1,133 +0,0 @@
-local getFunctionHover = require 'core.hover.function'
-local getFunctionHoverAsLib = require 'core.hover.lib_function'
-local getFunctionHoverAsEmmy = require 'core.hover.emmy_function'
-local findLib = require 'core.find_lib'
-local buildValueName = require 'core.hover.name'
-local findSource = require 'core.find_source'
-
-local function findCall(vm, pos)
- local results = {}
- vm:eachSource(function (src)
- if src.type == 'call'
- and src.start <= pos
- and src.finish >= pos
- then
- results[#results+1] = src
- end
- end)
- if #results == 0 then
- return nil
- end
- -- 可能处于 'func1(func2(' 的嵌套中,将最近的call放到最前面
- table.sort(results, function (a, b)
- return a.start > b.start
- end)
- return results
-end
-
-local function getSelect(args, pos)
- if not args then
- return 1
- end
- for i, arg in ipairs(args) do
- if arg.start <= pos and arg.finish >= pos - 1 then
- return i
- end
- end
- return #args + 1
-end
-
-local function getFunctionSource(call)
- local simple = call:get 'simple'
- for i, source in ipairs(simple) do
- if source == call then
- return simple[i-1]
- end
- end
- return nil
-end
-
-local function getHover(call, pos)
- local args = call:bindCall()
- if not args then
- return nil
- end
-
- local value = call:findCallFunction()
- if not value then
- return nil
- end
-
- local select = getSelect(args, pos)
- local source = getFunctionSource(call)
- local object = source:get 'object'
- local lib, fullkey = findLib(source)
- local name = fullkey or buildValueName(source)
- local hover
- if lib then
- hover = getFunctionHoverAsLib(name, lib, object, select)
- else
- local emmy = value:getEmmy()
- if emmy and emmy.type == 'emmy.functionType' then
- hover = getFunctionHoverAsEmmy(name, emmy, object, select)
- else
- ---@type emmyFunction
- local func = value:getFunction()
- hover = getFunctionHover(name, func, object, select)
- local overLoads = func and func:getEmmyOverLoads()
- if overLoads then
- for _, ol in ipairs(overLoads) do
- hover = getFunctionHoverAsEmmy(name, ol, object, select)
- end
- end
- end
- end
- return hover
-end
-
-local function isInFunctionOrTable(call, pos)
- local args = call:bindCall()
- if not args then
- return false
- end
- local select = getSelect(args, pos)
- local arg = args[select]
- if not arg then
- return false
- end
- if arg.type == 'function' or arg.type == 'table' then
- return true
- end
- return false
-end
-
-return function (vm, pos)
- local source = findSource(vm, pos) or findSource(vm, pos-1)
- if not source or source.type == 'string' then
- return
- end
- local calls = findCall(vm, pos)
- if not calls or #calls == 0 then
- return nil
- end
-
- local nearCall = calls[1]
- if isInFunctionOrTable(nearCall, pos) then
- return nil
- end
-
- local hover = getHover(nearCall, pos)
- if not hover then
- return nil
- end
-
- -- skip `name(`
- local head = #hover.name + 1
- hover.label = ('%s(%s)'):format(hover.name, hover.argStr)
- if hover.argLabel then
- hover.argLabel[1] = hover.argLabel[1] + head
- hover.argLabel[2] = hover.argLabel[2] + head
- end
-
- return { hover }
-end
diff --git a/script/core/snippet.lua b/script/core/snippet.lua
deleted file mode 100644
index 7532ce9b..00000000
--- a/script/core/snippet.lua
+++ /dev/null
@@ -1,64 +0,0 @@
-local snippet = {}
-
-local function add(cate, key, label)
- return function (text)
- if not snippet[cate] then
- snippet[cate] = {}
- end
- if not snippet[cate][key] then
- snippet[cate][key] = {}
- end
- snippet[cate][key][#snippet[cate][key]+1] = {
- label = label,
- text = text,
- }
- end
-end
-
-add('key', 'do', 'do .. end') [[
-do
- $0
-end]]
-
-add('key', 'elseif', 'elseif .. then')
-[[elseif ${1:true} then]]
-
-add('key', 'for', 'for .. in') [[
-for ${1:key, value} in ${2:pairs(t)} do
- $0
-end]]
-
-add('key', 'for', 'for i = ..') [[
-for ${1:i} = ${2:1}, ${3:10, 2} do
- $0
-end]]
-
-add('key', 'function', 'function ()') [[
-function $1(${2:arg1, arg2, arg3})
- $0
-end]]
-
-add('key', 'local', 'local function') [[
-local function ${1:name}(${2:arg1, arg2, arg3})
- $0
-end]]
-
-add('key', 'if', 'if .. then') [[
-if ${1:true} then
- $0
-end]]
-
-add('key', 'repeat', 'repeat .. until') [[
-repeat
- $0
-until ${1:true}]]
-
-add('key', 'while', 'while .. do') [[
-while ${1:true} do
- $0
-end]]
-
-add('key', 'return', 'do return end')
-[[do return ${1:true} end]]
-
-return snippet
diff --git a/script/emmy/alias.lua b/script/emmy/alias.lua
deleted file mode 100644
index be47cb4f..00000000
--- a/script/emmy/alias.lua
+++ /dev/null
@@ -1,42 +0,0 @@
-local listMgr = require 'vm.list'
-
----@class EmmyAlias
-local mt = {}
-mt.__index = mt
-mt.type = 'emmy.alias'
-
-function mt:getName()
- return self.name
-end
-
-function mt:getSource()
- return listMgr.get(self.source)
-end
-
-function mt:bindType(type)
- if type then
- self._bindType = type
- else
- return self._bindType
- end
-end
-
-function mt:addEnum(enum)
- self._enum[#self._enum+1] = enum
-end
-
-function mt:eachEnum(callback)
- for _, enum in ipairs(self._enum) do
- callback(enum)
- end
-end
-
-return function (manager, source)
- local self = setmetatable({
- name = source[1][1],
- source = source.id,
- _manager = manager,
- _enum = {},
- }, mt)
- return self
-end
diff --git a/script/emmy/arrayType.lua b/script/emmy/arrayType.lua
deleted file mode 100644
index c5d73500..00000000
--- a/script/emmy/arrayType.lua
+++ /dev/null
@@ -1,37 +0,0 @@
-local listMgr = require 'vm.list'
-
----@class EmmyArrayType
-local mt = {}
-mt.__index = mt
-mt.type = 'emmy.arrayType'
-
-function mt:getType()
- return 'table'
-end
-
-function mt:getName()
- return self.name
-end
-
-function mt:getSource()
- return listMgr.get(self.source)
-end
-
-function mt:setValue(value)
- self.value = value
- self._child:setValue(value)
-end
-
-function mt:getValue()
- return self.value
-end
-
-return function (manager, source, child)
- local self = setmetatable({
- name = child:getName(),
- source = source.id,
- _child = child,
- _manager = manager,
- }, mt)
- return self
-end
diff --git a/script/emmy/class.lua b/script/emmy/class.lua
deleted file mode 100644
index 6728e2c4..00000000
--- a/script/emmy/class.lua
+++ /dev/null
@@ -1,61 +0,0 @@
-local listMgr = require 'vm.list'
-
----@class EmmyClass
-local mt = {}
-mt.__index = mt
-mt.type = 'emmy.class'
-
-function mt:getType()
- return self.name
-end
-
-function mt:getName()
- return self.name
-end
-
-function mt:getSource()
- return listMgr.get(self.source)
-end
-
-function mt:setValue(value)
- self.value = value
-end
-
-function mt:getValue()
- return self.value
-end
-
-function mt:eachChild(callback)
- self._manager:eachClass(self.name, function (obj)
- if obj.type == 'emmy.typeUnit' then
- callback(obj)
- end
- end)
-end
-
-function mt:addField(field)
- if not self._fields then
- self._fields = {}
- end
- self._fields[#self._fields+1] = field
-end
-
-function mt:eachField(callback)
- if not self._fields then
- return
- end
- ---@param field EmmyField
- for _, field in ipairs(self._fields) do
- callback(field)
- end
-end
-
-return function (manager, name, extends, source)
- local self = setmetatable({
- name = name,
- source = source.id,
- extends = extends,
- _manager = manager,
- }, mt)
- return self
-end
diff --git a/script/emmy/field.lua b/script/emmy/field.lua
deleted file mode 100644
index f9e9cbf6..00000000
--- a/script/emmy/field.lua
+++ /dev/null
@@ -1,45 +0,0 @@
-local listMgr = require 'vm.list'
-
----@class EmmyField
-local mt = {}
-mt.__index = mt
-mt.type = 'emmy.field'
-
-function mt:getName()
- return self.name
-end
-
-function mt:getSource()
- return listMgr.get(self.source)
-end
-
-function mt:bindType(type)
- if type then
- self._bindType = type
- else
- return self._bindType
- end
-end
-
-function mt:bindValue(value)
- if value then
- self._bindValue = value
- else
- if self._bindValue then
- if not self._bindValue:getSource() then
- self._bindValue = nil
- end
- end
- return self._bindValue
- end
-end
-
-return function (manager, source)
- local self = setmetatable({
- name = source[2][1],
- source = source.id,
- visible = source[1],
- _manager = manager,
- }, mt)
- return self
-end
diff --git a/script/emmy/funcType.lua b/script/emmy/funcType.lua
deleted file mode 100644
index 2c073fe2..00000000
--- a/script/emmy/funcType.lua
+++ /dev/null
@@ -1,64 +0,0 @@
-local listMgr = require 'vm.list'
-
----@class EmmyFunctionType
-local mt = {}
-mt.__index = mt
-mt.type = 'emmy.functionType'
-
-function mt:getType()
- return 'function'
-end
-
-function mt:getName()
- return 'function'
-end
-
-function mt:getSource()
- return listMgr.get(self.source)
-end
-
-function mt:setValue(value)
- self.value = value
-end
-
-function mt:getValue()
- return self.value
-end
-
-function mt:addParam(name, type)
- self._params[#self._params+1] = { name, type }
-end
-
-function mt:addReturn(type)
- self._returns[#self._returns+1] = type
-end
-
-function mt:eachParam(callback)
- for _, data in ipairs(self._params) do
- callback(data[1], data[2])
- end
-end
-
-function mt:eachReturn(callback)
- for _, rtn in ipairs(self._returns) do
- callback(rtn)
- end
-end
-
-function mt:bindFunction(func)
- if func then
- self._bindFunction = func
- else
- return self._bindFunction
- end
-end
-
-return function (manager, source)
- local self = setmetatable({
- source = source.id,
- _params = {},
- _returns = {},
- _manager = manager,
- }, mt)
- return self
-end
diff --git a/script/emmy/generic.lua b/script/emmy/generic.lua
deleted file mode 100644
index 82fd0c4b..00000000
--- a/script/emmy/generic.lua
+++ /dev/null
@@ -1,27 +0,0 @@
-local listMgr = require 'vm.list'
-
----@class EmmyGeneric
-local mt = {}
-mt.__index = mt
-mt.type = 'emmy.generic'
-
-function mt:getName()
- return self.name:getName()
-end
-
-function mt:setValue(value)
- self._value = value
-end
-
-function mt:getValue()
- return self._value
-end
-
-return function (manager, defs)
- for _, def in ipairs(defs) do
- setmetatable(def, mt)
- def._manager = manager
- def._binds = {}
- end
- return defs
-end
diff --git a/script/emmy/init.lua b/script/emmy/init.lua
deleted file mode 100644
index 769c9e05..00000000
--- a/script/emmy/init.lua
+++ /dev/null
@@ -1 +0,0 @@
-return require 'vm.emmy.emmy'
diff --git a/script/emmy/manager.lua b/script/emmy/manager.lua
deleted file mode 100644
index c80e05df..00000000
--- a/script/emmy/manager.lua
+++ /dev/null
@@ -1,221 +0,0 @@
-local listMgr = require 'vm.list'
-local sourceMgr = require 'vm.source'
-local newClass = require 'emmy.class'
-local newType = require 'emmy.type'
-local newTypeUnit = require 'emmy.typeUnit'
-local newAlias = require 'emmy.alias'
-local newParam = require 'emmy.param'
-local newReturn = require 'emmy.return'
-local newField = require 'emmy.field'
-local newGeneric = require 'emmy.generic'
-local newArrayType = require 'emmy.arrayType'
-local newTableType = require 'emmy.tableType'
-local newFuncType = require 'emmy.funcType'
-
-local mt = {}
-mt.__index = mt
-mt.__name = 'emmyMgr'
-
-function mt:flushClass(name)
- local list = self._class[name]
- if not list then
- return
- end
- local version = listMgr.getVersion()
- if version == list.version then
- return
- end
- for srcId in pairs(list) do
- if not listMgr.get(srcId) then
- list[srcId] = nil
- end
- end
- if not next(list) then
- self._class[name] = nil
- return
- end
- list.version = version
-end
-
-function mt:eachClassByName(name, callback)
- self:flushClass(name)
- local list = self._class[name]
- if not list then
- return
- end
- for k, class in pairs(list) do
- if k ~= 'version' then
- local res = callback(class)
- if res ~= nil then
- return res
- end
- end
- end
-end
-
-function mt:eachClass(...)
- local n = select('#', ...)
- if n == 1 then
- local callback = ...
- for name in pairs(self._class) do
- local res = self:eachClassByName(name, callback)
- if res ~= nil then
- return res
- end
- end
- else
- local name, callback = ...
- return self:eachClassByName(name, callback)
- end
-end
-
-function mt:getClass(name)
- self:flushClass(name)
- local list = self._class[name]
- local version = listMgr.getVersion()
- if not list then
- list = {
- version = version,
- }
- self._class[name] = list
- end
- return list
-end
-
-function mt:newClass(name, extends, source)
- local list = self:getClass(name)
- list[source.id] = newClass(self, name, extends, source)
- return list[source.id]
-end
-
-function mt:addClass(source)
- local className = source[1][1]
- local extends = source[2] and source[2][1]
- local class = self:newClass(className, extends, source)
- return class
-end
-
-function mt:addType(source)
- local typeObj = newType(self, source)
- for i, obj in ipairs(source) do
- local typeUnit = newTypeUnit(self, obj)
- local className = obj[1]
- if className then
- local list = self:getClass(className)
- list[source.id] = typeUnit
- end
- typeUnit:setParent(typeObj)
- typeObj._childs[i] = typeUnit
- obj:set('emmy.typeUnit', typeUnit)
- end
- return typeObj
-end
-
-function mt:addArrayType(source)
- local typeObj = self:addType(source)
- local arrayTypeObj = newArrayType(self, source, typeObj)
- return arrayTypeObj
-end
-
-function mt:addTableType(source, keyType, valueType)
- local typeObj = newTableType(self, source, keyType, valueType)
- return typeObj
-end
-
-function mt:addFunctionType(source)
- local typeObj = newFuncType(self, source)
- return typeObj
-end
-
-function mt:addAlias(source, typeObj)
- local aliasName = source[1][1]
- local aliasObj = newAlias(self, source)
- aliasObj:bindType(typeObj)
- local list = self:getClass(aliasName)
- list[source.id] = aliasObj
- for i = 3, #source do
- aliasObj:addEnum(source[i])
- end
- return aliasObj
-end
-
-function mt:addParam(source, bind)
- local paramObj = newParam(self, source)
- if bind.type == 'emmy.generic' then
- paramObj:bindGeneric(bind)
- else
- paramObj:bindType(bind)
- self:eachClass(bind:getType(), function (class)
- if class.type == 'emmy.alias' then
- class:eachEnum(function (enum)
- paramObj:addEnum(enum)
- end)
- end
- end)
- end
- for i = 3, #source do
- paramObj:addEnum(source[i])
- end
- paramObj:setOption(source.option)
- return paramObj
-end
-
-function mt:addReturn(source, bind, name)
- local returnObj = newReturn(self, source, name)
- if bind then
- if bind.type == 'emmy.generic' then
- returnObj:bindGeneric(bind)
- else
- returnObj:bindType(bind)
- end
- end
- return returnObj
-end
-
-function mt:addField(source, typeObj, value)
- local fieldObj = newField(self, source)
- fieldObj:bindType(typeObj)
- fieldObj:bindValue(value)
- return fieldObj
-end
-
-function mt:addGeneric(defs)
- local genericObj = newGeneric(self, defs)
- return genericObj
-end
-
-function mt:remove()
-end
-
-function mt:count()
- local count = 0
- for _, list in pairs(self._class) do
- for k in pairs(list) do
- if k ~= 'version' then
- count = count + 1
- end
- end
- end
- return count
-end
-
-return function ()
- ---@class emmyMgr
- local self = setmetatable({
- _class = {},
- }, mt)
-
- local source = sourceMgr.dummy()
- self:newClass('any', nil, source)
- self:newClass('string', 'any', source)
- self:newClass('number', 'any', source)
- self:newClass('integer', 'number', source)
- self:newClass('boolean', 'any', source)
- self:newClass('table', 'any', source)
- self:newClass('function', 'any', source)
- self:newClass('nil', 'any', source)
- self:newClass('userdata', 'any', source)
- self:newClass('thread', 'any', source)
-
- return self
-end
diff --git a/script/emmy/param.lua b/script/emmy/param.lua
deleted file mode 100644
index 12ac0633..00000000
--- a/script/emmy/param.lua
+++ /dev/null
@@ -1,70 +0,0 @@
-local listMgr = require 'vm.list'
-
----@class EmmyParam
-local mt = {}
-mt.__index = mt
-mt.type = 'emmy.param'
-
-function mt:getName()
- return self.name
-end
-
-function mt:getType()
- if self._bindType then
- return self._bindType:getType()
- else
- return 'any'
- end
-end
-
-function mt:getSource()
- return listMgr.get(self.source)
-end
-
-function mt:bindType(type)
- if type then
- self._bindType = type
- else
- return self._bindType
- end
-end
-
-function mt:bindGeneric(generic)
- if generic then
- self._bindGeneric = generic
- else
- return self._bindGeneric
- end
-end
-
-function mt:addEnum(enum)
- self._enum[#self._enum+1] = enum
-end
-
-function mt:eachEnum(callback)
- for _, enum in ipairs(self._enum) do
- callback(enum)
- end
-end
-
-function mt:setOption(option)
- self._option = option
-end
-
-function mt:getOption()
- return self._option
-end
-
-return function (manager, source)
- local self = setmetatable({
- source = source.id,
- _manager = manager,
- _enum = {},
- }, mt)
- if source.type == 'emmyParam' then
- self.name = source[1][1]
- elseif source.type == 'emmyVararg' then
- self.name = '...'
- end
- return self
-end
diff --git a/script/emmy/return.lua b/script/emmy/return.lua
deleted file mode 100644
index 1fc97923..00000000
--- a/script/emmy/return.lua
+++ /dev/null
@@ -1,36 +0,0 @@
-local listMgr = require 'vm.list'
-
----@class EmmyReturn
-local mt = {}
-mt.__index = mt
-mt.type = 'emmy.return'
-
-function mt:getSource()
- return listMgr.get(self.source)
-end
-
-function mt:bindType(type)
- if type then
- self._bindType = type
- else
- return self._bindType
- end
-end
-
-function mt:bindGeneric(generic)
- if generic then
- self._bindGeneric = generic
- else
- return self._bindGeneric
- end
-end
-
-return function (manager, source, name)
- local self = setmetatable({
- source = source.id,
- name = name and name[1],
- option = source.option,
- _manager = manager,
- }, mt)
- return self
-end
diff --git a/script/emmy/tableType.lua b/script/emmy/tableType.lua
deleted file mode 100644
index 3882cd61..00000000
--- a/script/emmy/tableType.lua
+++ /dev/null
@@ -1,40 +0,0 @@
-local listMgr = require 'vm.list'
-
----@class EmmyTableType
-local mt = {}
-mt.__index = mt
-mt.type = 'emmy.tableType'
-
-function mt:getType()
- return 'table'
-end
-
-function mt:getKeyType()
- return self.keyType
-end
-
-function mt:getValueType()
- return self.valueType
-end
-
-function mt:getSource()
- return listMgr.get(self.source)
-end
-
-function mt:setValue(value)
- self.value = value
-end
-
-function mt:getValue()
- return self.value
-end
-
-return function (manager, source, keyType, valueType)
- local self = setmetatable({
- source = source.id,
- keyType = keyType,
- valueType = valueType,
- _manager = manager,
- }, mt)
- return self
-end
diff --git a/script/emmy/type.lua b/script/emmy/type.lua
deleted file mode 100644
index 8977cf2d..00000000
--- a/script/emmy/type.lua
+++ /dev/null
@@ -1,59 +0,0 @@
-local listMgr = require 'vm.list'
-
-local function buildName(source)
- local names = {}
- for i, type in ipairs(source) do
- if type.type == 'emmyName' then
- names[i] = type[1]
- elseif type.type == 'emmyArrayType' then
- names[i] = type[1][1]..'[]'
- end
- end
- return table.concat(names, '|')
-end
-
----@class EmmyType
-local mt = {}
-mt.__index = mt
-mt.type = 'emmy.type'
-
-function mt:getType()
- return self.name
-end
-
-function mt:getName()
- return self.name
-end
-
-function mt:getSource()
- return listMgr.get(self.source)
-end
-
-function mt:eachClass(callback)
- for _, typeUnit in ipairs(self._childs) do
- ---@type EmmyTypeUnit
- local emmyTypeUnit = typeUnit
- emmyTypeUnit:getClass(callback)
- end
-end
-
-function mt:setValue(value)
- self.value = value
- for _, typeUnit in ipairs(self._childs) do
- typeUnit:setValue(value)
- end
-end
-
-function mt:getValue()
- return self.value
-end
-
-return function (manager, source)
- local self = setmetatable({
- name = buildName(source),
- source = source.id,
- _manager = manager,
- _childs = {},
- }, mt)
- return self
-end
diff --git a/script/emmy/typeUnit.lua b/script/emmy/typeUnit.lua
deleted file mode 100644
index 73d7ea6b..00000000
--- a/script/emmy/typeUnit.lua
+++ /dev/null
@@ -1,51 +0,0 @@
-local listMgr = require 'vm.list'
-
----@class EmmyTypeUnit
-local mt = {}
-mt.__index = mt
-mt.type = 'emmy.typeUnit'
-
-function mt:getType()
- return self.name
-end
-
-function mt:getName()
- return self.name
-end
-
-function mt:getSource()
- return listMgr.get(self.source)
-end
-
-function mt:getClass(callback)
- self._manager:eachClass(self:getName(), function (class)
- if class.type == 'emmy.class' then
- callback(class)
- end
- end)
-end
-
-function mt:setValue(value)
- self.value = value
-end
-
-function mt:getValue()
- return self.value
-end
-
-function mt:setParent(parent)
- self.parent = parent
-end
-
-function mt:getParent()
- return self.parent
-end
-
-return function (manager, source)
- local self = setmetatable({
- name = source[1],
- source = source.id,
- _manager = manager,
- }, mt)
- return self
-end
diff --git a/script/file-uri.lua b/script/file-uri.lua
deleted file mode 100644
index ba44f2e7..00000000
--- a/script/file-uri.lua
+++ /dev/null
@@ -1,89 +0,0 @@
-local platform = require 'bee.platform'
-
-local escPatt = '[^%w%-%.%_%~%/]'
-
-local function esc(c)
- return ('%%%02X'):format(c:byte())
-end
-
-local function normalize(str)
- return str:gsub('%%(%x%x)', function (n)
- return string.char(tonumber(n, 16))
- end)
-end
-
-local m = {}
-
--- c:\my\files --> file:///c%3A/my/files
--- /usr/home --> file:///usr/home
--- \\server\share\some\path --> file://server/share/some/path
-
---- path -> uri
----@param path string
----@return string uri
-function m.encode(path)
- local authority = ''
- if platform.OS == 'Windows' then
- path = path:gsub('\\', '/')
- end
-
- if path:sub(1, 2) == '//' then
- local idx = path:find('/', 3)
- if idx then
- authority = path:sub(3, idx)
- path = path:sub(idx + 1)
- if path == '' then
- path = '/'
- end
- else
- authority = path:sub(3)
- path = '/'
- end
- end
-
- if path:sub(1, 1) ~= '/' then
- path = '/' .. path
- end
-
- -- lower-case windows drive letters in /C:/fff or C:/fff
- local start, finish, drive = path:find '/(%u):'
- if drive then
- path = path:sub(1, start) .. drive:lower() .. path:sub(finish, -1)
- end
-
- local uri = 'file://'
- .. authority:gsub(escPatt, esc)
- .. path:gsub(escPatt, esc)
- return uri
-end
-
--- file:///c%3A/my/files --> c:\my\files
--- file:///usr/home --> /usr/home
--- file://server/share/some/path --> \\server\share\some\path
-
---- uri -> path
----@param uri string
----@return string path
-function m.decode(uri)
- local scheme, authority, path = uri:match('([^:]*):?/?/?([^/]*)(.*)')
- if not scheme then
- return ''
- end
- scheme = normalize(scheme)
- authority = normalize(authority)
- path = normalize(path)
- local value
- if scheme == 'file' and #authority > 0 and #path > 1 then
- value = '//' .. authority .. path
- elseif path:match '/%a:' then
- value = path:sub(2, 2):lower() .. path:sub(3)
- else
- value = path
- end
- if platform.OS == 'Windows' then
- value = value:gsub('/', '\\')
- end
- return value
-end
-
-return m
diff --git a/script/filename.lua b/script/filename.lua
deleted file mode 100644
index a28b004c..00000000
--- a/script/filename.lua
+++ /dev/null
@@ -1,65 +0,0 @@
-local platform = require 'bee.platform'
-local config = require 'config'
-local m = {}
-
-local TrueName = {}
-
-function m.getFileName(path)
- local name = path:string()
- if platform.OS == 'Windows' then
- local lname = name:lower()
- TrueName[lname] = name
- return lname
- else
- return name
- end
-end
-
-function m.getTrueName(name)
- return TrueName[name] or name
-end
-
-local function split(str, sep)
- local t = {}
- for s in str:gmatch('[^' .. sep .. ']+') do
- t[#t+1] = s
- end
- return t
-end
-
-function m.similarity(a, b)
- local ta = split(a, '/\\')
- local tb = split(b, '/\\')
- for i = 1, #ta do
- if ta[i] ~= tb[i] then
- return i - 1
- end
- end
- return #ta
-end
-
-function m.isLuaFile(path)
- local pathStr = path:string()
- for k, v in pairs(config.other.associations) do
- if v == 'lua' then
- k = k:gsub('^%*', '')
- if m.fileNameEq(pathStr:sub(-#k), k) then
- return true
- end
- end
- end
- if m.fileNameEq(pathStr:sub(-4), '.lua') then
- return true
- end
- return false
-end
-
-function m.fileNameEq(a, b)
- if platform.OS == 'Windows' then
- return a:lower() == b:lower()
- else
- return a == b
- end
-end
-
-return m
diff --git a/script/files/file.lua b/script/files/file.lua
deleted file mode 100644
index 81aabba5..00000000
--- a/script/files/file.lua
+++ /dev/null
@@ -1,158 +0,0 @@
----@class file
-local mt = {}
-mt.__index = mt
-mt.type = 'file'
-mt._uri = ''
-mt._oldText = ''
-mt._text = ''
-mt._version = -1
-mt._vmCost = 0.0
-mt._lineCost = 0.0
-
----@param buf string
-function mt:setText(buf)
- self._oldText = self._text
- self._text = buf
-end
-
----@return string
-function mt:getText()
- return self._text
-end
-
----@return string
-function mt:getOldText()
- return self._oldText
-end
-
-function mt:clearOldText()
- self._oldText = nil
-end
-
----@param version integer
-function mt:setVersion(version)
- self._version = version
-end
-
----@return integer
-function mt:getVersion()
- return self._version
-end
-
-function mt:remove()
- if self._removed then
- return
- end
- self._removed = true
- self._text = nil
- self._version = nil
- if self._vm then
- self._vm:remove()
- end
-end
-
----@return boolean
-function mt:isRemoved()
- return self._removed == true
-end
-
----@param vm VM
----@param version integer
----@param cost number
-function mt:saveVM(vm, version, cost)
- if self._vm then
- self._vm:remove()
- end
- self._vm = vm
- if vm then
- vm:setVersion(version)
- end
- self._vmCost = cost
-end
-
----@return VM
-function mt:getVM()
- return self._vm
-end
-
----@return number
-function mt:getVMCost()
- return self._vmCost
-end
-
-function mt:removeVM()
- if not self._vm then
- return
- end
- self._vm:remove()
- self._vm = nil
-end
-
----@param lines table
----@param cost number
-function mt:saveLines(lines, cost)
- self._lines = lines
- self._lineCost = cost
-end
-
----@return table
-function mt:getLines()
- return self._lines
-end
-
-function mt:getComments()
- return self.comments
-end
-
----@return file
-function mt:getParent()
- return self._parent
-end
-
----@param uri uri
-function mt:addChild(uri)
- self._child[uri] = true
-end
-
----@param uri uri
-function mt:removeChild(uri)
- self._child[uri] = nil
-end
-
----@param uri uri
-function mt:addParent(uri)
- self._parent[uri] = true
-end
-
----@param uri uri
-function mt:removeParent(uri)
- self._parent[uri] = nil
-end
-
-function mt:eachChild()
- return pairs(self._child)
-end
-
-function mt:eachParent()
- return pairs(self._parent)
-end
-
----@param err table
-function mt:setAstErr(err)
- self._astErr = err
-end
-
----@return table
-function mt:getAstErr()
- return self._astErr
-end
-
----@param uri string
-return function (uri)
- local self = setmetatable({
- _uri = uri,
- _parent = {},
- _child = {},
- }, mt)
- return self
-end
diff --git a/script/files/files.lua b/script/files/files.lua
deleted file mode 100644
index 88ff7444..00000000
--- a/script/files/files.lua
+++ /dev/null
@@ -1,115 +0,0 @@
-local file = require 'files.file'
-
----@class files
-local mt = {}
-mt.__index = mt
-mt.type = 'files'
-mt._fileCount = 0
----@type table<uri, file>
-mt._files = nil
-
----@param uri uri
----@param text string
-function mt:save(uri, text, version)
- local f = self._files[uri]
- if not f then
- f = file(uri)
- self._files[uri] = f
- self._fileCount = self._fileCount + 1
- end
- f:setText(text)
- f:setVersion(version)
-end
-
----@param uri uri
-function mt:remove(uri)
- local f = self._files[uri]
- if not f then
- return
- end
-
- f:remove()
- self._files[uri] = nil
- self._fileCount = self._fileCount - 1
-end
-
----@param uri uri
-function mt:open(uri, text)
- self._open[uri] = text
-end
-
----@param uri uri
-function mt:close(uri)
- self._open[uri] = nil
-end
-
----@param uri uri
----@return boolean
-function mt:isOpen(uri)
- return self._open[uri] ~= nil
-end
-
----@param uri uri
-function mt:setLibrary(uri)
- self._library[uri] = true
-end
-
----@param uri uri
----@return uri
-function mt:isLibrary(uri)
- return self._library[uri] == true
-end
-
----@param uri uri
-function mt:isDead(uri)
- local f = self._files[uri]
- if not f then
- return true
- end
- if f:isRemoved() then
- return true
- end
- return f:getVersion() == -1
-end
-
----@param uri uri
----@return file
-function mt:get(uri)
- return self._files[uri]
-end
-
-function mt:clear()
- for _, f in pairs(self._files) do
- f:remove()
- end
- self._files = {}
- self._library = {}
- self._fileCount = nil
-end
-
-function mt:clearVM()
- for _, f in pairs(self._files) do
- f:removeVM()
- end
-end
-
-function mt:eachFile()
- return pairs(self._files)
-end
-
-function mt:eachOpened()
- return pairs(self._open)
-end
-
-function mt:count()
- return self._fileCount
-end
-
-return function ()
- local self = setmetatable({
- _files = {},
- _open = {},
- _library = {},
- }, mt)
- return self
-end
diff --git a/script/files/init.lua b/script/files/init.lua
deleted file mode 100644
index e090874d..00000000
--- a/script/files/init.lua
+++ /dev/null
@@ -1 +0,0 @@
-return require 'files.files'
diff --git a/script/glob/gitignore.lua b/script/glob/gitignore.lua
deleted file mode 100644
index ddd50fff..00000000
--- a/script/glob/gitignore.lua
+++ /dev/null
@@ -1,226 +0,0 @@
-local m = require 'lpeglabel'
-local matcher = require 'glob.matcher'
-
-local function prop(name, pat)
- return m.Cg(m.Cc(true), name) * pat
-end
-
-local function object(type, pat)
- return m.Ct(
- m.Cg(m.Cc(type), 'type') *
- m.Cg(pat, 'value')
- )
-end
-
-local function expect(p, err)
- return p + m.T(err)
-end
-
-local parser = m.P {
- 'Main',
- ['Sp'] = m.S(' \t')^0,
- ['Slash'] = m.S('/')^1,
- ['Main'] = m.Ct(m.V'Sp' * m.P'{' * m.V'Pattern' * (',' * expect(m.V'Pattern', 'Miss exp after ","'))^0 * m.P'}')
- + m.Ct(m.V'Pattern')
- + m.T'Main Failed'
- ,
- ['Pattern'] = m.Ct(m.V'Sp' * prop('neg', m.P'!') * expect(m.V'Unit', 'Miss exp after "!"'))
- + m.Ct(m.V'Unit')
- ,
- ['NeedRoot'] = prop('root', (m.P'.' * m.V'Slash' + m.V'Slash')),
- ['Unit'] = m.V'Sp' * m.V'NeedRoot'^-1 * expect(m.V'Exp', 'Miss exp') * m.V'Sp',
- ['Exp'] = m.V'Sp' * (m.V'FSymbol' + object('/', m.V'Slash') + m.V'Word')^0 * m.V'Sp',
- ['Word'] = object('word', m.Ct((m.V'CSymbol' + m.V'Char' - m.V'FSymbol')^1)),
- ['CSymbol'] = object('*', m.P'*')
- + object('?', m.P'?')
- + object('[]', m.V'Range')
- ,
- ['SimpleChar'] = m.P(1) - m.S',{}[]*?/',
- ['EscChar'] = m.P'\\' / '' * m.P(1),
- ['Char'] = object('char', m.Cs((m.V'EscChar' + m.V'SimpleChar')^1)),
- ['FSymbol'] = object('**', m.P'**'),
- ['Range'] = m.P'[' * m.Ct(m.V'RangeUnit'^0) * m.P']'^-1,
- ['RangeUnit'] = m.Ct(- m.P']' * m.C(m.P(1)) * (m.P'-' * - m.P']' * m.C(m.P(1)))^-1),
-}
-
----@class gitignore
-local mt = {}
-mt.__index = mt
-mt.__name = 'gitignore'
-
-function mt:addPattern(pat)
- if type(pat) ~= 'string' then
- return
- end
- self.pattern[#self.pattern+1] = pat
- if self.options.ignoreCase then
- pat = pat:lower()
- end
- local states, err = parser:match(pat)
- if not states then
- self.errors[#self.errors+1] = {
- pattern = pat,
- message = err
- }
- return
- end
- for _, state in ipairs(states) do
- self.matcher[#self.matcher+1] = matcher(state)
- end
-end
-
-function mt:setOption(op, val)
- if val == nil then
- val = true
- end
- self.options[op] = val
-end
-
----@param key string | "'type'" | "'list'"
----@param func function | "function (path) end"
-function mt:setInterface(key, func)
- if type(func) ~= 'function' then
- return
- end
- self.interface[key] = func
-end
-
-function mt:callInterface(name, ...)
- local func = self.interface[name]
- return func(...)
-end
-
-function mt:hasInterface(name)
- return self.interface[name] ~= nil
-end
-
-function mt:checkDirectory(catch, path, matcher)
- if not self:hasInterface 'type' then
- return true
- end
- if not matcher:isNeedDirectory() then
- return true
- end
- if #catch < #path then
- -- if path is 'a/b/c' and catch is 'a/b'
- -- then the catch must be a directory
- return true
- else
- return self:callInterface('type', path) == 'directory'
- end
-end
-
-function mt:simpleMatch(path)
- for i = #self.matcher, 1, -1 do
- local matcher = self.matcher[i]
- local catch = matcher(path)
- if catch and self:checkDirectory(catch, path, matcher) then
- if matcher:isNegative() then
- return false
- else
- return true
- end
- end
- end
- return nil
-end
-
-function mt:finishMatch(path)
- local paths = {}
- for filename in path:gmatch '[^/\\]+' do
- paths[#paths+1] = filename
- end
- for i = 1, #paths do
- local newPath = table.concat(paths, '/', 1, i)
- local passed = self:simpleMatch(newPath)
- if passed == true then
- return true
- elseif passed == false then
- return false
- end
- end
- return false
-end
-
-function mt:scan(callback)
- local files = {}
- if type(callback) ~= 'function' then
- callback = nil
- end
- local list = {}
- local result = self:callInterface('list', '')
- if type(result) ~= 'table' then
- return files
- end
- for _, path in ipairs(result) do
- list[#list+1] = path:match '([^/\\]+)[/\\]*$'
- end
- while #list > 0 do
- local current = list[#list]
- if not current then
- break
- end
- list[#list] = nil
- if not self:simpleMatch(current) then
- local fileType = self:callInterface('type', current)
- if fileType == 'file' then
- if callback then
- callback(current)
- end
- files[#files+1] = current
- elseif fileType == 'directory' then
- local result = self:callInterface('list', current)
- if type(result) == 'table' then
- for _, path in ipairs(result) do
- local filename = path:match '([^/\\]+)[/\\]*$'
- if filename
- and filename ~= '.'
- and filename ~= '..' then
- list[#list+1] = current .. '/' .. filename
- end
- end
- end
- end
- end
- end
- return files
-end
-
-function mt:__call(path)
- if self.options.ignoreCase then
- path = path:lower()
- end
- return self:finishMatch(path)
-end
-
-return function (pattern, options, interface)
- local self = setmetatable({
- pattern = {},
- options = {},
- matcher = {},
- errors = {},
- interface = {},
- }, mt)
-
- if type(pattern) == 'table' then
- for _, pat in ipairs(pattern) do
- self:addPattern(pat)
- end
- else
- self:addPattern(pattern)
- end
-
- if type(options) == 'table' then
- for op, val in pairs(options) do
- self:setOption(op, val)
- end
- end
-
- if type(interface) == 'table' then
- for key, func in pairs(interface) do
- self:setInterface(key, func)
- end
- end
-
- return self
-end
diff --git a/script/glob/glob.lua b/script/glob/glob.lua
deleted file mode 100644
index 9cfbdc7e..00000000
--- a/script/glob/glob.lua
+++ /dev/null
@@ -1,124 +0,0 @@
-local m = require 'lpeglabel'
-local matcher = require 'glob.matcher'
-
-local function prop(name, pat)
- return m.Cg(m.Cc(true), name) * pat
-end
-
-local function object(type, pat)
- return m.Ct(
- m.Cg(m.Cc(type), 'type') *
- m.Cg(pat, 'value')
- )
-end
-
-local function expect(p, err)
- return p + m.T(err)
-end
-
-local parser = m.P {
- 'Main',
- ['Sp'] = m.S(' \t')^0,
- ['Slash'] = m.P('/')^1,
- ['Main'] = m.Ct(m.V'Sp' * m.P'{' * m.V'Pattern' * (',' * expect(m.V'Pattern', 'Miss exp after ","'))^0 * m.P'}')
- + m.Ct(m.V'Pattern')
- + m.T'Main Failed'
- ,
- ['Pattern'] = m.Ct(m.V'Sp' * prop('neg', m.P'!') * expect(m.V'Unit', 'Miss exp after "!"'))
- + m.Ct(m.V'Unit')
- ,
- ['NeedRoot'] = prop('root', (m.P'.' * m.V'Slash' + m.V'Slash')),
- ['Unit'] = m.V'Sp' * m.V'NeedRoot'^-1 * expect(m.V'Exp', 'Miss exp') * m.V'Sp',
- ['Exp'] = m.V'Sp' * (m.V'FSymbol' + object('/', m.V'Slash') + m.V'Word')^0 * m.V'Sp',
- ['Word'] = object('word', m.Ct((m.V'CSymbol' + m.V'Char' - m.V'FSymbol')^1)),
- ['CSymbol'] = object('*', m.P'*')
- + object('?', m.P'?')
- + object('[]', m.V'Range')
- ,
- ['SimpleChar'] = m.P(1) - m.S',{}[]*?/',
- ['EscChar'] = m.P'\\' / '' * m.P(1),
- ['Char'] = object('char', m.Cs((m.V'EscChar' + m.V'SimpleChar')^1)),
- ['FSymbol'] = object('**', m.P'**'),
- ['RangeWord'] = 1 - m.P']',
- ['Range'] = m.P'[' * m.Ct(m.V'RangeUnit'^0) * m.P']'^-1,
- ['RangeUnit'] = m.Ct(m.C(m.V'RangeWord') * m.P'-' * m.C(m.V'RangeWord'))
- + m.V'RangeWord',
-}
-
-local mt = {}
-mt.__index = mt
-mt.__name = 'glob'
-
-function mt:addPattern(pat)
- if type(pat) ~= 'string' then
- return
- end
- self.pattern[#self.pattern+1] = pat
- if self.options.ignoreCase then
- pat = pat:lower()
- end
- local states, err = parser:match(pat)
- if not states then
- self.errors[#self.errors+1] = {
- pattern = pat,
- message = err
- }
- return
- end
- for _, state in ipairs(states) do
- if state.neg then
- self.refused[#self.refused+1] = matcher(state)
- else
- self.passed[#self.passed+1] = matcher(state)
- end
- end
-end
-
-function mt:setOption(op, val)
- if val == nil then
- val = true
- end
- self.options[op] = val
-end
-
-function mt:__call(path)
- if self.options.ignoreCase then
- path = path:lower()
- end
- for _, refused in ipairs(self.refused) do
- if refused(path) then
- return false
- end
- end
- for _, passed in ipairs(self.passed) do
- if passed(path) then
- return true
- end
- end
- return false
-end
-
-return function (pattern, options)
- local self = setmetatable({
- pattern = {},
- options = {},
- passed = {},
- refused = {},
- errors = {},
- }, mt)
-
- if type(pattern) == 'table' then
- for _, pat in ipairs(pattern) do
- self:addPattern(pat)
- end
- else
- self:addPattern(pattern)
- end
-
- if type(options) == 'table' then
- for op, val in pairs(options) do
- self:setOption(op, val)
- end
- end
- return self
-end
diff --git a/script/glob/init.lua b/script/glob/init.lua
deleted file mode 100644
index 6578a0d4..00000000
--- a/script/glob/init.lua
+++ /dev/null
@@ -1,4 +0,0 @@
-return {
- glob = require 'glob.glob',
- gitignore = require 'glob.gitignore',
-}
diff --git a/script/glob/matcher.lua b/script/glob/matcher.lua
deleted file mode 100644
index f4c2b12c..00000000
--- a/script/glob/matcher.lua
+++ /dev/null
@@ -1,151 +0,0 @@
-local m = require 'lpeglabel'
-
-local Slash = m.S('/\\')^1
-local Symbol = m.S',{}[]*?/\\'
-local Char = 1 - Symbol
-local Path = Char^1 * Slash
-local NoWord = #(m.P(-1) + Symbol)
-local function whatHappened()
- return m.Cmt(m.P(1)^1, function (...)
- print(...)
- end)
-end
-
-local mt = {}
-mt.__index = mt
-mt.__name = 'matcher'
-
-function mt:exp(state, index)
- local exp = state[index]
- if not exp then
- return
- end
- if exp.type == 'word' then
- return self:word(exp, state, index + 1)
- elseif exp.type == 'char' then
- return self:char(exp, state, index + 1)
- elseif exp.type == '**' then
- return self:anyPath(exp, state, index + 1)
- elseif exp.type == '*' then
- return self:anyChar(exp, state, index + 1)
- elseif exp.type == '?' then
- return self:oneChar(exp, state, index + 1)
- elseif exp.type == '[]' then
- return self:range(exp, state, index + 1)
- elseif exp.type == '/' then
- return self:slash(exp, state, index + 1)
- end
-end
-
-function mt:word(exp, state, index)
- local current = self:exp(exp.value, 1)
- local after = self:exp(state, index)
- if after then
- return current * Slash * after
- else
- return current
- end
-end
-
-function mt:char(exp, state, index)
- local current = m.P(exp.value)
- local after = self:exp(state, index)
- if after then
- return current * after * NoWord
- else
- return current * NoWord
- end
-end
-
-function mt:anyPath(_, state, index)
- local after = self:exp(state, index)
- if after then
- return m.P {
- 'Main',
- Main = after
- + Path * m.V'Main'
- }
- else
- return Path^0
- end
-end
-
-function mt:anyChar(_, state, index)
- local after = self:exp(state, index)
- if after then
- return m.P {
- 'Main',
- Main = after
- + Char * m.V'Main'
- }
- else
- return Char^0
- end
-end
-
-function mt:oneChar(_, state, index)
- local after = self:exp(state, index)
- if after then
- return Char * after
- else
- return Char
- end
-end
-
-function mt:range(exp, state, index)
- local after = self:exp(state, index)
- local ranges = {}
- local selects = {}
- for _, range in ipairs(exp.value) do
- if #range == 1 then
- selects[#selects+1] = range[1]
- elseif #range == 2 then
- ranges[#ranges+1] = range[1] .. range[2]
- end
- end
- local current = m.S(table.concat(selects)) + m.R(table.unpack(ranges))
- if after then
- return current * after
- else
- return current
- end
-end
-
-function mt:slash(_, state, index)
- local after = self:exp(state, index)
- if after then
- return after
- else
- self.needDirectory = true
- return nil
- end
-end
-
-function mt:pattern(state)
- if state.root then
- return m.C(self:exp(state, 1))
- else
- return m.C(self:anyPath(nil, state, 1))
- end
-end
-
-function mt:isNeedDirectory()
- return self.needDirectory == true
-end
-
-function mt:isNegative()
- return self.state.neg == true
-end
-
-function mt:__call(path)
- return self.matcher:match(path)
-end
-
-return function (state, options)
- local self = setmetatable({
- options = options,
- state = state,
- }, mt)
- self.matcher = self:pattern(state)
- return self
-end
diff --git a/script/json-beautify.lua b/script/json-beautify.lua
deleted file mode 100644
index 1d2a6cc0..00000000
--- a/script/json-beautify.lua
+++ /dev/null
@@ -1,120 +0,0 @@
-local json = require "json"
-local type = type
-local next = next
-local error = error
-local table_concat = table.concat
-local table_sort = table.sort
-local string_rep = string.rep
-local math_type = math.type
-local setmetatable = setmetatable
-local getmetatable = getmetatable
-
-local statusMark
-local statusQue
-local statusDep
-local statusOpt
-
-local defaultOpt = {
- newline = "\n",
- indent = " ",
-}
-defaultOpt.__index = defaultOpt
-
-local function encode_newline()
- statusQue[#statusQue+1] = statusOpt.newline..string_rep(statusOpt.indent, statusDep)
-end
-
-local encode_map = {}
-for k ,v in next, json.encode_map do
- encode_map[k] = v
-end
-
-local encode_string = json.encode_map.string
-
-local function encode(v)
- local res = encode_map[type(v)](v)
- statusQue[#statusQue+1] = res
-end
-
-function encode_map.table(t)
- local first_val = next(t)
- if first_val == nil then
- if getmetatable(t) == json.object then
- return "{}"
- else
- return "[]"
- end
- end
- if statusMark[t] then
- error("circular reference")
- end
- statusMark[t] = true
- if type(first_val) == 'string' then
- local key = {}
- for k in next, t do
- if type(k) ~= "string" then
- error("invalid table: mixed or invalid key types")
- end
- key[#key+1] = k
- end
- table_sort(key)
- statusQue[#statusQue+1] = "{"
- statusDep = statusDep + 1
- encode_newline()
- local k = key[1]
- statusQue[#statusQue+1] = encode_string(k)
- statusQue[#statusQue+1] = ": "
- encode(t[k])
- for i = 2, #key do
- local k = key[i]
- statusQue[#statusQue+1] = ","
- encode_newline()
- statusQue[#statusQue+1] = encode_string(k)
- statusQue[#statusQue+1] = ": "
- encode(t[k])
- end
- statusDep = statusDep - 1
- encode_newline()
- statusMark[t] = nil
- return "}"
- else
- local max = 0
- for k in next, t do
- if math_type(k) ~= "integer" or k <= 0 then
- error("invalid table: mixed or invalid key types")
- end
- if max < k then
- max = k
- end
- end
- statusQue[#statusQue+1] = "["
- statusDep = statusDep + 1
- encode_newline()
- encode(t[1])
- for i = 2, max do
- statusQue[#statusQue+1] = ","
- encode_newline()
- encode(t[i])
- end
- statusDep = statusDep - 1
- encode_newline()
- statusMark[t] = nil
- return "]"
- end
-end
-
-local function beautify(v, option)
- if type(v) == "string" then
- v = json.decode(v)
- end
- statusMark = {}
- statusQue = {}
- statusDep = 0
- statusOpt = option and setmetatable(option, defaultOpt) or defaultOpt
- encode(v)
- return table_concat(statusQue)
-end
-
-json.beautify = beautify
-
-return json
diff --git a/script/json.lua b/script/json.lua
deleted file mode 100644
index 46261d7d..00000000
--- a/script/json.lua
+++ /dev/null
@@ -1,450 +0,0 @@
-local type = type
-local next = next
-local error = error
-local tonumber = tonumber
-local tostring = tostring
-local utf8_char = utf8.char
-local table_concat = table.concat
-local table_sort = table.sort
-local string_char = string.char
-local string_byte = string.byte
-local string_find = string.find
-local string_match = string.match
-local string_gsub = string.gsub
-local string_sub = string.sub
-local string_format = string.format
-local math_type = math.type
-local setmetatable = setmetatable
-local getmetatable = getmetatable
-local Inf = math.huge
-
-local json = {}
-json.object = {}
-
--- json.encode --
-local statusMark
-local statusQue
-
-local encode_map = {}
-
-local encode_escape_map = {
- [ "\"" ] = "\\\"",
- [ "\\" ] = "\\\\",
- [ "/" ] = "\\/",
- [ "\b" ] = "\\b",
- [ "\f" ] = "\\f",
- [ "\n" ] = "\\n",
- [ "\r" ] = "\\r",
- [ "\t" ] = "\\t",
-}
-
-local decode_escape_set = {}
-local decode_escape_map = {}
-for k, v in next, encode_escape_map do
- decode_escape_map[v] = k
- decode_escape_set[string_byte(v, 2)] = true
-end
-
-for i = 0, 31 do
- local c = string_char(i)
- if not encode_escape_map[c] then
- encode_escape_map[c] = string_format("\\u%04x", i)
- end
-end
-
-local function encode(v)
- local res = encode_map[type(v)](v)
- statusQue[#statusQue+1] = res
-end
-
-encode_map["nil"] = function ()
- return "null"
-end
-
-function encode_map.string(v)
- return '"' .. string_gsub(v, '[\0-\31\\"]', encode_escape_map) .. '"'
-end
-local encode_string = encode_map.string
-
-local function convertreal(v)
- local g = string_format('%.16g', v)
- if tonumber(g) == v then
- return g
- end
- return string_format('%.17g', v)
-end
-
-function encode_map.number(v)
- if v ~= v or v <= -Inf or v >= Inf then
- error("unexpected number value '" .. tostring(v) .. "'")
- end
- return string_gsub(convertreal(v), ',', '.')
-end
-
-function encode_map.boolean(v)
- if v then
- return "true"
- else
- return "false"
- end
-end
-
-function encode_map.table(t)
- local first_val = next(t)
- if first_val == nil then
- if getmetatable(t) == json.object then
- return "{}"
- else
- return "[]"
- end
- end
- if statusMark[t] then
- error("circular reference")
- end
- statusMark[t] = true
- if type(first_val) == 'string' then
- local key = {}
- for k in next, t do
- if type(k) ~= "string" then
- error("invalid table: mixed or invalid key types")
- end
- key[#key+1] = k
- end
- table_sort(key)
- statusQue[#statusQue+1] = "{"
- local k = key[1]
- statusQue[#statusQue+1] = encode_string(k)
- statusQue[#statusQue+1] = ":"
- encode(t[k])
- for i = 2, #key do
- local k = key[i]
- statusQue[#statusQue+1] = ","
- statusQue[#statusQue+1] = encode_string(k)
- statusQue[#statusQue+1] = ":"
- encode(t[k])
- end
- statusMark[t] = nil
- return "}"
- else
- local max = 0
- for k in next, t do
- if math_type(k) ~= "integer" or k <= 0 then
- error("invalid table: mixed or invalid key types")
- end
- if max < k then
- max = k
- end
- end
- statusQue[#statusQue+1] = "["
- encode(t[1])
- for i = 2, max do
- statusQue[#statusQue+1] = ","
- encode(t[i])
- end
- statusMark[t] = nil
- return "]"
- end
-end
-
-local function encode_unexpected(v)
- if v == json.null then
- return "null"
- else
- error("unexpected type '"..type(v).."'")
- end
-end
-encode_map[ "function" ] = encode_unexpected
-encode_map[ "userdata" ] = encode_unexpected
-encode_map[ "thread" ] = encode_unexpected
-
-function json.encode(v)
- statusMark = {}
- statusQue = {}
- encode(v)
- return table_concat(statusQue)
-end
-
-json.encode_map = encode_map
-
--- json.decode --
-
-local statusBuf
-local statusPos
-local statusTop
-local statusAry = {}
-local statusRef = {}
-
-local function find_line()
- local line = 1
- local pos = 1
- while true do
- local f, _, nl1, nl2 = string_find(statusBuf, '([\n\r])([\n\r]?)', pos)
- if not f then
- return line, statusPos - pos + 1
- end
- local newpos = f + ((nl1 == nl2 or nl2 == '') and 1 or 2)
- if newpos > statusPos then
- return line, statusPos - pos + 1
- end
- pos = newpos
- line = line + 1
- end
-end
-
-local function decode_error(msg)
- error(string_format("ERROR: %s at line %d col %d", msg, find_line()))
-end
-
-local function get_word()
- return string_match(statusBuf, "^[^ \t\r\n%]},]*", statusPos)
-end
-
-local function next_byte()
- statusPos = string_find(statusBuf, "[^ \t\r\n]", statusPos)
- if statusPos then
- return string_byte(statusBuf, statusPos)
- end
- statusPos = #statusBuf + 1
- decode_error("unexpected character '<eol>'")
-end
-
-local function expect_byte(c)
- local _, pos = string_find(statusBuf, c, statusPos)
- if not pos then
- decode_error(string_format("expected '%s'", string_sub(c, #c)))
- end
- statusPos = pos
-end
-
-local function decode_unicode_surrogate(s1, s2)
- return utf8_char(0x10000 + (tonumber(s1, 16) - 0xd800) * 0x400 + (tonumber(s2, 16) - 0xdc00))
-end
-
-local function decode_unicode_escape(s)
- return utf8_char(tonumber(s, 16))
-end
-
-local function decode_string()
- local has_unicode_escape = false
- local has_escape = false
- local i = statusPos + 1
- while true do
- i = string_find(statusBuf, '["\\\0-\31]', i)
- if not i then
- decode_error "expected closing quote for string"
- end
- local x = string_byte(statusBuf, i)
- if x < 32 then
- statusPos = i
- decode_error "control character in string"
- end
- if x == 34 --[[ '"' ]] then
- local s = string_sub(statusBuf, statusPos + 1, i - 1)
- if has_unicode_escape then
- s = string_gsub(string_gsub(s
- , "\\u([dD][89aAbB]%x%x)\\u([dD][c-fC-F]%x%x)", decode_unicode_surrogate)
- , "\\u(%x%x%x%x)", decode_unicode_escape)
- end
- if has_escape then
- s = string_gsub(s, "\\.", decode_escape_map)
- end
- statusPos = i + 1
- return s
- end
- --assert(x == 92 --[[ "\\" ]])
- local nx = string_byte(statusBuf, i+1)
- if nx == 117 --[[ "u" ]] then
- if not string_match(statusBuf, "^%x%x%x%x", i+2) then
- statusPos = i
- decode_error "invalid unicode escape in string"
- end
- has_unicode_escape = true
- i = i + 6
- else
- if not decode_escape_set[nx] then
- statusPos = i
- decode_error("invalid escape char '" .. (nx and string_char(nx) or "<eol>") .. "' in string")
- end
- has_escape = true
- i = i + 2
- end
- end
-end
-
-local function decode_number()
- local word = get_word()
- if not (
- string_match(word, '^.[0-9]*$')
- or string_match(word, '^.[0-9]*%.[0-9]+$')
- or string_match(word, '^.[0-9]*[Ee][+-]?[0-9]+$')
- or string_match(word, '^.[0-9]*%.[0-9]+[Ee][+-]?[0-9]+$')
- ) then
- decode_error("invalid number '" .. word .. "'")
- end
- statusPos = statusPos + #word
- return tonumber(word)
-end
-
-local function decode_number_negative()
- local word = get_word()
- if not (
- string_match(word, '^.[1-9][0-9]*$')
- or string_match(word, '^.[1-9][0-9]*%.[0-9]+$')
- or string_match(word, '^.[1-9][0-9]*[Ee][+-]?[0-9]+$')
- or string_match(word, '^.[1-9][0-9]*%.[0-9]+[Ee][+-]?[0-9]+$')
- or word == "-0"
- or string_match(word, '^.0%.[0-9]+$')
- or string_match(word, '^.0[Ee][+-]?[0-9]+$')
- or string_match(word, '^.0%.[0-9]+[Ee][+-]?[0-9]+$')
- ) then
- decode_error("invalid number '" .. word .. "'")
- end
- statusPos = statusPos + #word
- return tonumber(word)
-end
-
-local function decode_number_zero()
- local word = get_word()
- if not (
- #word == 1
- or string_match(word, '^.%.[0-9]+$')
- or string_match(word, '^.[Ee][+-]?[0-9]+$')
- or string_match(word, '^.%.[0-9]+[Ee][+-]?[0-9]+$')
- ) then
- decode_error("invalid number '" .. word .. "'")
- end
- statusPos = statusPos + #word
- return tonumber(word)
-end
-
-local function decode_true()
- if string_sub(statusBuf, statusPos, statusPos+3) ~= "true" then
- decode_error("invalid literal '" .. get_word() .. "'")
- end
- statusPos = statusPos + 4
- return true
-end
-
-local function decode_false()
- if string_sub(statusBuf, statusPos, statusPos+4) ~= "false" then
- decode_error("invalid literal '" .. get_word() .. "'")
- end
- statusPos = statusPos + 5
- return false
-end
-
-local function decode_null()
- if string_sub(statusBuf, statusPos, statusPos+3) ~= "null" then
- decode_error("invalid literal '" .. get_word() .. "'")
- end
- statusPos = statusPos + 4
- return json.null
-end
-
-local function decode_array()
- statusPos = statusPos + 1
- local res = {}
- if next_byte() == 93 --[[ "]" ]] then
- statusPos = statusPos + 1
- return res
- end
- statusTop = statusTop + 1
- statusAry[statusTop] = true
- statusRef[statusTop] = res
- return res
-end
-
-local function decode_object()
- statusPos = statusPos + 1
- local res = {}
- if next_byte() == 125 --[[ "}" ]] then
- statusPos = statusPos + 1
- return setmetatable(res, json.object)
- end
- statusTop = statusTop + 1
- statusAry[statusTop] = false
- statusRef[statusTop] = res
- return res
-end
-
-local decode_uncompleted_map = {
- [ string_byte '"' ] = decode_string,
- [ string_byte "0" ] = decode_number_zero,
- [ string_byte "1" ] = decode_number,
- [ string_byte "2" ] = decode_number,
- [ string_byte "3" ] = decode_number,
- [ string_byte "4" ] = decode_number,
- [ string_byte "5" ] = decode_number,
- [ string_byte "6" ] = decode_number,
- [ string_byte "7" ] = decode_number,
- [ string_byte "8" ] = decode_number,
- [ string_byte "9" ] = decode_number,
- [ string_byte "-" ] = decode_number_negative,
- [ string_byte "t" ] = decode_true,
- [ string_byte "f" ] = decode_false,
- [ string_byte "n" ] = decode_null,
- [ string_byte "[" ] = decode_array,
- [ string_byte "{" ] = decode_object,
-}
-local function unexpected_character()
- decode_error("unexpected character '" .. string_sub(statusBuf, statusPos, statusPos) .. "'")
-end
-
-local decode_map = {}
-for i = 0, 255 do
- decode_map[i] = decode_uncompleted_map[i] or unexpected_character
-end
-
-local function decode()
- return decode_map[next_byte()]()
-end
-
-local function decode_item()
- local top = statusTop
- local ref = statusRef[top]
- if statusAry[top] then
- ref[#ref+1] = decode()
- else
- expect_byte '^[ \t\r\n]*"'
- local key = decode_string()
- expect_byte '^[ \t\r\n]*:'
- statusPos = statusPos + 1
- ref[key] = decode()
- end
- if top == statusTop then
- repeat
- local chr = next_byte(); statusPos = statusPos + 1
- if chr == 44 --[[ "," ]] then
- return
- end
- if statusAry[statusTop] then
- if chr ~= 93 --[[ "]" ]] then decode_error "expected ']' or ','" end
- else
- if chr ~= 125 --[[ "}" ]] then decode_error "expected '}' or ','" end
- end
- statusTop = statusTop - 1
- until statusTop == 0
- end
-end
-
-function json.decode(str)
- if type(str) ~= "string" then
- error("expected argument of type string, got " .. type(str))
- end
- statusBuf = str
- statusPos = 1
- statusTop = 0
- local res = decode()
- while statusTop > 0 do
- decode_item()
- end
- if string_find(statusBuf, "[^ \t\r\n]", statusPos) then
- decode_error "trailing garbage"
- end
- return res
-end
-
--- Generate a lightuserdata
-json.null = debug.upvalueid(decode, 1)
-
-return json
diff --git a/script/language.lua b/script/language.lua
deleted file mode 100644
index 3294c5b2..00000000
--- a/script/language.lua
+++ /dev/null
@@ -1,136 +0,0 @@
-local fs = require 'bee.filesystem'
-local lni = require 'lni'
-
-local function supportLanguage()
- local list = {}
- for path in (ROOT / 'locale'):list_directory() do
- if fs.is_directory(path) then
- list[#list+1] = path:filename():string():lower()
- end
- end
- return list
-end
-
-local function osLanguage()
- return LANG:lower()
-end
-
-local function getLanguage(id)
- local support = supportLanguage()
- -- 检查是否支持语言
- if support[id] then
- return id
- end
- -- 根据语言的前2个字母来找近似语言
- for _, lang in ipairs(support) do
- if lang:sub(1, 2) == id:sub(1, 2) then
- return lang
- end
- end
- -- 使用英文
- return 'enUS'
-end
-
-local function loadFileByLanguage(name, language)
- local path = ROOT / 'locale' / language / (name .. '.lni')
- local buf = io.load(path)
- if not buf then
- return {}
- end
- local suc, tbl = xpcall(lni, log.error, buf, path:string())
- if not suc then
- return {}
- end
- return tbl
-end
-
-local function formatAsArray(str, ...)
- local index = 0
- local args = {...}
- return str:gsub('%{(.-)%}', function (pat)
- local id, fmt
- local pos = pat:find(':', 1, true)
- if pos then
- id = pat:sub(1, pos-1)
- fmt = pat:sub(pos+1)
- else
- id = pat
- fmt = 's'
- end
- id = tonumber(id)
- if not id then
- index = index + 1
- id = index
- end
- return ('%'..fmt):format(args[id])
- end)
-end
-
-local function formatAsTable(str, ...)
- local args = ...
- return str:gsub('%{(.-)%}', function (pat)
- local id, fmt
- local pos = pat:find(':', 1, true)
- if pos then
- id = pat:sub(1, pos-1)
- fmt = pat:sub(pos+1)
- else
- id = pat
- fmt = 's'
- end
- if not id then
- return
- end
- return ('%'..fmt):format(args[id])
- end)
-end
-
-local function loadLang(name, language)
- local tbl = loadFileByLanguage(name, 'en-US')
- if language ~= 'en-US' then
- local other = loadFileByLanguage(name, language)
- for k, v in pairs(other) do
- tbl[k] = v
- end
- end
- return setmetatable(tbl, {
- __index = function (self, key)
- self[key] = key
- return key
- end,
- __call = function (self, key, ...)
- local str = self[key]
- local suc, res
- if type(...) == 'table' then
- suc, res = pcall(formatAsTable, str, ...)
- else
- suc, res = pcall(formatAsArray, str, ...)
- end
- if suc then
- return res
- else
- -- 这里不能使用翻译,以免死循环
- log.warn(('[%s][%s-%s] formated error: %s'):format(
- language, name, key, str
- ))
- return str
- end
- end,
- })
-end
-
-local function init()
- local id = osLanguage()
- local language = getLanguage(id)
- log.info(('VSC language: %s'):format(id))
- log.info(('LS language: %s'):format(language))
- return setmetatable({ id = language }, {
- __index = function (self, name)
- local tbl = loadLang(name, language)
- self[name] = tbl
- return tbl
- end,
- })
-end
-
-return init()
diff --git a/script/log.lua b/script/log.lua
deleted file mode 100644
index 71a903d9..00000000
--- a/script/log.lua
+++ /dev/null
@@ -1,116 +0,0 @@
-local fs = require 'bee.filesystem'
-
-local log = {}
-
-log.file = nil
-log.start_time = os.time() - os.clock()
-log.size = 0
-log.max_size = 100 * 1024 * 1024
-
-local function trim_src(src)
- src = src:sub(log.prefix_len + 3, -5)
- src = src:gsub('^[/\\]+', '')
- src = src:gsub('[\\/]+', '.')
- return src
-end
-
-local function init_log_file()
- if not log.file then
- log.file = io.open(log.path, 'w')
- if not log.file then
- return
- end
- log.file:write('')
- log.file:close()
- log.file = io.open(log.path, 'ab')
- if not log.file then
- return
- end
- log.file:setvbuf 'no'
- end
-end
-
-local function push_log(level, ...)
- if not log.path then
- return
- end
- if log.size > log.max_size then
- return
- end
- local t = table.pack(...)
- for i = 1, t.n do
- t[i] = tostring(t[i])
- end
- local str = table.concat(t, '\t', 1, t.n)
- if level == 'error' then
- str = str .. '\n' .. debug.traceback(nil, 3)
- io.stderr:write(str .. '\n')
- end
- init_log_file()
- if not log.file then
- return
- end
- local sec, ms = math.modf(log.start_time + os.clock())
- local timestr = os.date('%Y-%m-%d %H:%M:%S', sec)
- local info = debug.getinfo(3, 'Sl')
- local buf
- if info and info.currentline > 0 then
- buf = ('[%s.%03.f][%s][%s:%s]: %s\n'):format(timestr, ms * 1000, level, trim_src(info.source), info.currentline, str)
- else
- buf = ('[%s.%03.f][%s]: %s\n'):format(timestr, ms * 1000, level, str)
- end
- log.file:write(buf)
- log.size = log.size + #buf
- if log.size > log.max_size then
- log.file:write('[REACH MAX SIZE]')
- end
- return str
-end
-
-function log.info(...)
- push_log('info', ...)
-end
-
-function log.debug(...)
- push_log('debug', ...)
-end
-
-function log.trace(...)
- push_log('trace', ...)
-end
-
-function log.warn(...)
- push_log('warn', ...)
-end
-
-function log.error(...)
- return push_log('error', ...)
-end
-
-function log.init(root, path)
- local lastBuf
- if log.file then
- log.file:close()
- log.file = nil
- local file = io.open(log.path, 'rb')
- if file then
- lastBuf = file:read 'a'
- file:close()
- end
- end
- log.path = path:string()
- log.prefix_len = #root:string()
- log.size = 0
- if not fs.exists(path:parent_path()) then
- fs.create_directories(path:parent_path())
- end
- if lastBuf then
- init_log_file()
- if log.file then
- log.file:write(lastBuf)
- log.size = log.size + #lastBuf
- end
- end
-end
-
-return log
diff --git a/script/meta/type.lua b/script/meta/type.lua
deleted file mode 100644
index 9b47d5bc..00000000
--- a/script/meta/type.lua
+++ /dev/null
@@ -1,4 +0,0 @@
----@class uri string
----@class path
-
-"This is syntax error, due to `workspace.library`, this error never shows."
diff --git a/script/method/completionItem/resolve.lua b/script/method/completionItem/resolve.lua
deleted file mode 100644
index 0e55d311..00000000
--- a/script/method/completionItem/resolve.lua
+++ /dev/null
@@ -1,34 +0,0 @@
-local config = require 'config'
-
-return function (lsp, item)
- local context = config.config.completion.displayContext
- if context <= 0 then
- return item
- end
- if not item.data then
- return item
- end
- local offset = item.data.offset
- local uri = item.data.uri
- local _, lines, text = lsp:getVM(uri)
- if not lines then
- return item
- end
- local row = lines:rowcol(offset)
- local firstRow = lines[row]
- local lastRow = lines[math.min(row + context - 1, #lines)]
- local snip = text:sub(firstRow.start, lastRow.finish)
- local document = ([[
-%s
-
-------------
-```lua
-%s
-```
-]]):format(item.documentation and item.documentation.value or '', snip)
- item.documentation = {
- kind = 'markdown',
- value = document,
- }
- return item
-end
diff --git a/script/method/exit.lua b/script/method/exit.lua
deleted file mode 100644
index fa550243..00000000
--- a/script/method/exit.lua
+++ /dev/null
@@ -1,4 +0,0 @@
-return function ()
- log.info('Server exited.')
- os.exit(true)
-end
diff --git a/script/method/init.lua b/script/method/init.lua
deleted file mode 100644
index 2bdbacc3..00000000
--- a/script/method/init.lua
+++ /dev/null
@@ -1,35 +0,0 @@
-local method = {}
-
-local function init(name)
- method[name] = require('method.' .. name:gsub('/', '.'))
-end
-
-init 'exit'
-init 'initialize'
-init 'initialized'
-init 'shutdown'
-init 'completionItem/resolve'
-init 'textDocument/codeAction'
-init 'textDocument/completion'
-init 'textDocument/definition'
-init 'textDocument/didOpen'
-init 'textDocument/didChange'
-init 'textDocument/didClose'
-init 'textDocument/documentHighlight'
-init 'textDocument/documentSymbol'
-init 'textDocument/foldingRange'
-init 'textDocument/hover'
-init 'textDocument/implementation'
-init 'textDocument/onTypeFormatting'
-init 'textDocument/publishDiagnostics'
-init 'textDocument/rename'
-init 'textDocument/references'
-init 'textDocument/semanticTokens/full'
-init 'textDocument/signatureHelp'
-init 'workspace/didChangeConfiguration'
-init 'workspace/didChangeWatchedFiles'
-init 'workspace/didChangeWorkspaceFolders'
-init 'workspace/executeCommand'
-init 'workspace/symbol'
-
-return method
diff --git a/script/method/initialize.lua b/script/method/initialize.lua
deleted file mode 100644
index dfa066b3..00000000
--- a/script/method/initialize.lua
+++ /dev/null
@@ -1,76 +0,0 @@
-local workspace = require 'workspace'
-local nonil = require 'without-check-nil'
-local client = require 'client'
-local json = require 'json'
-local sp = require 'bee.subprocess'
-
-local function allWords()
- local str = [[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.:('"[,#*@| ]]
- local list = {}
- for c in str:gmatch '.' do
- list[#list+1] = c
- end
- return list
-end
-
---- @param lsp LSP
---- @param params table
---- @return table
-return function (lsp, params)
- lsp._inited = true
- lsp.client = params
- client.init(params)
- log.info(table.dump(params))
- log.debug('ProcessID', sp.get_id())
-
- if params.rootUri and params.rootUri ~= json.null then
- lsp:addWorkspace('root', params.rootUri)
- end
-
- local server = {
- serverInfo = {
- name = 'sumneko.lua',
- version = 'alpha',
- },
- capabilities = {
- hoverProvider = true,
- definitionProvider = true,
- referencesProvider = true,
- renameProvider = true,
- documentSymbolProvider = true,
- documentHighlightProvider = true,
- codeActionProvider = true,
- foldingRangeProvider = true,
- workspaceSymbolProvider = true,
- signatureHelpProvider = {
- triggerCharacters = { '(', ',' },
- },
- -- 文本同步方式
- textDocumentSync = {
- -- 打开关闭文本时通知
- openClose = true,
- -- 文本改变时完全通知 TODO 支持差量更新(2)
- change = 1,
- },
- documentOnTypeFormattingProvider = {
- firstTriggerCharacter = '}',
- },
- executeCommandProvider = {
- commands = {
- 'lua.removeSpace:' .. sp.get_id(),
- 'lua.solve:' .. sp.get_id(),
- },
- },
- }
- }
-
- nonil.enable()
- if not params.capabilities.textDocument.completion.dynamicRegistration then
- server.capabilities.completionProvider = {
- triggerCharacters = allWords(),
- }
- end
- nonil.disable()
-
- return server
-end
diff --git a/script/method/initialized.lua b/script/method/initialized.lua
deleted file mode 100644
index a3cf44c7..00000000
--- a/script/method/initialized.lua
+++ /dev/null
@@ -1,81 +0,0 @@
-local rpc = require 'rpc'
-
---- @param lsp LSP
---- @return boolean
-return function (lsp)
- if #lsp.workspaces > 0 then
- for _, ws in ipairs(lsp.workspaces) do
- -- 请求工作目录
- local uri = ws.uri
- -- 请求配置
- rpc:request('workspace/configuration', {
- items = {
- {
- scopeUri = uri,
- section = 'Lua',
- },
- {
- scopeUri = uri,
- section = 'files.associations',
- },
- {
- scopeUri = uri,
- section = 'files.exclude',
- }
- },
- }, function (configs)
- lsp:onUpdateConfig(configs[1], {
- associations = configs[2],
- exclude = configs[3],
- })
- end)
- end
- else
- -- 请求配置
- rpc:request('workspace/configuration', {
- items = {
- {
- section = 'Lua',
- },
- {
- section = 'files.associations',
- },
- {
- section = 'files.exclude',
- }
- },
- }, function (configs)
- lsp:onUpdateConfig(configs[1], {
- associations = configs[2],
- exclude = configs[3],
- })
- end)
- end
-
- rpc:request('client/registerCapability', {
- registrations = {
- -- 监视文件变化
- {
- id = '0',
- method = 'workspace/didChangeWatchedFiles',
- registerOptions = {
- watchers = {
- {
- globPattern = '**/',
- kind = 1 | 2 | 4,
- }
- },
- },
- },
- -- 配置变化
- {
- id = '1',
- method = 'workspace/didChangeConfiguration',
- }
- }
- }, function ()
- log.debug('client/registerCapability Success!')
- end)
-
- return true
-end
diff --git a/script/method/shutdown.lua b/script/method/shutdown.lua
deleted file mode 100644
index bb81306e..00000000
--- a/script/method/shutdown.lua
+++ /dev/null
@@ -1,4 +0,0 @@
-return function ()
- log.info('Server shutdown.')
- return true
-end
diff --git a/script/method/textDocument/codeAction.lua b/script/method/textDocument/codeAction.lua
deleted file mode 100644
index 80a40a0c..00000000
--- a/script/method/textDocument/codeAction.lua
+++ /dev/null
@@ -1,26 +0,0 @@
-local core = require 'core'
-
---- @param lsp LSP
---- @param params table
---- @return table
-return function (lsp, params)
- local uri = params.textDocument.uri
- local vm, lines = lsp:getVM(uri)
- if not vm then
- return
- end
- local diagnostics = params.context.diagnostics
- local range = params.range
-
- local results = core.codeAction(lsp
- , uri
- , diagnostics
- , range
- )
-
- if #results == 0 then
- return nil
- end
-
- return results
-end
diff --git a/script/method/textDocument/completion.lua b/script/method/textDocument/completion.lua
deleted file mode 100644
index 0042c2c3..00000000
--- a/script/method/textDocument/completion.lua
+++ /dev/null
@@ -1,133 +0,0 @@
-local core = require 'core'
-local parser = require 'parser'
-
-local function posToRange(lines, start, finish)
- local start_row, start_col = lines:rowcol(start)
- local finish_row, finish_col = lines:rowcol(finish)
- return {
- start = {
- line = start_row - 1,
- character = start_col - 1,
- },
- ['end'] = {
- line = finish_row - 1,
- character = finish_col,
- },
- }
-end
-
-local function fastCompletion(lsp, params, lines)
- local uri = params.textDocument.uri
- local text, oldText = lsp:getText(uri)
- -- lua是从1开始的,因此都要+1
- local position = lines:positionAsChar(params.position.line + 1, params.position.character)
-
- local vm = lsp:getVM(uri)
- if not vm then
- vm = lsp:loadVM(uri)
- if not vm then
- return nil
- end
- end
-
- local items = core.completion(vm, text, position, oldText)
- if not items or #items == 0 then
- vm = lsp:loadVM(uri)
- if not vm then
- return nil
- end
- items = core.completion(vm, text, position)
- if not items or #items == 0 then
- return nil
- end
- end
-
- return items, position
-end
-
-local function finishCompletion(lsp, params, lines)
- local uri = params.textDocument.uri
- local text = lsp:getText(uri)
- -- lua是从1开始的,因此都要+1
- local position = lines:positionAsChar(params.position.line + 1, params.position.character)
-
- local vm = lsp:loadVM(uri)
- if not vm then
- return nil
- end
-
- local items = core.completion(vm, text, position)
- if not items or #items == 0 then
- return nil
- end
-
- return items
-end
-
-local function cuterFactory(lines, text, position)
- local start = position
- local head = ''
- for i = position, position - 100, -1 do
- if not text:sub(i, i):match '[%w_]' then
- start = i + 1
- head = text:sub(start, position)
- break
- end
- end
- return function (insertText)
- return {
- newText = insertText,
- range = posToRange(lines, start, position)
- }
- end
-end
-
---- @param lsp LSP
---- @param params table
---- @return table
-return function (lsp, params)
- local uri = params.textDocument.uri
- local text, oldText = lsp:getText(uri)
- if not text then
- return nil
- end
-
- local lines = parser:lines(text, 'utf8')
- local items, position = fastCompletion(lsp, params, lines)
- --local items = finishCompletion(lsp, params, lines)
- if not items then
- return nil
- end
-
- -- TODO 在协议阶段将 `insertText` 转化为 `textEdit` ,
- -- 以避免不同客户端对 `insertText` 实现的不一致。
- -- 重构后直接在 core 中使用 `textEdit` 。
- local cuter = cuterFactory(lines, text, position)
-
- for i, item in ipairs(items) do
- item.sortText = ('%04d'):format(i)
- item.insertTextFormat = 2
-
- if item.textEdit then
- item.textEdit.range = posToRange(lines, item.textEdit.start, item.textEdit.finish)
- item.textEdit.start = nil
- item.textEdit.finish = nil
- else
- item.textEdit = cuter(item.insertText or item.label)
- end
- if item.additionalTextEdits then
- for _, textEdit in ipairs(item.additionalTextEdits) do
- textEdit.range = posToRange(lines, textEdit.start, textEdit.finish)
- textEdit.start = nil
- textEdit.finish = nil
- end
- end
- end
-
- local response = {
- isIncomplete = false,
- items = items,
- }
-
- return response
-end
diff --git a/script/method/textDocument/definition.lua b/script/method/textDocument/definition.lua
deleted file mode 100644
index dbf9e41c..00000000
--- a/script/method/textDocument/definition.lua
+++ /dev/null
@@ -1,88 +0,0 @@
-local core = require 'core'
-
-local function findResult(lsp, uri, position)
- local vm = lsp:getVM(uri)
-
- local positions, isGlobal = core.definition(vm, position, 'definition')
- if not positions then
- return nil, isGlobal
- end
-
- local locations = {}
- for i, position in ipairs(positions) do
- local start, finish, valueUri = position[1], position[2], (position[3] or uri)
- local vm, valueLines = lsp:getVM(valueUri)
- if valueLines then
- local start_row, start_col = valueLines:rowcol(start)
- local finish_row, finish_col = valueLines:rowcol(finish)
- locations[#locations+1] = {
- uri = valueUri,
- range = {
- start = {
- line = start_row - 1,
- character = start_col - 1,
- },
- ['end'] = {
- line = finish_row - 1,
- -- 这里不用-1,因为前端期待的是匹配完成后的位置
- character = finish_col,
- },
- }
- }
- elseif vm then
- locations[#locations+1] = {
- uri = valueUri,
- range = {
- start = {
- line = 0,
- character = 0,
- },
- ['end'] = {
- line = 0,
- character = 0,
- },
- }
- }
- end
- end
-
- if #locations == 0 then
- return nil, isGlobal
- end
-
- return locations, isGlobal
-end
-
-local LastTask
-
----@param lsp LSP
----@param params table
-return function (lsp, params)
- local uri = params.textDocument.uri
- local vm, lines = lsp:loadVM(uri)
- if not vm then
- return nil
- end
-
- if LastTask then
- LastTask:remove()
- LastTask = nil
- end
-
- -- lua是从1开始的,因此都要+1
- local position = lines:positionAsChar(params.position.line + 1, params.position.character)
-
- return function (response)
- local clock = os.clock()
- LastTask = ac.loop(0.1, function ()
- local result, isGlobal = findResult(lsp, uri, position)
- if isGlobal and lsp:isWaitingCompile() and os.clock() - clock < 1 then
- return
- end
- response(result)
- LastTask:remove()
- LastTask = nil
- end)
- LastTask:onTimer()
- end
-end
diff --git a/script/method/textDocument/didChange.lua b/script/method/textDocument/didChange.lua
deleted file mode 100644
index fd8af6ba..00000000
--- a/script/method/textDocument/didChange.lua
+++ /dev/null
@@ -1,22 +0,0 @@
-local fn = require 'filename'
-
---- @param lsp LSP
---- @param params table
---- @return boolean
-return function (lsp, params)
- local doc = params.textDocument
- local change = params.contentChanges
- local ws = lsp:findWorkspaceFor(doc.uri)
- if ws then
- local path = ws:relativePathByUri(doc.uri)
- if not path or not fn.isLuaFile(path) then
- return
- end
- if not lsp:isOpen(doc.uri) and ws.gitignore(path:string()) then
- return
- end
- end
- -- TODO 支持差量更新
- lsp:saveText(doc.uri, doc.version, change[1].text)
- return true
-end
diff --git a/script/method/textDocument/didClose.lua b/script/method/textDocument/didClose.lua
deleted file mode 100644
index 3cfd98e8..00000000
--- a/script/method/textDocument/didClose.lua
+++ /dev/null
@@ -1,8 +0,0 @@
---- @param lsp LSP
---- @param params table
---- @return boolean
-return function (lsp, params)
- local doc = params.textDocument
- lsp:close(doc.uri)
- return true
-end
diff --git a/script/method/textDocument/didOpen.lua b/script/method/textDocument/didOpen.lua
deleted file mode 100644
index 732ddacf..00000000
--- a/script/method/textDocument/didOpen.lua
+++ /dev/null
@@ -1,13 +0,0 @@
-local furi = require 'uri'
-
---- @param lsp LSP
---- @param params table
---- @return boolean
-return function (lsp, params)
- local doc = params.textDocument
- if #lsp.workspaces == 0 then
- lsp:addWorkspace('root', furi.encode(furi.decode(doc.uri):parent_path()))
- end
- lsp:open(doc.uri, doc.version, doc.text)
- return true
-end
diff --git a/script/method/textDocument/documentHighlight.lua b/script/method/textDocument/documentHighlight.lua
deleted file mode 100644
index 2a6768f2..00000000
--- a/script/method/textDocument/documentHighlight.lua
+++ /dev/null
@@ -1,40 +0,0 @@
-local core = require 'core'
-
---- @param lsp LSP
---- @param params table
---- @return table
-return function (lsp, params)
- local uri = params.textDocument.uri
- local vm, lines = lsp:loadVM(uri)
- if not vm then
- return nil
- end
- local position = lines:positionAsChar(params.position.line + 1, params.position.character)
- local positions = core.highlight(vm, position)
- if not positions then
- return nil
- end
-
- local result = {}
- for i, position in ipairs(positions) do
- local start, finish = position[1], position[2]
- local start_row, start_col = lines:rowcol(start)
- local finish_row, finish_col = lines:rowcol(finish)
- result[i] = {
- range = {
- start = {
- line = start_row - 1,
- character = start_col - 1,
- },
- ['end'] = {
- line = finish_row - 1,
- -- 这里不用-1,因为前端期待的是匹配完成后的位置
- character = finish_col,
- },
- },
- kind = position[3],
- }
- end
-
- return result
-end
diff --git a/script/method/textDocument/documentSymbol.lua b/script/method/textDocument/documentSymbol.lua
deleted file mode 100644
index a84afb7a..00000000
--- a/script/method/textDocument/documentSymbol.lua
+++ /dev/null
@@ -1,75 +0,0 @@
-local core = require 'core'
-local lang = require 'language'
-
-local timerCache = {}
-
-local function posToRange(lines, start, finish)
- local start_row, start_col = lines:rowcol(start)
- local finish_row, finish_col = lines:rowcol(finish)
- return {
- start = {
- line = start_row - 1,
- character = start_col - 1,
- },
- ['end'] = {
- line = finish_row - 1,
- character = finish_col,
- },
- }
-end
-
-local function convertRange(lines, symbol)
- symbol.range = posToRange(lines, symbol.range[1], symbol.range[2])
- symbol.selectionRange = posToRange(lines, symbol.selectionRange[1], symbol.selectionRange[2])
- if symbol.name == '' then
- symbol.name = lang.script.SYMBOL_ANONYMOUS
- end
-
- if symbol.children then
- for _, child in ipairs(symbol.children) do
- convertRange(lines, child)
- end
- end
-end
-
---- @param lsp LSP
---- @param params table
---- @return function
-return function (lsp, params)
- local uri = params.textDocument.uri
-
- if timerCache[uri] then
- timerCache[uri]:remove()
- timerCache[uri] = nil
- end
-
- return function (response)
- local clock = os.clock()
- timerCache[uri] = ac.loop(0.1, function (t)
- local vm, lines = lsp:getVM(uri)
- if not vm then
- if os.clock() - clock > 10 then
- t:remove()
- timerCache[uri] = nil
- response(nil)
- end
- return
- end
-
- t:remove()
- timerCache[uri] = nil
-
- local symbols = core.documentSymbol(vm)
- if not symbols then
- response(nil)
- return
- end
-
- for _, symbol in ipairs(symbols) do
- convertRange(lines, symbol)
- end
-
- response(symbols)
- end)
- end
-end
diff --git a/script/method/textDocument/foldingRange.lua b/script/method/textDocument/foldingRange.lua
deleted file mode 100644
index 6395c908..00000000
--- a/script/method/textDocument/foldingRange.lua
+++ /dev/null
@@ -1,60 +0,0 @@
-local core = require 'core'
-
-local timerCache = {}
-
-local function convertRange(lines, range)
- local start_row, start_col = lines:rowcol(range.start)
- local finish_row, finish_col = lines:rowcol(range.finish)
- local result = {
- startLine = start_row - 1,
- endLine = finish_row - 2,
- kind = range.kind,
- }
- if result.startLine >= result.endLine then
- return nil
- end
- return result
-end
-
---- @param lsp LSP
---- @param params table
---- @return function
-return function (lsp, params)
- local uri = params.textDocument.uri
- if timerCache[uri] then
- timerCache[uri]:remove()
- timerCache[uri] = nil
- end
-
- return function (response)
- local clock = os.clock()
- timerCache[uri] = ac.loop(0.1, function (t)
- local vm, lines = lsp:getVM(uri)
- if not vm then
- if os.clock() - clock > 10 then
- t:remove()
- timerCache[uri] = nil
- response(nil)
- end
- return
- end
-
- t:remove()
- timerCache[uri] = nil
-
- local comments = lsp:getComments(uri)
- local ranges = core.foldingRange(vm, comments)
- if not ranges then
- response(nil)
- return
- end
-
- local results = {}
- for _, range in ipairs(ranges) do
- results[#results+1] = convertRange(lines, range)
- end
-
- response(results)
- end)
- end
-end
diff --git a/script/method/textDocument/hover.lua b/script/method/textDocument/hover.lua
deleted file mode 100644
index a456bb0a..00000000
--- a/script/method/textDocument/hover.lua
+++ /dev/null
@@ -1,69 +0,0 @@
-local core = require 'core'
-local config = require 'config'
-
-local function convertRange(lines, range)
- local start_row, start_col = lines:rowcol(range.start)
- local finish_row, finish_col = lines:rowcol(range.finish)
- local result = {
- start = {
- line = start_row - 1,
- character = start_col - 1,
- },
- ['end'] = {
- line = finish_row - 1,
- -- 这里不用-1,因为前端期待的是匹配完成后的位置
- character = finish_col,
- },
- }
- return result
-end
-
---- @param lsp LSP
---- @param params table
---- @return table
-return function (lsp, params)
- if not config.config.hover.enable then
- return nil
- end
- local uri = params.textDocument.uri
- local vm, lines = lsp:loadVM(uri)
- if not vm then
- return nil
- end
- -- lua是从1开始的,因此都要+1
- local position = lines:positionAsChar(params.position.line + 1, params.position.character)
-
- local source = core.findSource(vm, position)
- if not source then
- return nil
- end
-
- local hover = core.hover(source, lsp)
- if not hover then
- return nil
- end
-
- local text = ([[
-```lua
-%s
-```
-```lua
-%s
-```
-%s
-```lua
-%s
-```
-%s
-]]):format(hover.label or '', hover.overloads or '', hover.description or '', hover.enum or '', hover.doc or '')
-
- local response = {
- contents = {
- value = text:gsub("```lua\n\n```", ""),
- kind = 'markdown',
- },
- range = hover.range and convertRange(lines, hover.range),
- }
-
- return response
-end
diff --git a/script/method/textDocument/implementation.lua b/script/method/textDocument/implementation.lua
deleted file mode 100644
index 94991fd4..00000000
--- a/script/method/textDocument/implementation.lua
+++ /dev/null
@@ -1,111 +0,0 @@
-local core = require 'core'
-
-local function checkWorkSpaceComplete(lsp, source)
- if not source:bindValue() then
- return
- end
- if not source:bindValue():get 'cross file' then
- return
- end
- lsp:checkWorkSpaceComplete()
-end
-
-local function findResult(lsp, params)
- local uri = params.textDocument.uri
- local vm, lines = lsp:loadVM(uri)
- if not vm then
- return nil
- end
- -- lua是从1开始的,因此都要+1
- local position = lines:positionAsChar(params.position.line + 1, params.position.character)
- local source = core.findSource(vm, position)
- if not source then
- return nil
- end
-
- checkWorkSpaceComplete(lsp, source)
-
- local positions = core.implementation(vm, source, lsp)
- if not positions then
- return nil
- end
-
- local locations = {}
- for i, position in ipairs(positions) do
- local start, finish, valueUri = position[1], position[2], (position[3] or uri)
- local _, valueLines = lsp:loadVM(valueUri)
- if valueLines then
- local start_row, start_col = valueLines:rowcol(start)
- local finish_row, finish_col = valueLines:rowcol(finish)
- locations[#locations+1] = {
- uri = valueUri,
- range = {
- start = {
- line = start_row - 1,
- character = start_col - 1,
- },
- ['end'] = {
- line = finish_row - 1,
- -- 这里不用-1,因为前端期待的是匹配完成后的位置
- character = finish_col,
- },
- }
- }
- else
- locations[#locations+1] = {
- uri = valueUri,
- range = {
- start = {
- line = 0,
- character = 0,
- },
- ['end'] = {
- line = 0,
- character = 0,
- },
- }
- }
- end
- end
-
- if #locations == 0 then
- return nil
- end
-
- return locations
-end
-
-local LastTask
-
---- @param lsp LSP
---- @param params table
---- @return function
-return function (lsp, params)
- if LastTask then
- LastTask:remove()
- LastTask = nil
- end
- local result = findResult(lsp, params)
- if result then
- return result
- end
- return function (response)
- local count = 0
- LastTask = ac.loop(0.1, function ()
- local result = findResult(lsp, params)
- if result then
- LastTask:remove()
- LastTask = nil
- response(result)
- return
- end
- count = count + 1
- if lsp:isWaitingCompile() and count < 10 then
- return
- end
- LastTask:remove()
- LastTask = nil
- response(nil)
- end)
- end
-end
diff --git a/script/method/textDocument/onTypeFormatting.lua b/script/method/textDocument/onTypeFormatting.lua
deleted file mode 100644
index 8485ab49..00000000
--- a/script/method/textDocument/onTypeFormatting.lua
+++ /dev/null
@@ -1,17 +0,0 @@
---- @param lsp LSP
---- @param params table
---- @return any
-return function (lsp, params)
- local uri = params.textDocument.uri
- local vm, lines = lsp:loadVM(uri)
- --log.debug(table.dump(params))
- if not vm then
- return nil
- end
- local position = lines:position(params.position.line + 1, params.position.character)
- local ch = params.ch
- local options = params.options
- local tabSize = options.tabSize
- local insertSpaces = options.insertSpaces
- return nil
-end
diff --git a/script/method/textDocument/publishDiagnostics.lua b/script/method/textDocument/publishDiagnostics.lua
deleted file mode 100644
index 0a2900fa..00000000
--- a/script/method/textDocument/publishDiagnostics.lua
+++ /dev/null
@@ -1,166 +0,0 @@
-local core = require 'core'
-local lang = require 'language'
-local config = require 'config'
-
-local DiagnosticSeverity = {
- Error = 1,
- Warning = 2,
- Information = 3,
- Hint = 4,
-}
-
---[[
-/**
- * Represents a related message and source code location for a diagnostic. This should be
- * used to point to code locations that cause or related to a diagnostics, e.g when duplicating
- * a symbol in a scope.
- */
-export interface DiagnosticRelatedInformation {
- /**
- * The location of this related diagnostic information.
- */
- location: Location;
-
- /**
- * The message of this related diagnostic information.
- */
- message: string;
-}
-]]--
-
-local function getRange(start, finish, lines)
- local start_row, start_col = lines:rowcol(start)
- local finish_row, finish_col = lines:rowcol(finish)
- return {
- start = {
- line = start_row - 1,
- character = start_col - 1,
- },
- ['end'] = {
- line = finish_row - 1,
- -- 这里不用-1,因为前端期待的是匹配完成后的位置
- character = finish_col,
- },
- }
-end
-
-local function createInfo(lsp, data, lines)
- local diagnostic = {
- source = lang.script.DIAG_DIAGNOSTICS,
- range = getRange(data.start, data.finish, lines),
- severity = data.level,
- message = data.message,
- code = data.code,
- tags = data.tags,
- }
- if data.related then
- local related = {}
- for _, info in ipairs(data.related) do
- local _, lines = lsp:getVM(info.uri)
- if lines then
- local message = info.message
- if not message then
- local start_line = lines:rowcol(info.start)
- local finish_line = lines:rowcol(info.finish)
- local chars = {}
- for n = start_line, finish_line do
- chars[#chars+1] = lines:line(n)
- end
- message = table.concat(chars, '\n')
- end
- related[#related+1] = {
- message = message,
- location = {
- uri = info.uri,
- range = getRange(info.start, info.finish, lines),
- }
- }
- end
- end
- diagnostic.relatedInformation = related
- end
- return diagnostic
-end
-
-local function buildError(err, lines, uri)
- local diagnostic = {
- source = lang.script.DIAG_SYNTAX_CHECK,
- message = lang.script('PARSER_'..err.type, err.info)
- }
- if err.version then
- local currentVersion = err.info and err.info.version or config.config.runtime.version
- if type(err.version) == 'table' then
- diagnostic.message = ('%s(%s)'):format(diagnostic.message, lang.script('DIAG_NEED_VERSION', table.concat(err.version, '/'), currentVersion))
- else
- diagnostic.message = ('%s(%s)'):format(diagnostic.message, lang.script('DIAG_NEED_VERSION', err.version, currentVersion))
- end
- end
- if err.level == 'error' then
- diagnostic.severity = DiagnosticSeverity.Error
- else
- diagnostic.severity = DiagnosticSeverity.Warning
- end
- local startrow, startcol = lines:rowcol(err.start)
- local endrow, endcol = lines:rowcol(err.finish)
- if err.type == 'UNKNOWN' then
- local _, max = lines:range(endrow)
- endcol = max
- end
- local range = {
- start = {
- line = startrow - 1,
- character = startcol - 1,
- },
- ['end'] = {
- line = endrow - 1,
- character = endcol,
- },
- }
- diagnostic.range = range
-
- local related = err.info and err.info.related
- if related then
- local start_line = lines:rowcol(related[1])
- local finish_line = lines:rowcol(related[2])
- local chars = {}
- for n = start_line, finish_line do
- chars[#chars+1] = lines:line(n)
- end
- local message = table.concat(chars, '\n')
- diagnostic.relatedInformation = {
- {
- message = message,
- location = {
- uri = uri,
- range = getRange(related[1], related[2], lines),
- }
- }
- }
- end
- return diagnostic
-end
-
---- @param lsp LSP
---- @param params table
---- @return table
-return function (lsp, params)
- local vm = params.vm
- local lines = params.lines
- local uri = params.uri
- local errs = lsp:getAstErrors(uri)
-
- local diagnostics = {}
- if vm then
- local datas = core.diagnostics(vm, lines, uri)
- for _, data in ipairs(datas) do
- diagnostics[#diagnostics+1] = createInfo(lsp, data, lines)
- end
- end
- if errs then
- for _, err in ipairs(errs) do
- diagnostics[#diagnostics+1] = buildError(err, lines, uri)
- end
- end
-
- return diagnostics
-end
diff --git a/script/method/textDocument/references.lua b/script/method/textDocument/references.lua
deleted file mode 100644
index 6421145e..00000000
--- a/script/method/textDocument/references.lua
+++ /dev/null
@@ -1,89 +0,0 @@
-local core = require 'core'
-local LastTask
-
-local function findReferences(lsp, uri, position)
- local vm = lsp:getVM(uri)
-
- local positions, isGlobal = core.definition(vm, position, 'reference')
- if not positions then
- return nil, isGlobal
- end
-
- local locations = {}
- for i, position in ipairs(positions) do
- local start, finish, valueUri = position[1], position[2], (position[3] or uri)
- local vm, valueLines = lsp:getVM(valueUri)
- if valueLines then
- local start_row, start_col = valueLines:rowcol(start)
- local finish_row, finish_col = valueLines:rowcol(finish)
- locations[#locations+1] = {
- uri = valueUri,
- range = {
- start = {
- line = start_row - 1,
- character = start_col - 1,
- },
- ['end'] = {
- line = finish_row - 1,
- -- 这里不用-1,因为前端期待的是匹配完成后的位置
- character = finish_col,
- },
- }
- }
- elseif vm then
- locations[#locations+1] = {
- uri = valueUri,
- range = {
- start = {
- line = 0,
- character = 0,
- },
- ['end'] = {
- line = 0,
- character = 0,
- },
- }
- }
- end
- end
-
- if #locations == 0 then
- return nil, isGlobal
- end
-
- return locations, isGlobal
-end
-
---- @param lsp LSP
---- @param params table
---- @return function
-return function (lsp, params)
- local uri = params.textDocument.uri
- local declarat = params.context.includeDeclaration
- local vm, lines = lsp:loadVM(uri)
- if not vm then
- return nil
- end
-
- if LastTask then
- LastTask:remove()
- LastTask = nil
- end
-
- -- lua是从1开始的,因此都要+1
- local position = lines:positionAsChar(params.position.line + 1, params.position.character)
-
- return function (response)
- local clock = os.clock()
- LastTask = ac.loop(0.1, function ()
- local positions, isGlobal = findReferences(lsp, uri, position)
- if isGlobal and lsp:isWaitingCompile() and os.clock() - clock < 5 then
- return
- end
- response(positions)
- LastTask:remove()
- LastTask = nil
- end)
- LastTask:onTimer()
- end
-end
diff --git a/script/method/textDocument/rename.lua b/script/method/textDocument/rename.lua
deleted file mode 100644
index b637141d..00000000
--- a/script/method/textDocument/rename.lua
+++ /dev/null
@@ -1,53 +0,0 @@
-local core = require 'core'
-
---- @param lsp LSP
---- @param params table
---- @return table
-return function (lsp, params)
- local uri = params.textDocument.uri
- local newName = params.newName
- local vm, lines = lsp:loadVM(uri)
- if not vm then
- return {}
- end
- local position = lines:positionAsChar(params.position.line + 1, params.position.character)
- local positions = core.rename(vm, position, newName)
- if not positions then
- return {}
- end
-
- local changes = {}
- for _, position in ipairs(positions) do
- local start, finish, uri = position[1], position[2], position[3]
- local _, lines = lsp:getVM(uri)
- if not lines then
- goto CONTINUE
- end
- local start_row, start_col = lines:rowcol(start)
- local finish_row, finish_col = lines:rowcol(finish)
- if not changes[uri] then
- changes[uri] = {}
- end
- changes[uri][#changes[uri]+1] = {
- newText = newName,
- range = {
- start = {
- line = start_row - 1,
- character = start_col - 1,
- },
- ['end'] = {
- line = finish_row - 1,
- -- 这里不用-1,因为前端期待的是匹配完成后的位置
- character = finish_col,
- },
- }
- }
- ::CONTINUE::
- end
-
- local response = {
- changes = changes,
- }
-
- return response
-end
diff --git a/script/method/textDocument/semanticTokens/full.lua b/script/method/textDocument/semanticTokens/full.lua
deleted file mode 100644
index bc6d2bcb..00000000
--- a/script/method/textDocument/semanticTokens/full.lua
+++ /dev/null
@@ -1,206 +0,0 @@
-local TokenTypes = require 'constant.TokenTypes'
-local TokenModifiers = require 'constant.TokenModifiers'
-local findLib = require 'core.find_lib'
-
-local timerCache = {}
-local constLib = {
- ['_G'] = true,
- ['_VERSION'] = true,
- ['math.pi'] = true,
- ['math.huge'] = true,
- ['math.maxinteger'] = true,
- ['math.mininteger'] = true,
- ['utf8.charpattern'] = true,
- ['io.stdin'] = true,
- ['io.stdout'] = true,
- ['io.stderr'] = true,
- ['package.config'] = true,
- ['package.cpath'] = true,
- ['package.loaded'] = true,
- ['package.loaders'] = true,
- ['package.path'] = true,
- ['package.preload'] = true,
- ['package.searchers'] = true
-}
-
-local Care = {
- ['name'] = function (source, sources)
- if source[1] == '' then
- return
- end
- if source:get 'global' then
- if findLib(source) then
- if source[1] == '_G' then
- return
- end
- sources[#sources+1] = {
- start = source.start,
- finish = source.finish,
- type = TokenTypes.namespace,
- modifieres = TokenModifiers.static,
- }
- return
- end
- sources[#sources+1] = {
- start = source.start,
- finish = source.finish,
- type = TokenTypes.namespace,
- modifieres = TokenModifiers.deprecated,
- }
- elseif source:get 'table index' then
- sources[#sources+1] = {
- start = source.start,
- finish = source.finish,
- type = TokenTypes.property,
- modifieres = TokenModifiers.declaration,
- }
- elseif source:bindLocal() then
- if source:get 'arg' then
- sources[#sources+1] = {
- start = source.start,
- finish = source.finish,
- type = TokenTypes.parameter,
- modifieres = TokenModifiers.declaration,
- }
- end
- if source[1] == '_ENV'
- or source[1] == 'self' then
- return
- end
- local value = source:bindValue()
- local func = value:getFunction()
- if func and func:getSource().name == source then
- sources[#sources+1] = {
- start = source.start,
- finish = source.finish,
- type = TokenTypes.interface,
- modifieres = TokenModifiers.declaration,
- }
- return
- end
- sources[#sources+1] = {
- start = source.start,
- finish = source.finish,
- type = TokenTypes.variable,
- }
- end
- end,
-}
-
-local function buildTokens(sources, lines)
- local tokens = {}
- local lastLine = 0
- local lastStartChar = 0
- for i, source in ipairs(sources) do
- local row, col = lines:rowcol(source.start)
- local line = row - 1
- local startChar = col - 1
- local deltaLine = line - lastLine
- local deltaStartChar
- if deltaLine == 0 then
- deltaStartChar = startChar - lastStartChar
- else
- deltaStartChar = startChar
- end
- lastLine = line
- lastStartChar = startChar
- local len = i * 5 - 5
- tokens[len + 1] = deltaLine
- tokens[len + 2] = deltaStartChar
- tokens[len + 3] = source.finish - source.start + 1 -- length
- tokens[len + 4] = source.type
- tokens[len + 5] = source.modifieres or 0
- end
- return tokens
-end
-
-local function resolveTokens(vm, lines)
- local sources = {}
- for _, source in ipairs(vm.sources) do
- if Care[source.type] then
- Care[source.type](source, sources)
- end
- end
-
- -- 先进行排序
- table.sort(sources, function (a, b)
- return a.start < b.start
- end)
-
- local tokens = buildTokens(sources, lines)
-
- return tokens
-end
-
-local function toArray(map)
- local array = {}
- for k in pairs(map) do
- array[#array+1] = k
- end
- table.sort(array, function (a, b)
- return map[a] < map[b]
- end)
- return array
-end
-
-local function testTokens(vm, lines)
- local text = vm.text
- local sources = {}
- local init = 1
- while true do
- local start, finish = text:find('[%w_%.]+', init)
- if not start then
- break
- end
- init = finish + 1
- local token = text:sub(start, finish)
- local type = token:match '[%w_]+'
- local mod = token:match '%.([%w_]+)'
- sources[#sources+1] = {
- start = start,
- finish = finish,
- type = TokenTypes[type],
- modifieres = TokenModifiers[mod] or 0,
- }
- end
- local tokens = buildTokens(sources, lines)
- log.debug(table.dump(sources))
- log.debug(table.dump(tokens))
- return tokens
-end
-
---- @param lsp LSP
---- @param params table
---- @return function
-return function (lsp, params)
- local uri = params.textDocument.uri
-
- if timerCache[uri] then
- timerCache[uri]:remove()
- timerCache[uri] = nil
- end
-
- return function (response)
- local clock = os.clock()
- timerCache[uri] = ac.loop(0.1, function (t)
- local vm, lines = lsp:getVM(uri)
- if not vm then
- if os.clock() - clock > 10 then
- t:remove()
- timerCache[uri] = nil
- response(nil)
- end
- return
- end
-
- t:remove()
- timerCache[uri] = nil
-
- local tokens = resolveTokens(vm, lines)
- --local tokens = testTokens(vm, lines)
- response {
- data = tokens,
- }
- end)
- end
-end
diff --git a/script/method/textDocument/signatureHelp.lua b/script/method/textDocument/signatureHelp.lua
deleted file mode 100644
index 69cf6c8f..00000000
--- a/script/method/textDocument/signatureHelp.lua
+++ /dev/null
@@ -1,58 +0,0 @@
-local core = require 'core'
-local config = require 'config'
-
---- @param lsp LSP
---- @param params table
---- @return table
-return function (lsp, params)
- if not config.config.signatureHelp.enable then
- return
- end
- local uri = params.textDocument.uri
- local vm, lines = lsp:loadVM(uri)
- if not vm then
- return
- end
- local position = lines:position(params.position.line + 1, params.position.character + 1)
- local hovers = core.signature(vm, position)
- if not hovers then
- return
- end
-
- local hover = hovers[1]
- local desc = {}
- desc[#desc+1] = hover.description
- desc[#desc+1] = hover.doc
- local active
- local signatures = {}
- for i, hover in ipairs(hovers) do
- local signature = {
- label = hover.label,
- documentation = {
- kind = 'markdown',
- value = table.concat(desc, '\n\n'),
- },
- }
- if hover.argLabel then
- if not active then
- active = i
- end
- signature.parameters = {
- {
- label = {
- hover.argLabel[1] - 1,
- hover.argLabel[2],
- }
- }
- }
- end
- signatures[i] = signature
- end
-
- local response = {
- signatures = signatures,
- activeSignature = active and active - 1 or 0,
- }
-
- return response
-end
diff --git a/script/method/workspace/didChangeConfiguration.lua b/script/method/workspace/didChangeConfiguration.lua
deleted file mode 100644
index eca61ebc..00000000
--- a/script/method/workspace/didChangeConfiguration.lua
+++ /dev/null
@@ -1,30 +0,0 @@
-local rpc = require 'rpc'
-
---- @param lsp LSP
-return function (lsp)
- for _, ws in ipairs(lsp.workspaces) do
- local uri = ws.uri
- -- 请求配置
- rpc:request('workspace/configuration', {
- items = {
- {
- scopeUri = uri,
- section = 'Lua',
- },
- {
- scopeUri = uri,
- section = 'files.associations',
- },
- {
- scopeUri = uri,
- section = 'files.exclude',
- }
- },
- }, function (configs)
- lsp:onUpdateConfig(configs[1], {
- associations = configs[2],
- exclude = configs[3],
- })
- end)
- end
-end
diff --git a/script/method/workspace/didChangeWatchedFiles.lua b/script/method/workspace/didChangeWatchedFiles.lua
deleted file mode 100644
index 37466a50..00000000
--- a/script/method/workspace/didChangeWatchedFiles.lua
+++ /dev/null
@@ -1,52 +0,0 @@
-local fs = require 'bee.filesystem'
-local uric = require 'uri'
-local fn = require 'filename'
-
-local FileChangeType = {
- Created = 1,
- Changed = 2,
- Deleted = 3,
-}
-
---- @param lsp LSP
---- @param params table
-return function (lsp, params)
- local needReset = {}
- local needRescan
- for _, change in ipairs(params.changes) do
- local ws = lsp:findWorkspaceFor(change.uri)
- if not ws then
- goto CONTINUE
- end
- local path = uric.decode(change.uri)
- if not path then
- goto CONTINUE
- end
- if change.type == FileChangeType.Created then
- ws:addFile(path)
- if lsp:getVM(change.uri) then
- needReset[ws] = true
- end
- elseif change.type == FileChangeType.Deleted then
- ws:removeFile(path)
- if lsp:getVM(change.uri) then
- needReset[ws] = true
- end
- end
- -- 排除类文件发生更改需要重新扫描
- local filename = path:filename():string()
- if fn.fileNameEq(filename, '.gitignore')
- or fn.fileNameEq(filename, '.gitmodules')
- then
- needRescan = true
- end
- ::CONTINUE::
- end
- if needRescan then
- lsp:reScanFiles()
- end
- -- 缓存过的文件发生变化后,重新计算
- for ws, _ in pairs(needReset) do
- ws:reset()
- end
-end
diff --git a/script/method/workspace/didChangeWorkspaceFolders.lua b/script/method/workspace/didChangeWorkspaceFolders.lua
deleted file mode 100644
index 25c06f4b..00000000
--- a/script/method/workspace/didChangeWorkspaceFolders.lua
+++ /dev/null
@@ -1,43 +0,0 @@
-local rpc = require 'rpc'
-
---- @param lsp LSP
---- @param params table
-return function (lsp, params)
- local event = params.event
-
- for _, removed in ipairs(event.removed) do
- lsp:removeWorkspace(removed.name, removed.uri)
- end
-
- for _, added in ipairs(event.added) do
- lsp:addWorkspace(added.name, added.uri)
- end
-
- local ws = lsp.workspaces[1]
- if ws then
- -- 请求工作目录
- local uri = ws.uri
- -- 请求配置
- rpc:request('workspace/configuration', {
- items = {
- {
- scopeUri = uri,
- section = 'Lua',
- },
- {
- scopeUri = uri,
- section = 'files.associations',
- },
- {
- scopeUri = uri,
- section = 'files.exclude',
- }
- },
- }, function (configs)
- lsp:onUpdateConfig(configs[1], {
- associations = configs[2],
- exclude = configs[3],
- })
- end)
- end
-end
diff --git a/script/method/workspace/executeCommand.lua b/script/method/workspace/executeCommand.lua
deleted file mode 100644
index 2ad449e9..00000000
--- a/script/method/workspace/executeCommand.lua
+++ /dev/null
@@ -1,182 +0,0 @@
-local rpc = require 'rpc'
-local lang = require 'language'
-
-local command = {}
-
-local function isContainPos(obj, start, finish)
- if obj.start <= start and obj.finish >= finish then
- return true
- end
- return false
-end
-
-local function isInString(vm, start, finish)
- return vm:eachSource(function (source)
- if source.type == 'string' and isContainPos(source, start, finish) then
- return true
- end
- end)
-end
-
-local function posToRange(lines, start, finish)
- local start_row, start_col = lines:rowcol(start)
- local finish_row, finish_col = lines:rowcol(finish)
- return {
- start = {
- line = start_row - 1,
- character = start_col - 1,
- },
- ['end'] = {
- line = finish_row - 1,
- character = finish_col,
- },
- }
-end
-
---- @param lsp LSP
---- @param data table
-command['lua.removeSpace'] = function (lsp, data)
- local uri = data.uri
- local vm, lines = lsp:getVM(uri)
- if not vm then
- return
- end
-
- local textEdit = {}
- for i = 1, #lines do
- local line = lines:line(i)
- local pos = line:find '[ \t]+$'
- if pos then
- local start, finish = lines:range(i)
- start = start + pos - 1
- if isInString(vm, start, finish) then
- goto NEXT_LINE
- end
- textEdit[#textEdit+1] = {
- range = posToRange(lines, start, finish),
- newText = '',
- }
- goto NEXT_LINE
- end
-
- ::NEXT_LINE::
- end
-
- if #textEdit == 0 then
- return
- end
-
- rpc:request('workspace/applyEdit', {
- label = lang.script.COMMAND_REMOVE_SPACE,
- edit = {
- changes = {
- [uri] = textEdit,
- }
- },
- })
-end
-
-local opMap = {
- ['+'] = true,
- ['-'] = true,
- ['*'] = true,
- ['/'] = true,
- ['//'] = true,
- ['^'] = true,
- ['<<'] = true,
- ['>>'] = true,
- ['&'] = true,
- ['|'] = true,
- ['~'] = true,
- ['..'] = true,
-}
-
-local literalMap = {
- ['number'] = true,
- ['boolean'] = true,
- ['string'] = true,
- ['table'] = true,
-}
-
---- @param lsp LSP
---- @param data table
-command['lua.solve'] = function (lsp, data)
- local uri = data.uri
- local vm, lines = lsp:getVM(uri)
- if not vm then
- return
- end
-
- local start = lines:position(data.range.start.line + 1, data.range.start.character + 1)
- local finish = lines:position(data.range['end'].line + 1, data.range['end'].character)
-
- local result = vm:eachSource(function (source)
- if not isContainPos(source, start, finish) then
- return
- end
- if source.op ~= 'or' then
- return
- end
- local first = source[1]
- local second = source[2]
- -- (a + b) or 0 --> a + (b or 0)
- do
- if opMap[first.op]
- and first.type ~= 'unary'
- and not second.op
- and literalMap[second.type]
- then
- return {
- start = source[1][2].start,
- finish = source[2].finish,
- }
- end
- end
- -- a or (b + c) --> (a or b) + c
- do
- if opMap[second.op]
- and second.type ~= 'unary'
- and not first.op
- and literalMap[second[1].type]
- then
- return {
- start = source[1].start,
- finish = source[2][1].finish,
- }
- end
- end
- end)
-
- if not result then
- return
- end
-
- rpc:request('workspace/applyEdit', {
- label = lang.script.COMMAND_ADD_BRACKETS,
- edit = {
- changes = {
- [uri] = {
- {
- range = posToRange(lines, result.start, result.start - 1),
- newText = '(',
- },
- {
- range = posToRange(lines, result.finish + 1, result.finish),
- newText = ')',
- },
- }
- }
- },
- })
-end
-
---- @param lsp LSP
---- @param params table
-return function (lsp, params)
- local name = params.command:gsub(':.+', '')
- if not command[name] then
- return
- end
- local result = command[name](lsp, params.arguments[1])
- return result
-end
diff --git a/script/method/workspace/symbol.lua b/script/method/workspace/symbol.lua
deleted file mode 100644
index 71ae658a..00000000
--- a/script/method/workspace/symbol.lua
+++ /dev/null
@@ -1,103 +0,0 @@
-local matchKey = require 'core.matchKey'
-
-local SymbolKind = {
- File = 1,
- Module = 2,
- Namespace = 3,
- Package = 4,
- Class = 5,
- Method = 6,
- Property = 7,
- Field = 8,
- Constructor = 9,
- Enum = 10,
- Interface = 11,
- Function = 12,
- Variable = 13,
- Constant = 14,
- String = 15,
- Number = 16,
- Boolean = 17,
- Array = 18,
- Object = 19,
- Key = 20,
- Null = 21,
- EnumMember = 22,
- Struct = 23,
- Event = 24,
- Operator = 25,
- TypeParameter = 26,
-}
-
-local function convertRange(lines, range)
- local start_row, start_col = lines:rowcol(range.start)
- local finish_row, finish_col = lines:rowcol(range.finish)
- local result = {
- start = {
- line = start_row - 1,
- character = start_col - 1,
- },
- ['end'] = {
- line = finish_row - 1,
- -- 这里不用-1,因为前端期待的是匹配完成后的位置
- character = finish_col,
- },
- }
- return result
-end
-
-local function collect(results, source, uri, lines)
- if source:action() ~= 'set'
- and source:action() ~= 'local' then
- return
- end
- local kind = SymbolKind.Variable
- local value = source:bindValue()
- if value and value:getFunction() then
- kind = SymbolKind.Function
- else
- if source:get 'global' then
- kind = SymbolKind.Namespace
- elseif source:get 'table index' then
- kind = SymbolKind.EnumMember
- end
- end
- results[#results+1] = {
- name = source[1],
- kind = kind,
- location = {
- uri = uri,
- range = convertRange(lines, source),
- }
- }
-end
-
-local function searchVM(lsp, results, query, uri)
- local vm, lines = lsp:getVM(uri)
- if not vm then
- return
- end
- vm:eachSource(function (src)
- if src.type == 'name' then
- if src[1] == '' then
- return
- end
- if matchKey(query, src[1]) then
- collect(results, src, uri, lines)
- end
- end
- end)
-end
-
---- @param lsp LSP
---- @param params table
-return function (lsp, params)
- local query = params.query
- local results = {}
-
- for uri in lsp:eachFile() do
- searchVM(lsp, results, query, uri)
- end
-
- return results
-end
diff --git a/script/parser/ast.lua b/script/parser/ast.lua
deleted file mode 100644
index af2c76c9..00000000
--- a/script/parser/ast.lua
+++ /dev/null
@@ -1,1913 +0,0 @@
-local tonumber = tonumber
-local string_char = string.char
-local utf8_char = utf8.char
-local type = type
-local table = table
-
-local Errs
-local State
-local function pushError(err)
- 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
-
--- goto 单独处理
-local RESERVED = {
- ['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 VersionOp = {
- ['&'] = {'Lua 5.3', 'Lua 5.4'},
- ['~'] = {'Lua 5.3', 'Lua 5.4'},
- ['|'] = {'Lua 5.3', 'Lua 5.4'},
- ['<<'] = {'Lua 5.3', 'Lua 5.4'},
- ['>>'] = {'Lua 5.3', 'Lua 5.4'},
- ['//'] = {'Lua 5.3', 'Lua 5.4'},
-}
-
-local function checkOpVersion(op, start)
- local versions = VersionOp[op]
- if not versions then
- return
- end
- for i = 1, #versions do
- if versions[i] == State.Version then
- return
- end
- end
- pushError {
- type = 'UNSUPPORT_SYMBOL',
- start = start,
- finish = start + #op - 1,
- version = versions,
- info = {
- version = State.Version,
- }
- }
-end
-
-local Exp
-
-local function expSplit(list, start, finish, level)
- if start == finish then
- return list[start]
- end
- local info = Exp[level]
- if not info then
- return
- end
- local func = info[1]
- return func(list, start, finish, level)
-end
-
-local function binaryForward(list, start, finish, level)
- local info = Exp[level]
- for i = finish-1, start+2, -1 do
- local op = list[i]
- if info[op] then
- local e1 = expSplit(list, start, i-2, level)
- if not e1 then
- goto CONTINUE
- end
- local e2 = expSplit(list, i+1, finish, level+1)
- if not e2 then
- goto CONTINUE
- end
- checkOpVersion(op, list[i-1])
- return {
- type = 'binary',
- op = op,
- start = e1.start,
- finish = e2.finish,
- [1] = e1,
- [2] = e2,
- }
- end
- ::CONTINUE::
- end
- return expSplit(list, start, finish, level+1)
-end
-
-local function binaryBackward(list, start, finish, level)
- local info = Exp[level]
- for i = start+2, finish-1 do
- local op = list[i]
- if info[op] then
- local e1 = expSplit(list, start, i-2, level+1)
- if not e1 then
- goto CONTINUE
- end
- local e2 = expSplit(list, i+1, finish, level)
- if not e2 then
- goto CONTINUE
- end
- checkOpVersion(op, list[i-1])
- return {
- type = 'binary',
- op = op,
- start = e1.start,
- finish = e2.finish,
- [1] = e1,
- [2] = e2,
- }
- end
- ::CONTINUE::
- end
- return expSplit(list, start, finish, level+1)
-end
-
-local function unary(list, start, finish, level)
- local info = Exp[level]
- local op = list[start+1]
- if info[op] then
- local e1 = expSplit(list, start+2, finish, level)
- if e1 then
- checkOpVersion(op, list[start])
- return {
- type = 'unary',
- op = op,
- start = list[start],
- finish = e1.finish,
- [1] = e1,
- }
- end
- end
- return expSplit(list, start, finish, level+1)
-end
-
-local function checkMissEnd(start)
- if not State.MissEndErr then
- return
- end
- local err = State.MissEndErr
- State.MissEndErr = nil
- local _, finish = State.Lua:find('[%w_]+', start)
- if not finish then
- return
- end
- err.info.related = { start, finish }
- pushError {
- type = 'MISS_END',
- start = start,
- finish = finish,
- }
-end
-
-Exp = {
- {
- ['or'] = true,
- binaryForward,
- },
- {
- ['and'] = true,
- binaryForward,
- },
- {
- ['<='] = true,
- ['>='] = true,
- ['<'] = true,
- ['>'] = true,
- ['~='] = true,
- ['=='] = true,
- binaryForward,
- },
- {
- ['|'] = true,
- binaryForward,
- },
- {
- ['~'] = true,
- binaryForward,
- },
- {
- ['&'] = true,
- binaryForward,
- },
- {
- ['<<'] = true,
- ['>>'] = true,
- binaryForward,
- },
- {
- ['..'] = true,
- binaryBackward,
- },
- {
- ['+'] = true,
- ['-'] = true,
- binaryForward,
- },
- {
- ['*'] = true,
- ['//'] = true,
- ['/'] = true,
- ['%'] = true,
- binaryForward,
- },
- {
- ['^'] = true,
- binaryBackward,
- },
- {
- ['not'] = true,
- ['#'] = true,
- ['~'] = true,
- ['-'] = true,
- unary,
- },
-}
-
-local Defs = {
- Nil = function (pos)
- return {
- type = 'nil',
- start = pos,
- finish = pos + 2,
- }
- end,
- True = function (pos)
- return {
- type = 'boolean',
- start = pos,
- finish = pos + 3,
- [1] = true,
- }
- end,
- False = function (pos)
- return {
- type = 'boolean',
- start = pos,
- finish = pos + 4,
- [1] = false,
- }
- end,
- LongComment = function (beforeEq, afterEq, str, finish, missPos)
- State.Comments[#State.Comments+1] = {
- start = beforeEq,
- finish = finish,
- }
- if missPos then
- local endSymbol = ']' .. ('='):rep(afterEq-beforeEq) .. ']'
- local s, _, w = str:find('(%][%=]*%])[%c%s]*$')
- if s then
- pushError {
- type = 'ERR_LCOMMENT_END',
- start = missPos - #str + s - 1,
- finish = missPos - #str + s + #w - 2,
- info = {
- symbol = endSymbol,
- },
- fix = {
- title = 'FIX_LCOMMENT_END',
- {
- start = missPos - #str + s - 1,
- finish = missPos - #str + s + #w - 2,
- text = endSymbol,
- }
- },
- }
- end
- pushError {
- type = 'MISS_SYMBOL',
- start = missPos,
- finish = missPos,
- info = {
- symbol = endSymbol,
- },
- fix = {
- title = 'ADD_LCOMMENT_END',
- {
- start = missPos,
- finish = missPos,
- text = endSymbol,
- }
- },
- }
- end
- end,
- CLongComment = function (start1, finish1, start2, finish2)
- pushError {
- type = 'ERR_C_LONG_COMMENT',
- start = start1,
- finish = finish2 - 1,
- fix = {
- title = 'FIX_C_LONG_COMMENT',
- {
- start = start1,
- finish = finish1 - 1,
- text = '--[[',
- },
- {
- start = start2,
- finish = finish2 - 1,
- text = '--]]'
- },
- }
- }
- end,
- CCommentPrefix = function (start, finish)
- pushError {
- type = 'ERR_COMMENT_PREFIX',
- start = start,
- finish = finish - 1,
- fix = {
- title = 'FIX_COMMENT_PREFIX',
- {
- start = start,
- finish = finish - 1,
- text = '--',
- },
- }
- }
- return false
- end,
- String = function (start, quote, str, finish)
- return {
- type = 'string',
- start = start,
- finish = finish - 1,
- [1] = str,
- [2] = quote,
- }
- end,
- LongString = function (beforeEq, afterEq, str, missPos)
- if missPos then
- local endSymbol = ']' .. ('='):rep(afterEq-beforeEq) .. ']'
- local s, _, w = str:find('(%][%=]*%])[%c%s]*$')
- if s then
- pushError {
- type = 'ERR_LSTRING_END',
- start = missPos - #str + s - 1,
- finish = missPos - #str + s + #w - 2,
- info = {
- symbol = endSymbol,
- },
- fix = {
- title = 'FIX_LSTRING_END',
- {
- start = missPos - #str + s - 1,
- finish = missPos - #str + s + #w - 2,
- text = endSymbol,
- }
- },
- }
- end
- pushError {
- type = 'MISS_SYMBOL',
- start = missPos,
- finish = missPos,
- info = {
- symbol = endSymbol,
- },
- fix = {
- title = 'ADD_LSTRING_END',
- {
- start = missPos,
- finish = missPos,
- text = endSymbol,
- }
- },
- }
- end
- return '[' .. ('='):rep(afterEq-beforeEq) .. '[', str
- end,
- Char10 = function (char)
- char = tonumber(char)
- if not char or char < 0 or char > 255 then
- return ''
- end
- return string_char(char)
- end,
- Char16 = function (pos, char)
- if State.Version == 'Lua 5.1' then
- pushError {
- type = 'ERR_ESC',
- start = pos-1,
- finish = pos,
- version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'},
- info = {
- version = State.Version,
- }
- }
- return char
- end
- return string_char(tonumber(char, 16))
- end,
- CharUtf8 = function (pos, char)
- if State.Version ~= 'Lua 5.3'
- and State.Version ~= 'Lua 5.4'
- and State.Version ~= 'LuaJIT'
- then
- pushError {
- type = 'ERR_ESC',
- start = pos-3,
- finish = pos-2,
- version = {'Lua 5.3', 'Lua 5.4', 'LuaJIT'},
- info = {
- version = State.Version,
- }
- }
- return char
- end
- if #char == 0 then
- pushError {
- type = 'UTF8_SMALL',
- start = pos-3,
- finish = pos,
- }
- return ''
- end
- local v = tonumber(char, 16)
- if not v then
- for i = 1, #char do
- if not tonumber(char:sub(i, i), 16) then
- pushError {
- type = 'MUST_X16',
- start = pos + i - 1,
- finish = pos + i - 1,
- }
- end
- end
- return ''
- end
- if State.Version == 'Lua 5.4' then
- if v < 0 or v > 0x7FFFFFFF then
- pushError {
- type = 'UTF8_MAX',
- start = pos-3,
- finish = pos+#char,
- info = {
- min = '00000000',
- max = '7FFFFFFF',
- }
- }
- end
- else
- if v < 0 or v > 0x10FFFF then
- pushError {
- type = 'UTF8_MAX',
- start = pos-3,
- finish = pos+#char,
- version = v <= 0x7FFFFFFF and 'Lua 5.4' or nil,
- info = {
- min = '000000',
- max = '10FFFF',
- }
- }
- end
- end
- if v >= 0 and v <= 0x10FFFF then
- return utf8_char(v)
- end
- return ''
- end,
- Number = function (start, number, finish)
- local n = tonumber(number)
- if n then
- State.LastNumber = {
- type = 'number',
- start = start,
- finish = finish - 1,
- [1] = n,
- [2] = number,
- }
- return State.LastNumber
- else
- pushError {
- type = 'MALFORMED_NUMBER',
- start = start,
- finish = finish - 1,
- }
- State.LastNumber = {
- type = 'number',
- start = start,
- finish = finish - 1,
- [1] = 0,
- }
- return State.LastNumber
- end
- end,
- FFINumber = function (start, symbol)
- if math.type(State.LastNumber[1]) == 'float' then
- pushError {
- type = 'UNKNOWN_SYMBOL',
- start = start,
- finish = start + #symbol - 1,
- info = {
- symbol = symbol,
- }
- }
- State.LastNumber[1] = 0
- return
- end
- if State.Version ~= 'LuaJIT' then
- pushError {
- type = 'UNSUPPORT_SYMBOL',
- start = start,
- finish = start + #symbol - 1,
- version = 'LuaJIT',
- info = {
- version = State.Version,
- }
- }
- State.LastNumber[1] = 0
- end
- end,
- ImaginaryNumber = function (start, symbol)
- if State.Version ~= 'LuaJIT' then
- pushError {
- type = 'UNSUPPORT_SYMBOL',
- start = start,
- finish = start + #symbol - 1,
- version = 'LuaJIT',
- info = {
- version = State.Version,
- }
- }
- end
- State.LastNumber[1] = 0
- end,
- Name = function (start, str, finish)
- local isKeyWord
- if RESERVED[str] then
- isKeyWord = true
- elseif str == 'goto' then
- if State.Version ~= 'Lua 5.1' and State.Version ~= 'LuaJIT' then
- isKeyWord = true
- end
- end
- if isKeyWord then
- pushError {
- type = 'KEYWORD',
- start = start,
- finish = finish - 1,
- }
- end
- return {
- type = 'name',
- start = start,
- finish = finish - 1,
- [1] = str,
- }
- end,
- Simple = function (first, ...)
- if ... then
- local obj = {
- type = 'simple',
- start = first.start,
- first, ...,
- }
- local last = obj[#obj]
- obj.finish = last.finish
- return obj
- elseif first == '' then
- return nil
- else
- return first
- end
- end,
- SimpleCall = function (simple)
- if not simple then
- return nil
- end
- if simple.type ~= 'simple' then
- pushError {
- type = 'EXP_IN_ACTION',
- start = simple.start,
- finish = simple.finish,
- }
- return simple
- end
- local last = simple[#simple]
- if last.type == 'call' then
- return simple
- end
- local colon = simple[#simple-1]
- if colon and colon.type == ':' then
- -- 型如 `obj:method`,将错误让给MISS_SYMBOL
- return simple
- end
- pushError {
- type = 'EXP_IN_ACTION',
- start = simple[1].start,
- finish = last.finish,
- }
- return simple
- end,
- Exp = function (first, ...)
- if not ... then
- return first
- end
- local list = {first, ...}
- return expSplit(list, 1, #list, 1)
- end,
- Prefix = function (start, exp, finish)
- exp.brackets = true
- return exp
- end,
- Index = function (start, exp, finish)
- return {
- type = 'index',
- start = start,
- finish = finish - 1,
- [1] = exp,
- }
- end,
- Call = function (start, arg, finish)
- if arg == nil then
- return {
- type = 'call',
- start = start,
- finish = finish - 1,
- }
- end
- if arg.type == 'list' then
- arg.type = 'call'
- arg.start = start
- arg.finish = finish - 1
- return arg
- end
- local obj = {
- type = 'call',
- start = start,
- finish = finish - 1,
- [1] = arg,
- }
- return obj
- end,
- DOTS = function (start)
- return {
- type = '...',
- start = start,
- finish = start + 2,
- }
- end,
- DotsAsArg = function (obj)
- State.Dots[#State.Dots] = true
- return obj
- end,
- DotsAsExp = function (obj)
- if not State.Dots[#State.Dots] then
- pushError {
- type = 'UNEXPECT_DOTS',
- start = obj.start,
- finish = obj.finish,
- }
- end
- return obj
- end,
- COLON = function (start)
- return {
- type = ':',
- start = start,
- finish = start,
- }
- end,
- DOT = function (start)
- return {
- type = '.',
- start = start,
- finish = start,
- }
- end,
- Function = function (start, argStart, arg, argFinish, ...)
- local obj = {
- type = 'function',
- start = start,
- arg = arg,
- argStart = argStart - 1,
- argFinish = argFinish,
- ...
- }
- local max = #obj
- obj.finish = obj[max] - 1
- obj[max] = nil
- if obj.argFinish > obj.finish then
- obj.argFinish = obj.finish
- end
- checkMissEnd(start)
- return obj
- end,
- NamedFunction = function (start, name, argStart, arg, argFinish, ...)
- local obj = {
- type = 'function',
- start = start,
- name = name,
- arg = arg,
- argStart = argStart - 1,
- argFinish = argFinish,
- ...
- }
- local max = #obj
- obj.finish = obj[max] - 1
- obj[max] = nil
- if obj.argFinish > obj.finish then
- obj.argFinish = obj.finish
- end
- checkMissEnd(start)
- return obj
- end,
- LocalFunction = function (start, name, argStart, arg, argFinish, ...)
- local obj = {
- type = 'localfunction',
- start = start,
- name = name,
- arg = arg,
- argStart = argStart - 1,
- argFinish = argFinish,
- ...
- }
- local max = #obj
- obj.finish = obj[max] - 1
- obj[max] = nil
- if obj.argFinish > obj.finish then
- obj.argFinish = obj.finish
- end
-
- if name.type ~= 'name' then
- pushError {
- type = 'UNEXPECT_LFUNC_NAME',
- start = name.start,
- finish = name.finish,
- }
- end
-
- checkMissEnd(start)
- return obj
- end,
- Table = function (start, ...)
- local args = {...}
- local max = #args
- local finish = args[max] - 1
- local table = {
- type = 'table',
- start = start,
- finish = finish
- }
- start = start + 1
- local wantField = true
- for i = 1, max-1 do
- local arg = args[i]
- local isField = type(arg) == 'table'
- local isEmmy = isField and arg.type:sub(1, 4) == 'emmy'
- if wantField and not isField then
- pushError {
- type = 'MISS_EXP',
- start = start,
- finish = arg - 1,
- }
- elseif not wantField and isField and not isEmmy then
- pushError {
- type = 'MISS_SEP_IN_TABLE',
- start = start,
- finish = arg.start-1,
- }
- end
- if isField then
- table[#table+1] = arg
- if not isEmmy then
- wantField = false
- start = arg.finish + 1
- end
- else
- wantField = true
- start = arg
- end
- end
- return table
- end,
- NewField = function (key, value)
- return {
- type = 'pair',
- start = key.start,
- finish = value.finish,
- key, value,
- }
- end,
- NewIndex = function (key, value)
- return {
- type = 'pair',
- start = key.start,
- finish = value.finish,
- key, value,
- }
- end,
- List = function (first, second, ...)
- if second then
- local list = {
- type = 'list',
- start = first.start,
- first, second, ...
- }
- local last = list[#list]
- list.finish = last.finish
- return list
- elseif type(first) == 'table' then
- return first
- else
- return nil
- end
- end,
- ArgList = function (...)
- if ... == '' then
- return nil
- end
- local args = table.pack(...)
- local list = {}
- local max = args.n
- args.n = nil
- local wantName = true
- for i = 1, max do
- local obj = args[i]
- if type(obj) == 'number' then
- if wantName then
- pushError {
- type = 'MISS_NAME',
- start = obj,
- finish = obj,
- }
- end
- wantName = true
- else
- if not wantName then
- pushError {
- type = 'MISS_SYMBOL',
- start = obj.start-1,
- finish = obj.start-1,
- info = {
- symbol = ',',
- }
- }
- end
- wantName = false
- list[#list+1] = obj
- if obj.type == '...' then
- if i < max then
- local a = args[i+1]
- local b = args[max]
- pushError {
- type = 'ARGS_AFTER_DOTS',
- start = type(a) == 'number' and a or a.start,
- finish = type(b) == 'number' and b or b.finish,
- }
- end
- break
- end
- end
- end
- if wantName then
- local last = args[max]
- pushError {
- type = 'MISS_NAME',
- start = last+1,
- finish = last+1,
- }
- end
- if #list == 0 then
- return nil
- elseif #list == 1 then
- return list[1]
- else
- list.type = 'list'
- list.start = list[1].start
- list.finish = list[#list].finish
- return list
- end
- end,
- CallArgList = function (start, ...)
- local args = {...}
- local max = #args
- local finish = args[max] - 1
- local exps = {
- type = 'list',
- start = start,
- finish = finish,
- }
- local wantExp = true
- for i = 1, max-1 do
- local arg = args[i]
- local isExp = type(arg) == 'table'
- if wantExp and not isExp then
- pushError {
- type = 'MISS_EXP',
- start = start,
- finish = arg - 1,
- }
- elseif not wantExp and isExp then
- pushError {
- type = 'MISS_SYMBOL',
- start = start,
- finish = arg.start-1,
- info = {
- symbol = ',',
- }
- }
- end
- if isExp then
- exps[#exps+1] = arg
- wantExp = false
- start = arg.finish + 1
- else
- wantExp = true
- start = arg
- end
- end
- if wantExp then
- pushError {
- type = 'MISS_EXP',
- start = start,
- finish = finish,
- }
- end
- if #exps == 0 then
- return nil
- elseif #exps == 1 then
- return exps[1]
- else
- return exps
- end
- end,
- Nothing = function ()
- return nil
- end,
- None = function()
- return
- end,
- Skip = function ()
- return false
- end,
- Set = function (keys, values)
- return {
- type = 'set',
- keys, values,
- }
- end,
- LocalTag = function (...)
- if not ... or ... == '' then
- return nil
- end
- local tags = {...}
- for i, tag in ipairs(tags) do
- if State.Version ~= 'Lua 5.4' then
- pushError {
- type = 'UNSUPPORT_SYMBOL',
- start = tag.start,
- finish = tag.finish,
- version = 'Lua 5.4',
- info = {
- version = State.Version,
- }
- }
- elseif tag[1] ~= 'const' and tag[1] ~= 'close' then
- pushError {
- type = 'UNKNOWN_TAG',
- start = tag.start,
- finish = tag.finish,
- info = {
- tag = tag[1],
- }
- }
- elseif i > 1 then
- pushError {
- type = 'MULTI_TAG',
- start = tag.start,
- finish = tag.finish,
- info = {
- tag = tag[1],
- }
- }
- end
- end
- return tags
- end,
- LocalName = function (name, tags)
- name.tags = tags
- return name
- end,
- Local = function (keys, values)
- return {
- type = 'local',
- keys, values,
- }
- end,
- DoBody = function (...)
- if ... == '' then
- return {
- type = 'do',
- }
- else
- return {
- type = 'do',
- ...
- }
- end
- end,
- Do = function (start, action, finish)
- action.start = start
- action.finish = finish - 1
- checkMissEnd(start)
- return action
- end,
- Break = function (finish, ...)
- if State.Break > 0 then
- local breakChunk = {
- type = 'break',
- }
- if not ... then
- return breakChunk
- end
- local action = select(-1, ...)
- if not action then
- return breakChunk
- end
- if State.Version == 'Lua 5.1' or State.Version == 'LuaJIT' then
- pushError {
- type = 'ACTION_AFTER_BREAK',
- start = finish - #'break',
- finish = finish - 1,
- }
- end
- return breakChunk, action
- else
- pushError {
- type = 'BREAK_OUTSIDE',
- start = finish - #'break',
- finish = finish - 1,
- }
- if not ... then
- return false
- end
- local action = select(-1, ...)
- if not action then
- return false
- end
- return action
- end
- end,
- BreakStart = function ()
- State.Break = State.Break + 1
- end,
- BreakEnd = function ()
- State.Break = State.Break - 1
- end,
- Return = function (start, exp, finish)
- if not finish then
- finish = exp
- exp = {
- type = 'return',
- start = start,
- finish = finish - 1,
- }
- else
- if exp.type == 'list' then
- exp.type = 'return'
- exp.start = start
- exp.finish = finish - 1
- else
- exp = {
- type = 'return',
- start = start,
- finish = finish - 1,
- [1] = exp,
- }
- end
- end
- return exp
- end,
- Label = function (start, name, finish)
- if State.Version == 'Lua 5.1' then
- pushError {
- type = 'UNSUPPORT_SYMBOL',
- start = start,
- finish = finish - 1,
- version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'},
- info = {
- version = State.Version,
- }
- }
- return false
- end
- name.type = 'label'
- local labels = State.Label[#State.Label]
- local str = name[1]
- if labels[str] then
- --pushError {
- -- type = 'REDEFINE_LABEL',
- -- start = name.start,
- -- finish = name.finish,
- -- info = {
- -- label = str,
- -- related = {labels[str].start, labels[str].finish},
- -- }
- --}
- else
- labels[str] = name
- end
- return name
- end,
- GoTo = function (start, name, finish)
- if State.Version == 'Lua 5.1' then
- pushError {
- type = 'UNSUPPORT_SYMBOL',
- start = start,
- finish = finish - 1,
- version = {'Lua 5.2', 'Lua 5.3', 'Lua 5.4', 'LuaJIT'},
- info = {
- version = State.Version,
- }
- }
- return false
- end
- name.type = 'goto'
- local labels = State.Label[#State.Label]
- labels[#labels+1] = name
- return name
- end,
- -- TODO 这里的检查不完整,但是完整的检查比较复杂,开销比较高
- -- 不能jump到另一个局部变量的作用域
- -- 函数会切断goto与label
- -- 不能从block外jump到block内,但是可以从block内jump到block外
- BlockStart = function ()
- State.Label[#State.Label+1] = {}
- State.Dots[#State.Dots+1] = false
- end,
- BlockEnd = function ()
- local labels = State.Label[#State.Label]
- State.Label[#State.Label] = nil
- State.Dots[#State.Dots] = nil
- for i = 1, #labels do
- local name = labels[i]
- local str = name[1]
- if not labels[str] then
- pushError {
- type = 'NO_VISIBLE_LABEL',
- start = name.start,
- finish = name.finish,
- info = {
- label = str,
- }
- }
- end
- end
- end,
- IfBlock = function (exp, start, ...)
- local obj = {
- filter = exp,
- start = start,
- ...
- }
- local max = #obj
- obj.finish = obj[max]
- obj[max] = nil
- return obj
- end,
- ElseIfBlock = function (exp, start, ...)
- local obj = {
- filter = exp,
- start = start,
- ...
- }
- local max = #obj
- obj.finish = obj[max]
- obj[max] = nil
- return obj
- end,
- ElseBlock = function (start, ...)
- local obj = {
- start = start,
- ...
- }
- local max = #obj
- obj.finish = obj[max]
- obj[max] = nil
- return obj
- end,
- If = function (start, ...)
- local obj = {
- type = 'if',
- start = start,
- ...
- }
- local max = #obj
- obj.finish = obj[max] - 1
- obj[max] = nil
- checkMissEnd(start)
- return obj
- end,
- Loop = function (start, arg, min, max, step, ...)
- local obj = {
- type = 'loop',
- start = start,
- arg = arg,
- min = min,
- max = max,
- step = step,
- ...
- }
- local max = #obj
- obj.finish = obj[max] - 1
- obj[max] = nil
- checkMissEnd(start)
- return obj
- end,
- In = function (start, arg, exp, ...)
- local obj = {
- type = 'in',
- start = start,
- arg = arg,
- exp = exp,
- ...
- }
- local max = #obj
- obj.finish = obj[max] - 1
- obj[max] = nil
- checkMissEnd(start)
- return obj
- end,
- While = function (start, filter, ...)
- local obj = {
- type = 'while',
- start = start,
- filter = filter,
- ...
- }
- local max = #obj
- obj.finish = obj[max] - 1
- obj[max] = nil
- checkMissEnd(start)
- return obj
- end,
- Repeat = function (start, ...)
- local obj = {
- type = 'repeat',
- start = start,
- ...
- }
- local max = #obj
- obj.finish = obj[max] - 1
- obj.filter = obj[max-1]
- obj[max] = nil
- obj[max-1] = nil
- return obj
- end,
- Lua = function (...)
- if ... == '' then
- return {}
- end
- return {...}
- end,
-
- -- EmmyLua 支持
- EmmyName = function (start, str)
- return {
- type = 'emmyName',
- start = start,
- finish = start + #str - 1,
- [1] = str,
- }
- end,
- DirtyEmmyName = function (pos)
- pushError {
- type = 'MISS_NAME',
- level = 'warning',
- start = pos,
- finish = pos,
- }
- return {
- type = 'emmyName',
- start = pos-1,
- finish = pos-1,
- [1] = ''
- }
- end,
- EmmyClass = function (class, startPos, extends)
- if extends and extends[1] == '' then
- extends.start = startPos
- end
- return {
- type = 'emmyClass',
- start = class.start,
- finish = (extends or class).finish,
- [1] = class,
- [2] = extends,
- }
- end,
- EmmyType = function (typeDef)
- return typeDef
- end,
- EmmyCommonType = function (...)
- local result = {
- type = 'emmyType',
- ...
- }
- for i = 1, #result // 2 do
- local startPos = result[i * 2]
- local emmyName = result[i * 2 + 1]
- if emmyName[1] == '' then
- emmyName.start = startPos
- end
- result[i + 1] = emmyName
- end
- for i = #result // 2 + 2, #result do
- result[i] = nil
- end
- result.start = result[1].start
- result.finish = result[#result].finish
- return result
- end,
- EmmyArrayType = function (start, emmy, _, finish)
- emmy.type = 'emmyArrayType'
- emmy.start = start
- emmy.finish = finish - 1
- return emmy
- end,
- EmmyTableType = function (start, keyType, valueType, finish)
- return {
- type = 'emmyTableType',
- start = start,
- finish = finish - 1,
- [1] = keyType,
- [2] = valueType,
- }
- end,
- EmmyFunctionType = function (start, args, returns, finish)
- local result = {
- start = start,
- finish = finish - 1,
- type = 'emmyFunctionType',
- args = args,
- returns = returns,
- }
- return result
- end,
- EmmyFunctionRtns = function (...)
- return {...}
- end,
- EmmyFunctionArgs = function (...)
- local args = {...}
- args[#args] = nil
- return args
- end,
- EmmyAlias = function (name, emmyName, ...)
- return {
- type = 'emmyAlias',
- start = name.start,
- finish = emmyName.finish,
- name,
- emmyName,
- ...
- }
- end,
- EmmyParam = function (argName, emmyName, option, ...)
- local emmy = {
- type = 'emmyParam',
- option = option,
- argName,
- emmyName,
- ...
- }
- emmy.start = emmy[1].start
- emmy.finish = emmy[#emmy].finish
- return emmy
- end,
- EmmyReturn = function (start, type, name, finish, option)
- local emmy = {
- type = 'emmyReturn',
- option = option,
- start = start,
- finish = finish - 1,
- [1] = type,
- [2] = name,
- }
- return emmy
- end,
- EmmyField = function (access, fieldName, ...)
- local obj = {
- type = 'emmyField',
- access, fieldName,
- ...
- }
- obj.start = obj[2].start
- obj.finish = obj[3].finish
- return obj
- end,
- EmmyGenericBlock = function (genericName, parentName)
- return {
- start = genericName.start,
- finish = parentName and parentName.finish or genericName.finish,
- genericName,
- parentName,
- }
- end,
- EmmyGeneric = function (...)
- local emmy = {
- type = 'emmyGeneric',
- ...
- }
- emmy.start = emmy[1].start
- emmy.finish = emmy[#emmy].finish
- return emmy
- end,
- EmmyVararg = function (typeName)
- return {
- type = 'emmyVararg',
- start = typeName.start,
- finish = typeName.finish,
- typeName,
- }
- end,
- EmmyLanguage = function (language)
- return {
- type = 'emmyLanguage',
- start = language.start,
- finish = language.finish,
- language,
- }
- end,
- EmmySee = function (start, className, methodName, finish)
- return {
- type = 'emmySee',
- start = start,
- finish = finish - 1,
- className, methodName
- }
- end,
- EmmyOverLoad = function (EmmyFunctionType)
- EmmyFunctionType.type = 'emmyOverLoad'
- return EmmyFunctionType
- end,
- EmmyIncomplete = function (emmyName)
- emmyName.type = 'emmyIncomplete'
- return emmyName
- end,
- EmmyComment = function (...)
- local lines = {...}
- for i = 2, #lines do
- local line = lines[i]
- if line:sub(1, 1) == '|' then
- lines[i] = '\n' .. line:sub(2)
- end
- end
- return {
- type = 'emmyComment',
- [1] = table.concat(lines, '\n'),
- }
- end,
- EmmyOption = function (options)
- if not options or options == '' then
- return nil
- end
- local option = {}
- for _, pair in ipairs(options) do
- if pair.type == 'pair' then
- local key = pair[1]
- local value = pair[2]
- if key.type == 'name' then
- option[key[1]] = value[1]
- end
- end
- end
- return option
- end,
- EmmyTypeEnum = function (default, enum, comment)
- enum.type = 'emmyEnum'
- if default ~= '' then
- enum.default = true
- end
- enum.comment = comment
- return enum
- end,
-
- -- 捕获错误
- UnknownSymbol = function (start, symbol)
- pushError {
- type = 'UNKNOWN_SYMBOL',
- start = start,
- finish = start + #symbol - 1,
- info = {
- symbol = symbol,
- }
- }
- return
- end,
- UnknownAction = function (start, symbol)
- pushError {
- type = 'UNKNOWN_SYMBOL',
- start = start,
- finish = start + #symbol - 1,
- info = {
- symbol = symbol,
- }
- }
- return false
- end,
- DirtyName = function (pos)
- pushError {
- type = 'MISS_NAME',
- start = pos,
- finish = pos,
- }
- return {
- type = 'name',
- start = pos-1,
- finish = pos-1,
- [1] = ''
- }
- end,
- DirtyExp = function (pos)
- pushError {
- type = 'MISS_EXP',
- start = pos,
- finish = pos,
- }
- return {
- type = 'name',
- start = pos,
- finish = pos,
- [1] = ''
- }
- end,
- MissExp = function (pos)
- pushError {
- type = 'MISS_EXP',
- start = pos,
- finish = pos,
- }
- end,
- MissExponent = function (start, finish)
- pushError {
- type = 'MISS_EXPONENT',
- start = start,
- finish = finish - 1,
- }
- end,
- MissQuote1 = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = '"'
- }
- }
- end,
- MissQuote2 = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = "'"
- }
- }
- end,
- MissEscX = function (pos)
- pushError {
- type = 'MISS_ESC_X',
- start = pos-2,
- finish = pos+1,
- }
- end,
- MissTL = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = '{',
- }
- }
- end,
- MissTR = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = '}',
- }
- }
- return pos + 1
- end,
- MissBR = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = ']',
- }
- }
- return pos + 1
- end,
- MissPL = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = '(',
- }
- }
- end,
- DirtyPR = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = ')',
- }
- }
- return pos + 1
- end,
- MissPR = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = ')',
- }
- }
- end,
- ErrEsc = function (pos)
- pushError {
- type = 'ERR_ESC',
- start = pos-1,
- finish = pos,
- }
- end,
- MustX16 = function (pos, str)
- pushError {
- type = 'MUST_X16',
- start = pos,
- finish = pos + #str - 1,
- }
- end,
- MissAssign = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = '=',
- }
- }
- end,
- MissTableSep = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = ','
- }
- }
- end,
- MissField = function (pos)
- pushError {
- type = 'MISS_FIELD',
- start = pos,
- finish = pos,
- }
- end,
- MissMethod = function (pos)
- pushError {
- type = 'MISS_METHOD',
- start = pos,
- finish = pos,
- }
- end,
- MissLabel = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = '::',
- }
- }
- end,
- MissEnd = function (pos)
- State.MissEndErr = pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = 'end',
- }
- }
- end,
- MissDo = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = 'do',
- }
- }
- end,
- MissComma = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = ',',
- }
- }
- end,
- MissIn = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = 'in',
- }
- }
- end,
- MissUntil = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = 'until',
- }
- }
- end,
- MissThen = function (pos)
- pushError {
- type = 'MISS_SYMBOL',
- start = pos,
- finish = pos,
- info = {
- symbol = 'then',
- }
- }
- end,
- ExpInAction = function (start, exp, finish)
- pushError {
- type = 'EXP_IN_ACTION',
- start = start,
- finish = finish - 1,
- }
- return exp
- end,
- AfterReturn = function (rtn, ...)
- if not ... then
- return rtn
- end
- local action = select(-1, ...)
- if not action then
- return rtn
- end
- pushError {
- type = 'ACTION_AFTER_RETURN',
- start = rtn.start,
- finish = rtn.finish,
- }
- return rtn, action
- end,
- MissIf = function (start, block)
- pushError {
- type = 'MISS_SYMBOL',
- start = start,
- finish = start,
- info = {
- symbol = 'if',
- }
- }
- return block
- end,
- MissGT = function (start)
- pushError {
- type = 'MISS_SYMBOL',
- start = start,
- finish = start,
- info = {
- symbol = '>'
- }
- }
- end,
- ErrAssign = function (start, finish)
- pushError {
- type = 'ERR_ASSIGN_AS_EQ',
- start = start,
- finish = finish - 1,
- fix = {
- title = 'FIX_ASSIGN_AS_EQ',
- {
- start = start,
- finish = finish - 1,
- text = '=',
- }
- }
- }
- end,
- ErrEQ = function (start, finish)
- pushError {
- type = 'ERR_EQ_AS_ASSIGN',
- start = start,
- finish = finish - 1,
- fix = {
- title = 'FIX_EQ_AS_ASSIGN',
- {
- start = start,
- finish = finish - 1,
- text = '==',
- }
- }
- }
- return '=='
- end,
- ErrUEQ = function (start, finish)
- pushError {
- type = 'ERR_UEQ',
- start = start,
- finish = finish - 1,
- fix = {
- title = 'FIX_UEQ',
- {
- start = start,
- finish = finish - 1,
- text = '~=',
- }
- }
- }
- return '=='
- end,
- ErrThen = function (start, finish)
- pushError {
- type = 'ERR_THEN_AS_DO',
- start = start,
- finish = finish - 1,
- fix = {
- title = 'FIX_THEN_AS_DO',
- {
- start = start,
- finish = finish - 1,
- text = 'then',
- }
- }
- }
- end,
- ErrDo = function (start, finish)
- pushError {
- type = 'ERR_DO_AS_THEN',
- start = start,
- finish = finish - 1,
- fix = {
- title = 'FIX_DO_AS_THEN',
- {
- start = start,
- finish = finish - 1,
- text = 'do',
- }
- }
- }
- end,
-}
-
-local function init(state, errs)
- State = state
- Errs = errs
-end
-
-return {
- defs = Defs,
- init = init,
-}
diff --git a/script/parser/calcline.lua b/script/parser/calcline.lua
deleted file mode 100644
index 26f475d9..00000000
--- a/script/parser/calcline.lua
+++ /dev/null
@@ -1,93 +0,0 @@
-local m = require 'lpeglabel'
-
-local row
-local fl
-local NL = (m.P'\r\n' + m.S'\r\n') * m.Cp() / function (pos)
- row = row + 1
- fl = pos
-end
-local ROWCOL = (NL + m.P(1))^0
-local function rowcol(str, n)
- row = 1
- fl = 1
- ROWCOL:match(str:sub(1, n))
- local col = n - fl + 1
- return row, col
-end
-
-local function rowcol_utf8(str, n)
- row = 1
- fl = 1
- ROWCOL:match(str:sub(1, n))
- return row, utf8.len(str, fl, n)
-end
-
-local function position(str, _row, _col)
- local cur = 1
- local row = 1
- while true do
- if row == _row then
- return cur + _col - 1
- elseif row > _row then
- return cur - 1
- end
- local pos = str:find('[\r\n]', cur)
- if not pos then
- return #str
- end
- row = row + 1
- if str:sub(pos, pos+1) == '\r\n' then
- cur = pos + 2
- else
- cur = pos + 1
- end
- end
-end
-
-local function position_utf8(str, _row, _col)
- local cur = 1
- local row = 1
- while true do
- if row == _row then
- return utf8.offset(str, _col, cur)
- elseif row > _row then
- return cur - 1
- end
- local pos = str:find('[\r\n]', cur)
- if not pos then
- return #str
- end
- row = row + 1
- if str:sub(pos, pos+1) == '\r\n' then
- cur = pos + 2
- else
- cur = pos + 1
- end
- end
-end
-
-local NL = m.P'\r\n' + m.S'\r\n'
-
-local function line(str, row)
- local count = 0
- local res
- local LINE = m.Cmt((1 - NL)^0, function (_, _, c)
- count = count + 1
- if count == row then
- res = c
- return false
- end
- return true
- end)
- local MATCH = (LINE * NL)^0 * LINE
- MATCH:match(str)
- return res
-end
-
-return {
- rowcol = rowcol,
- rowcol_utf8 = rowcol_utf8,
- position = position,
- position_utf8 = position_utf8,
- line = line,
-}
diff --git a/script/parser/grammar.lua b/script/parser/grammar.lua
deleted file mode 100644
index e5d5ee69..00000000
--- a/script/parser/grammar.lua
+++ /dev/null
@@ -1,630 +0,0 @@
-local re = require 'parser.relabel'
-local m = require 'lpeglabel'
-local ast = require 'parser.ast'
-
-local scriptBuf = ''
-local compiled = {}
-local parser
-local defs = ast.defs
-
--- goto 可以作为名字,合法性之后处理
-local RESERVED = {
- ['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,
-}
-
-defs.nl = (m.P'\r\n' + m.S'\r\n')
-defs.s = m.S' \t'
-defs.S = - defs.s
-defs.ea = '\a'
-defs.eb = '\b'
-defs.ef = '\f'
-defs.en = '\n'
-defs.er = '\r'
-defs.et = '\t'
-defs.ev = '\v'
-defs['nil'] = m.Cp() / function () return nil end
-defs['false'] = m.Cp() / function () return false end
-defs.NotReserved = function (_, _, str)
- if RESERVED[str] then
- return false
- end
- return true
-end
-defs.Reserved = function (_, _, str)
- if RESERVED[str] then
- return true
- end
- return false
-end
-defs.None = function () end
-defs.np = m.Cp() / function (n) return n+1 end
-
-m.setmaxstack(1000)
-
-local eof = re.compile '!. / %{SYNTAX_ERROR}'
-
-local function grammar(tag)
- return function (script)
- scriptBuf = script .. '\r\n' .. scriptBuf
- compiled[tag] = re.compile(scriptBuf, defs) * eof
- end
-end
-
-local function errorpos(pos, err)
- return {
- type = 'UNKNOWN',
- start = pos or 0,
- finish = pos or 0,
- err = err,
- }
-end
-
-grammar 'Comment' [[
-Comment <- LongComment / '--' ShortComment
-LongComment <- ('--[' {} {:eq: '='* :} {} '['
- {(!CommentClose .)*}
- (CommentClose {} / {} {}))
- -> LongComment
- / (
- {} '/*' {}
- (!'*/' .)*
- {} '*/' {}
- )
- -> CLongComment
-CommentClose <- ']' =eq ']'
-ShortComment <- (!%nl .)*
-]]
-
-grammar 'Sp' [[
-Sp <- (Comment / %nl / %s)*
-Sps <- (Comment / %nl / %s)+
-]]
-
-grammar 'Common' [[
-Word <- [a-zA-Z0-9_]
-Cut <- !Word
-X16 <- [a-fA-F0-9]
-Rest <- (!%nl .)*
-
-AND <- Sp {'and'} Cut
-BREAK <- Sp 'break' Cut
-DO <- Sp 'do' Cut
- / Sp ({} 'then' Cut {}) -> ErrDo
-ELSE <- Sp 'else' Cut
-ELSEIF <- Sp 'elseif' Cut
-END <- Sp 'end' Cut
-FALSE <- Sp 'false' Cut
-FOR <- Sp 'for' Cut
-FUNCTION <- Sp 'function' Cut
-GOTO <- Sp 'goto' Cut
-IF <- Sp 'if' Cut
-IN <- Sp 'in' Cut
-LOCAL <- Sp 'local' Cut
-NIL <- Sp 'nil' Cut
-NOT <- Sp 'not' Cut
-OR <- Sp {'or'} Cut
-REPEAT <- Sp 'repeat' Cut
-RETURN <- Sp 'return' Cut
-THEN <- Sp 'then' Cut
- / Sp ({} 'do' Cut {}) -> ErrThen
-TRUE <- Sp 'true' Cut
-UNTIL <- Sp 'until' Cut
-WHILE <- Sp 'while' Cut
-
-Esc <- '\' -> ''
- EChar
-EChar <- 'a' -> ea
- / 'b' -> eb
- / 'f' -> ef
- / 'n' -> en
- / 'r' -> er
- / 't' -> et
- / 'v' -> ev
- / '\'
- / '"'
- / "'"
- / %nl
- / ('z' (%nl / %s)*) -> ''
- / ({} 'x' {X16 X16}) -> Char16
- / ([0-9] [0-9]? [0-9]?) -> Char10
- / ('u{' {} {Word*} '}') -> CharUtf8
- -- 错误处理
- / 'x' {} -> MissEscX
- / 'u' !'{' {} -> MissTL
- / 'u{' Word* !'}' {} -> MissTR
- / {} -> ErrEsc
-
-BOR <- Sp {'|'}
-BXOR <- Sp {'~'} !'='
-BAND <- Sp {'&'}
-Bshift <- Sp {BshiftList}
-BshiftList <- '<<'
- / '>>'
-Concat <- Sp {'..'}
-Adds <- Sp {AddsList}
-AddsList <- '+'
- / '-'
-Muls <- Sp {MulsList}
-MulsList <- '*'
- / '//'
- / '/'
- / '%'
-Unary <- Sp {} {UnaryList}
-UnaryList <- NOT
- / '#'
- / '-'
- / '~' !'='
-POWER <- Sp {'^'}
-
-BinaryOp <- Sp {} {'or'} Cut
- / Sp {} {'and'} Cut
- / Sp {} {'<=' / '>=' / '<'!'<' / '>'!'>' / '~=' / '=='}
- / Sp {} ({} '=' {}) -> ErrEQ
- / Sp {} ({} '!=' {}) -> ErrUEQ
- / Sp {} {'|'}
- / Sp {} {'~'}
- / Sp {} {'&'}
- / Sp {} {'<<' / '>>'}
- / Sp {} {'..'} !'.'
- / Sp {} {'+' / '-'}
- / Sp {} {'*' / '//' / '/' / '%'}
- / Sp {} {'^'}
-UnaryOp <- Sp {} {'not' Cut / '#' / '~' !'=' / '-' !'-'}
-
-PL <- Sp '('
-PR <- Sp ')'
-BL <- Sp '[' !'[' !'='
-BR <- Sp ']'
-TL <- Sp '{'
-TR <- Sp '}'
-COMMA <- Sp ','
-SEMICOLON <- Sp ';'
-DOTS <- Sp ({} '...') -> DOTS
-DOT <- Sp ({} '.' !'.') -> DOT
-COLON <- Sp ({} ':' !':') -> COLON
-LABEL <- Sp '::'
-ASSIGN <- Sp '=' !'='
-AssignOrEQ <- Sp ({} '==' {}) -> ErrAssign
- / Sp '='
-
-Nothing <- {} -> Nothing
-
-DirtyBR <- BR {} / {} -> MissBR
-DirtyTR <- TR {} / {} -> MissTR
-DirtyPR <- PR {} / {} -> DirtyPR
-DirtyLabel <- LABEL / {} -> MissLabel
-NeedPR <- PR / {} -> MissPR
-NeedEnd <- END / {} -> MissEnd
-NeedDo <- DO / {} -> MissDo
-NeedAssign <- ASSIGN / {} -> MissAssign
-NeedComma <- COMMA / {} -> MissComma
-NeedIn <- IN / {} -> MissIn
-NeedUntil <- UNTIL / {} -> MissUntil
-]]
-
-grammar 'Nil' [[
-Nil <- Sp ({} -> Nil) NIL
-]]
-
-grammar 'Boolean' [[
-Boolean <- Sp ({} -> True) TRUE
- / Sp ({} -> False) FALSE
-]]
-
-grammar 'String' [[
-String <- Sp ({} StringDef {})
- -> String
-StringDef <- {'"'}
- {~(Esc / !%nl !'"' .)*~} -> 1
- ('"' / {} -> MissQuote1)
- / {"'"}
- {~(Esc / !%nl !"'" .)*~} -> 1
- ("'" / {} -> MissQuote2)
- / ('[' {} {:eq: '='* :} {} '[' %nl?
- {(!StringClose .)*} -> 1
- (StringClose / {}))
- -> LongString
-StringClose <- ']' =eq ']'
-]]
-
-grammar 'Number' [[
-Number <- Sp ({} {NumberDef} {}) -> Number
- NumberSuffix?
- ErrNumber?
-NumberDef <- Number16 / Number10
-NumberSuffix<- ({} {[uU]? [lL] [lL]}) -> FFINumber
- / ({} {[iI]}) -> ImaginaryNumber
-ErrNumber <- ({} {([0-9a-zA-Z] / '.')+}) -> UnknownSymbol
-
-Number10 <- Float10 Float10Exp?
- / Integer10 Float10? Float10Exp?
-Integer10 <- [0-9]+ ('.' [0-9]*)?
-Float10 <- '.' [0-9]+
-Float10Exp <- [eE] [+-]? [0-9]+
- / ({} [eE] [+-]? {}) -> MissExponent
-
-Number16 <- '0' [xX] Float16 Float16Exp?
- / '0' [xX] Integer16 Float16? Float16Exp?
-Integer16 <- X16+ ('.' X16*)?
- / ({} {Word*}) -> MustX16
-Float16 <- '.' X16+
- / '.' ({} {Word*}) -> MustX16
-Float16Exp <- [pP] [+-]? [0-9]+
- / ({} [pP] [+-]? {}) -> MissExponent
-]]
-
-grammar 'Name' [[
-Name <- Sp ({} NameBody {})
- -> Name
-NameBody <- {[a-zA-Z_] [a-zA-Z0-9_]*}
-FreeName <- Sp ({} {NameBody=>NotReserved} {})
- -> Name
-MustName <- Name / DirtyName
-DirtyName <- {} -> DirtyName
-]]
-
-grammar 'Exp' [[
-Exp <- (UnUnit (BinaryOp (UnUnit / {} -> DirtyExp))*)
- -> Exp
-UnUnit <- ExpUnit
- / UnaryOp+ (ExpUnit / {} -> DirtyExp)
-ExpUnit <- Nil
- / Boolean
- / String
- / Number
- / DOTS -> DotsAsExp
- / Table
- / Function
- / Simple
-
-Simple <- (Prefix (Sp Suffix)*)
- -> Simple
-Prefix <- Sp ({} PL DirtyExp DirtyPR)
- -> Prefix
- / FreeName
-Index <- ({} BL DirtyExp DirtyBR) -> Index
-Suffix <- DOT Name / DOT {} -> MissField
- / Method (!(Sp CallStart) {} -> MissPL)?
- / ({} Table {}) -> Call
- / ({} String {}) -> Call
- / Index
- / ({} PL CallArgList DirtyPR) -> Call
-Method <- COLON Name / COLON {} -> MissMethod
-CallStart <- PL
- / TL
- / '"'
- / "'"
- / '[' '='* '['
-
-DirtyExp <- Exp
- / {} -> DirtyExp
-MaybeExp <- Exp / MissExp
-MissExp <- {} -> MissExp
-ExpList <- Sp (MaybeExp (COMMA (MaybeExp))*)
- -> List
-MustExpList <- Sp (Exp (COMMA (MaybeExp))*)
- -> List
-CallArgList <- Sp ({} (COMMA {} / Exp)+ {})
- -> CallArgList
- / %nil
-NameList <- (MustName (COMMA MustName)*)
- -> List
-
-ArgList <- (DOTS -> DotsAsArg / Name / Sp {} COMMA)*
- -> ArgList
-
-Table <- Sp ({} TL TableFields? DirtyTR)
- -> Table
-TableFields <- (Emmy / TableSep {} / TableField)+
-TableSep <- COMMA / SEMICOLON
-TableField <- NewIndex / NewField / Exp
-NewIndex <- Sp (Index NeedAssign DirtyExp)
- -> NewIndex
-NewField <- (MustName ASSIGN DirtyExp)
- -> NewField
-
-Function <- Sp ({} FunctionBody {})
- -> Function
-FuncArg <- PL {} ArgList {} NeedPR
- / {} {} -> MissPL Nothing {}
-FunctionBody<- FUNCTION BlockStart FuncArg
- (Emmy / !END Action)*
- BlockEnd
- NeedEnd
-
-BlockStart <- {} -> BlockStart
-BlockEnd <- {} -> BlockEnd
-
--- 纯占位,修改了 `relabel.lua` 使重复定义不抛错
-Action <- !END .
-Set <- END
-Emmy <- '---@'
-]]
-
-grammar 'Action' [[
-Action <- Sp (CrtAction / UnkAction)
-CrtAction <- Semicolon
- / Do
- / Break
- / Return
- / Label
- / GoTo
- / If
- / For
- / While
- / Repeat
- / NamedFunction
- / LocalFunction
- / Local
- / Set
- / Call
- / ExpInAction
-UnkAction <- ({} {Word+})
- -> UnknownAction
- / ({} '//' {} (LongComment / ShortComment))
- -> CCommentPrefix
- / ({} {. (!Sps !CrtAction .)*})
- -> UnknownAction
-ExpInAction <- Sp ({} Exp {})
- -> ExpInAction
-
-Semicolon <- SEMICOLON
- -> Skip
-SimpleList <- (Simple (COMMA Simple)*)
- -> List
-
-Do <- Sp ({} 'do' Cut DoBody NeedEnd {})
- -> Do
-DoBody <- (Emmy / !END Action)*
- -> DoBody
-
-Break <- BREAK ({} Semicolon* AfterBreak?)
- -> Break
-AfterBreak <- Sp !END !UNTIL !ELSEIF !ELSE Action
-BreakStart <- {} -> BreakStart
-BreakEnd <- {} -> BreakEnd
-
-Return <- (ReturnBody Semicolon* AfterReturn?)
- -> AfterReturn
-ReturnBody <- Sp ({} RETURN MustExpList? {})
- -> Return
-AfterReturn <- Sp !END !UNTIL !ELSEIF !ELSE Action
-
-Label <- Sp ({} LABEL MustName DirtyLabel {}) -> Label
-
-GoTo <- Sp ({} GOTO MustName {}) -> GoTo
-
-If <- Sp ({} IfBody {})
- -> If
-IfHead <- (IfPart -> IfBlock)
- / ({} ElseIfPart -> ElseIfBlock)
- -> MissIf
- / ({} ElsePart -> ElseBlock)
- -> MissIf
-IfBody <- IfHead
- (ElseIfPart -> ElseIfBlock)*
- (ElsePart -> ElseBlock)?
- NeedEnd
-IfPart <- IF DirtyExp THEN
- {} (Emmy / !ELSEIF !ELSE !END Action)* {}
- / IF DirtyExp {}->MissThen
- {} {}
-ElseIfPart <- ELSEIF DirtyExp THEN
- {} (Emmy / !ELSE !ELSEIF !END Action)* {}
- / ELSEIF DirtyExp {}->MissThen
- {} {}
-ElsePart <- ELSE
- {} (Emmy / !END Action)* {}
-
-For <- Loop / In
- / FOR
-
-Loop <- Sp ({} LoopBody {})
- -> Loop
-LoopBody <- FOR LoopStart LoopFinish LoopStep NeedDo
- BreakStart
- (Emmy / !END Action)*
- BreakEnd
- NeedEnd
-LoopStart <- MustName AssignOrEQ DirtyExp
-LoopFinish <- NeedComma DirtyExp
-LoopStep <- COMMA DirtyExp
- / NeedComma Exp
- / Nothing
-
-In <- Sp ({} InBody {})
- -> In
-InBody <- FOR InNameList NeedIn ExpList NeedDo
- BreakStart
- (Emmy / !END Action)*
- BreakEnd
- NeedEnd
-InNameList <- &IN DirtyName
- / NameList
-
-While <- Sp ({} WhileBody {})
- -> While
-WhileBody <- WHILE DirtyExp NeedDo
- BreakStart
- (Emmy / !END Action)*
- BreakEnd
- NeedEnd
-
-Repeat <- Sp ({} RepeatBody {})
- -> Repeat
-RepeatBody <- REPEAT
- BreakStart
- (Emmy / !UNTIL Action)*
- BreakEnd
- NeedUntil DirtyExp
-
-LocalTag <- (Sp '<' Sp MustName Sp LocalTagEnd)*
- -> LocalTag
-LocalTagEnd <- '>' / {} -> MissGT
-Local <- (LOCAL LocalNameList (AssignOrEQ ExpList)?)
- -> Local
-Set <- (SimpleList AssignOrEQ ExpList?)
- -> Set
-LocalNameList
- <- (LocalName (COMMA LocalName)*)
- -> List
-LocalName <- (MustName LocalTag)
- -> LocalName
-
-Call <- Simple
- -> SimpleCall
-
-LocalFunction
- <- Sp ({} LOCAL FunctionNamedBody {})
- -> LocalFunction
-
-NamedFunction
- <- Sp ({} FunctionNamedBody {})
- -> NamedFunction
-FunctionNamedBody
- <- FUNCTION FuncName BlockStart FuncArg
- (Emmy / !END Action)*
- BlockEnd
- NeedEnd
-FuncName <- (MustName (DOT MustName)* FuncMethod?)
- -> Simple
-FuncMethod <- COLON Name / COLON {} -> MissMethod
-
--- 占位
-Emmy <- '---@'
-]]
-
-grammar 'Emmy' [[
-Emmy <- EmmyAction
- / EmmyComments
-EmmyAction <- EmmySp '---' %s* '@' EmmyBody ShortComment
-EmmySp <- (!'---' Comment / %s / %nl)*
-EmmyComments <- EmmyComment+
- -> EmmyComment
-EmmyComment <- EmmySp '---' %s* !'@' {(!%nl .)*}
-EmmyBody <- 'class' %s+ EmmyClass -> EmmyClass
- / 'type' %s+ EmmyType -> EmmyType
- / 'alias' %s+ EmmyAlias -> EmmyAlias
- / 'param' %s+ EmmyParam -> EmmyParam
- / 'return' %s+ EmmyReturn -> EmmyReturn
- / 'field' %s+ EmmyField -> EmmyField
- / 'generic' %s+ EmmyGeneric -> EmmyGeneric
- / 'vararg' %s+ EmmyVararg -> EmmyVararg
- / 'language' %s+ EmmyLanguage -> EmmyLanguage
- / 'see' %s+ EmmySee -> EmmySee
- / 'overload' %s+ EmmyOverLoad -> EmmyOverLoad
- / EmmyIncomplete
-
-EmmyName <- ({} {[a-zA-Z_] [a-zA-Z0-9_.]*})
- -> EmmyName
-MustEmmyName <- EmmyName / DirtyEmmyName
-DirtyEmmyName <- {} -> DirtyEmmyName
-EmmyLongName <- ({} {(!%nl .)+})
- -> EmmyName
-EmmyIncomplete <- MustEmmyName
- -> EmmyIncomplete
-
-EmmyClass <- (MustEmmyName EmmyParentClass?)
-EmmyParentClass <- %s* {} ':' %s* MustEmmyName
-
-EmmyType <- EmmyFunctionType
- / EmmyTableType
- / EmmyArrayType
- / EmmyCommonType
-EmmyCommonType <- EmmyTypeNames
- -> EmmyCommonType
-EmmyTypeNames <- EmmyTypeName (%s* {} '|' %s* !String EmmyTypeName)*
-EmmyTypeName <- EmmyFunctionType
- / EmmyTableType
- / EmmyArrayType
- / MustEmmyName
-EmmyTypeEnum <- %s* (%nl %s* '---')? '|' EmmyEnum
- -> EmmyTypeEnum
-EmmyEnum <- %s* {'>'?} %s* String (EmmyEnumComment / (!%nl !'|' .)*)
-EmmyEnumComment <- %s* '#' %s* {(!%nl .)*}
-
-EmmyAlias <- MustEmmyName %s* EmmyType EmmyTypeEnum*
-
-EmmyParam <- MustEmmyName %s* EmmyType %s* EmmyOption %s* EmmyTypeEnum*
-EmmyOption <- Table?
- -> EmmyOption
-
-EmmyReturn <- {} %nil %nil {} Table -> EmmyOption
- / {} EmmyType (%s* EmmyName/%nil) {} EmmyOption
-
-EmmyField <- (EmmyFieldAccess MustEmmyName %s* EmmyType)
-EmmyFieldAccess <- ({'public'} Cut %s*)
- / ({'protected'} Cut %s*)
- / ({'private'} Cut %s*)
- / {} -> 'public'
-
-EmmyGeneric <- EmmyGenericBlock
- (%s* ',' %s* EmmyGenericBlock)*
-EmmyGenericBlock<- (MustEmmyName %s* (':' %s* EmmyType)?)
- -> EmmyGenericBlock
-
-EmmyVararg <- EmmyType
-
-EmmyLanguage <- MustEmmyName
-
-EmmyArrayType <- ({} MustEmmyName -> EmmyCommonType {} '[' DirtyBR)
- -> EmmyArrayType
- / ({} PL EmmyCommonType DirtyPR '[' DirtyBR)
- -> EmmyArrayType
-
-EmmyTableType <- ({} 'table' Cut '<' %s* EmmyType %s* ',' %s* EmmyType %s* '>' {})
- -> EmmyTableType
-
-EmmyFunctionType<- ({} 'fun' Cut %s* EmmyFunctionArgs %s* EmmyFunctionRtns {})
- -> EmmyFunctionType
-EmmyFunctionArgs<- ('(' %s* EmmyFunctionArg %s* (',' %s* EmmyFunctionArg %s*)* DirtyPR)
- -> EmmyFunctionArgs
- / '(' %nil DirtyPR -> None
- / %nil
-EmmyFunctionRtns<- (':' %s* EmmyType (%s* ',' %s* EmmyType)*)
- -> EmmyFunctionRtns
- / %nil
-EmmyFunctionArg <- MustEmmyName %s* ':' %s* EmmyType
-
-EmmySee <- {} MustEmmyName %s* '#' %s* MustEmmyName {}
-EmmyOverLoad <- EmmyFunctionType
-]]
-
-grammar 'Lua' [[
-Lua <- Head?
- (Emmy / Action)* -> Lua
- BlockEnd
- Sp
-Head <- '#' (!%nl .)*
-]]
-
-return function (self, lua, mode)
- local gram = compiled[mode] or compiled['Lua']
- local r, _, pos = gram:match(lua)
- if not r then
- local err = errorpos(pos)
- return nil, err
- end
-
- return r
-end
diff --git a/script/parser/init.lua b/script/parser/init.lua
deleted file mode 100644
index 30596dbe..00000000
--- a/script/parser/init.lua
+++ /dev/null
@@ -1,9 +0,0 @@
-local api = {
- grammar = require 'parser.grammar',
- parse = require 'parser.parse',
- split = require 'parser.split',
- calcline = require 'parser.calcline',
- lines = require 'parser.lines',
-}
-
-return api
diff --git a/script/parser/lines.lua b/script/parser/lines.lua
deleted file mode 100644
index a5fe8116..00000000
--- a/script/parser/lines.lua
+++ /dev/null
@@ -1,190 +0,0 @@
-local m = require 'lpeglabel'
-
-local function utf8_len(buf, start, finish)
- local len, pos = utf8.len(buf, start, finish)
- if len then
- return len
- end
- return 1 + utf8_len(buf, start, pos-1) + utf8_len(buf, pos+1, finish)
-end
-
-local function Line(start, line, finish)
- line.start = start
- line.finish = finish - 1
- return line
-end
-
-local function Space(...)
- local line = {...}
- local sp = 0
- local tab = 0
- for i = 1, #line do
- if line[i] == ' ' then
- sp = sp + 1
- elseif line[i] == '\t' then
- tab = tab + 1
- end
- line[i] = nil
- end
- line.sp = sp
- line.tab = tab
- return line
-end
-
-local parser = m.P{
-'Lines',
-Lines = m.Ct(m.V'Line'^0 * m.V'LastLine'),
-Line = m.Cp() * m.V'Indent' * (1 - m.V'Nl')^0 * m.Cp() * m.V'Nl' / Line,
-LastLine= m.Cp() * m.V'Indent' * (1 - m.V'Nl')^0 * m.Cp() / Line,
-Nl = m.P'\r\n' + m.S'\r\n',
-Indent = m.C(m.S' \t')^0 / Space,
-}
-
-local mt = {}
-mt.__index = mt
-
-function mt:position(row, col, code)
- if row < 1 then
- return 1
- end
- code = code or self.code
- if row > #self then
- if code == 'utf8' then
- return utf8_len(self.buf) + 1
- else
- return #self.buf + 1
- end
- end
- local line = self[row]
- local next_line = self[row+1]
- local start = line.start
- local finish
- if next_line then
- finish = next_line.start - 1
- else
- finish = #self.buf + 1
- end
- local pos
- if code == 'utf8' then
- if start > finish then
- return finish
- end
- pos = utf8.offset(self.buf, col, start) or finish
- else
- pos = start + col - 1
- end
- if pos < start then
- pos = start
- elseif pos > finish then
- pos = finish
- end
- return pos
-end
-
-local function isCharByte(byte)
- if not byte then
- return false
- end
- -- [0-9]
- if byte >= 48 and byte <= 57 then
- return true
- end
- -- [A-Z]
- if byte >= 65 and byte <= 90 then
- return true
- end
- -- [a-z]
- if byte >= 97 and byte <= 122 then
- return true
- end
- -- <utf8>
- if byte >= 128 then
- return true
- end
- return false
-end
-
-function mt:positionAsChar(row, col, code)
- local pos = self:position(row, col, code)
- if isCharByte(self.buf:byte(pos, pos)) then
- return pos
- elseif isCharByte(self.buf:byte(pos+1, pos+1)) then
- return pos + 1
- end
- return pos
-end
-
-function mt:rowcol(pos, code)
- if pos < 1 then
- return 1, 1
- end
- code = code or self.code
- if pos >= #self.buf + 1 then
- local start = self[#self].start
- if code == 'utf8' then
- return #self, utf8_len(self.buf, start) + 1
- else
- return #self, #self.buf - start + 2
- end
- end
- local min = 1
- local max = #self
- for _ = 1, 100 do
- if max == min then
- local start = self[min].start
- if code == 'utf8' then
- return min, utf8_len(self.buf, start, pos)
- else
- return min, pos - start + 1
- end
- end
- local row = (max - min) // 2 + min
- local start = self[row].start
- if pos < start then
- max = row
- elseif pos > start then
- local next_start = self[row + 1].start
- if pos < next_start then
- if code == 'utf8' then
- return row, utf8_len(self.buf, start, pos)
- else
- return row, pos - start + 1
- end
- elseif pos > next_start then
- min = row + 1
- else
- return row + 1, 1
- end
- else
- return row, 1
- end
- end
- error('rowcol failed!')
-end
-
-function mt:line(i)
- local start, finish = self:range(i)
- return self.buf:sub(start, finish)
-end
-
-function mt:range(i)
- if i < 1 or i > #self then
- return 0, 0
- end
- return self[i].start, self[i].finish
-end
-
-function mt:set_code(code)
- self.code = code
-end
-
-return function (self, buf, code)
- local lines, err = parser:match(buf)
- if not lines then
- return nil, err
- end
- lines.buf = buf
- lines.code = code
-
- return setmetatable(lines, mt)
-end
diff --git a/script/parser/parse.lua b/script/parser/parse.lua
deleted file mode 100644
index 6ad79d9b..00000000
--- a/script/parser/parse.lua
+++ /dev/null
@@ -1,41 +0,0 @@
-local ast = require 'parser.ast'
-
-local Errs
-local State
-
-local function pushError(err)
- 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
-
-return function (self, lua, mode, version)
- Errs = {}
- State= {
- Break = 0,
- Label = {{}},
- Dots = {true},
- Version = version,
- Comments = {},
- Lua = lua,
- }
- ast.init(State, Errs)
- local suc, res, err = xpcall(self.grammar, debug.traceback, self, lua, mode)
- if not suc then
- return nil, res
- end
- if not res then
- pushError(err)
- return nil, Errs
- end
- return res, Errs, State.Comments
-end
diff --git a/script/parser/relabel.lua b/script/parser/relabel.lua
deleted file mode 100644
index ac902403..00000000
--- a/script/parser/relabel.lua
+++ /dev/null
@@ -1,361 +0,0 @@
--- $Id: re.lua,v 1.44 2013/03/26 20:11:40 roberto Exp $
-
--- imported functions and modules
-local tonumber, type, print, error = tonumber, type, print, error
-local pcall = pcall
-local setmetatable = setmetatable
-local tinsert, concat = table.insert, table.concat
-local rep = string.rep
-local m = require"lpeglabel"
-
--- 'm' will be used to parse expressions, and 'mm' will be used to
--- create expressions; that is, 're' runs on 'm', creating patterns
--- on 'mm'
-local mm = m
-
--- pattern's metatable
-local mt = getmetatable(mm.P(0))
-
-
-
--- No more global accesses after this point
-_ENV = nil
-
-
-local any = m.P(1)
-local dummy = mm.P(false)
-
-
-local errinfo = {
- NoPatt = "no pattern found",
- ExtraChars = "unexpected characters after the pattern",
-
- ExpPatt1 = "expected a pattern after '/'",
-
- ExpPatt2 = "expected a pattern after '&'",
- ExpPatt3 = "expected a pattern after '!'",
-
- ExpPatt4 = "expected a pattern after '('",
- ExpPatt5 = "expected a pattern after ':'",
- ExpPatt6 = "expected a pattern after '{~'",
- ExpPatt7 = "expected a pattern after '{|'",
-
- ExpPatt8 = "expected a pattern after '<-'",
-
- ExpPattOrClose = "expected a pattern or closing '}' after '{'",
-
- ExpNumName = "expected a number, '+', '-' or a name (no space) after '^'",
- ExpCap = "expected a string, number, '{}' or name after '->'",
-
- ExpName1 = "expected the name of a rule after '=>'",
- ExpName2 = "expected the name of a rule after '=' (no space)",
- ExpName3 = "expected the name of a rule after '<' (no space)",
-
- ExpLab1 = "expected a label after '{'",
-
- ExpNameOrLab = "expected a name or label after '%' (no space)",
-
- ExpItem = "expected at least one item after '[' or '^'",
-
- MisClose1 = "missing closing ')'",
- MisClose2 = "missing closing ':}'",
- MisClose3 = "missing closing '~}'",
- MisClose4 = "missing closing '|}'",
- MisClose5 = "missing closing '}'", -- for the captures
-
- MisClose6 = "missing closing '>'",
- MisClose7 = "missing closing '}'", -- for the labels
-
- MisClose8 = "missing closing ']'",
-
- MisTerm1 = "missing terminating single quote",
- MisTerm2 = "missing terminating double quote",
-}
-
-local function expect (pattern, label)
- return pattern + m.T(label)
-end
-
-
--- Pre-defined names
-local Predef = { nl = m.P"\n" }
-
-
-local mem
-local fmem
-local gmem
-
-
-local function updatelocale ()
- mm.locale(Predef)
- Predef.a = Predef.alpha
- Predef.c = Predef.cntrl
- Predef.d = Predef.digit
- Predef.g = Predef.graph
- Predef.l = Predef.lower
- Predef.p = Predef.punct
- Predef.s = Predef.space
- Predef.u = Predef.upper
- Predef.w = Predef.alnum
- Predef.x = Predef.xdigit
- Predef.A = any - Predef.a
- Predef.C = any - Predef.c
- Predef.D = any - Predef.d
- Predef.G = any - Predef.g
- Predef.L = any - Predef.l
- Predef.P = any - Predef.p
- Predef.S = any - Predef.s
- Predef.U = any - Predef.u
- Predef.W = any - Predef.w
- Predef.X = any - Predef.x
- mem = {} -- restart memoization
- fmem = {}
- gmem = {}
- local mt = {__mode = "v"}
- setmetatable(mem, mt)
- setmetatable(fmem, mt)
- setmetatable(gmem, mt)
-end
-
-
-updatelocale()
-
-
-
-local I = m.P(function (s,i) print(i, s:sub(1, i-1)); return i end)
-
-
-local function getdef (id, defs)
- local c = defs and defs[id]
- if not c then
- error("undefined name: " .. id)
- end
- return c
-end
-
-
-local function mult (p, n)
- local np = mm.P(true)
- while n >= 1 do
- if n%2 >= 1 then np = np * p end
- p = p * p
- n = n/2
- end
- return np
-end
-
-local function equalcap (s, i, c)
- if type(c) ~= "string" then return nil end
- local e = #c + i
- if s:sub(i, e - 1) == c then return e else return nil end
-end
-
-
-local S = (Predef.space + "--" * (any - Predef.nl)^0)^0
-
-local name = m.C(m.R("AZ", "az", "__") * m.R("AZ", "az", "__", "09")^0)
-
-local arrow = S * "<-"
-
--- a defined name only have meaning in a given environment
-local Def = name * m.Carg(1)
-
-local num = m.C(m.R"09"^1) * S / tonumber
-
-local String = "'" * m.C((any - "'" - m.P"\n")^0) * expect("'", "MisTerm1")
- + '"' * m.C((any - '"' - m.P"\n")^0) * expect('"', "MisTerm2")
-
-
-local defined = "%" * Def / function (c,Defs)
- local cat = Defs and Defs[c] or Predef[c]
- if not cat then
- error("name '" .. c .. "' undefined")
- end
- return cat
-end
-
-local Range = m.Cs(any * (m.P"-"/"") * (any - "]")) / mm.R
-
-local item = defined + Range + m.C(any - m.P"\n")
-
-local Class =
- "["
- * (m.C(m.P"^"^-1)) -- optional complement symbol
- * m.Cf(expect(item, "ExpItem") * (item - "]")^0, mt.__add)
- / function (c, p) return c == "^" and any - p or p end
- * expect("]", "MisClose8")
-
-local function adddef (t, k, exp)
- if t[k] then
- -- TODO 改了一下这里的代码,重复定义不会抛错
- --error("'"..k.."' already defined as a rule")
- else
- t[k] = exp
- end
- return t
-end
-
-local function firstdef (n, r) return adddef({n}, n, r) end
-
-
-local function NT (n, b)
- if not b then
- error("rule '"..n.."' used outside a grammar")
- else return mm.V(n)
- end
-end
-
-
-local exp = m.P{ "Exp",
- Exp = S * ( m.V"Grammar"
- + m.Cf(m.V"Seq" * (S * "/" * expect(S * m.V"Seq", "ExpPatt1"))^0, mt.__add) );
- Seq = m.Cf(m.Cc(m.P"") * m.V"Prefix" * (S * m.V"Prefix")^0, mt.__mul);
- Prefix = "&" * expect(S * m.V"Prefix", "ExpPatt2") / mt.__len
- + "!" * expect(S * m.V"Prefix", "ExpPatt3") / mt.__unm
- + m.V"Suffix";
- Suffix = m.Cf(m.V"Primary" *
- ( S * ( m.P"+" * m.Cc(1, mt.__pow)
- + m.P"*" * m.Cc(0, mt.__pow)
- + m.P"?" * m.Cc(-1, mt.__pow)
- + "^" * expect( m.Cg(num * m.Cc(mult))
- + m.Cg(m.C(m.S"+-" * m.R"09"^1) * m.Cc(mt.__pow)
- + name * m.Cc"lab"
- ),
- "ExpNumName")
- + "->" * expect(S * ( m.Cg((String + num) * m.Cc(mt.__div))
- + m.P"{}" * m.Cc(nil, m.Ct)
- + m.Cg(Def / getdef * m.Cc(mt.__div))
- ),
- "ExpCap")
- + "=>" * expect(S * m.Cg(Def / getdef * m.Cc(m.Cmt)),
- "ExpName1")
- )
- )^0, function (a,b,f) if f == "lab" then return a + mm.T(b) else return f(a,b) end end );
- Primary = "(" * expect(m.V"Exp", "ExpPatt4") * expect(S * ")", "MisClose1")
- + String / mm.P
- + Class
- + defined
- + "%" * expect(m.P"{", "ExpNameOrLab")
- * expect(S * m.V"Label", "ExpLab1")
- * expect(S * "}", "MisClose7") / mm.T
- + "{:" * (name * ":" + m.Cc(nil)) * expect(m.V"Exp", "ExpPatt5")
- * expect(S * ":}", "MisClose2")
- / function (n, p) return mm.Cg(p, n) end
- + "=" * expect(name, "ExpName2")
- / function (n) return mm.Cmt(mm.Cb(n), equalcap) end
- + m.P"{}" / mm.Cp
- + "{~" * expect(m.V"Exp", "ExpPatt6")
- * expect(S * "~}", "MisClose3") / mm.Cs
- + "{|" * expect(m.V"Exp", "ExpPatt7")
- * expect(S * "|}", "MisClose4") / mm.Ct
- + "{" * expect(m.V"Exp", "ExpPattOrClose")
- * expect(S * "}", "MisClose5") / mm.C
- + m.P"." * m.Cc(any)
- + (name * -arrow + "<" * expect(name, "ExpName3")
- * expect(">", "MisClose6")) * m.Cb("G") / NT;
- Label = num + name;
- Definition = name * arrow * expect(m.V"Exp", "ExpPatt8");
- Grammar = m.Cg(m.Cc(true), "G")
- * m.Cf(m.V"Definition" / firstdef * (S * m.Cg(m.V"Definition"))^0,
- adddef) / mm.P;
-}
-
-local pattern = S * m.Cg(m.Cc(false), "G") * expect(exp, "NoPatt") / mm.P
- * S * expect(-any, "ExtraChars")
-
-local function lineno (s, i)
- if i == 1 then return 1, 1 end
- local adjustment = 0
- -- report the current line if at end of line, not the next
- if s:sub(i,i) == '\n' then
- i = i-1
- adjustment = 1
- end
- local rest, num = s:sub(1,i):gsub("[^\n]*\n", "")
- local r = #rest
- return 1 + num, (r ~= 0 and r or 1) + adjustment
-end
-
-local function calcline (s, i)
- if i == 1 then return 1, 1 end
- local rest, line = s:sub(1,i):gsub("[^\n]*\n", "")
- local col = #rest
- return 1 + line, col ~= 0 and col or 1
-end
-
-
-local function splitlines(str)
- local t = {}
- local function helper(line) tinsert(t, line) return "" end
- helper((str:gsub("(.-)\r?\n", helper)))
- return t
-end
-
-local function compile (p, defs)
- if mm.type(p) == "pattern" then return p end -- already compiled
- p = p .. " " -- for better reporting of column numbers in errors when at EOF
- local ok, cp, label, poserr = pcall(function() return pattern:match(p, 1, defs) end)
- if not ok and cp then
- if type(cp) == "string" then
- cp = cp:gsub("^[^:]+:[^:]+: ", "")
- end
- error(cp, 3)
- end
- if not cp then
- local lines = splitlines(p)
- local line, col = lineno(p, poserr)
- local err = {}
- tinsert(err, "L" .. line .. ":C" .. col .. ": " .. errinfo[label])
- tinsert(err, lines[line])
- tinsert(err, rep(" ", col-1) .. "^")
- error("syntax error(s) in pattern\n" .. concat(err, "\n"), 3)
- end
- return cp
-end
-
-local function match (s, p, i)
- local cp = mem[p]
- if not cp then
- cp = compile(p)
- mem[p] = cp
- end
- return cp:match(s, i or 1)
-end
-
-local function find (s, p, i)
- local cp = fmem[p]
- if not cp then
- cp = compile(p) / 0
- cp = mm.P{ mm.Cp() * cp * mm.Cp() + 1 * mm.V(1) }
- fmem[p] = cp
- end
- local i, e = cp:match(s, i or 1)
- if i then return i, e - 1
- else return i
- end
-end
-
-local function gsub (s, p, rep)
- local g = gmem[p] or {} -- ensure gmem[p] is not collected while here
- gmem[p] = g
- local cp = g[rep]
- if not cp then
- cp = compile(p)
- cp = mm.Cs((cp / rep + 1)^0)
- g[rep] = cp
- end
- return cp:match(s)
-end
-
-
--- exported names
-local re = {
- compile = compile,
- match = match,
- find = find,
- gsub = gsub,
- updatelocale = updatelocale,
- calcline = calcline
-}
-
-return re
diff --git a/script/parser/split.lua b/script/parser/split.lua
deleted file mode 100644
index 6ce4a4e7..00000000
--- a/script/parser/split.lua
+++ /dev/null
@@ -1,9 +0,0 @@
-local m = require 'lpeglabel'
-
-local NL = m.P'\r\n' + m.S'\r\n'
-local LINE = m.C(1 - NL)
-
-return function (str)
- local MATCH = m.Ct((LINE * NL)^0 * LINE)
- return MATCH:match(str)
-end
diff --git a/script/plugin.lua b/script/plugin.lua
deleted file mode 100644
index eba224d6..00000000
--- a/script/plugin.lua
+++ /dev/null
@@ -1,98 +0,0 @@
-local fs = require 'bee.filesystem'
-local rpc = require 'rpc'
-local config = require 'config'
-local glob = require 'glob'
-local platform = require 'bee.platform'
-local sandbox = require 'sandbox'
-
-local Plugins
-
-local function showError(msg)
- local traceback = log.error(msg)
- rpc:notify('window/showMessage', {
- type = 3,
- message = traceback,
- })
- return traceback
-end
-
-local function showWarn(msg)
- log.warn(msg)
- rpc:notify('window/showMessage', {
- type = 3,
- message = msg,
- })
- return msg
-end
-
-local function scan(path, callback)
- if fs.is_directory(path) then
- for p in path:list_directory() do
- scan(p, callback)
- end
- else
- callback(path)
- end
-end
-
-local function loadPluginFrom(path, root)
- log.info('Load plugin from:', path:string())
- local env = setmetatable({}, { __index = _G })
- sandbox(path:filename():string(), root:string(), io.open, package.loaded, env)
- Plugins[#Plugins+1] = env
-end
-
-local function load(workspace)
- Plugins = nil
-
- if not config.config.plugin.enable then
- return
- end
- local suc, path = xpcall(fs.path, showWarn, config.config.plugin.path)
- if not suc then
- return
- end
-
- Plugins = {}
- local pluginPath
- if workspace then
- pluginPath = fs.absolute(workspace.root / path)
- else
- pluginPath = fs.absolute(path)
- end
- if not fs.is_directory(pluginPath) then
- pluginPath = pluginPath:parent_path()
- end
-
- local pattern = {config.config.plugin.path}
- local options = {
- ignoreCase = platform.OS == 'Windows'
- }
- local parser = glob.glob(pattern, options)
-
- scan(pluginPath:parent_path(), function (filePath)
- if parser(filePath:string()) then
- loadPluginFrom(filePath, pluginPath)
- end
- end)
-end
-
-local function call(name, ...)
- if not Plugins then
- return nil
- end
- for _, plugin in ipairs(Plugins) do
- if type(plugin[name]) == 'function' then
- local suc, res = xpcall(plugin[name], showError, ...)
- if suc and res ~= nil then
- return res
- end
- end
- end
- return nil
-end
-
-return {
- load = load,
- call = call,
-}
diff --git a/script/publish.lua b/script/publish.lua
deleted file mode 100644
index 7d0321aa..00000000
--- a/script/publish.lua
+++ /dev/null
@@ -1,189 +0,0 @@
-local currentPath = debug.getinfo(1, 'S').source:sub(2)
-local rootPath = currentPath:gsub('[^/\\]-$', '')
-if rootPath == '' then
- rootPath = './'
-end
-dofile(rootPath .. 'platform.lua')
-local fs = require 'bee.filesystem'
-local subprocess = require 'bee.subprocess'
-local platform = require 'bee.platform'
-ROOT = fs.absolute(fs.path(rootPath):parent_path())
-EXTENSION = ROOT:parent_path()
-
-require 'utility'
-local json = require 'json'
-
-local function loadPackage()
- local buf = io.load(EXTENSION / 'package.json')
- if not buf then
- error(ROOT:string() .. '|' .. EXTENSION:string())
- end
- local package = json.decode(buf)
- return package.version
-end
-
-local function updateNodeModules(out, postinstall)
- local current = fs.current_path()
- fs.current_path(out)
- local cmd = io.popen(postinstall)
- for line in cmd:lines 'l' do
- print(line)
- end
- local suc = cmd:close()
- if not suc then
- error('更新NodeModules失败!')
- end
- fs.current_path(current)
-end
-
-local function createDirectory(version)
- local out = EXTENSION / 'publish' / version
- fs.create_directories(out)
- return out
-end
-
-local function copyFiles(root, out)
- return function (dirs)
- local count = 0
- local function copy(relative, mode)
- local source = root / relative
- local target = out / relative
- if not fs.exists(source) then
- return
- end
- if fs.is_directory(source) then
- fs.create_directory(target)
- if mode == true then
- for path in source:list_directory() do
- copy(relative / path:filename(), true)
- end
- else
- for name, v in pairs(mode) do
- copy(relative / name, v)
- end
- end
- else
- fs.copy_file(source, target)
- count = count + 1
- end
- end
-
- copy(fs.path '', dirs)
- return count
- end
-end
-
-local function runTest(root)
- local ext = platform.OS == 'Windows' and '.exe' or ''
- local exe = root / platform.OS / 'bin' / 'lua-language-server' .. ext
- local test = root / 'test.lua'
- local lua = subprocess.spawn {
- exe,
- test,
- '-E',
- cwd = root,
- stdout = true,
- stderr = true,
- }
- for line in lua.stdout:lines 'l' do
- print(line)
- end
- lua:wait()
- local err = lua.stderr:read 'a'
- if err ~= '' then
- error(err)
- end
-end
-
-local function removeFiles(out)
- return function (dirs)
- local function remove(relative, mode)
- local target = out / relative
- if not fs.exists(target) then
- return
- end
- if fs.is_directory(target) then
- if mode == true then
- for path in target:list_directory() do
- remove(relative / path:filename(), true)
- end
- fs.remove(target)
- else
- for name, v in pairs(mode) do
- remove(relative / name, v)
- end
- end
- else
- fs.remove(target)
- end
- end
-
- remove(fs.path '', dirs)
- end
-end
-
-local version = loadPackage()
-print('版本号为:' .. version)
-
-local out = createDirectory(version)
-
-print('清理目录...')
-removeFiles(out)(true)
-
-print('开始复制文件...')
-local count = copyFiles(EXTENSION , out) {
- ['client'] = {
- ['node_modules'] = true,
- ['out'] = true,
- ['package-lock.json'] = true,
- ['package.json'] = true,
- ['tsconfig.json'] = true,
- },
- ['server'] = {
- ['Windows'] = true,
- ['macOS'] = true,
- ['Linux'] = true,
- ['libs'] = true,
- ['locale'] = true,
- ['src'] = true,
- ['test'] = true,
- ['main.lua'] = true,
- ['platform.lua'] = true,
- ['test.lua'] = true,
- ['build_package.lua'] = true,
- },
- ['images'] = {
- ['logo.png'] = true,
- },
- ['syntaxes'] = true,
- ['package-lock.json'] = true,
- ['package.json'] = true,
- ['README.md'] = true,
- ['tsconfig.json'] = true,
- ['package.nls.json'] = true,
- ['package.nls.zh-cn.json'] = true,
-}
-print(('复制了[%d]个文件'):format(count))
-
-print('开始测试...')
-runTest(out / 'server')
-
-print('删除多余文件...')
-removeFiles(out) {
- ['server'] = {
- ['log'] = true,
- ['test'] = true,
- ['test.lua'] = true,
- ['build_package.lua'] = true,
- },
-}
-
-local path = EXTENSION / 'publish' / 'lua'
-print('清理发布目录...')
-removeFiles(path)(true)
-
-print('复制到发布目录...')
-local count = copyFiles(out, path)(true)
-print(('复制了[%d]个文件'):format(count))
-
-print('完成')
diff --git a/script/rpc.lua b/script/rpc.lua
deleted file mode 100644
index 4a495695..00000000
--- a/script/rpc.lua
+++ /dev/null
@@ -1,91 +0,0 @@
-local json = require 'json'
-
-local TIMEOUT = 600.0
-
-local ID = 0
-local BUF = {}
-
-local function notify(self, method, params)
- local pack = {
- jsonrpc = '2.0',
- method = method,
- params = params,
- }
- local content = json.encode(pack)
- local buf = ('Content-Length: %d\r\n\r\n%s'):format(#content, content)
- io.write(buf)
-end
-
-local function request(self, method, params, callback)
- ID = ID + 1
- local pack = {
- jsonrpc = '2.0',
- id = ID,
- method = method,
- params = params,
- }
- local content = json.encode(pack)
- local buf = ('Content-Length: %d\r\n\r\n%s'):format(#content, content)
- BUF[ID] = {
- callback = callback,
- timeout = os.clock() + TIMEOUT,
- }
- io.write(buf)
-end
-
-local function requestWait(self, method, params, callback)
- ID = ID + 1
- local pack = {
- jsonrpc = '2.0',
- id = ID,
- method = method,
- params = params,
- }
- local content = json.encode(pack)
- local buf = ('Content-Length: %d\r\n\r\n%s'):format(#content, content)
- BUF[ID] = {
- callback = callback,
- }
- io.write(buf)
-end
-
-local function response(self, id, data)
- data.jsonrpc = '2.0'
- data.id = id
- local content = json.encode(data)
- local buf = ('Content-Length: %d\r\n\r\n%s'):format(#content, content)
- io.write(buf)
-end
-
-local function recieve(self, proto)
- local id = proto.id
- local data = BUF[id]
- if not data then
- log.warn('Recieve id not found: ', table.dump(proto))
- return
- end
- BUF[id] = nil
- if data.timeout and os.clock() > data.timeout then
- log.warn('Recieve timeout: ', table.dump(proto))
- if data.callback then
- local info = debug.getinfo(data.callback, 'S')
- log.warn('Call back info: ', info.source, info.linedefined)
- end
- return
- end
- if proto.error then
- log.warn('Recieve: ', table.dump(proto.error))
- return
- end
- if data.callback then
- data.callback(proto.result)
- end
-end
-
-return {
- notify = notify,
- request = request,
- requestWait = requestWait,
- response = response,
- recieve = recieve,
-}
diff --git a/script/sandbox.lua b/script/sandbox.lua
deleted file mode 100644
index 6b2f2a32..00000000
--- a/script/sandbox.lua
+++ /dev/null
@@ -1,167 +0,0 @@
-local function standard(loaded, env)
- local r = env or {}
- for _, s in ipairs {
- --'package',
- 'coroutine',
- 'table',
- --'io',
- 'os',
- 'string',
- 'math',
- 'utf8',
- 'debug',
- } do
- r[s] = _G[s]
- loaded[s] = _G[s]
- end
- for _, s in ipairs {
- 'assert',
- 'collectgarbage',
- --'dofile',
- 'error',
- 'getmetatable',
- 'ipairs',
- --'loadfile',
- 'load',
- 'next',
- 'pairs',
- 'pcall',
- 'print',
- 'rawequal',
- 'rawlen',
- 'rawget',
- 'rawset',
- 'select',
- 'setmetatable',
- 'tonumber',
- 'tostring',
- 'type',
- 'xpcall',
- '_VERSION',
- --'require',
- } do
- r[s] = _G[s]
- end
- return r
-end
-
-local function sandbox_env(loadlua, openfile, loaded, env)
- local _LOADED = loaded or {}
- local _E = standard(_LOADED, env)
- local _PRELOAD = {}
-
- _E.io = {
- open = openfile,
- }
-
- local function searchpath(name, path)
- local err = ''
- name = string.gsub(name, '%.', '/')
- for c in string.gmatch(path, '[^;]+') do
- local filename = string.gsub(c, '%?', name)
- local f = openfile(filename)
- if f then
- f:close()
- return filename
- end
- err = err .. ("\n\tno file '%s'"):format(filename)
- end
- return nil, err
- end
-
- local function searcher_preload(name)
- assert(type(_PRELOAD) == "table", "'package.preload' must be a table")
- if _PRELOAD[name] == nil then
- return ("\n\tno field package.preload['%s']"):format(name)
- end
- return _PRELOAD[name]
- end
-
- local function searcher_lua(name)
- assert(type(_E.package.path) == "string", "'package.path' must be a string")
- local filename, err = searchpath(name, _E.package.path)
- if not filename then
- return err
- end
- local f, err = loadlua(filename)
- if not f then
- error(("error loading module '%s' from file '%s':\n\t%s"):format(name, filename, err))
- end
- return f, filename
- end
-
- local function require_load(name)
- local msg = ''
- local _SEARCHERS = _E.package.searchers
- assert(type(_SEARCHERS) == "table", "'package.searchers' must be a table")
- for _, searcher in ipairs(_SEARCHERS) do
- local f, extra = searcher(name)
- if type(f) == 'function' then
- return f, extra
- elseif type(f) == 'string' then
- msg = msg .. f
- end
- end
- error(("module '%s' not found:%s"):format(name, msg))
- end
-
- _E.require = function(name)
- assert(type(name) == "string", ("bad argument #1 to 'require' (string expected, got %s)"):format(type(name)))
- local p = _LOADED[name]
- if p ~= nil then
- return p
- end
- local init, extra = require_load(name)
- if debug.getupvalue(init, 1) == '_ENV' then
- debug.setupvalue(init, 1, _E)
- end
- local res = init(name, extra)
- if res ~= nil then
- _LOADED[name] = res
- end
- if _LOADED[name] == nil then
- _LOADED[name] = true
- end
- return _LOADED[name]
- end
- _E.package = {
- config = [[
- \
- ;
- ?
- !
- -
- ]],
- loaded = _LOADED,
- path = '?.lua',
- preload = _PRELOAD,
- searchers = { searcher_preload, searcher_lua },
- searchpath = searchpath
- }
- return _E
-end
-
-return function(name, root, io_open, loaded, env)
- if not root:sub(-1):find '[/\\]' then
- root = root .. '/'
- end
- local function openfile(name, mode)
- return io_open(root .. name, mode)
- end
- local function loadlua(name)
- local f = openfile(name, 'r')
- if f then
- local str = f:read 'a'
- f:close()
- return load(str, '@' .. root .. name)
- end
- end
- local init = loadlua(name)
- if not init then
- return
- end
- if debug.getupvalue(init, 1) == '_ENV' then
- debug.setupvalue(init, 1, sandbox_env(loadlua, openfile, loaded, env))
- end
- return init()
-end
diff --git a/script/service.lua b/script/service.lua
deleted file mode 100644
index 44d75b58..00000000
--- a/script/service.lua
+++ /dev/null
@@ -1,1107 +0,0 @@
-local subprocess = require 'bee.subprocess'
-local method = require 'method'
-local thread = require 'bee.thread'
-local async = require 'async'
-local rpc = require 'rpc'
-local parser = require 'parser'
-local core = require 'core'
-local lang = require 'language'
-local updateTimer= require 'timer'
-local buildVM = require 'vm'
-local sourceMgr = require 'vm.source'
-local localMgr = require 'vm.local'
-local valueMgr = require 'vm.value'
-local chainMgr = require 'vm.chain'
-local functionMgr= require 'vm.function'
-local listMgr = require 'vm.list'
-local emmyMgr = require 'emmy.manager'
-local config = require 'config'
-local task = require 'task'
-local files = require 'files'
-local uric = require 'uri'
-local capability = require 'capability'
-local plugin = require 'plugin'
-local workspace = require 'workspace'
-local fn = require 'filename'
-local json = require 'json'
-
-local ErrorCodes = {
- -- Defined by JSON RPC
- ParseError = -32700,
- InvalidRequest = -32600,
- MethodNotFound = -32601,
- InvalidParams = -32602,
- InternalError = -32603,
- serverErrorStart = -32099,
- serverErrorEnd = -32000,
- ServerNotInitialized = -32002,
- UnknownErrorCode = -32001,
-
- -- Defined by the protocol.
- RequestCancelled = -32800,
-}
-
-local CachedVM = setmetatable({}, {__mode = 'kv'})
-
----@class LSP
-local mt = {}
-mt.__index = mt
----@type files
-mt._files = nil
-
-function mt:_callMethod(name, params)
- local optional
- if name:sub(1, 2) == '$/' then
- name = name:sub(3)
- optional = true
- end
- local f = method[name]
- if f then
- local clock = os.clock()
- local suc, res = xpcall(f, debug.traceback, self, params)
- local passed = os.clock() - clock
- if passed > 0.2 then
- log.debug(('Task [%s] takes [%.3f]sec.'):format(name, passed))
- end
- if suc then
- return res
- else
- local ok, r = pcall(table.dump, params)
- local dump = ok and r or '<Cyclic table>'
- log.debug(('Task [%s] failed, params: %s'):format(
- name, dump
- ))
- log.error(res)
- if res:find 'not enough memory' then
- self:restartDueToMemoryLeak()
- end
- return nil, {
- code = ErrorCodes.InternalError,
- message = r .. '\n' .. res,
- }
- end
- end
- if optional then
- return nil
- else
- return nil, {
- code = ErrorCodes.MethodNotFound,
- message = 'MethodNotFound',
- }
- end
-end
-
-function mt:responseProto(id, response, err)
- rpc:response(id, {
- error = err and err or nil,
- result = response and response or json.null,
- })
-end
-
-function mt:_doProto(proto)
- local id = proto.id
- local name = proto.method
- local params = proto.params
- local response, err = self:_callMethod(name, params)
- if not id then
- return
- end
- if type(response) == 'function' then
- response(function (final)
- self:responseProto(id, final)
- end)
- else
- self:responseProto(id, response, err)
- end
-end
-
-function mt:clearDiagnostics(uri)
- rpc:notify('textDocument/publishDiagnostics', {
- uri = uri,
- diagnostics = {},
- })
- self._needDiagnostics[uri] = nil
- log.debug('clearDiagnostics', uri)
-end
-
----@param uri uri
----@param compiled table
----@param mode string
----@return boolean
-function mt:needCompile(uri, compiled, mode)
- self._needDiagnostics[uri] = true
- if self._needCompile[uri] then
- return false
- end
- if not compiled then
- compiled = {}
- end
- if compiled[uri] then
- return false
- end
- self._needCompile[uri] = compiled
- if mode == 'child' then
- table.insert(self._needCompile, uri)
- else
- table.insert(self._needCompile, 1, uri)
- end
- return true
-end
-
-function mt:isNeedCompile(uri)
- return self._needCompile[uri]
-end
-
-function mt:isWaitingCompile()
- if self._needCompile[1] then
- return true
- else
- return false
- end
-end
-
----@param uri uri
----@param version integer
----@param text string
-function mt:saveText(uri, version, text)
- self._lastLoadedVM = uri
- self._files:save(uri, text, version)
- self:needCompile(uri)
-end
-
----@param uri uri
-function mt:isDeadText(uri)
- return self._files:isDead(uri)
-end
-
----@param name string
----@param uri uri
-function mt:addWorkspace(name, uri)
- log.info("Add workspace", name, uri)
- for _, ws in ipairs(self.workspaces) do
- if ws.name == name and ws.uri == uri then
- return
- end
- end
- local ws = workspace(self, name)
- ws:init(uri)
- table.insert(self.workspaces, ws)
- return ws
-end
-
----@param name string
----@param uri uri
-function mt:removeWorkspace(name, uri)
- log.info("Remove workspace", name, uri)
- local index
- for i, ws in ipairs(self.workspaces) do
- if ws.name == name and ws.uri == uri then
- index = i
- break
- end
- end
- if index then
- table.remove(self.workspaces, index)
- end
-end
-
----@param uri uri
----@return Workspace
-function mt:findWorkspaceFor(uri)
- if #self.workspaces == 0 then
- return nil
- end
- local path = uric.decode(uri)
- if not path then
- return nil
- end
- for _, ws in ipairs(self.workspaces) do
- if not ws:relativePathByUri(uri):string():match("^%.%.") then
- return ws
- end
- end
- log.info("No workspace for", uri)
- return nil
-end
-
----@param uri uri
----@return boolean
-function mt:isLua(uri)
- if fn.isLuaFile(uric.decode(uri)) then
- return true
- end
- return false
-end
-
-function mt:isIgnored(uri)
- local ws = self:findWorkspaceFor(uri)
- if not ws then
- return true
- end
- if not ws.gitignore then
- return true
- end
- local path = ws:relativePathByUri(uri)
- if not path then
- return true
- end
- if ws.gitignore(path:string()) then
- return true
- end
- return false
-end
-
----@param uri uri
----@param version integer
----@param text string
-function mt:open(uri, version, text)
- if not self:isLua(uri) then
- return
- end
- self:saveText(uri, version, text)
- self._files:open(uri, text)
-end
-
----@param uri uri
-function mt:close(uri)
- self._files:close(uri)
- if self._files:isLibrary(uri) then
- return
- end
- if not self:isLua(uri) or self:isIgnored(uri) then
- self:removeText(uri)
- end
-end
-
----@param uri uri
----@return boolean
-function mt:isOpen(uri)
- return self._files:isOpen(uri)
-end
-
-function mt:eachOpened()
- return self._files:eachOpened()
-end
-
-function mt:eachFile()
- return self._files:eachFile()
-end
-
----@param uri uri
----@param path path
----@param text string
-function mt:checkReadFile(uri, path, text)
- if not text then
- log.debug('No file: ', path)
- return false
- end
- local size = #text / 1000.0
- if size > config.config.workspace.preloadFileSize then
- log.info(('Skip large file, size: %.3f KB: %s'):format(size, uri))
- return false
- end
- if self:getCachedFileCount() >= config.config.workspace.maxPreload then
- if not self._hasShowHitMaxPreload then
- self._hasShowHitMaxPreload = true
- rpc:notify('window/showMessage', {
- type = 3,
- message = lang.script('MWS_MAX_PRELOAD', config.config.workspace.maxPreload),
- })
- end
- return false
- end
- return true
-end
-
----@param ws Workspace
----@param uri uri
----@param path path
----@param buf string
----@param compiled table
-function mt:readText(ws, uri, path, buf, compiled)
- if self:findWorkspaceFor(uri) ~= ws then
- log.debug('Read failed due to different workspace:', uri, debug.traceback())
- return
- end
- if self._files:get(uri) then
- log.debug('Read failed due to duplicate:', uri)
- return
- end
- if not self:isLua(uri) then
- log.debug('Read failed due to not lua:', uri)
- return
- end
- if not self._files:isOpen(uri) and self:isIgnored(uri) then
- log.debug('Read failed due to ignored:', uri)
- return
- end
- local text = buf or io.load(path)
- if not self._files:isOpen(uri) and not self:checkReadFile(uri, path, text) then
- log.debug('Read failed due to check failed:', uri)
- return
- end
- self._files:save(uri, text, 0)
- self:needCompile(uri, compiled)
-end
-
----@param ws Workspace
----@param uri uri
----@param path path
----@param buf string
----@param compiled table
-function mt:readLibrary(ws, uri, path, buf, compiled)
- if not self:isLua(uri) then
- return
- end
- if not self:checkReadFile(uri, path, buf) then
- return
- end
- self._files:save(uri, buf, 0, ws)
- self._files:setLibrary(uri)
- self:needCompile(uri, compiled)
- self:clearDiagnostics(uri)
-end
-
----@param uri uri
-function mt:removeText(uri)
- self._files:remove(uri)
- self:compileVM(uri)
- self:clearDiagnostics(uri)
-end
-
-function mt:getCachedFileCount()
- return self._files:count()
-end
-
-function mt:reCompile()
- if self.global then
- self.global:remove()
- end
- if self.chain then
- self.chain:remove()
- end
- if self.emmy then
- self.emmy:remove()
- end
-
- local compiled = {}
- self._files:clearVM()
-
- for _, obj in pairs(listMgr.list) do
- if obj.type == 'source' or obj.type == 'function' then
- obj:kill()
- end
- end
-
- self.global = core.global(self)
- self.chain = chainMgr()
- self.emmy = emmyMgr()
- self.globalValue = nil
- if self._compileTask then
- self._compileTask:remove()
- end
- self._needCompile = {}
- local n = 0
- for uri in self._files:eachFile() do
- self:needCompile(uri, compiled)
- n = n + 1
- end
- log.debug('reCompile:', n, self._files:count())
-
- self:_testMemory('skip')
-end
-
-function mt:reDiagnostic()
- for uri in self._files:eachFile() do
- self:clearDiagnostics(uri)
- self._needDiagnostics[uri] = true
- end
-end
-
-function mt:clearAllFiles()
- for uri in self._files:eachFile() do
- self:clearDiagnostics(uri)
- end
- self._files:clear()
-end
-
----@param uri uri
-function mt:loadVM(uri)
- local file = self._files:get(uri)
- if not file then
- return nil
- end
- if uri ~= self._lastLoadedVM then
- self:needCompile(uri)
- end
- if self._compileTask
- and not self._compileTask:isRemoved()
- and self._compileTask:get 'uri' == uri
- then
- self._compileTask:fastForward()
- else
- self:compileVM(uri)
- end
- if file:getVM() then
- self._lastLoadedVM = uri
- end
- return file:getVM(), file:getLines()
-end
-
-function mt:_markCompiled(uri, compiled)
- local newCompiled = self._needCompile[uri]
- if newCompiled then
- newCompiled[uri] = true
- self._needCompile[uri] = nil
- end
- for i, u in ipairs(self._needCompile) do
- if u == uri then
- table.remove(self._needCompile, i)
- break
- end
- end
- if newCompiled == compiled then
- return compiled
- end
- if not compiled then
- compiled = {}
- end
- for k, v in pairs(newCompiled) do
- compiled[k] = v
- end
- return compiled
-end
-
----@param file file
----@return table
-function mt:compileAst(file)
- local ast, err, comments = parser:parse(file:getText(), 'lua', config.config.runtime.version)
- file.comments = comments
- if ast then
- file:setAstErr(err)
- else
- if type(err) == 'string' then
- local message = lang.script('PARSER_CRASH', err)
- log.debug(message)
- rpc:notify('window/showMessage', {
- type = 3,
- message = lang.script('PARSER_CRASH', err:match '%.lua%:%d+%:(.+)' or err),
- })
- if message:find 'not enough memory' then
- self:restartDueToMemoryLeak()
- end
- end
- end
- return ast
-end
-
----@param file file
----@param uri uri
-function mt:_clearChainNode(file, uri)
- for pUri in file:eachParent() do
- local parent = self._files:get(pUri)
- if parent then
- parent:removeChild(uri)
- end
- end
-end
-
----@param file file
----@param compiled table
-function mt:_compileChain(file, compiled)
- if not compiled then
- compiled = {}
- end
- for uri in file:eachChild() do
- self:needCompile(uri, compiled, 'child')
- end
- for uri in file:eachParent() do
- self:needCompile(uri, compiled, 'parent')
- end
-end
-
-function mt:_compileGlobal(compiled)
- local uris = self.global:getAllUris()
- for _, uri in ipairs(uris) do
- self:needCompile(uri, compiled, 'global')
- end
-end
-
-function mt:_clearGlobal(uri)
- self.global:clearGlobal(uri)
-end
-
-function mt:_hasSetGlobal(uri)
- return self.global:hasSetGlobal(uri)
-end
-
----@param uri uri
-function mt:compileVM(uri)
- local file = self._files:get(uri)
- if not file then
- self:_markCompiled(uri)
- return nil
- end
- local compiled = self._needCompile[uri]
- if not compiled then
- return nil
- end
- file:removeVM()
-
- local clock = os.clock()
- local ast = self:compileAst(file)
- local version = file:getVersion()
- local astCost = os.clock() - clock
- if astCost > 0.1 then
- log.warn(('Compile Ast[%s] takes [%.3f] sec, size [%.3f]kb'):format(uri, astCost, #file:getText() / 1000))
- end
- file:clearOldText()
-
- self:_clearChainNode(file, uri)
- self:_clearGlobal(uri)
-
- local clock = os.clock()
- local vm, err = buildVM(ast, self, uri, file:getText())
- if vm then
- CachedVM[vm] = true
- end
- if self:isDeadText(uri)
- or file:isRemoved()
- or version ~= file:getVersion()
- then
- if vm then
- vm:remove()
- end
- return nil
- end
- if self._needCompile[uri] then
- self:_markCompiled(uri, compiled)
- self._needDiagnostics[uri] = true
- else
- if vm then
- vm:remove()
- end
- return nil
- end
- file:saveVM(vm, version, os.clock() - clock)
-
- local clock = os.clock()
- local lines = parser:lines(file:getText(), 'utf8')
- local lineCost = os.clock() - clock
- file:saveLines(lines, lineCost)
-
- if file:getVMCost() > 0.2 then
- log.debug(('Compile VM[%s] takes: %.3f sec'):format(uri, file:getVMCost()))
- end
- if not vm then
- error(err)
- end
-
- self:_compileChain(file, compiled)
- if self:_hasSetGlobal(uri) then
- self:_compileGlobal(compiled)
- end
-
- return file
-end
-
----@param uri uri
-function mt:doDiagnostics(uri)
- if not config.config.diagnostics.enable then
- self._needDiagnostics[uri] = nil
- return
- end
- if not self._needDiagnostics[uri] then
- return
- end
- local name = 'textDocument/publishDiagnostics'
- local file = self._files:get(uri)
- if not file
- or file:isRemoved()
- or not file:getVM()
- or file:getVM():isRemoved()
- or self._files:isLibrary(uri)
- then
- self._needDiagnostics[uri] = nil
- self:clearDiagnostics(uri)
- return
- end
- local data = {
- uri = uri,
- vm = file:getVM(),
- lines = file:getLines(),
- version = file:getVM():getVersion(),
- }
- local res = self:_callMethod(name, data)
- if self:isDeadText(uri) then
- return
- end
- if file:getVM():getVersion() ~= data.version then
- return
- end
- if self._needDiagnostics[uri] then
- self._needDiagnostics[uri] = nil
- else
- return
- end
- if res then
- rpc:notify(name, {
- uri = uri,
- diagnostics = res,
- })
- else
- self:clearDiagnostics(uri)
- end
-end
-
----@param uri uri
----@return file
-function mt:getFile(uri)
- return self._files:get(uri)
-end
-
----@param uri uri
----@return VM
----@return table
----@return string
-function mt:getVM(uri)
- local file = self._files:get(uri)
- if not file then
- return nil
- end
- return file:getVM(), file:getLines(), file:getText()
-end
-
----@param uri uri
----@return string
----@return string
-function mt:getText(uri)
- local file = self._files:get(uri)
- if not file then
- return nil
- end
- return file:getText(), file:getOldText()
-end
-
-function mt:getComments(uri)
- local file = self._files:get(uri)
- if not file then
- return nil
- end
- return file:getComments()
-end
-
----@param uri uri
----@return table
-function mt:getAstErrors(uri)
- local file = self._files:get(uri)
- if not file then
- return nil
- end
- return file:getAstErr()
-end
-
----@param child uri
----@param parent uri
-function mt:compileChain(child, parent)
- local parentFile = self._files:get(parent)
- local childFile = self._files:get(child)
-
- if not parentFile or not childFile then
- return
- end
- if parentFile == childFile then
- return
- end
-
- parentFile:addChild(child)
- childFile:addParent(parent)
-end
-
-function mt:checkWorkSpaceComplete()
- if self._hasCheckedWorkSpaceComplete then
- return
- end
- self._hasCheckedWorkSpaceComplete = true
- for _, ws in ipairs(self.workspaces) do
- if ws:isComplete() then
- return
- end
- end
- self._needShowComplete = true
- rpc:notify('window/showMessage', {
- type = 3,
- message = lang.script.MWS_NOT_COMPLETE,
- })
-end
-
-function mt:_createCompileTask()
- if not self:isWaitingCompile() and not next(self._needDiagnostics) then
- if self._needShowComplete then
- self._needShowComplete = nil
- rpc:notify('window/showMessage', {
- type = 3,
- message = lang.script.MWS_COMPLETE,
- })
- end
- end
- self._compileTask = task(function ()
- self:doDiagnostics(self._lastLoadedVM)
- local uri = self._needCompile[1]
- if uri then
- self._compileTask:set('uri', uri)
- pcall(function () self:compileVM(uri) end)
- else
- uri = next(self._needDiagnostics)
- if uri then
- self:doDiagnostics(uri)
- end
- end
- end)
-end
-
-function mt:_doCompileTask()
- if not self._compileTask or self._compileTask:isRemoved() then
- self:_createCompileTask()
- end
- while true do
- local res = self._compileTask:step()
- if res == 'stop' then
- self._compileTask:remove()
- break
- end
- if self._compileTask:isRemoved() then
- break
- end
- end
- self:_loadProto()
-end
-
-function mt:_loadProto()
- while true do
- local ok, protoStream = self._proto:pop()
- if not ok then
- break
- end
- local null = json.null
- json.null = nil
- local suc, proto = xpcall(json.decode, log.error, protoStream)
- json.null = null
- if not suc then
- break
- end
- if proto.method then
- self:_doProto(proto)
- else
- rpc:recieve(proto)
- end
- end
-end
-
-function mt:restartDueToMemoryLeak()
- rpc:requestWait('window/showMessageRequest', {
- type = 3,
- message = lang.script('DEBUG_MEMORY_LEAK', '[Lua]'),
- actions = {
- {
- title = lang.script.DEBUG_RESTART_NOW,
- }
- }
- }, function ()
- os.exit(true)
- end)
- ac.wait(5, function ()
- os.exit(true)
- end)
-end
-
-function mt:reScanFiles()
- log.debug('reScanFiles')
- self:clearAllFiles()
- for _, ws in ipairs(self.workspaces) do
- ws:scanFiles()
- end
- for uri, text in self:eachOpened() do
- self:open(uri, 0, text)
- end
-end
-
-function mt:onUpdateConfig(updated, other)
- local oldConfig = table.deepCopy(config.config)
- local oldOther = table.deepCopy(config.other)
- config:setConfig(updated, other)
- local newConfig = config.config
- local newOther = config.other
- if not table.equal(oldConfig.runtime, newConfig.runtime) then
- local library = require 'core.library'
- library.reload()
- self:reCompile()
- end
- if not table.equal(oldConfig.diagnostics, newConfig.diagnostics) then
- log.debug('reDiagnostic')
- self:reDiagnostic()
- end
- if newConfig.completion.enable then
- capability.completion.enable(self)
- else
- capability.completion.disable(self)
- end
- if newConfig.color.mode == 'Semantic' then
- capability.semantic.enable(self)
- else
- capability.semantic.disable()
- end
- if not table.equal(oldConfig.plugin, newConfig.plugin) then
- for _, ws in ipairs(self.workspaces) do
- plugin.load(ws)
- end
- end
- if not table.equal(oldConfig.workspace, newConfig.workspace)
- or not table.equal(oldConfig.plugin, newConfig.plugin)
- or not table.equal(oldOther.associations, newOther.associations)
- or not table.equal(oldOther.exclude, newOther.exclude)
- then
- self:reScanFiles()
- end
-end
-
-function mt:_testMemory(skipDead)
- local clock = os.clock()
- collectgarbage()
- log.debug('collectgarbage: ', ('%.3f'):format(os.clock() - clock))
-
- local clock = os.clock()
- local cachedVM = 0
- local cachedSource = 0
- local cachedFunction = 0
- for _, file in self._files:eachFile() do
- local vm = file:getVM()
- if vm and not vm:isRemoved() then
- cachedVM = cachedVM + 1
- cachedSource = cachedSource + #vm.sources
- cachedFunction = cachedFunction + #vm.funcs
- end
- end
- local aliveVM = 0
- local deadVM = 0
- for vm in pairs(CachedVM) do
- if vm:isRemoved() then
- deadVM = deadVM + 1
- else
- aliveVM = aliveVM + 1
- end
- end
-
- local alivedSource = 0
- local deadSource = 0
- for _, id in pairs(sourceMgr.watch) do
- if listMgr.get(id) then
- alivedSource = alivedSource + 1
- else
- deadSource = deadSource + 1
- end
- end
-
- local alivedFunction = 0
- local deadFunction = 0
- for _, id in pairs(functionMgr.watch) do
- if listMgr.get(id) then
- alivedFunction = alivedFunction + 1
- else
- deadFunction = deadFunction + 1
- end
- end
-
- local totalLocal = 0
- for _ in pairs(localMgr.watch) do
- totalLocal = totalLocal + 1
- end
-
- local totalValue = 0
- local deadValue = 0
- for value in pairs(valueMgr.watch) do
- totalValue = totalValue + 1
- if not value:getSource() then
- deadValue = deadValue + 1
- end
- end
-
- local totalEmmy = self.emmy:count()
-
- local mem = collectgarbage 'count'
- local threadInfo = async.info
- local threadBuf = {}
- for i, count in ipairs(threadInfo) do
- if count then
- threadBuf[i] = ('#%03d Mem: [%.3f]kb'):format(i, count)
- else
- threadBuf[i] = ('#%03d Mem: <Unknown>'):format(i)
- end
- end
-
- log.debug(('\n\z
- State\n\z
- Main Mem: [%.3f]kb\n\z
- %s\n\z
--------------------\n\z
- CachedVM: [%d]\n\z
- AlivedVM: [%d]\n\z
- DeadVM: [%d]\n\z
--------------------\n\z
- CachedSrc: [%d]\n\z
- AlivedSrc: [%d]\n\z
- DeadSrc: [%d]\n\z
--------------------\n\z
- CachedFunc:[%d]\n\z
- AlivedFunc:[%d]\n\z
- DeadFunc: [%d]\n\z
--------------------\n\z
- TotalVal: [%d]\n\z
- DeadVal: [%d]\n\z
--------------------\n\z
- TotalLoc: [%d]\n\z
- TotalEmmy: [%d]\n\z'):format(
- mem,
- table.concat(threadBuf, '\n'),
-
- cachedVM,
- aliveVM,
- deadVM,
-
- cachedSource,
- alivedSource,
- deadSource,
-
- cachedFunction,
- alivedFunction,
- deadFunction,
-
- totalValue,
- deadValue,
- totalLocal,
- totalEmmy
- ))
- log.debug('test memory: ', ('%.3f'):format(os.clock() - clock))
-
- -- TODO
- --if deadValue / totalValue >= 0.5 and not skipDead then
- -- self:_testFindDeadValues()
- --end
-end
-
-function mt:_testFindDeadValues()
- if self._testHasFoundDeadValues then
- return
- end
- self._testHasFoundDeadValues = true
-
- log.debug('Start find dead values, may takes few seconds...')
-
- local mark = {}
- local stack = {}
- local count = 0
- local clock = os.clock()
- local function push(info)
- stack[#stack+1] = info
- end
- local function pop()
- stack[#stack] = nil
- end
- local function showStack(uri)
- count = count + 1
- log.debug(uri, table.concat(stack, '->'))
- end
- local function scan(name, tbl)
- if count > 100 or os.clock() - clock > 5.0 then
- return
- end
- if type(tbl) ~= 'table' then
- return
- end
- if mark[tbl] then
- return
- end
- mark[tbl] = true
- if tbl.type then
- push(('%s<%s>'):format(name, tbl.type))
- else
- push(name)
- end
- if tbl.type == 'value' then
- if not tbl:getSource() then
- showStack(tbl.uri)
- end
- elseif tbl.type == 'files' then
- for k, v in tbl:eachFile() do
- scan(k, v)
- end
- else
- for k, v in pairs(tbl) do
- scan(k, v)
- end
- end
- pop()
- end
- scan('root', self._files)
- log.debug('Finish...')
-end
-
-function mt:onTick()
- self:_loadProto()
- self:_doCompileTask()
- if (os.clock() - self._clock >= 60 and not self:isWaitingCompile())
- or (os.clock() - self._clock >= 300)
- then
- self._clock = os.clock()
- self:_testMemory()
- end
-end
-
-function mt:listen()
- subprocess.filemode(io.stdin, 'b')
- subprocess.filemode(io.stdout, 'b')
- io.stdin:setvbuf 'no'
- io.stdout:setvbuf 'no'
-
- local _, out = async.run 'proto'
- self._proto = out
-
- local timerClock = 0.0
- while true do
- local startClock = os.clock()
- async.onTick()
- self:onTick()
-
- local delta = os.clock() - timerClock
- local suc, err = xpcall(updateTimer, log.error, delta)
- if not suc then
- io.stderr:write(err)
- io.stderr:flush()
- end
- timerClock = os.clock()
-
- local passedClock = os.clock() - startClock
- if passedClock > 0.1 then
- thread.sleep(0.0)
- else
- thread.sleep(0.001)
- end
- end
-end
-
---- @return LSP
-return function ()
- local session = setmetatable({
- _needCompile = {},
- _needDiagnostics = {},
- _clock = -100,
- _version = 0,
- _files = files(),
- }, mt)
- session.global = core.global(session)
- session.chain = chainMgr()
- session.emmy = emmyMgr()
- ---@type Workspace[]
- session.workspaces = {}
- return session
-end
diff --git a/script/task.lua b/script/task.lua
deleted file mode 100644
index bb1a7922..00000000
--- a/script/task.lua
+++ /dev/null
@@ -1,64 +0,0 @@
-local mt = {}
-mt.__index = mt
-mt.type = 'task'
-
-function mt:remove()
- if self._removed then
- return
- end
- self._removed = true
- coroutine.close(self.task)
-end
-
-function mt:isRemoved()
- return self._removed
-end
-
-function mt:step()
- if self._removed then
- return
- end
- local suc, res = coroutine.resume(self.task)
- if not suc then
- self:remove()
- log.error(debug.traceback(self.task, res))
- return
- end
- if coroutine.status(self.task) == 'dead' then
- self:remove()
- end
- return res
-end
-
-function mt:fastForward()
- if self._removed then
- return
- end
- while true do
- local suc = coroutine.resume(self.task)
- if not suc then
- self:remove()
- break
- end
- if coroutine.status(self.task) == 'dead' then
- self:remove()
- break
- end
- end
-end
-
-function mt:set(key, value)
- self.data[key] = value
-end
-
-function mt:get(key)
- return self.data[key]
-end
-
-return function (callback)
- local self = setmetatable({
- data = {},
- task = coroutine.create(callback),
- }, mt)
- return self
-end
diff --git a/script/timer.lua b/script/timer.lua
deleted file mode 100644
index dc36e6a0..00000000
--- a/script/timer.lua
+++ /dev/null
@@ -1,276 +0,0 @@
-local setmetatable = setmetatable
-local pairs = pairs
-local tableInsert = table.insert
-local mathMax = math.max
-local mathFloor = math.floor
-
-local curFrame = 0
-local maxFrame = 0
-local curIndex = 0
-local freeQueue = {}
-local timer = {}
-
-local function allocQueue()
- local n = #freeQueue
- if n > 0 then
- local r = freeQueue[n]
- freeQueue[n] = nil
- return r
- else
- return {}
- end
-end
-
-local function mTimeout(self, timeout)
- if self._pauseRemaining or self._running then
- return
- end
- local ti = curFrame + timeout
- local q = timer[ti]
- if q == nil then
- q = allocQueue()
- timer[ti] = q
- end
- self._timeoutFrame = ti
- self._running = true
- q[#q + 1] = self
-end
-
-local function mWakeup(self)
- if self._removed then
- return
- end
- self._running = false
- if self._onTimer then
- xpcall(self._onTimer, log.error, self)
- end
- if self._removed then
- return
- end
- if self._timerCount then
- if self._timerCount > 1 then
- self._timerCount = self._timerCount - 1
- mTimeout(self, self._timeout)
- else
- self._removed = true
- end
- else
- mTimeout(self, self._timeout)
- end
-end
-
-local function getRemaining(self)
- if self._removed then
- return 0
- end
- if self._pauseRemaining then
- return self._pauseRemaining
- end
- if self._timeoutFrame == curFrame then
- return self._timeout or 0
- end
- return self._timeoutFrame - curFrame
-end
-
-local function onTick()
- local q = timer[curFrame]
- if q == nil then
- curIndex = 0
- return
- end
- for i = curIndex + 1, #q do
- local callback = q[i]
- curIndex = i
- q[i] = nil
- if callback then
- mWakeup(callback)
- end
- end
- curIndex = 0
- timer[curFrame] = nil
- freeQueue[#freeQueue + 1] = q
-end
-
-function ac.clock()
- return curFrame / 1000.0
-end
-
-function ac.timer_size()
- local n = 0
- for _, ts in pairs(timer) do
- n = n + #ts
- end
- return n
-end
-
-function ac.timer_all()
- local tbl = {}
- for _, ts in pairs(timer) do
- for i, t in ipairs(ts) do
- if t then
- tbl[#tbl + 1] = t
- end
- end
- end
- return tbl
-end
-
-local function update(delta)
- if curIndex ~= 0 then
- curFrame = curFrame - 1
- end
- maxFrame = maxFrame + delta * 1000.0
- while curFrame < maxFrame do
- curFrame = curFrame + 1
- onTick()
- end
-end
-
-local mt = {}
-mt.__index = mt
-mt.type = 'timer'
-
-function mt:__tostring()
- return '[table:timer]'
-end
-
-function mt:__call()
- if self._onTimer then
- self:_onTimer()
- end
-end
-
-function mt:remove()
- self._removed = true
-end
-
-function mt:pause()
- if self._removed or self._pauseRemaining then
- return
- end
- self._pauseRemaining = getRemaining(self)
- self._running = false
- local ti = self._timeoutFrame
- local q = timer[ti]
- if q then
- for i = #q, 1, -1 do
- if q[i] == self then
- q[i] = false
- return
- end
- end
- end
-end
-
-function mt:resume()
- if self._removed or not self._pauseRemaining then
- return
- end
- local timeout = self._pauseRemaining
- self._pauseRemaining = nil
- mTimeout(self, timeout)
-end
-
-function mt:restart()
- if self._removed or self._pauseRemaining or not self._running then
- return
- end
- local ti = self._timeoutFrame
- local q = timer[ti]
- if q then
- for i = #q, 1, -1 do
- if q[i] == self then
- q[i] = false
- break
- end
- end
- end
- self._running = false
- mTimeout(self, self._timeout)
-end
-
-function mt:remaining()
- return getRemaining(self) / 1000.0
-end
-
-function mt:onTimer()
- self:_onTimer()
-end
-
-function ac.wait(timeout, onTimer)
- local t = setmetatable({
- ['_timeout'] = mathMax(mathFloor(timeout * 1000.0), 1),
- ['_onTimer'] = onTimer,
- ['_timerCount'] = 1,
- }, mt)
- mTimeout(t, t._timeout)
- return t
-end
-
-function ac.loop(timeout, onTimer)
- local t = setmetatable({
- ['_timeout'] = mathFloor(timeout * 1000.0),
- ['_onTimer'] = onTimer,
- }, mt)
- mTimeout(t, t._timeout)
- return t
-end
-
-function ac.timer(timeout, count, onTimer)
- if count == 0 then
- return ac.loop(timeout, onTimer)
- end
- local t = setmetatable({
- ['_timeout'] = mathFloor(timeout * 1000.0),
- ['_onTimer'] = onTimer,
- ['_timerCount'] = count,
- }, mt)
- mTimeout(t, t._timeout)
- return t
-end
-
-local function utimer_initialize(u)
- if not u._timers then
- u._timers = {}
- end
- if #u._timers > 0 then
- return
- end
- u._timers[1] = ac.loop(0.01, function()
- local timers = u._timers
- for i = #timers, 2, -1 do
- if timers[i]._removed then
- local len = #timers
- timers[i] = timers[len]
- timers[len] = nil
- end
- end
- if #timers == 1 then
- timers[1]:remove()
- timers[1] = nil
- end
- end)
-end
-
-function ac.uwait(u, timeout, onTimer)
- utimer_initialize(u)
- local t = ac.wait(timeout, onTimer)
- tableInsert(u._timers, t)
- return t
-end
-
-function ac.uloop(u, timeout, onTimer)
- utimer_initialize(u)
- local t = ac.loop(timeout, onTimer)
- tableInsert(u._timers, t)
- return t
-end
-
-function ac.utimer(u, timeout, count, onTimer)
- utimer_initialize(u)
- local t = ac.timer(timeout, count, onTimer)
- tableInsert(u._timers, t)
- return t
-end
-
-return update
diff --git a/script/uri.lua b/script/uri.lua
deleted file mode 100644
index 8f570daf..00000000
--- a/script/uri.lua
+++ /dev/null
@@ -1,15 +0,0 @@
-local fs = require 'bee.filesystem'
-local furi = require 'file-uri'
-
-local function encode(path)
- return furi.encode(path:string())
-end
-
-local function decode(uri)
- return fs.path(furi.decode(uri))
-end
-
-return {
- encode = encode,
- decode = decode,
-}
diff --git a/script/utility.lua b/script/utility.lua
deleted file mode 100644
index d7fc9dde..00000000
--- a/script/utility.lua
+++ /dev/null
@@ -1,234 +0,0 @@
-local fs = require 'bee.filesystem'
-
-local table_sort = table.sort
-local stringRep = string.rep
-local type = type
-local pairs = pairs
-local ipairs = ipairs
-local math_type = math.type
-local next = next
-local rawset = rawset
-local move = table.move
-local setmetatable = setmetatable
-local tableSort = table.sort
-local mathType = math.type
-
-local function formatNumber(n)
- local str = ('%.10f'):format(n)
- str = str:gsub('%.?0*$', '')
- return str
-end
-
-local TAB = setmetatable({}, { __index = function (self, n)
- self[n] = stringRep('\t', n)
- return self[n]
-end})
-
-local RESERVED = {
- ['and'] = true,
- ['break'] = true,
- ['do'] = true,
- ['else'] = true,
- ['elseif'] = true,
- ['end'] = true,
- ['false'] = true,
- ['for'] = true,
- ['function'] = true,
- ['goto'] = true,
- ['if'] = true,
- ['in'] = true,
- ['local'] = true,
- ['nil'] = true,
- ['not'] = true,
- ['or'] = true,
- ['repeat'] = true,
- ['return'] = true,
- ['then'] = true,
- ['true'] = true,
- ['until'] = true,
- ['while'] = true,
-}
-
-function table.dump(tbl)
- if type(tbl) ~= 'table' then
- return ('%q'):format(tbl)
- end
- local lines = {}
- local mark = {}
- lines[#lines+1] = '{'
- local function unpack(tbl, tab)
- if mark[tbl] and mark[tbl] > 0 then
- lines[#lines+1] = TAB[tab+1] .. '"<Loop>"'
- return
- end
- if #lines > 10000 then
- lines[#lines+1] = TAB[tab+1] .. '"<Large>"'
- return
- end
- mark[tbl] = (mark[tbl] or 0) + 1
- local keys = {}
- local keymap = {}
- local integerFormat = '[%d]'
- if #tbl >= 10 then
- local width = math.log(#tbl, 10)
- integerFormat = ('[%%0%dd]'):format(math.ceil(width))
- end
- for key in pairs(tbl) do
- if type(key) == 'string' then
- if not key:match('^[%a_][%w_]*$')
- or #key >= 32
- or RESERVED[key]
- then
- keymap[key] = ('[%q]'):format(key)
- else
- keymap[key] = key
- end
- elseif mathType(key) == 'integer' then
- keymap[key] = integerFormat:format(key)
- else
- keymap[key] = ('["<%s>"]'):format(key)
- end
- keys[#keys+1] = key
- end
- local mt = getmetatable(tbl)
- if not mt or not mt.__pairs then
- tableSort(keys, function (a, b)
- return keymap[a] < keymap[b]
- end)
- end
- for _, key in ipairs(keys) do
- local value = tbl[key]
- local tp = type(value)
- if tp == 'table' then
- lines[#lines+1] = ('%s%s = {'):format(TAB[tab+1], keymap[key])
- unpack(value, tab+1)
- lines[#lines+1] = ('%s},'):format(TAB[tab+1])
- elseif tp == 'string' or tp == 'boolean' then
- lines[#lines+1] = ('%s%s = %q,'):format(TAB[tab+1], keymap[key], value)
- elseif tp == 'number' then
- lines[#lines+1] = ('%s%s = %s,'):format(TAB[tab+1], keymap[key], formatNumber(value))
- elseif tp == 'nil' then
- else
- lines[#lines+1] = ('%s%s = %s,'):format(TAB[tab+1], keymap[key], tostring(value))
- end
- end
- mark[tbl] = mark[tbl] - 1
- end
- unpack(tbl, 0)
- lines[#lines+1] = '}'
- return table.concat(lines, '\r\n')
-end
-
-local function sort_table(tbl)
- if not tbl then
- tbl = {}
- end
- local mt = {}
- local keys = {}
- local mark = {}
- local n = 0
- for key in next, tbl do
- n=n+1;keys[n] = key
- mark[key] = true
- end
- table_sort(keys)
- function mt:__newindex(key, value)
- rawset(self, key, value)
- n=n+1;keys[n] = key
- mark[key] = true
- if type(value) == 'table' then
- sort_table(value)
- end
- end
- function mt:__pairs()
- local list = {}
- local m = 0
- for key in next, self do
- if not mark[key] then
- m=m+1;list[m] = key
- end
- end
- if m > 0 then
- move(keys, 1, n, m+1)
- table_sort(list)
- for i = 1, m do
- local key = list[i]
- keys[i] = key
- mark[key] = true
- end
- n = n + m
- end
- local i = 0
- return function ()
- i = i + 1
- local key = keys[i]
- return key, self[key]
- end
- end
-
- return setmetatable(tbl, mt)
-end
-
-function table.container(tbl)
- return sort_table(tbl)
-end
-
-function table.equal(a, b)
- local tp1, tp2 = type(a), type(b)
- if tp1 ~= tp2 then
- return false
- end
- if tp1 == 'table' then
- local mark = {}
- for k in pairs(a) do
- if not table.equal(a[k], b[k]) then
- return false
- end
- mark[k] = true
- end
- for k in pairs(b) do
- if not mark[k] then
- return false
- end
- end
- return true
- end
- return a == b
-end
-
-function table.deepCopy(a)
- local t = {}
- for k, v in pairs(a) do
- if type(v) == 'table' then
- t[k] = table.deepCopy(v)
- else
- t[k] = v
- end
- end
- return t
-end
-
-function io.load(file_path)
- local f, e = io.open(file_path:string(), 'rb')
- if not f then
- return nil, e
- end
- if f:read(3) ~= '\xEF\xBB\xBF' then
- f:seek("set")
- end
- local buf = f:read 'a'
- f:close()
- return buf
-end
-
-function io.save(file_path, content)
- local f, e = io.open(file_path:string(), "wb")
-
- if f then
- f:write(content)
- f:close()
- return true
- else
- return false, e
- end
-end
diff --git a/script/vm/chain.lua b/script/vm/chain.lua
deleted file mode 100644
index 6e7c6ac7..00000000
--- a/script/vm/chain.lua
+++ /dev/null
@@ -1,65 +0,0 @@
-local valueMgr = require 'vm.value'
-local sourceMgr = require 'vm.source'
-
-local mt = {}
-mt.__index = mt
-mt.type = 'chain'
-
-mt.min = 100
-mt.max = 100
-mt.count = 0
-
-function mt:clearCache()
- if self.count <= self.max then
- return
- end
- local clock = os.clock()
- local n = 0
- for uri, value in pairs(self.cache) do
- local ok = value:eachInfo(function ()
- return true
- end)
- if ok then
- n = n + 1
- else
- value:getSource():kill()
- self.cache[uri] = nil
- end
- end
- self.count = n
- self.max = self.count * 1.1 + 10
- if self.max < self.min then
- self.max = self.min
- end
- local passed = os.clock() - clock
- if passed > 0.1 then
- log.warn(('chain:clearCache takes: [%.3f]sec, self.count: %d'):format(passed, self.count))
- end
-end
-
-function mt:get(uri)
- if not self.cache[uri] then
- self.count = self.count + 1
- self:clearCache()
- self.cache[uri] = valueMgr.create('any', sourceMgr.dummy())
- self.cache[uri]:markGlobal()
- self.cache[uri].uri = uri
- end
- return self.cache[uri]
-end
-
-function mt:remove()
- if self.removed then
- return
- end
- self.removed = true
- for _, value in pairs(self.cache) do
- value:getSource():kill()
- end
-end
-
-return function ()
- return setmetatable({
- cache = {},
- }, mt)
-end
diff --git a/script/vm/emmy.lua b/script/vm/emmy.lua
deleted file mode 100644
index 67242f9a..00000000
--- a/script/vm/emmy.lua
+++ /dev/null
@@ -1,372 +0,0 @@
-local functionMgr = require 'vm.function'
-local library = require 'vm.library'
-local mt = require 'vm.manager'
-
-function mt:clearEmmy()
- self._emmy = nil
- self._emmyParams = nil
- self._emmyReturns = nil
- self._emmyGeneric = nil
- self._emmyComment = nil
- self._emmyOverLoads = nil
-end
-
-function mt:doEmmy(action)
- local tp = action.type
- if tp == 'emmyClass' then
- self:doEmmyClass(action)
- elseif tp == 'emmyType' then
- self:doEmmyType(action)
- elseif tp == 'emmyAlias' then
- self:doEmmyAlias(action)
- elseif tp == 'emmyParam' then
- self:doEmmyParam(action)
- elseif tp == 'emmyReturn' then
- self:doEmmyReturn(action)
- elseif tp == 'emmyField' then
- self:doEmmyField(action)
- elseif tp == 'emmyGeneric' then
- self:doEmmyGeneric(action)
- elseif tp == 'emmyVararg' then
- self:doEmmyVararg(action)
- elseif tp == 'emmyLanguage' then
- elseif tp == 'emmyArrayType' then
- self:doEmmyArrayType(action)
- elseif tp == 'emmyTableType' then
- self:doEmmyTableType(action)
- elseif tp == 'emmyFunctionType' then
- self:doEmmyFunctionType(action)
- elseif tp == 'emmySee' then
- self:doEmmySee(action)
- elseif tp == 'emmyOverLoad' then
- self:doEmmyOverLoad(action)
- elseif tp == 'emmyIncomplete' then
- self:doEmmyIncomplete(action)
- elseif tp == 'emmyComment' then
- self:doEmmyComment(action)
- end
-end
-
-function mt:getEmmy()
- local emmy = self._emmy
- self._emmy = nil
- return emmy
-end
-
-function mt:addEmmyParam(param)
- if not self._emmyParams then
- self._emmyParams = {}
- end
- self._emmyParams[#self._emmyParams+1] = param
-end
-
-function mt:addEmmyReturn(rtn)
- if not self._emmyReturns then
- self._emmyReturns = {}
- end
- self._emmyReturns[#self._emmyReturns+1] = rtn
-end
-
-function mt:addEmmyOverLoad(funcObj)
- if not self._emmyOverLoads then
- self._emmyOverLoads = {}
- end
- self._emmyOverLoads[#self._emmyOverLoads+1] = funcObj
-end
-
-function mt:getEmmyParams()
- local params = self._emmyParams
- self._emmyParams = nil
- return params
-end
-
-function mt:getEmmyReturns()
- local returns = self._emmyReturns
- self._emmyReturns = nil
- return returns
-end
-
-function mt:getEmmyOverLoads()
- local overLoads = self._emmyOverLoads
- self._emmyOverLoads = nil
- return overLoads
-end
-
-function mt:getEmmyGeneric()
- local generic = self._emmyGeneric
- self._emmyGeneric = nil
- return generic
-end
-
----@return string
-function mt:getEmmyComment()
- local comment = self._emmyComment
- self._emmyComment = nil
- return comment
-end
-
-function mt:doEmmyClass(action)
- ---@type emmyMgr
- local emmyMgr = self.emmyMgr
- self:instantSource(action)
- self:instantSource(action[1])
- local class = emmyMgr:addClass(action)
- action:set('emmy class', class:getName())
- action[1]:set('emmy class', class:getName())
- local extends = action[2]
- if extends then
- self:instantSource(extends)
- extends:set('emmy class', extends[1])
- end
- self._emmy = class
- action:set('emmy.class', class)
- if self.lsp then
- self.lsp.global:markSet(self:getUri())
- end
-end
-
-function mt:buildEmmyType(action)
- ---@type emmyMgr
- local emmyMgr = self.emmyMgr
- self:instantSource(action)
- for _, obj in ipairs(action) do
- self:instantSource(obj)
- obj:set('emmy class', obj[1])
- end
- local type = emmyMgr:addType(action)
- return type
-end
-
-function mt:doEmmyType(action)
- local type = self:buildEmmyType(action)
- self._emmy = type
- if self.lsp then
- self.lsp.global:markGet(self:getUri())
- end
- return type
-end
-
-function mt:doEmmyAlias(action)
- ---@type emmyMgr
- local emmyMgr = self.emmyMgr
- self:instantSource(action)
- self:instantSource(action[1])
- local type = self:buildEmmyAnyType(action[2])
- local alias = emmyMgr:addAlias(action, type)
- action:set('emmy.alias', alias)
- action[1]:set('emmy class', alias:getName())
- self._emmy = type
- if self.lsp then
- self.lsp.global:markSet(self:getUri())
- end
-end
-
-function mt:getGenericByType(type)
- local generics = self._emmyGeneric
- if not generics then
- return
- end
- if #type ~= 1 then
- return
- end
- local name = type[1][1]
- for _, generic in ipairs(generics) do
- if generic:getName() == name then
- return generic
- end
- end
- return nil
-end
-
-function mt:doEmmyParam(action)
- ---@type emmyMgr
- local emmyMgr = self.emmyMgr
- self:instantSource(action)
- self:instantSource(action[1])
- local type = self:getGenericByType(action[2]) or self:buildEmmyAnyType(action[2])
- local param = emmyMgr:addParam(action, type)
- action:set('emmy.param', param)
- self:addEmmyParam(param)
- if self.lsp then
- self.lsp.global:markGet(self:getUri())
- end
-end
-
-function mt:doEmmyReturn(action)
- ---@type emmyMgr
- local emmyMgr = self.emmyMgr
- self:instantSource(action)
- local type = action[1] and (self:getGenericByType(action[1]) or self:buildEmmyAnyType(action[1]))
- local name = action[2]
- local rtn = emmyMgr:addReturn(action, type, name)
- action:set('emmy.return', rtn)
- self:addEmmyReturn(rtn)
- if self.lsp then
- self.lsp.global:markGet(self:getUri())
- end
-end
-
-function mt:doEmmyField(action)
- ---@type emmyMgr
- local emmyMgr = self.emmyMgr
- self:instantSource(action)
- self:instantSource(action[2])
- local type = self:buildEmmyAnyType(action[3])
- local value = self:createValue('nil', action[2])
- local field = emmyMgr:addField(action, type, value)
- value:setEmmy(type)
- action:set('emmy.field', field)
-
- local class = self._emmy
- if not self._emmy or self._emmy.type ~= 'emmy.class' then
- return
- end
- class:addField(field)
- action:set('target class', class)
-end
-
-function mt:doEmmyGeneric(action)
- ---@type emmyMgr
- local emmyMgr = self.emmyMgr
- self:instantSource(action)
-
- local defs = {}
- for i, obj in ipairs(action) do
- defs[i] = {}
- defs[i].name = self:instantSource(obj[1])
- if obj[2] then
- defs[i].type = self:buildEmmyAnyType(obj[2])
- end
- end
-
- local generic = emmyMgr:addGeneric(defs)
- self._emmyGeneric = generic
-end
-
-function mt:doEmmyVararg(action)
- ---@type emmyMgr
- local emmyMgr = self.emmyMgr
- self:instantSource(action)
- local type = self:getGenericByType(action[1]) or self:buildEmmyAnyType(action[1])
- local param = emmyMgr:addParam(action, type)
- action:set('emmy.param', param)
- self:addEmmyParam(param)
- if self.lsp then
- self.lsp.global:markGet(self:getUri())
- end
-end
-
-function mt:buildEmmyArrayType(action)
- ---@type emmyMgr
- local emmyMgr = self.emmyMgr
- self:instantSource(action)
- for _, obj in ipairs(action) do
- self:instantSource(obj)
- action:set('emmy class', obj[1])
- end
- local type = emmyMgr:addArrayType(action)
- return type
-end
-
-function mt:doEmmyArrayType(action)
- local type = self:buildEmmyArrayType(action)
- self._emmy = type
- if self.lsp then
- self.lsp.global:markGet(self:getUri())
- end
- return type
-end
-
-function mt:buildEmmyTableType(action)
- ---@type emmyMgr
- local emmyMgr = self.emmyMgr
- self:instantSource(action)
- local keyType = self:buildEmmyAnyType(action[1])
- local valueType = self:buildEmmyAnyType(action[2])
- local type = emmyMgr:addTableType(action, keyType, valueType)
- return type
-end
-
-function mt:doEmmyTableType(action)
- local type = self:buildEmmyTableType(action)
- self._emmy = type
- if self.lsp then
- self.lsp.global:markGet(self:getUri())
- end
- return type
-end
-
-function mt:buildEmmyFunctionType(source)
- ---@type emmyMgr
- local emmyMgr = self.emmyMgr
- self:instantSource(source)
- local funcObj = emmyMgr:addFunctionType(source)
- ---@type emmyFunction
- local func = functionMgr.create(source)
- local args = source.args
- if args then
- for i = 1, #args // 2 do
- local nameSource = args[i*2-1]
- local typeSource = args[i*2]
- local paramType = self:buildEmmyAnyType(typeSource)
- funcObj:addParam(nameSource[1], paramType)
- local value = self:createValue(paramType:getType(), typeSource)
- value:setEmmy(paramType)
- self:instantSource(nameSource)
- local arg = func:addArg(nameSource[1], nameSource, value)
- arg:set('emmy arg', true)
- end
- end
- local returns = source.returns
- if returns then
- for i = 1, #returns do
- local returnSource = returns[i]
- local returnType = self:buildEmmyAnyType(returnSource)
- funcObj:addReturn(returnType)
- local value = self:createValue(returnType:getType(), returnSource)
- value:setEmmy(returnType)
- func:setReturn(i, value)
- end
- end
- funcObj:bindFunction(func)
- return funcObj
-end
-
-function mt:doEmmyFunctionType(action)
- local funcObj = self:buildEmmyFunctionType(action)
- self._emmy = funcObj
- return funcObj
-end
-
-function mt:buildEmmyAnyType(source)
- if source.type == 'emmyType' then
- return self:buildEmmyType(source)
- elseif source.type == 'emmyArrayType' then
- return self:buildEmmyArrayType(source)
- elseif source.type == 'emmyTableType' then
- return self:buildEmmyTableType(source)
- elseif source.type == 'emmyFunctionType' then
- return self:buildEmmyFunctionType(source)
- else
- error('Unknown emmy type: ' .. table.dump(source))
- end
-end
-
-function mt:doEmmyIncomplete(action)
- self:instantSource(action)
-end
-
-function mt:doEmmyComment(action)
- self._emmyComment = action[1]
-end
-
-function mt:doEmmySee(action)
- self:instantSource(action)
- self:instantSource(action[2])
- action[2]:set('emmy see', action)
-end
-
-function mt:doEmmyOverLoad(action)
- local funcObj = self:buildEmmyFunctionType(action)
- self:addEmmyOverLoad(funcObj)
-end
diff --git a/script/vm/function.lua b/script/vm/function.lua
deleted file mode 100644
index be58bf7c..00000000
--- a/script/vm/function.lua
+++ /dev/null
@@ -1,553 +0,0 @@
-local createMulti = require 'vm.multi'
-local valueMgr = require 'vm.value'
-local localMgr = require 'vm.local'
-local sourceMgr = require 'vm.source'
-local listMgr = require 'vm.list'
-
-local Watch = setmetatable({}, {__mode = 'kv'})
-
----@class emmyFunction
-local mt = {}
-mt.__index = mt
-mt.type = 'function'
-mt._runed = 0
-mt._top = 0
-
-function mt:getSource()
- return listMgr.get(self.source)
-end
-
-function mt:getUri()
- local source = self:getSource()
- return source and source.uri or ''
-end
-
-function mt:push(source, ischunk)
- if self._removed then
- return
- end
- self._top = self._top + 1
- self.locals[self._top] = {}
- self.finishs[self._top] = source and source.finish or math.maxinteger
-end
-
-function mt:markChunk()
- self.chunk[self._top] = true
-end
-
-function mt:pop()
- if self._removed then
- return
- end
- local closed = self.finishs[self._top]
- local closedLocals = self.locals[self._top]
- self.locals[self._top] = nil
- self.chunk[self._top] = nil
- for _, loc in pairs(closedLocals) do
- loc:close(closed)
- end
- self._top = self._top - 1
-end
-
-function mt:saveLocal(name, loc)
- if self._removed then
- return
- end
- if loc.type ~= 'local' then
- error('saveLocal必须是local')
- end
- if not loc:getSource() then
- return
- end
- local old = self:loadLocal(name)
- if old then
- loc:shadow(old)
- end
- self.locals[self._top][name] = loc
-end
-
-function mt:saveUpvalue(name, loc)
- if self._removed then
- return
- end
- if loc.type ~= 'local' then
- error('saveLocal必须是local')
- end
- self.upvalues[name] = loc
-end
-
-function mt:loadLocal(name)
- for i = self._top, 1, -1 do
- local locals = self.locals[i]
- local loc = locals[name]
- if loc then
- return loc
- end
- if self.chunk[i] then
- break
- end
- end
- local uv = self.upvalues[name]
- if uv then
- return uv
- end
- return nil
-end
-
-function mt:eachLocal(callback)
- local mark = {}
- for i = self._top, 1, -1 do
- local locals = self.locals[i]
- for name, loc in pairs(locals) do
- if not mark[name] then
- mark[name] = true
- local res = callback(name, loc)
- if res ~= nil then
- return res
- end
- end
- end
- if self.chunk[i] then
- break
- end
- end
- for name, loc in pairs(self.upvalues) do
- if not mark[name] then
- mark[name] = true
- local res = callback(name, loc)
- if res ~= nil then
- return res
- end
- end
- end
- return nil
-end
-
-function mt:saveLabel(label)
- if self._removed then
- return
- end
- if not self._label then
- self._label = {}
- end
- self._label[#self._label+1] = label
-end
-
-function mt:loadLabel(name)
- if not self._label then
- return nil
- end
- for _, label in ipairs(self._label) do
- if label:getName() == name then
- return label
- end
- end
- return nil
-end
-
-function mt:rawSetReturn(index, value)
- if self._removed then
- return
- end
- self:set('hasReturn', true)
- if not self.returns then
- self.returns = createMulti()
- end
- if value then
- self.returns:set(index, value)
- if self._global then
- value:markGlobal()
- end
- end
-end
-
-function mt:setReturn(index, value)
- local emmy = self._emmyReturns and self._emmyReturns[index]
- if emmy then
- if emmy:bindType() or emmy:bindGeneric() then
- return
- end
- end
- return self:rawSetReturn(index, value)
-end
-
-function mt:mergeReturn(index, value)
- if self._removed then
- return
- end
- local emmy = self._emmyReturns and self._emmyReturns[index]
- if emmy then
- if emmy:bindType() or emmy:bindGeneric() then
- return
- end
- end
- self:set('hasReturn', true)
- if not self.returns then
- self.returns = createMulti()
- end
- if value then
- if self.returns[index] then
- self.returns[index]:mergeValue(value)
- self.returns[index] = value
- else
- self.returns:set(index, value)
- end
- end
- if self._global then
- value:markGlobal()
- end
-end
-
-function mt:getReturn(index)
- if self._removed then
- return nil
- end
- if self.maxReturns and index and self.maxReturns < index then
- return nil
- end
- if not self.returns then
- self.returns = createMulti()
- end
- if index then
- return self.returns:get(index)
- else
- return self.returns
- end
-end
-
-function mt:returnDots(index)
- if not self.returns then
- self.returns = createMulti()
- end
- --self.returns[index] = createMulti()
-end
-
-function mt:loadDots()
- if not self._dots then
- self._dots = createMulti()
- end
- self._dotsLoad = true
- return self._dots
-end
-
-function mt:setObject(value, source)
- self._objectValue = value
- self._objectSource = source
-end
-
-function mt:getObject()
- return self._objectSource, self._objectValue
-end
-
-function mt:hasRuned()
- return self._runed > 0
-end
-
-function mt:needSkip()
- return self._runed > 3
-end
-
----@param vm VM
-function mt:run(vm)
- if self._removed then
- return
- end
- if not self:getSource() then
- return
- end
-
- self._runed = self._runed + 1
-
- -- 第一次运行函数时,创建函数的参数
- if self._runed == 1 then
- -- 如果是面向对象形式的函数,创建隐藏的参数self
- if self._objectSource then
- local loc = localMgr.create('self', vm:instantSource(self._objectSource), self._objectValue)
- loc:set('hide', true)
- loc:set('start', self:getSource().start)
- loc:close(self:getSource().finish)
- self:saveUpvalue('self', loc)
- self.args[#self.args+1] = loc
- end
-
- -- 显性声明的参数
- self:createArgs(vm)
- end
-
- if self:needSkip() then
- return
- end
-
- -- 向局部变量中填充参数
- for i, loc in ipairs(self.args) do
- loc:setValue(self.argValues[i])
- local emmyParam = self:findEmmyParamByName(loc:getName())
- if emmyParam then
- local typeObj = emmyParam:bindType()
- if typeObj then
- loc:getValue():setEmmy(typeObj)
- end
- local genericObj = emmyParam:bindGeneric()
- if genericObj then
- genericObj:setValue(loc:getValue())
- end
- end
- end
- if self._dots then
- local emmyParam = self:findEmmyParamByName('...')
- self._dots = createMulti()
- for i = #self.args + 1, #self.argValues do
- local value = self.argValues[i]
- self._dots:push(value)
- if emmyParam then
- local typeObj = emmyParam:bindType()
- if typeObj then
- value:setEmmy(typeObj)
- end
- local genericObj = emmyParam:bindGeneric()
- if genericObj then
- genericObj:setValue(value)
- end
- end
- end
- if emmyParam then
- local typeObj = emmyParam:bindType()
- if typeObj then
- self._dots:setEmmy(typeObj)
- end
- local genericObj = emmyParam:bindGeneric()
- if genericObj then
- local value = self._dots:first()
- if value then
- genericObj:setValue(value)
- end
- end
- end
- end
-
- -- 填充返回值
- if self._emmyReturns then
- for i, rtn in ipairs(self._emmyReturns) do
- local value = vm:createValue('nil', rtn:getSource())
- local typeObj = rtn:bindType()
- if typeObj then
- value:setEmmy(typeObj)
- end
- local genericObj = rtn:bindGeneric()
- if genericObj then
- local destValue = genericObj:getValue()
- if destValue then
- value:mergeType(destValue)
- end
- end
- self:rawSetReturn(i, value)
- end
- end
-end
-
-function mt:eachEmmyReturn(callback)
- if not self._emmyReturns then
- return
- end
- for _, rtn in ipairs(self._emmyReturns) do
- callback(rtn)
- end
-end
-
-function mt:setArgs(values)
- for i = 1, #self.argValues do
- self.argValues[i] = nil
- end
- for i = 1, #values do
- self.argValues[i] = values[i]
- end
-end
-
-function mt:findEmmyParamByName(name)
- local params = self._emmyParams
- if not params then
- return nil
- end
- for i = #params, 1, -1 do
- local param = params[i]
- if param:getName() == name then
- return param
- end
- end
- return nil
-end
-
-function mt:findEmmyParamByIndex(index)
- local arg = self.args[index]
- if not arg then
- return nil
- end
- local name = arg:getName()
- return self:findEmmyParamByName(name)
-end
-
-function mt:addArg(name, source, value, close)
- local loc = localMgr.create(name, source, value)
- loc:close(close)
- self:saveUpvalue(name, loc)
- self.args[#self.args+1] = loc
- return loc
-end
-
-function mt:createArg(vm, arg, close)
- vm:instantSource(arg)
- arg:set('arg', self)
- if arg.type == 'name' then
- vm:instantSource(arg)
- local value = valueMgr.create('nil', arg)
- self:addArg(arg[1], arg, value, close)
- elseif arg.type == '...' then
- self._dots = createMulti()
- self._dotsSource = arg
- end
-end
-
-function mt:createLibArg(arg, source)
- if arg.type == '...' then
- self._dots = createMulti()
- else
- local name = arg.name or '_'
- local loc = localMgr.create(name, source, valueMgr.create('any', source))
- self:saveUpvalue(name, loc)
- self.args[#self.args+1] = loc
- end
-end
-
-function mt:hasDots()
- return self._dots ~= nil
-end
-
-function mt:createArgs(vm)
- if not self:getSource() then
- return
- end
- local args = self:getSource().arg
- if not args then
- return
- end
- local close = self:getSource().finish
- if args.type == 'list' then
- for _, arg in ipairs(args) do
- self:createArg(vm, arg, close)
- end
- else
- self:createArg(vm, args, close)
- end
-end
-
-function mt:set(name, v)
- if not self._flag then
- self._flag = {}
- end
- self._flag[name] = v
-end
-
-function mt:get(name)
- if not self._flag then
- return nil
- end
- return self._flag[name]
-end
-
-function mt:getSource()
- if self._removed then
- return nil
- end
- return listMgr.get(self.source)
-end
-
-function mt:kill()
- if self._removed then
- return
- end
- self._removed = true
- listMgr.clear(self.id)
-end
-
-function mt:markGlobal()
- if self._global then
- return
- end
- self._global = true
- if self.returns then
- self.returns:eachValue(function (_, v)
- v:markGlobal()
- end)
- end
-end
-
-function mt:setEmmy(params, returns, overLoads)
- if params then
- self._emmyParams = params
- for _, param in ipairs(params) do
- param:getSource():set('emmy function', self)
- if param:getSource()[1] then
- param:getSource()[1]:set('emmy function', self)
- end
- end
- end
- if returns then
- self._emmyReturns = returns
- for _, rtn in ipairs(returns) do
- rtn:getSource():set('emmy function', self)
- end
- end
- if overLoads then
- self._emmyOverLoads = overLoads
- for _, ol in ipairs(overLoads) do
- ol:getSource():set('emmy function', self)
- end
- end
-end
-
----@param comment string
-function mt:setComment(comment)
- self._comment = comment
-end
-
----@return string
-function mt:getComment()
- return self._comment
-end
-
-function mt:getEmmyParams()
- return self._emmyParams
-end
-
-function mt:getEmmyOverLoads()
- return self._emmyOverLoads
-end
-
-local function create(source)
- if not source then
- error('Function need source')
- end
- local id = source.id
- if not id then
- error('Not instanted source')
- end
- local self = setmetatable({
- source = id,
- locals = {},
- upvalues = {},
- chunk = {},
- finishs = {},
- args = {},
- argValues = {},
- }, mt)
-
- local id = listMgr.add(self)
- self.id = id
- Watch[self] = id
- return self
-end
-
-return {
- create = create,
- watch = Watch,
-}
diff --git a/script/vm/global.lua b/script/vm/global.lua
deleted file mode 100644
index af30ffdd..00000000
--- a/script/vm/global.lua
+++ /dev/null
@@ -1,25 +0,0 @@
-local library = require 'core.library'
-local libraryBuilder = require 'vm.library'
-local sourceMgr = require 'vm.source'
-
-return function (lsp)
- local global = lsp and lsp.globalValue
- if not global then
- libraryBuilder.clear()
- local t = {}
- for name, lib in pairs(library.global) do
- t[name] = libraryBuilder.value(lib)
- end
-
- global = t._G
- global:markGlobal()
- global:set('ENV', true)
- for k, v in pairs(t) do
- global:setChild(k, v, sourceMgr.dummy())
- end
- end
- if lsp then
- lsp.globalValue = global
- end
- return global
-end
diff --git a/script/vm/init.lua b/script/vm/init.lua
deleted file mode 100644
index 87576ba5..00000000
--- a/script/vm/init.lua
+++ /dev/null
@@ -1 +0,0 @@
-return require 'vm.vm'
diff --git a/script/vm/ipairs.lua b/script/vm/ipairs.lua
deleted file mode 100644
index cb8356da..00000000
--- a/script/vm/ipairs.lua
+++ /dev/null
@@ -1,51 +0,0 @@
-local mt = require 'vm.manager'
-local library = require 'vm.library'
-
----@param func emmyFunction
-function mt:callIpairs(func, values, source)
- local tbl = values[1]
- func:setReturn(1, library.special['@ipairs'])
- func:setReturn(2, tbl)
-end
-
----@param func emmyFunction
-function mt:callAtIpairs(func, values, source)
- local tbl = values[1]
- if tbl then
- local emmy = tbl:getEmmy()
- if emmy then
- if emmy.type == 'emmy.arrayType' then
- local value = self:createValue(emmy:getName(), source)
- func:setReturn(2, value)
- end
- end
- end
-end
-
----@param func emmyFunction
-function mt:callPairs(func, values, source)
- local tbl = values[1]
- func:setReturn(1, library.special['next'])
- func:setReturn(2, tbl)
-end
-
----@param func emmyFunction
-function mt:callNext(func, values, source)
- local tbl = values[1]
- if tbl then
- local emmy = tbl:getEmmy()
- if emmy then
- if emmy.type == 'emmy.arrayType' then
- local key = self:createValue('integer', source)
- local value = self:createValue(emmy:getName(), source)
- func:setReturn(1, key)
- func:setReturn(2, value)
- elseif emmy.type == 'emmy.tableType' then
- local key = self:createValue(emmy:getKeyType():getType(), source)
- local value = self:createValue(emmy:getValueType():getType(), source)
- func:setReturn(1, key)
- func:setReturn(2, value)
- end
- end
- end
-end
diff --git a/script/vm/label.lua b/script/vm/label.lua
deleted file mode 100644
index c0e0dfb8..00000000
--- a/script/vm/label.lua
+++ /dev/null
@@ -1,75 +0,0 @@
-local listMgr = require 'vm.list'
-
-local Sort = 0
-
-local mt = {}
-mt.__index = mt
-mt.type = 'label'
-
-function mt:getName()
- return self.name
-end
-
-function mt:addInfo(tp, source)
- if not source then
- error('No source')
- end
- local id = source.id
- if not id then
- error('Not instanted source')
- end
- if self._info[id] then
- return
- end
- Sort = Sort + 1
- local info = {
- type = tp,
- source = id,
- _sort = Sort,
- }
-
- self._info[id] = info
-end
-
-function mt:eachInfo(callback)
- local list = {}
- for srcId, info in pairs(self._info) do
- local src = listMgr.get(srcId)
- if src then
- list[#list+1] = info
- else
- self._info[srcId] = nil
- end
- end
- table.sort(list, function (a, b)
- return a._sort < b._sort
- end)
- for i = 1, #list do
- local info = list[i]
- local res = callback(info, listMgr.get(info.source))
- if res ~= nil then
- return res
- end
- end
- return nil
-end
-
-function mt:getSource()
- return listMgr.get(self.source)
-end
-
-return function (name, source)
- if not source then
- error('No source')
- end
- local id = source.id
- if not id then
- error('Not instanted source')
- end
- local self = setmetatable({
- name = name,
- source = id,
- _info = {},
- }, mt)
- return self
-end
diff --git a/script/vm/library.lua b/script/vm/library.lua
deleted file mode 100644
index 018d69f3..00000000
--- a/script/vm/library.lua
+++ /dev/null
@@ -1,112 +0,0 @@
-local sourceMgr = require 'vm.source'
-
-local valueMgr
-local functionMgr
-
-local CHILD_CACHE = {}
-local VALUE_CACHE = {}
-local Special = {}
-
-local buildLibValue
-local buildLibChild
-
-function buildLibValue(lib)
- if VALUE_CACHE[lib] then
- return VALUE_CACHE[lib]
- end
- if not valueMgr then
- valueMgr = require 'vm.value'
- functionMgr = require 'vm.function'
- end
- local tp = lib.type
- local value
- if tp == 'table' then
- value = valueMgr.create('table', sourceMgr.dummy())
- elseif tp == 'function' then
- local dummySource = sourceMgr.dummy()
- value = valueMgr.create('function', dummySource)
- local func = functionMgr.create(dummySource)
- value:setFunction(func)
- if lib.args then
- for _, arg in ipairs(lib.args) do
- func:createLibArg(arg, sourceMgr.dummy())
- end
- end
- if lib.returns then
- for i, rtn in ipairs(lib.returns) do
- if rtn.type == '...' then
- func:returnDots(i)
- else
- func:setReturn(i, buildLibValue(rtn))
- end
- end
- if lib.special == 'pairs' then
- func:setReturn(1, Special['next'])
- end
- if lib.special == 'ipairs' then
- func:setReturn(1, Special['@ipairs'])
- end
- end
- elseif tp == 'string' then
- value = valueMgr.create('string', sourceMgr.dummy())
- elseif tp == 'boolean' then
- value = valueMgr.create('boolean', sourceMgr.dummy())
- elseif tp == 'number' then
- value = valueMgr.create('number', sourceMgr.dummy())
- elseif tp == 'integer' then
- value = valueMgr.create('integer', sourceMgr.dummy())
- elseif tp == 'nil' then
- value = valueMgr.create('nil', sourceMgr.dummy())
- else
- value = valueMgr.create(tp or 'any', sourceMgr.dummy())
- end
- value:setLib(lib)
- VALUE_CACHE[lib] = value
-
- if lib.child then
- for fName, fLib in pairs(lib.child) do
- local fValue = buildLibValue(fLib)
- value:rawSet(fName, fValue)
- value:addInfo('set child', sourceMgr.dummy(), fName, fValue)
- end
- end
-
- if lib.special == 'next' then
- Special['next'] = value
- end
- if lib.special == '@ipairs' then
- Special['@ipairs'] = value
- return nil
- end
-
- return value
-end
-
-function buildLibChild(lib)
- if not valueMgr then
- valueMgr = require 'vm.value'
- functionMgr = require 'vm.function'
- end
- if CHILD_CACHE[lib] then
- return CHILD_CACHE[lib]
- end
- local child = {}
- for fName, fLib in pairs(lib.child) do
- local fValue = buildLibValue(fLib)
- child[fName] = fValue
- end
- CHILD_CACHE[lib] = child
- return child
-end
-
-local function clearCache()
- CHILD_CACHE = {}
- VALUE_CACHE = {}
-end
-
-return {
- value = buildLibValue,
- child = buildLibChild,
- clear = clearCache,
- special = Special,
-}
diff --git a/script/vm/list.lua b/script/vm/list.lua
deleted file mode 100644
index 234f241f..00000000
--- a/script/vm/list.lua
+++ /dev/null
@@ -1,30 +0,0 @@
-local Id = 0
-local Version = 0
-local List = {}
-
-local function get(id)
- return List[id]
-end
-
-local function add(obj)
- Id = Id + 1
- List[Id] = obj
- return Id
-end
-
-local function clear(id)
- List[id] = nil
- Version = Version + 1
-end
-
-local function getVersion()
- return Version
-end
-
-return {
- get = get,
- add = add,
- clear = clear,
- list = List,
- getVersion = getVersion,
-}
diff --git a/script/vm/local.lua b/script/vm/local.lua
deleted file mode 100644
index 7e8af0f1..00000000
--- a/script/vm/local.lua
+++ /dev/null
@@ -1,191 +0,0 @@
-local listMgr = require 'vm.list'
-
-local Sort = 0
-local Watch = setmetatable({}, {__mode = 'kv'})
-
----@class Local
-local mt = {}
-mt.__index = mt
-mt.type = 'local'
-mt._close = math.maxinteger
-
-function mt:setValue(value)
- if not value then
- return
- end
- if self.value then
- --self.value:mergeValue(value)
- self.value:mergeType(value)
- self.value = value
- else
- self.value = value
- end
- if self._emmy then
- self.value:setEmmy(self._emmy)
- end
- return value
-end
-
-function mt:getValue()
- return self.value
-end
-
-function mt:addInfo(tp, source)
- if not source then
- error('No source')
- end
- local id = source.id
- if not id then
- error('Not instanted source')
- end
- if self._info[id] then
- return
- end
- Sort = Sort + 1
- local info = {
- type = tp,
- source = id,
- _sort = Sort,
- }
-
- self._info[id] = info
-end
-
-function mt:eachInfo(callback)
- local list = {}
- for srcId, info in pairs(self._info) do
- local src = listMgr.get(srcId)
- if src then
- list[#list+1] = info
- else
- self._info[srcId] = nil
- end
- end
- table.sort(list, function (a, b)
- return a._sort < b._sort
- end)
- for i = 1, #list do
- local info = list[i]
- local res = callback(info, listMgr.get(info.source))
- if res ~= nil then
- return res
- end
- end
- return nil
-end
-
-function mt:set(name, v)
- if not self._flag then
- self._flag = {}
- end
- self._flag[name] = v
-end
-
-function mt:get(name)
- if not self._flag then
- return nil
- end
- return self._flag[name]
-end
-
-function mt:getName()
- return self.name
-end
-
-function mt:shadow(old)
- if not old then
- if not self._shadow then
- return nil
- end
- for i = #self._shadow, 1, -1 do
- local loc = self._shadow[i]
- if not loc:getSource() then
- table.remove(self._shadow, i)
- end
- end
- return self._shadow
- end
- local group = old._shadow
- if not group then
- group = {}
- group[#group+1] = old
- end
- group[#group+1] = self
- self._shadow = group
-
- if not self:getSource() then
- log.error('local no source')
- return
- end
-
- old:close(self:getSource().start - 1)
-end
-
-function mt:close(pos)
- if pos then
- if pos <= 0 then
- pos = math.maxinteger
- end
- self._close = pos
- else
- return self._close
- end
-end
-
-function mt:getSource()
- return listMgr.get(self.source)
-end
-
-local EMMY_TYPE = {
- ['emmy.class'] = true,
- ['emmy.type'] = true,
- ['emmy.arrayType'] = true,
- ['emmy.tableType'] = true,
- ['emmy.functionType'] = true,
-}
-
-function mt:setEmmy(emmy)
- if not emmy then
- return
- end
- if self.value and EMMY_TYPE[emmy.type] then
- self.value:setEmmy(emmy)
- end
-end
-
----@param comment string
-function mt:setComment(comment)
- self._comment = comment
-end
-
----@return string
-function mt:getComment()
- return self._comment
-end
-
-local function create(name, source, value, tags)
- if not value then
- error('Local must has a value')
- end
- if not source then
- error('No source')
- end
- local id = source.id
- if not id then
- error('Not instanted source')
- end
- local self = setmetatable({
- name = name,
- source = id,
- value = value,
- tags = tags,
- _info = {},
- }, mt)
- Watch[self] = true
- return self
-end
-
-return {
- create = create,
- watch = Watch,
-}
diff --git a/script/vm/manager.lua b/script/vm/manager.lua
deleted file mode 100644
index b9762d2e..00000000
--- a/script/vm/manager.lua
+++ /dev/null
@@ -1,17 +0,0 @@
----@class VM
-local mt = {}
-mt.__index = mt
-mt.type = 'vm'
-mt._version = -1
-
----@param version integer
-function mt:setVersion(version)
- self._version = version
-end
-
----@return integer
-function mt:getVersion()
- return self._version
-end
-
-return mt
diff --git a/script/vm/module.lua b/script/vm/module.lua
deleted file mode 100644
index 60191bf3..00000000
--- a/script/vm/module.lua
+++ /dev/null
@@ -1,56 +0,0 @@
-local mt = require 'vm.manager'
-local createMulti = require 'vm.multi'
-
---[[
-function module(name, ...)
- local env = {}
- for _, opt in ipairs {...} do
- opt(env)
- end
- @ENV = env
-end
---]]
-function mt:callModuel(func, values)
- local envLoc = self:loadLocal('@ENV')
- if not envLoc then
- return
- end
- local source = self:getDefaultSource()
- local newEnvValue = self:createValue('table', source)
- local args = createMulti()
-
- args:push(newEnvValue)
-
- for i = 2, #values do
- local value = values[i]
- -- opt(env)
- self:call(value, args, source)
- end
-
- -- @ENV = env
- envLoc:setValue(newEnvValue)
-end
-
---[[
-function package.seeall(env)
- setmetatable(env, { __index = @ENV })
-end
---]]
-function mt:callSeeAll(func, values)
- local newEnv = values[1]
- if not newEnv then
- return
- end
- local envLoc = self:loadLocal('@ENV')
- if not envLoc then
- return
- end
- local oldEnv = envLoc:getValue()
- if not oldEnv then
- return
- end
- local source = self:getDefaultSource()
- local meta = self:createValue('table', source)
- meta:setChild('__index', oldEnv, source)
- newEnv:setMetaTable(meta)
-end
diff --git a/script/vm/multi.lua b/script/vm/multi.lua
deleted file mode 100644
index 4b27b8cf..00000000
--- a/script/vm/multi.lua
+++ /dev/null
@@ -1,83 +0,0 @@
-local mt = {}
-mt.__index = mt
-mt.type = 'multi'
-mt.len = 0
-
-function mt:push(value, isLast)
- if value and value.type == 'list' then
- if isLast then
- for _, v in ipairs(value) do
- self.len = self.len + 1
- self[self.len] = v
- end
- else
- self.len = self.len + 1
- self[self.len] = value[1]
- end
- else
- self.len = self.len + 1
- self[self.len] = value
- end
-end
-
-function mt:get(index)
- return self[index]
-end
-
-function mt:set(index, value)
- if index > self.len then
- self.len = index
- end
- self[index] = value
-end
-
-function mt:first()
- local value = self[1]
- if not value then
- return nil
- end
- if value.type == 'multi' then
- return value:first()
- else
- return value
- end
-end
-
-function mt:eachValue(callback)
- local i = 0
- for n, value in ipairs(self) do
- if value.type == 'multi' then
- if n == self.len then
- value:eachValue(function (_, nvalue)
- i = i + 1
- callback(i, nvalue)
- end)
- else
- i = i + 1
- value:first()
- end
- else
- i = i + 1
- callback(i, value)
- end
- end
-end
-
-function mt:merge(other)
- other:eachValue(function (_, value)
- self:push(value)
- end)
-end
-
-function mt:setEmmy(emmy)
- self._emmy = emmy
-end
-
-function mt:getEmmy()
- return self._emmy
-end
-
-return function ()
- local self = setmetatable({}, mt)
- return self
-end
diff --git a/script/vm/pcall.lua b/script/vm/pcall.lua
deleted file mode 100644
index e5d1e26f..00000000
--- a/script/vm/pcall.lua
+++ /dev/null
@@ -1,50 +0,0 @@
-local mt = require 'vm.manager'
-local multi = require 'vm.multi'
-
-function mt:callPcall(func, values, source)
- local funcValue = values:first()
- if not funcValue then
- return
- end
- local realFunc = funcValue:getFunction()
- if not realFunc then
- return
- end
- local argList = multi()
- values:eachValue(function (i, v)
- if i >= 2 then
- argList:push(v)
- end
- end)
- self:call(funcValue, argList, source)
- if realFunc ~= func then
- func:setReturn(1, self:createValue('boolean', source))
- realFunc:getReturn():eachValue(function (i, v)
- func:setReturn(i + 1, v)
- end)
- end
-end
-
-function mt:callXpcall(func, values, source)
- local funcValue = values:first()
- if not funcValue then
- return
- end
- local realFunc = funcValue:getFunction()
- if not realFunc then
- return
- end
- local argList = multi()
- values:eachValue(function (i, v)
- if i >= 3 then
- argList:push(v)
- end
- end)
- self:call(funcValue, argList, source)
- if realFunc ~= func then
- func:setReturn(1, self:createValue('boolean', source))
- realFunc:getReturn():eachValue(function (i, v)
- func:setReturn(i + 1, v)
- end)
- end
-end
diff --git a/script/vm/raw.lua b/script/vm/raw.lua
deleted file mode 100644
index f8c35734..00000000
--- a/script/vm/raw.lua
+++ /dev/null
@@ -1,30 +0,0 @@
-local mt = require 'vm.manager'
-
-function mt:callRawSet(func, values, source)
- local tbl = values[1]
- local index = values[2]
- local value = values[3]
- if not tbl or not index or not value then
- return
- end
- if index:getLiteral() then
- index = index:getLiteral()
- end
- tbl:addInfo('set child', source, index)
- tbl:rawSet(index, value, source)
- func:setReturn(1, tbl)
-end
-
-function mt:callRawGet(func, values, source)
- local tbl = values[1]
- local index = values[2]
- if not tbl or not index then
- return
- end
- if index:getLiteral() then
- index = index:getLiteral()
- end
- tbl:addInfo('get child', source, index)
- local value = tbl:rawGet(index)
- func:setReturn(1, value)
-end
diff --git a/script/vm/source.lua b/script/vm/source.lua
deleted file mode 100644
index 7a10a38e..00000000
--- a/script/vm/source.lua
+++ /dev/null
@@ -1,183 +0,0 @@
-local listMgr = require 'vm.list'
-
----@class source
-local mt = {}
-mt.__index = mt
-mt.type = 'source'
-mt.uri = ''
-mt.start = 0
-mt.finish = 0
-mt.id = 0
-
-local Watch = setmetatable({}, {__mode = 'k'})
-
-function mt:bindLocal(loc, action)
- if loc then
- self._bindLocal = loc
- self._bindValue = loc:getValue()
- self._action = action
- loc:addInfo(action, self)
- else
- if not self._bindLocal then
- return nil
- end
- if not self._bindLocal:getSource() then
- self._bindLocal = nil
- return nil
- end
- return self._bindLocal
- end
-end
-
-function mt:bindLabel(label, action)
- if label then
- self._bindLabel = label
- self._action = action
- label:addInfo(action, self)
- else
- return self._bindLabel
- end
-end
-
-function mt:bindFunction(func)
- if func then
- self._bindFunction = func
- else
- return self._bindFunction
- end
-end
-
-function mt:bindValue(value, action)
- if value then
- self._bindValue = value
- self._action = action
- value:addInfo(action, self)
- else
- return self._bindValue
- end
-end
-
-function mt:bindCall(args)
- if args then
- self._bindCallArgs = args
- else
- return self._bindCallArgs
- end
-end
-
-function mt:bindMetatable(meta)
- if meta then
- self._bindMetatable = meta
- else
- return self._bindMetatable
- end
-end
-
-function mt:action()
- return self._action
-end
-
-function mt:setUri(uri)
- self.uri = uri
-end
-
-function mt:getUri()
- return self.uri
-end
-
-function mt:set(name, v)
- if not self._flag then
- self._flag = {}
- end
- self._flag[name] = v
-end
-
-function mt:get(name)
- if not self._flag then
- return nil
- end
- return self._flag[name]
-end
-
-function mt:getName()
- return self[1]
-end
-
-function mt:kill()
- self._dead = true
- listMgr.clear(self.id)
-end
-
-function mt:isDead()
- return self._dead
-end
-
-function mt:findValue()
- local value = self:bindValue()
- if not value then
- return nil
- end
- if not value:isGlobal() then
- return value
- end
- if self.type ~= 'name' then
- return value
- end
- local parent = self:get 'parent'
- if not parent then
- return value
- end
- local name = self[1]
- if type(name) ~= 'string' then
- return value
- end
- return parent:getChild(name) or value
-end
-
-function mt:findCallFunction()
- local simple = self:get 'simple'
- if not simple then
- return nil
- end
- local source
- for i = 1, #simple do
- if simple[i] == self then
- source = simple[i-1]
- end
- end
- if not source then
- return nil
- end
- local value = source:bindValue()
- if value and value:getFunction() then
- return value
- end
- value = source:findValue()
- if value and value:getFunction() then
- return value
- end
- return nil
-end
-
-local function instant(source)
- if source.id then
- return false
- end
- local id = listMgr.add(source)
- source.id = id
- Watch[source] = id
- setmetatable(source, mt)
- return true
-end
-
-local function dummy()
- local src = {}
- instant(src)
- return src
-end
-
-return {
- instant = instant,
- watch = Watch,
- dummy = dummy,
-}
diff --git a/script/vm/special.lua b/script/vm/special.lua
deleted file mode 100644
index e93c4445..00000000
--- a/script/vm/special.lua
+++ /dev/null
@@ -1,130 +0,0 @@
-local mt = require 'vm.manager'
-local multi = require 'vm.multi'
-local library = require 'core.library'
-local libraryBuilder = require 'vm.library'
-local plugin = require 'plugin'
-
----@param func emmyFunction
----@param values table
-function mt:callEmmySpecial(func, values, source)
- local emmyParams = func:getEmmyParams()
- for index, param in ipairs(emmyParams) do
- local option = param:getOption()
- if option and type(option.special) == 'string' then
- self:checkEmmyParam(func, values, index, option.special, source)
- end
- end
-end
-
----@param func emmyFunction
----@param values table
----@param index integer
----@param special string
-function mt:checkEmmyParam(func, values, index, special, source)
- if special == 'dofile:1' then
- self:callEmmyDoFile(func, values, index)
- elseif special == 'loadfile:1' then
- self:callEmmyLoadFile(func, values, index)
- elseif special == 'pcall:1' then
- self:callEmmyPCall(func, values, index, source)
- elseif special == 'require:1' then
- self:callEmmyRequire(func, values, index)
- end
-end
-
----@param func emmyFunction
----@param values table
----@param index integer
-function mt:callEmmyDoFile(func, values, index)
- if not values[index] then
- values[index] = self:createValue('any', self:getDefaultSource())
- end
- local str = values[index]:getLiteral()
- if type(str) ~= 'string' then
- return
- end
- local requireValue = self:tryRequireOne(str, values[index], 'dofile')
- if not requireValue then
- requireValue = self:createValue('any', self:getDefaultSource())
- requireValue.isRequire = true
- end
- func:setReturn(1, requireValue)
-end
-
----@param func emmyFunction
----@param values table
----@param index integer
-function mt:callEmmyLoadFile(func, values, index)
- if not values[index] then
- values[index] = self:createValue('any', self:getDefaultSource())
- end
- local str = values[index]:getLiteral()
- if type(str) ~= 'string' then
- return
- end
- local requireValue = self:tryRequireOne(str, values[index], 'loadfile')
- if not requireValue then
- requireValue = self:createValue('any', self:getDefaultSource())
- requireValue:set('cross file', true)
- end
- func:setReturn(1, requireValue)
-end
-
----@param func emmyFunction
----@param values table
----@param index integer
----@param source source
-function mt:callEmmyPCall(func, values, index, source)
- local funcValue = values[index]
- if not funcValue then
- return
- end
- local realFunc = funcValue:getFunction()
- if not realFunc then
- return
- end
- local argList = multi()
- values:eachValue(function (i, v)
- if i > index then
- argList:push(v)
- end
- end)
- self:call(funcValue, argList, source)
- if realFunc ~= func then
- func:setReturn(1, self:createValue('boolean', source))
- realFunc:getReturn():eachValue(function (i, v)
- func:setReturn(i + 1, v)
- end)
- end
-end
-
----@param func emmyFunction
----@param values table
----@param index integer
-function mt:callEmmyRequire(func, values, index)
- if not values[index] then
- values[index] = self:createValue('any', self:getDefaultSource())
- end
- local strValue = values[index]
- local strSource = strValue:getSource()
- if not strSource then
- return nil
- end
- local str = strValue:getLiteral()
- local raw = self.text:sub(strSource.start, strSource.finish)
- str = plugin.call('OnRequirePath', str, raw) or str
- local lib = library.library[str]
- if lib then
- local value = libraryBuilder.value(lib)
- value:markGlobal()
- func:setReturn(1, value)
- return
- else
- local requireValue = self:tryRequireOne(str, strValue, 'require')
- if not requireValue then
- requireValue = self:createValue('any', self:getDefaultSource())
- requireValue:set('cross file', true)
- end
- func:setReturn(1, requireValue)
- end
-end
diff --git a/script/vm/value.lua b/script/vm/value.lua
deleted file mode 100644
index 5de0d8e8..00000000
--- a/script/vm/value.lua
+++ /dev/null
@@ -1,634 +0,0 @@
-local libraryBuilder = require 'vm.library'
-local library = require 'core.library'
-local listMgr = require 'vm.list'
-local config = require 'config'
-
-local Sort = 0
-local Watch = setmetatable({}, {__mode = 'kv'})
-local TypeLevel = {
- ['table'] = 1.0,
- ['function'] = 0.9,
- ['string'] = 0.8,
- ['integer'] = 0.7,
- ['number'] = 0.6,
-}
-
----@class value
-local mt = {}
-mt.__index = mt
-mt.type = 'value'
-mt.uri = ''
-mt._global = false
-
-local function create (tp, source, literal)
- if tp == '...' then
- error('Value type cant be ...')
- end
- if not source then
- error('No source')
- end
- local id = source.id
- if not id then
- error('Not instanted source')
- end
- local self = setmetatable({
- source = id,
- _type = {},
- _literal = literal,
- _info = {},
- }, mt)
- if type(tp) == 'table' then
- for i = 1, #tp do
- self:setType(tp[i], 1.0 / #tp)
- end
- else
- self:setType(tp, 1.0)
- end
- Watch[self] = true
- return self
-end
-
-function mt:setType(tp, rate)
- if type(tp) == 'table' then
- for _, ctp in ipairs(tp) do
- self:setType(ctp, rate)
- end
- return
- end
- if tp == '...' then
- error('Value type cant be ...')
- end
- if not tp then
- tp = 'nil'
- end
- if tp == 'any' or tp == 'nil' then
- rate = 0.0
- end
- if tp == 'integer' then
- local version = config.config.runtime.version
- if version ~= 'Lua 5.3' and version ~= 'Lua 5.4' then
- tp = 'number'
- end
- end
- local current = self._type[tp] or 0.0
- if rate > current then
- self._type[tp] = rate
- end
-end
-
-function mt:getType()
- if self:getEmmy() then
- return self:getEmmy():getType(), 1.0
- end
- if not self._type then
- return 'nil', 0.0
- end
- local mRate = 0.0
- local mType
- for tp, rate in pairs(self._type) do
- if rate > mRate then
- mRate = rate
- mType = tp
- elseif rate == mRate then
- local level1 = TypeLevel[tp] or 0.0
- local level2 = TypeLevel[mType] or 0.0
- if level1 > level2 then
- mRate = rate
- mType = tp
- end
- end
- end
- return mType or 'any', mRate
-end
-
-function mt:rawSet(index, value, source)
- if index == nil then
- return
- end
- if not self._child then
- self._child = {}
- end
- if self._child[index] then
- if self._global then
- self._child[index]:mergeValue(value)
- else
- self._child[index]:mergeType(value)
- self._child[index]:mergeInfo(value)
- end
- self._child[index] = value
- else
- self._child[index] = value
- end
- self:addInfo('set child', source, index, self._child[index])
- if self._global then
- self._child[index]:markGlobal()
- end
-end
-
-function mt:rawGet(index)
- if not self._child then
- return nil
- end
- self:flushChild()
- local child = self._child[index]
- if not child then
- return nil
- end
- return child
-end
-
-function mt:setChild(index, value, source)
- if index == nil then
- return
- end
- self:setType('table', 0.5)
- self:rawSet(index, value, source)
- return value
-end
-
-function mt:getLibChild(index)
- local tp = self:getType()
- local lib = library.object[tp]
- if lib then
- local childs = libraryBuilder.child(lib)
- return childs[index]
- end
- return nil
-end
-
-function mt:eachLibChild(callback)
- local tp = self:getType()
- local lib = library.object[tp]
- if lib then
- local childs = libraryBuilder.child(lib)
- for k, v in pairs(childs) do
- callback(k, v)
- end
- end
-end
-
-function mt:getChild(index, source)
- self:setType('table', 0.5)
- local parent = self
- local value
- -- 最多检查3层 __index
- for _ = 1, 3 do
- value = parent:rawGet(index)
- if value then
- break
- end
- local method = parent:getMetaMethod('__index')
- if not method then
- value = parent:getLibChild(index)
- break
- end
- parent = method
- end
- if not value and source then
- local emmy = self:getEmmy()
- if emmy then
- if emmy.type == 'emmy.arrayType' then
- if type(index) == 'number' then
- value = create(emmy:getName(), source)
- end
- elseif emmy.type == 'emmy.tableType' then
- value = create(emmy:getValueType():getType(), source)
- end
- end
- if not value then
- value = create('any', source)
- end
- self:setChild(index, value)
- value.uri = self.uri
- end
- return value
-end
-
-function mt:setMetaTable(metatable)
- local source = metatable:getSource()
- if not source then
- return
- end
- source:bindMetatable(metatable)
- self._meta = metatable.source
-end
-
-function mt:getMetaTable()
- if not self._meta then
- return nil
- end
- local metaSource = listMgr.get(self._meta)
- if not metaSource then
- self._meta = nil
- return nil
- end
- return metaSource:bindMetatable()
-end
-
-function mt:getMetaMethod(name)
- local meta = self:getMetaTable()
- if not meta then
- return nil
- end
- return meta:rawGet(name)
-end
-
-function mt:flushChild()
- if not self._child then
- return nil
- end
- -- 非全局值不会出现dead child
- if not self._global then
- return
- end
- local listVersion = listMgr.getVersion()
- if self._flushVersion == listVersion then
- return
- end
- self._flushVersion = listVersion
- local alived = {}
- local infos = self._info
- local count = 0
- for srcId, info in pairs(infos) do
- local src = listMgr.get(srcId)
- if src then
- if info.type == 'set child' or info.type == 'get child' then
- if info[1] then
- alived[info[1]] = true
- end
- end
- count = count + 1
- else
- infos[srcId] = nil
- end
- end
- infos._count = count
- infos._limit = count * 1.1 + 10
- infos._version = listMgr.getVersion()
- for index in pairs(self._child) do
- if not alived[index] then
- self._child[index] = nil
- end
- end
-end
-
-function mt:rawEach(callback, mark)
- if not self._child then
- return nil
- end
- self:flushChild()
- for index, value in pairs(self._child) do
- if mark then
- if mark[index] then
- goto CONTINUE
- end
- mark[index] = true
- end
- local res = callback(index, value)
- if res ~= nil then
- return res
- end
- ::CONTINUE::
- end
- return nil
-end
-
-function mt:eachChild(callback)
- local mark = {}
- local parent = self
- -- 最多检查3层 __index
- for _ = 1, 3 do
- local res = parent:rawEach(callback, mark)
- if res ~= nil then
- return res
- end
- local method = parent:getMetaMethod('__index')
- if not method then
- return parent:eachLibChild(callback)
- end
- parent = method
- end
-end
-
-function mt:mergeType(value)
- if self == value then
- return
- end
- if not value then
- return
- end
- if self._emmy and not value._emmy then
- value._emmy = self._emmy
- return
- elseif not self._emmy and value._emmy then
- self._emmy = value._emmy
- return
- end
- if value._type then
- for tp, rate in pairs(value._type) do
- self:setType(tp, rate)
- end
- end
- value._type = self._type
-end
-
-function mt:mergeInfo(value)
- if self == value then
- return
- end
- if not value then
- return
- end
- local infos = self._info
- for srcId, info in pairs(value._info) do
- local src = listMgr.get(srcId)
- if src and not infos[srcId] then
- infos[srcId] = info
- infos._count = (infos._count or 0) + 1
- end
- end
- value._info = infos
-end
-
-function mt:mergeValue(value)
- if self == value then
- return
- end
- if not value then
- return
- end
- local list = {self, value}
- local pos = 1
- while true do
- local a, b = list[pos], list[pos+1]
- if not a then
- break
- end
- pos = pos + 2
- list[a] = true
- list[b] = true
- a:mergeType(b)
- a:mergeInfo(b)
-
- a:flushChild()
- b:flushChild()
- local global = a._global or b._global
- if b._child then
- if not a._child then
- a._child = {}
- end
- for k, bc in pairs(b._child) do
- local ac = a._child[k]
- if ac and ac ~= bc and global then
- if list[ac] and list[bc] then
- else
- list[#list+1] = ac
- list[#list+1] = bc
- end
- end
- a._child[k] = bc
- end
- end
- b._child = a._child
- if global then
- a:markGlobal()
- b:markGlobal()
- end
-
- if b._meta then
- a._meta = b._meta
- end
- if b._func then
- a._func = b._func
- end
- if b._lib then
- a._lib = b._lib
- end
- if b.uri then
- a.uri = b.uri
- end
- end
-end
-
-function mt:addInfo(tp, source, ...)
- if not source then
- return
- end
- if not source.start then
- error('Miss start: ' .. table.dump(source))
- end
- local id = source.id
- if not id then
- error('Not instanted source')
- end
- if not tp then
- error('Miss info type')
- end
-
- local infos = self._info
- if infos[id] then
- return
- end
- Sort = Sort + 1
- local info = {
- type = tp,
- source = id,
- _sort = Sort,
- ...
- }
- infos[id] = info
- infos._count = (infos._count or 0) + 1
- local version = listMgr.getVersion()
- -- 只有全局值需要压缩info
- if self._global and infos._count > (infos._limit or 10) and infos._version ~= version then
- local count = 0
- for srcId in pairs(infos) do
- local src = listMgr.get(srcId)
- if src then
- count = count + 1
- else
- infos[srcId] = nil
- end
- end
- infos._count = count
- infos._limit = count * 1.1 + 10
- infos._version = version
- end
-end
-
-function mt:eachInfo(callback)
- local clock = os.clock()
- local infos = self._info
- local list = {}
- for srcId, info in pairs(infos) do
- local src = listMgr.get(srcId)
- if src then
- list[#list+1] = info
- else
- infos[srcId] = nil
- end
- end
- infos._count = #list
- infos._limit = infos._count * 1.1 + 10
- infos._version = listMgr.getVersion()
- --local clock2 = os.clock()
- --table.sort(list, function (a, b)
- -- return a._sort < b._sort
- --end)
- local passed = os.clock() - clock
- if passed > 0.1 then
- log.warn(('eachInfo takes: [%.3f]sec, #list: %d'):format(passed, #list))
- end
- for i = 1, #list do
- local info = list[i]
- local res = callback(info, listMgr.get(info.source))
- if res ~= nil then
- return res
- end
- end
- return nil
-end
-
-function mt:setFunction(func)
- self._func = func.id
- if self._global then
- func:markGlobal()
- end
-end
-
-function mt:getFunction()
- local id = self._func
- local func = listMgr.get(id)
- if not func then
- return nil
- end
- if func._removed then
- return nil
- end
- if not func:getSource() then
- func = nil
- listMgr.clear(id)
- end
- return func
-end
-
-function mt:setLib(lib)
- self._lib = lib
-end
-
-function mt:getLib()
- return self._lib
-end
-
-function mt:getLiteral()
- return self._literal
-end
-
-function mt:set(name, v)
- if not self._flag then
- self._flag = {}
- end
- self._flag[name] = v
-end
-
-function mt:get(name)
- if not self._flag then
- return nil
- end
- return self._flag[name]
-end
-
-function mt:getSource()
- return listMgr.get(self.source)
-end
-
-function mt:markGlobal()
- if self._global then
- return
- end
- self._global = true
- self:rawEach(function (index, value)
- value:markGlobal()
- end)
- local func = self:getFunction()
- if func then
- func:markGlobal()
- end
-end
-
-function mt:isGlobal()
- return self._global
-end
-
-function mt:setEmmy(emmy)
- if not emmy then
- return
- end
- if emmy.type == 'emmy.class' then
- ---@type EmmyClass
- local emmyClass = emmy
- emmyClass:setValue(self)
- emmyClass:eachChild(function (obj)
- local value = obj:getValue()
- if value then
- value:mergeValue(self)
- end
- end)
- emmyClass:eachField(function (field)
- local name = field:getName()
- local value = field:bindValue()
- self:setChild(name, value, field:getSource())
- end)
- elseif emmy.type == 'emmy.type' then
- ---@type EmmyType
- local emmyType = emmy
- emmyType:setValue(self)
- emmyType:eachClass(function (class)
- if class then
- self:mergeValue(class:getValue())
- end
- end)
- elseif emmy.type == 'emmy.arrayType' then
- ---@type EmmyArrayType
- local emmyArrayType = emmy
- emmyArrayType:setValue(self)
- elseif emmy.type == 'emmy.tableType' then
- ---@type EmmyTableType
- local emmyTableType = emmy
- emmyTableType:setValue(self)
- elseif emmy.type == 'emmy.functionType' then
- ---@type EmmyFunctionType
- local emmyFuncType = emmy
- emmyFuncType:setValue(self)
- self:setFunction(emmyFuncType:bindFunction())
- else
- return
- end
- self._emmy = emmy
- self:markGlobal()
-end
-
-function mt:getEmmy()
- if not self._emmy then
- return nil
- end
- local source = self._emmy.source
- if not listMgr.get(source) then
- self._emmy = nil
- return nil
- end
- return self._emmy
-end
-
-function mt:setComment(comment)
- self._comment = comment
-end
-
-function mt:getComment(comment)
- return self._comment
-end
-
-return {
- create = create,
- watch = Watch,
-}
diff --git a/script/vm/vm.lua b/script/vm/vm.lua
deleted file mode 100644
index 02cb574c..00000000
--- a/script/vm/vm.lua
+++ /dev/null
@@ -1,1341 +0,0 @@
-local library = require 'core.library'
-local valueMgr = require 'vm.value'
-local localMgr = require 'vm.local'
-local createLabel = require 'vm.label'
-local functionMgr = require 'vm.function'
-local sourceMgr = require 'vm.source'
-local buildGlobal = require 'vm.global'
-local createMulti = require 'vm.multi'
-local libraryBuilder = require 'vm.library'
-local emmyMgr = require 'emmy.manager'
-local config = require 'config'
-local mt = require 'vm.manager'
-local plugin = require 'plugin'
-
-require 'vm.module'
-require 'vm.raw'
-require 'vm.pcall'
-require 'vm.ipairs'
-require 'vm.emmy'
-require 'vm.special'
-
--- TODO source测试
---rawset(_G, 'CachedSource', setmetatable({}, { __mode = 'kv' }))
-
-function mt:getDefaultSource()
- return self:instantSource {
- start = 0,
- finish = 0,
- }
-end
-
-function mt:scopePush(source)
- self.currentFunction:push(source)
-end
-
-function mt:scopePop()
- self.currentFunction:pop()
-end
-
-function mt:buildTable(source)
- local tbl = self:createValue('table', source)
- if not source then
- return tbl
- end
- local n = 0
- for index, obj in ipairs(source) do
- local emmy = self:getEmmy()
- if obj.type == 'pair' then
- local value = self:getFirstInMulti(self:getExp(obj[2]))
- if value then
- local key = obj[1]
- self:instantSource(obj)
- self:instantSource(key)
- key:bindValue(value, 'set')
- value:setEmmy(emmy)
- if key.type == 'index' then
- local index = self:getIndex(key)
- key:set('parent', tbl)
- tbl:setChild(index, value, key)
- else
- if key.type == 'name' then
- key:set('parent', tbl)
- key:set('table index', true)
- tbl:setChild(key[1], value, key)
- end
- end
- end
- elseif obj.type:sub(1, 4) == 'emmy' then
- self:doEmmy(obj)
- else
- local value = self:getExp(obj)
- if value.type == 'multi' then
- if index == #source then
- value:eachValue(function (_, v)
- n = n + 1
- tbl:setChild(n, v, obj)
- end)
- else
- n = n + 1
- local v = self:getFirstInMulti(value)
- tbl:setChild(n, v, obj)
- end
- else
- n = n + 1
- tbl:setChild(n, value, obj)
- end
- -- 处理写了一半的 key = value,把name类的数组元素视为哈希键
- if obj.type == 'name' then
- obj:set('table index', true)
- end
- end
- end
- return tbl
-end
-
-function mt:runFunction(func)
- func:run(self)
-
- if not func:getSource() then
- return
- end
-
- if func:needSkip() then
- return
- end
-
- -- 暂时使用这种方式激活参数的source
- for _, arg in ipairs(func.args) do
- if arg:getSource() ~= func:getObject() then
- self:bindLocal(arg:getSource(), arg, 'local')
- end
- end
-
- local originFunction = self:getCurrentFunction()
- self:setCurrentFunction(func)
- func:push(func:getSource())
- func:markChunk()
-
- self:doActions(func:getSource())
-
- func:pop()
- self:setCurrentFunction(originFunction)
-end
-
-function mt:buildFunction(exp)
- if exp and exp:bindFunction() then
- return exp:bindFunction()
- end
-
- local value = self:createFunction(exp)
-
- if not exp then
- return value
- end
-
- exp:bindFunction(value)
- local func = value:getFunction()
-
- self:eachLocal(function (name, loc)
- func:saveUpvalue(name, loc)
- end)
-
- return value
-end
-
-function mt:forList(list, callback)
- if not list then
- return
- end
- if list.type == 'list' then
- for i = 1, #list do
- callback(list[i])
- end
- else
- callback(list)
- end
-end
-
-function mt:callSetMetaTable(func, values, source)
- if not values[1] then
- values[1] = self:createValue('any', self:getDefaultSource())
- end
- if not values[2] then
- values[2] = self:createValue('any', self:getDefaultSource())
- end
- func:setReturn(1, values[1])
- values[1]:setMetaTable(values[2])
-end
-
-function mt:tryRequireOne(str, strValue, mode)
- if not self.lsp then
- return nil
- end
- local ws = self.lsp:findWorkspaceFor(self:getUri())
- if not ws then
- return nil
- end
- local strSource = strValue:getSource()
- if not strSource then
- return nil
- end
- if type(str) == 'string' then
- -- 支持 require 'xxx' 的转到定义
- self:instantSource(strSource)
- local uri
- if mode == 'require' then
- uri = ws:searchPath(self:getUri(), str)
- elseif mode == 'loadfile' then
- uri = ws:loadPath(self:getUri(), str)
- elseif mode == 'dofile' then
- uri = ws:loadPath(self:getUri(), str)
- end
- if not uri then
- return nil
- end
-
- strSource:set('target uri', uri)
- self.lsp:compileChain(self:getUri(), uri)
- return self.lsp.chain:get(uri)
- end
- return nil
-end
-
-function mt:callRequire(func, values)
- if not values[1] then
- values[1] = self:createValue('any', self:getDefaultSource())
- end
- local strValue = values[1]
- local strSource = strValue:getSource()
- if not strSource then
- return nil
- end
- local str = strValue:getLiteral()
- local raw = self.text:sub(strSource.start, strSource.finish)
- str = plugin.call('OnRequirePath', str, raw) or str
- local lib = library.library[str]
- if lib then
- local value = libraryBuilder.value(lib)
- value:markGlobal()
- func:setReturn(1, value)
- return
- else
- local requireValue = self:tryRequireOne(str, values[1], 'require')
- if not requireValue then
- requireValue = self:createValue('any', self:getDefaultSource())
- requireValue:set('cross file', true)
- end
- func:setReturn(1, requireValue)
- end
-end
-
-function mt:callLoadFile(func, values)
- if not values[1] then
- values[1] = self:createValue('any', self:getDefaultSource())
- end
- local strValue = values[1]
- local requireValue = self:tryRequireOne(strValue:getLiteral(), values[1], 'loadfile')
- if not requireValue then
- requireValue = self:createValue('any', self:getDefaultSource())
- requireValue:set('cross file', true)
- end
- func:setReturn(1, requireValue)
-end
-
-function mt:callDoFile(func, values)
- if not values[1] then
- values[1] = self:createValue('any', self:getDefaultSource())
- end
- local strValue = values[1]
- local requireValue = self:tryRequireOne(strValue:getLiteral(), values[1], 'dofile')
- if not requireValue then
- requireValue = self:createValue('any', self:getDefaultSource())
- requireValue.isRequire = true
- end
- func:setReturn(1, requireValue)
-end
-
-function mt:callLibrary(func, values, source, lib)
- if lib.args then
- for i, arg in ipairs(lib.args) do
- local value = values[i]
- if value and arg.type ~= '...' then
- value:setType(arg.type, 0.6)
- end
- end
- end
- if lib.returns then
- for i, rtn in ipairs(lib.returns) do
- if rtn.type == '...' then
- --func:getReturn(i):setType('any', 0.0)
- else
- if rtn.type == 'boolean' or rtn.type == 'number' or rtn.type == 'integer' or rtn.type == 'string' then
- func:setReturn(i, self:createValue(rtn.type, self:getDefaultSource()))
- end
- local value = func:getReturn(i)
- if value then
- value:setType(rtn.type or 'any', 0.6)
- end
- end
- end
- end
- if lib.special then
- if lib.special == 'setmetatable' then
- self:callSetMetaTable(func, values, source)
- elseif lib.special == 'require' then
- self:callRequire(func, values)
- elseif lib.special == 'loadfile' then
- self:callLoadFile(func, values)
- elseif lib.special == 'dofile' then
- self:callDoFile(func, values)
- elseif lib.special == 'module' then
- self:callModuel(func, values)
- elseif lib.special == 'seeall' then
- self:callSeeAll(func, values)
- elseif lib.special == 'rawset' then
- self:callRawSet(func, values, source)
- elseif lib.special == 'rawget' then
- self:callRawGet(func, values, source)
- elseif lib.special == 'pcall' then
- self:callPcall(func, values, source)
- elseif lib.special == 'xpcall' then
- self:callXpcall(func, values, source)
- elseif lib.special == 'ipairs' then
- self:callIpairs(func, values, source)
- elseif lib.special == '@ipairs' then
- self:callAtIpairs(func, values, source)
- elseif lib.special == 'pairs' then
- self:callPairs(func, values, source)
- elseif lib.special == 'next' then
- self:callNext(func, values, source)
- end
- else
- -- 如果lib的参数中有function,则立即执行function
- if lib.args then
- local args
- for i = 1, #lib.args do
- local value = values[i]
- if value and value:getFunction() then
- if not args then
- args = createMulti()
- end
- self:call(value, args, source)
- end
- end
- end
- end
-end
-
-function mt:call(value, values, source)
- local lib = value:getLib()
- ---@type emmyFunction
- local func = value:getFunction()
- value:setType('function', 0.5)
- if not func then
- return
- end
- self:instantSource(source)
- if lib then
- self:callLibrary(func, values, source, lib)
- else
- if func:getSource() then
- if not source:get 'called' then
- source:set('called', true)
- func:setArgs(values)
- self:runFunction(func)
- end
- else
- func:mergeReturn(1, self:createValue('any', source))
- end
- if func:getEmmyParams() then
- self:callEmmySpecial(func, values, source)
- end
- end
-
- return func:getReturn()
-end
-
-function mt:createValue(tp, source, literal)
- local value = valueMgr.create(tp, source, literal)
- value.uri = self:getUri()
- return value
-end
-
-function mt:getName(name, source)
- if source then
- self:instantSource(source)
- if source:bindLocal() then
- local loc = source:bindLocal()
- return loc:getValue()
- end
- end
- local loc = self:loadLocal(name)
- if loc then
- source:bindLocal(loc, 'get')
- return loc:getValue()
- end
- local global = source:bindValue()
- if global then
- return global
- end
- local ENV
- if self.envType == '_ENV' then
- ENV = self:loadLocal('_ENV')
- else
- ENV = self:loadLocal('@ENV')
- end
- local ENVValue = ENV:getValue()
- ENVValue:addInfo('get child', source, name)
- global = ENVValue:getChild(name, source)
- source:bindValue(global, 'get')
- source:set('global', true)
- source:set('parent', ENVValue)
- if not global:getLib() then
- if self.lsp then
- self.lsp.global:markGet(self:getUri())
- end
- end
- return global
-end
-
-function mt:setName(name, source, value)
- self:instantSource(source)
- local loc = self:loadLocal(name)
- if loc then
- loc:setValue(value)
- source:bindLocal(loc, 'set')
- return
- end
- local global = source:bindValue()
- if global then
- return global
- end
- local ENV
- if self.envType == '_ENV' then
- ENV = self:loadLocal('_ENV')
- else
- ENV = self:loadLocal('@ENV')
- end
- local ENVValue = ENV:getValue()
- source:bindValue(value, 'set')
- ENVValue:setChild(name, value, source)
- source:set('global', true)
- source:set('parent', ENVValue)
- if self.lsp then
- self.lsp.global:markSet(self:getUri())
- end
-end
-
-function mt:getIndex(source)
- local child = source[1]
- if child.type == 'name' then
- local value = self:getName(child[1], child)
- child:set('in index', source)
- return value
- elseif child.type == 'string' or child.type == 'number' or child.type == 'boolean' then
- self:instantSource(child)
- child:set('in index', source)
- return child[1]
- else
- local index = self:getExp(child)
- return self:getFirstInMulti(index)
- end
-end
-
-function mt:unpackList(list)
- local values = createMulti()
- local exps = createMulti()
- if not list then
- return values
- end
- if list.type == 'list' or list.type == 'call' or list.type == 'return' then
- for i, exp in ipairs(list) do
- self:instantSource(exp)
- exps:push(exp)
- if exp.type == '...' then
- values:merge(self:loadDots())
- break
- end
- local value = self:getExp(exp)
- if value.type == 'multi' then
- if i == #list then
- value:eachValue(function (_, v)
- values:push(v)
- end)
- else
- values:push(self:getFirstInMulti(value))
- end
- else
- values:push(value)
- end
- end
- elseif list.type == '...' then
- self:instantSource(list)
- exps:push(list)
- values:merge(self:loadDots())
- else
- self:instantSource(list)
- exps:push(list)
- local value = self:getExp(list)
- if value.type == 'multi' then
- value:eachValue(function (_, v)
- values:push(v)
- end)
- else
- values:push(value)
- end
- end
- return values, exps
-end
-
-function mt:getFirstInMulti(multi)
- if not multi then
- return multi
- end
- if multi.type == 'multi' then
- return self:getFirstInMulti(multi[1])
- else
- return multi
- end
-end
-
-function mt:getSimple(simple, max)
- self:instantSource(simple)
- local first = simple[1]
- self:instantSource(first)
- local value = self:getExp(first)
- value = self:getFirstInMulti(value) or valueMgr.create('nil', self:getDefaultSource())
- first:bindValue(value, 'get')
- if not max then
- max = #simple
- elseif max < 0 then
- max = #simple + 1 + max
- end
- local object
- for i = 2, max do
- local source = simple[i]
- self:instantSource(source)
- source:set('simple', simple)
- value = self:getFirstInMulti(value) or valueMgr.create('nil', self:getDefaultSource())
-
- if source.type == 'call' then
- local values, args = self:unpackList(source)
- local func = value
- if object then
- table.insert(values, 1, object)
- table.insert(args, 1, simple[i-3])
- source:set('has object', true)
- end
- object = nil
- source:bindCall(args)
- value = self:call(func, values, source) or valueMgr.create('any', self:getDefaultSource())
- elseif source.type == 'index' then
- local child = source[1]
- local index = self:getIndex(source)
- child:set('parent', value)
- value:addInfo('get child', source, index)
- value = value:getChild(index, source)
- source:bindValue(value, 'get')
- elseif source.type == 'name' then
- source:set('parent', value)
- source:set('object', object)
- value:addInfo('get child', source, source[1])
- value = value:getChild(source[1], source)
- source:bindValue(value, 'get')
- elseif source.type == ':' then
- object = value
- source:set('parent', value)
- source:set('object', object)
- elseif source.type == '.' then
- source:set('parent', value)
- end
- end
- return value
-end
-
-function mt:isTrue(v)
- if v:getType() == 'nil' then
- return false
- end
- if v:getType() == 'boolean' and not v:getLiteral() then
- return false
- end
- return true
-end
-
-function mt:getBinary(exp)
- self:instantSource(exp)
- local v1 = self:getExp(exp[1])
- local v2 = self:getExp(exp[2])
- v1 = self:getFirstInMulti(v1) or valueMgr.create('nil', exp[1])
- v2 = self:getFirstInMulti(v2) or valueMgr.create('nil', exp[2])
- local op = exp.op
- -- TODO 搜索元方法
- if op == 'or' then
- if self:isTrue(v1) then
- return v1
- else
- return v2
- end
- elseif op == 'and' then
- if self:isTrue(v1) then
- return v2
- else
- return v1
- end
- elseif op == '<='
- or op == '>='
- or op == '<'
- or op == '>'
- then
- v1:setType('number', 0.5)
- v2:setType('number', 0.5)
- v1:setType('string', 0.1)
- v2:setType('string', 0.1)
- return self:createValue('boolean', exp)
- elseif op == '~='
- or op == '=='
- then
- return self:createValue('boolean', exp)
- elseif op == '|'
- or op == '~'
- or op == '&'
- or op == '<<'
- or op == '>>'
- then
- v1:setType('integer', 0.5)
- v2:setType('integer', 0.5)
- v1:setType('number', 0.5)
- v2:setType('number', 0.5)
- v1:setType('string', 0.1)
- v2:setType('string', 0.1)
- if math.type(v1:getLiteral()) == 'integer' and math.type(v2:getLiteral()) == 'integer' then
- if op == '|' then
- return self:createValue('integer', exp, v1:getLiteral() | v2:getLiteral())
- elseif op == '~' then
- return self:createValue('integer', exp, v1:getLiteral() ~ v2:getLiteral())
- elseif op == '&' then
- return self:createValue('integer', exp, v1:getLiteral() &v2:getLiteral())
- elseif op == '<<' then
- return self:createValue('integer', exp, v1:getLiteral() << v2:getLiteral())
- elseif op == '>>' then
- return self:createValue('integer', exp, v1:getLiteral() >> v2:getLiteral())
- end
- end
- return self:createValue('integer', exp)
- elseif op == '..' then
- v1:setType('string', 0.5)
- v2:setType('string', 0.5)
- v1:setType('number', 0.1)
- v2:setType('number', 0.1)
- if type(v1:getLiteral()) == 'string' and type(v2:getLiteral()) == 'string' then
- return self:createValue('string', exp, v1:getLiteral() .. v2:getLiteral())
- end
- return self:createValue('string', exp)
- elseif op == '+'
- or op == '-'
- or op == '*'
- or op == '/'
- or op == '^'
- or op == '%'
- or op == '//'
- then
- v1:setType('number', 0.5)
- v2:setType('number', 0.5)
- if type(v1:getLiteral()) == 'number' and type(v2:getLiteral()) == 'number' then
- if op == '+' then
- return self:createValue('number', exp, v1:getLiteral() + v2:getLiteral())
- elseif op == '-' then
- return self:createValue('number', exp, v1:getLiteral() - v2:getLiteral())
- elseif op == '*' then
- return self:createValue('number', exp, v1:getLiteral() * v2:getLiteral())
- elseif op == '/' then
- if v2:getLiteral() ~= 0 then
- return self:createValue('number', exp, v1:getLiteral() / v2:getLiteral())
- end
- elseif op == '^' then
- return self:createValue('number', exp, v1:getLiteral() ^ v2:getLiteral())
- elseif op == '%' then
- if v2:getLiteral() ~= 0 then
- return self:createValue('number', exp, v1:getLiteral() % v2:getLiteral())
- end
- elseif op == '//' then
- if v2:getLiteral() ~= 0 then
- return self:createValue('number', exp, v1:getLiteral() // v2:getLiteral())
- end
- end
- end
- return self:createValue('number', exp)
- end
- return nil
-end
-
-function mt:getUnary(exp)
- self:instantSource(exp)
- local v1 = self:getExp(exp[1])
- v1 = self:getFirstInMulti(v1) or self:createValue('nil', exp[1])
- local op = exp.op
- -- TODO 搜索元方法
- if op == 'not' then
- return self:createValue('boolean', exp)
- elseif op == '#' then
- v1:setType('table', 0.5)
- v1:setType('string', 0.5)
- if type(v1:getLiteral()) == 'string' then
- return self:createValue('integer', exp, #v1:getLiteral())
- end
- return self:createValue('integer', exp)
- elseif op == '-' then
- v1:setType('number', 0.5)
- if type(v1:getLiteral()) == 'number' then
- return self:createValue('number', exp, -v1:getLiteral())
- end
- return self:createValue('number', exp)
- elseif op == '~' then
- v1:setType('integer', 0.5)
- if math.type(v1:getLiteral()) == 'integer' then
- return self:createValue('integer', exp, ~v1:getLiteral())
- end
- return self:createValue('integer', exp)
- end
- return nil
-end
-
-function mt:getExp(exp)
- self:instantSource(exp)
- local tp = exp.type
- if tp == 'nil' then
- return self:createValue('nil', exp)
- elseif tp == 'string' then
- return self:createValue('string', exp, exp[1])
- elseif tp == 'boolean' then
- return self:createValue('boolean', exp, exp[1])
- elseif tp == 'number' then
- return self:createValue('number', exp, exp[1])
- elseif tp == 'name' then
- local value = self:getName(exp[1], exp)
- return value
- elseif tp == 'simple' then
- return self:getSimple(exp)
- elseif tp == 'index' then
- return self:getIndex(exp)
- elseif tp == 'binary' then
- return self:getBinary(exp)
- elseif tp == 'unary' then
- return self:getUnary(exp)
- elseif tp == 'function' then
- return self:buildFunction(exp)
- elseif tp == 'table' then
- return self:buildTable(exp)
- elseif tp == '...' then
- return self:loadDots()
- elseif tp == 'list' then
- return self:getMultiByList(exp)
- end
- error('Unkown exp type: ' .. tostring(tp))
-end
-
-function mt:getMultiByList(list)
- local multi = createMulti()
- for i, exp in ipairs(list) do
- multi:push(self:getExp(exp), i == #list)
- end
- return multi
-end
-
-function mt:doDo(action)
- self:instantSource(action)
- self:scopePush(action)
- self:doActions(action)
- self:scopePop()
-end
-
-function mt:doReturn(action)
- if #action == 0 then
- return
- end
- self:instantSource(action)
- local values = self:unpackList(action)
- local func = self:getCurrentFunction()
- values:eachValue(function (n, value)
- value.uri = self:getUri()
- func:mergeReturn(n, value)
- local source = action[n] or value:getSource()
- if not source or source.start == 0 then
- source = self:getDefaultSource()
- end
- value:addInfo('return', source)
- end)
-end
-
-function mt:doLabel(source)
- local name = source[1]
- local label = self:loadLabel(name)
- if label then
- self:bindLabel(source, label, 'set')
- else
- label = self:createLabel(name, source, 'set')
- end
-end
-
-function mt:createLabel(name, source, action)
- local label = self:bindLabel(source)
- if label then
- self:saveLabel(label)
- return label
- end
-
- label = createLabel(name, source)
- self:saveLabel(label)
- self:bindLabel(source, label, action)
- return label
-end
-
-function mt:doGoTo(source)
- local name = source[1]
- local label = self:loadLabel(name)
- if label then
- self:bindLabel(source, label, 'get')
- else
- label = self:createLabel(name, source, 'get')
- end
-end
-
-function mt:setOne(var, value, emmy, comment)
- if not value then
- value = valueMgr.create('nil', self:getDefaultSource())
- end
- value:setEmmy(emmy)
- value:setComment(comment)
- self:instantSource(var)
- if var.type == 'name' then
- self:setName(var[1], var, value)
- elseif var.type == 'simple' then
- local parent = self:getSimple(var, -2)
- parent = self:getFirstInMulti(parent)
- if not parent then
- return
- end
- local key = var[#var]
- self:instantSource(key)
- key:set('simple', var)
- if key.type == 'index' then
- local index = self:getIndex(key)
- key[1]:set('parent', parent)
- parent:setChild(index, value, key[1])
- elseif key.type == 'name' then
- local index = key[1]
- key:set('parent', parent)
- parent:setChild(index, value, key)
- end
- key:bindValue(value, 'set')
- end
-end
-
-function mt:doSet(action)
- local emmy = self:getEmmy()
- local comment = self:getEmmyComment()
- if not action[2] then
- return
- end
- self:instantSource(action)
- -- 要先计算值
- local vars = action[1]
- local exps = action[2]
- local value = self:getExp(exps)
- local values = {}
- if value.type == 'multi' then
- if not emmy then
- emmy = value:getEmmy()
- end
- value:eachValue(function (i, v)
- values[i] = v
- end)
- else
- values[1] = value
- end
- local i = 0
- self:forList(vars, function (var)
- i = i + 1
- self:setOne(var, values[i], emmy, comment)
- end)
-end
-
-function mt:doLocal(action)
- local emmy = self:getEmmy()
- local comment = self:getEmmyComment()
- self:instantSource(action)
- local vars = action[1]
- local exps = action[2]
- local values
- if exps then
- local value = self:getExp(exps)
- values = {}
- if value.type == 'multi' then
- if not emmy then
- emmy = value:getEmmy()
- end
- value:eachValue(function (i, v)
- values[i] = v
- end)
- else
- values[1] = value
- end
- end
- local i = 0
- self:forList(vars, function (key)
- i = i + 1
- local value
- if values then
- value = values[i]
- end
- self:createLocal(key[1], key, value, emmy, comment)
- end)
-end
-
-function mt:doIf(action)
- self:instantSource(action)
- for _, block in ipairs(action) do
- if block.filter then
- self:getExp(block.filter)
- end
-
- self:scopePush(block)
- self:doActions(block)
- self:scopePop()
- end
-end
-
-function mt:doLoop(action)
- self:instantSource(action)
- local min = self:getFirstInMulti(self:getExp(action.min))
- self:getExp(action.max)
- if action.step then
- self:getExp(action.step)
- end
-
- self:scopePush(action)
- self:createLocal(action.arg[1], action.arg, min)
- self:doActions(action)
- self:scopePop()
-end
-
-function mt:doIn(action)
- local emmyParams = self:getEmmyParams()
- self:instantSource(action)
- local args = self:unpackList(action.exp)
-
- self:scopePush(action)
- local func = table.remove(args, 1) or valueMgr.create('any', self:getDefaultSource())
- local values = self:call(func, args, action) or createMulti()
- self:forList(action.arg, function (arg)
- self:instantSource(arg)
- local value = table.remove(values, 1) or self:createValue('nil', arg)
- if emmyParams then
- for i = #emmyParams, 1, -1 do
- local emmyParam = emmyParams[i]
- if emmyParam and emmyParam:getName() == arg[1] then
- value:setEmmy(emmyParam:bindType())
- end
- end
- end
- self:createLocal(arg[1], arg, value)
- end)
-
- self:doActions(action)
-
- self:scopePop()
-end
-
-function mt:doWhile(action)
- self:instantSource(action)
- self:getExp(action.filter)
-
- self:scopePush(action)
- self:doActions(action)
- self:scopePop()
-end
-
-function mt:doRepeat(action)
- self:instantSource(action)
- self:scopePush(action)
- self:doActions(action)
- self:getExp(action.filter)
- self:scopePop()
-end
-
-function mt:doFunction(action)
- self:instantSource(action)
- local name = action.name
- if name then
- self:instantSource(name)
- if name.type == 'simple' then
- local parent = self:getSimple(name, -2)
- if name[#name-1].type == ':' then
- local value = self:buildFunction(action)
- local source = name[#name]
- self:instantSource(source)
- source:set('simple', name)
- source:set('parent', parent)
- source:set('object', parent)
- if source.type == 'index' then
- local index = self:getIndex(source)
- parent:setChild(index, value, source[1])
- elseif source.type == 'name' then
- local index = source[1]
- parent:setChild(index, value, source)
- end
- source:bindValue(value, 'set')
-
- local func = value:getFunction()
- if func then
- if #name == 3 then
- -- function x:b()
- local loc = self:loadLocal(name[1][1])
- if loc then
- func:setObject(parent, loc:getSource())
- else
- func:setObject(parent, name[#name-2])
- end
- else
- func:setObject(parent, name[#name-2])
- end
- end
- else
- local value = self:buildFunction(action)
- local source = name[#name]
- self:instantSource(source)
- source:set('simple', name)
- source:set('parent', parent)
- if source.type == 'index' then
- local index = self:getIndex(source)
- parent:setChild(index, value, source[1])
- elseif source.type == 'name' then
- local index = source[1]
- parent:setChild(index, value, source)
- end
- source:bindValue(value, 'set')
- end
- else
- local value = self:buildFunction(action)
- self:setName(name[1], name, value)
- end
- else
- self:buildFunction(action)
- end
-end
-
-function mt:doLocalFunction(action)
- self:instantSource(action)
- local name = action.name
- if name then
- self:instantSource(name)
- if name.type == 'simple' then
- self:doFunction(action)
- else
- local loc = self:createLocal(name[1], name)
- local func = self:buildFunction(action)
- func:addInfo('local', name)
- loc:setValue(func)
- name:bindValue(func, 'local')
- end
- end
-end
-
-function mt:doAction(action)
- if not action then
- -- Skip
- return
- end
- if coroutine.isyieldable() then
- if self.lsp:isNeedCompile(self.uri) then
- coroutine.yield()
- if self._removed then
- coroutine.yield('stop')
- return
- end
- else
- self:remove()
- coroutine.yield('stop')
- return
- end
- end
- local tp = action.type
- if tp:sub(1, 4) == 'emmy' then
- self:doEmmy(action)
- return
- end
- if tp == 'do' then
- self:doDo(action)
- elseif tp == 'break' then
- elseif tp == 'return' then
- self:doReturn(action)
- elseif tp == 'label' then
- self:doLabel(action)
- elseif tp == 'goto' then
- self:doGoTo(action)
- elseif tp == 'set' then
- self:doSet(action)
- elseif tp == 'local' then
- self:doLocal(action)
- elseif tp == 'simple' then
- -- call
- self:getSimple(action)
- action:set('as action', true)
- elseif tp == 'if' then
- self:doIf(action)
- elseif tp == 'loop' then
- self:doLoop(action)
- elseif tp == 'in' then
- self:doIn(action)
- elseif tp == 'while' then
- self:doWhile(action)
- elseif tp == 'repeat' then
- self:doRepeat(action)
- elseif tp == 'function' then
- self:doFunction(action)
- elseif tp == 'localfunction' then
- self:doLocalFunction(action)
- else
- self:getExp(action)
- action:set('as action', true)
- end
- self:clearEmmy()
-end
-
-function mt:doActions(actions)
- for _, action in ipairs(actions) do
- self:doAction(action)
- end
-end
-
-function mt:createFunction(source)
- local value = self:createValue('function', source)
- local func = functionMgr.create(source)
- func:setEmmy(self:getEmmyParams(), self:getEmmyReturns(), self:getEmmyOverLoads())
- func:setComment(self:getEmmyComment())
- value:setFunction(func)
- value:setType('function', 1.0)
- if source:getUri() == self.uri then
- self.funcs[#self.funcs+1] = func
- end
- return value
-end
-
-function mt:callLeftFuncions()
- for _, func in ipairs(self.funcs) do
- if not func:hasRuned() then
- self:runFunction(func)
- end
- end
-end
-
-function mt:setCurrentFunction(func)
- self.currentFunction = func
-end
-
-function mt:getCurrentFunction()
- return self.currentFunction
-end
-
-function mt:saveLocal(name, loc)
- self.currentFunction:saveLocal(name, loc)
-end
-
-function mt:saveUpvalue(name, loc)
- self.currentFunction:saveUpvalue(name, loc)
-end
-
-function mt:loadLocal(name)
- return self.currentFunction:loadLocal(name)
-end
-
-function mt:eachLocal(callback)
- return self.currentFunction:eachLocal(callback)
-end
-
-function mt:saveLabel(label)
- self.currentFunction:saveLabel(label)
-end
-
-function mt:loadLabel(name)
- return self.currentFunction:loadLabel(name)
-end
-
-function mt:loadDots()
- return self.currentFunction:loadDots()
-end
-
-function mt:getUri()
- return self.currentFunction and self.currentFunction:getUri() or self.uri
-end
-
-function mt:instantSource(source)
- if self:isRemoved() then
- error('dead vm')
- return nil
- end
- if sourceMgr.instant(source) then
- source:setUri(self:getUri())
- self.sources[#self.sources+1] = source
- --CachedSource[source] = true
- end
- return source
-end
-
-function mt:bindLocal(source, loc, action)
- if not source then
- return
- end
- self:instantSource(source)
- if loc then
- source:bindLocal(loc, action)
- else
- return source:bindLocal()
- end
-end
-
-function mt:bindLabel(source, label, action)
- self:instantSource(source)
- if label then
- source:bindLabel(label, action)
- else
- return source:bindLabel()
- end
-end
-
-function mt:createLocal(key, source, value, emmy, comment)
- local loc = self:bindLocal(source)
- if not value then
- value = self:createValue('nil', source)
- end
- if loc then
- loc:setValue(value)
- loc:setEmmy(emmy)
- self:saveLocal(key, loc)
- return loc
- end
-
- loc = localMgr.create(key, source, value, source.tags)
- loc:setEmmy(emmy)
- loc:setComment(comment)
- self:saveLocal(key, loc)
- self:bindLocal(source, loc, 'local')
- loc:close(self:getCurrentFunction():getSource().finish)
- value:addInfo('local', source)
- return loc
-end
-
-function mt:createUpvalue(key, source, value)
- local loc = self:bindLocal(source)
- if not value then
- value = self:createValue('nil', source)
- end
- if loc then
- loc:setValue(value)
- self:saveUpvalue(key, loc)
- return loc
- end
-
- loc = localMgr.create(key, source, value)
- self:saveUpvalue(key, loc)
- self:bindLocal(source, loc, 'local')
- value:addInfo('local', source)
- return loc
-end
-
-function mt:createEnvironment(ast)
- -- 整个文件是一个函数
- self.main = self:createFunction(ast)
- self:setCurrentFunction(self.main:getFunction())
- if self.lsp then
- self.main:getFunction():mergeReturn(1, self.lsp.chain:get(self.uri))
- end
- -- 全局变量`_G`
- local global = buildGlobal(self.lsp)
- local env
- if self.envType == '_ENV' then
- -- 隐藏的上值`_ENV`
- env = self:createUpvalue('_ENV', self:getDefaultSource(), global)
- else
- -- 为了实现方便,fenv也使用隐藏上值来实现
- -- 使用了非法标识符保证用户无法访问
- env = self:createUpvalue('@ENV', self:getDefaultSource(), global)
- end
- env:set('hide', true)
- self.env = env
-end
-
-function mt:eachSource(callback)
- if self._removed then
- return
- end
- local sources = self.sources
- for i = 1, #sources do
- local res = callback(sources[i])
- if res ~= nil then
- return res
- end
- end
-end
-
-function mt:isRemoved()
- return self._removed == true
-end
-
-function mt:remove()
- if self._removed then
- return
- end
- self._removed = true
- for _, source in ipairs(self.sources) do
- source:kill()
- end
- self.sources = nil
- for _, func in ipairs(self.funcs) do
- func:kill()
- end
- self.funcs = nil
-end
-
-local function compile(vm, ast, lsp, uri)
- -- 创建初始环境
- ast.uri = vm.uri
- -- 根据运行版本决定环境实现方式
- if config.config.runtime.version == 'Lua 5.1' or config.config.runtime.version == 'LuaJIT' then
- vm.envType = 'fenv'
- else
- vm.envType = '_ENV'
- end
- vm:instantSource(ast)
- vm:createEnvironment(ast)
-
- -- 检查所有没有调用过的函数,调用一遍
- vm:callLeftFuncions()
-
- return vm
-end
-
-return function (ast, lsp, uri, text)
- if not ast then
- return nil, 'Ast failed'
- end
- local vm = setmetatable({
- funcs = {},
- sources = {},
- main = nil,
- env = nil,
- emmy = nil,
- ---@type emmyMgr
- emmyMgr = lsp and lsp.emmy or emmyMgr(),
- lsp = lsp,
- uri = uri or '',
- text = text or '',
- }, mt)
- local suc, res = xpcall(compile, log.error, vm, ast, lsp, uri)
- if not suc then
- vm:remove()
- return nil, res
- end
- return res
-end
diff --git a/script/without-check-nil.lua b/script/without-check-nil.lua
deleted file mode 100644
index cc7da9d4..00000000
--- a/script/without-check-nil.lua
+++ /dev/null
@@ -1,126 +0,0 @@
-local m = {}
-
-local mt = {}
-mt.__add = function (a, b)
- if a == nil then a = 0 end
- if b == nil then b = 0 end
- return a + b
-end
-mt.__sub = function (a, b)
- if a == nil then a = 0 end
- if b == nil then b = 0 end
- return a - b
-end
-mt.__mul = function (a, b)
- if a == nil then a = 0 end
- if b == nil then b = 0 end
- return a * b
-end
-mt.__div = function (a, b)
- if a == nil then a = 0 end
- if b == nil then b = 0 end
- return a / b
-end
-mt.__mod = function (a, b)
- if a == nil then a = 0 end
- if b == nil then b = 0 end
- return a % b
-end
-mt.__pow = function (a, b)
- if a == nil then a = 0 end
- if b == nil then b = 0 end
- return a ^ b
-end
-mt.__unm = function ()
- return 0
-end
-mt.__concat = function (a, b)
- if a == nil then a = '' end
- if b == nil then b = '' end
- return a .. b
-end
-mt.__len = function ()
- return 0
-end
-mt.__lt = function (a, b)
- if a == nil then a = 0 end
- if b == nil then b = 0 end
- return a < b
-end
-mt.__le = function (a, b)
- if a == nil then a = 0 end
- if b == nil then b = 0 end
- return a <= b
-end
-mt.__index = function () end
-mt.__newindex = function () end
-mt.__call = function () end
-mt.__pairs = function () end
-mt.__ipairs = function () end
-if _VERSION == 'Lua 5.3' or _VERSION == 'Lua 5.4' then
- mt.__idiv = load[[
- local a, b = ...
- if a == nil then a = 0 end
- if b == nil then b = 0 end
- return a // b
- ]]
- mt.__band = load[[
- local a, b = ...
- if a == nil then a = 0 end
- if b == nil then b = 0 end
- return a & b
- ]]
- mt.__bor = load[[
- local a, b = ...
- if a == nil then a = 0 end
- if b == nil then b = 0 end
- return a | b
- ]]
- mt.__bxor = load[[
- local a, b = ...
- if a == nil then a = 0 end
- if b == nil then b = 0 end
- return a ~ b
- ]]
- mt.__bnot = load[[
- return ~ 0
- ]]
- mt.__shl = load[[
- local a, b = ...
- if a == nil then a = 0 end
- if b == nil then b = 0 end
- return a << b
- ]]
- mt.__shr = load[[
- local a, b = ...
- if a == nil then a = 0 end
- if b == nil then b = 0 end
- return a >> b
- ]]
-end
-
-for event, func in pairs(mt) do
- mt[event] = function (...)
- local watch = m.watch
- if not watch then
- return func(...)
- end
- local care, result = watch(event, ...)
- if not care then
- return func(...)
- end
- return result
- end
-end
-
-function m.enable()
- debug.setmetatable(nil, mt)
-end
-
-function m.disable()
- if debug.getmetatable(nil) == mt then
- debug.setmetatable(nil, nil)
- end
-end
-
-return m
diff --git a/script/workspace.lua b/script/workspace.lua
deleted file mode 100644
index 0795877c..00000000
--- a/script/workspace.lua
+++ /dev/null
@@ -1,468 +0,0 @@
-local fs = require 'bee.filesystem'
-local async = require 'async'
-local config = require 'config'
-local ll = require 'lpeglabel'
-local platform = require 'bee.platform'
-local glob = require 'glob'
-local uric = require 'uri'
-local fn = require 'filename'
-
---- @class Workspace
-local mt = {}
-mt.__index = mt
-
-function mt:listenLoadFile()
- self._loadFileRequest = async.run('loadfile', nil, function (filename, mode, buf)
- local path = fs.path(filename)
- local name = fn.getFileName(path)
- local uri = uric.encode(path)
- self.files[name] = uri
- if mode == 'workspace' then
- self.lsp:readText(self, uri, path, buf, self._currentScanCompiled)
- elseif mode == 'library' then
- self.lsp:readLibrary(self, uri, path, buf, self._currentScanCompiled)
- else
- error('Unknown mode:' .. tostring(mode))
- end
- end)
-end
-
-function mt:buildScanPattern()
- local pattern = {}
-
- -- config.workspace.ignoreDir
- for path in pairs(config.config.workspace.ignoreDir) do
- pattern[#pattern+1] = path
- end
- -- config.files.exclude
- for path, ignore in pairs(config.other.exclude) do
- if ignore then
- pattern[#pattern+1] = path
- end
- end
- -- config.workspace.ignoreSubmodules
- if config.config.workspace.ignoreSubmodules then
- local buf = io.load(self.root / '.gitmodules')
- if buf then
- for path in buf:gmatch('path = ([^\r\n]+)') do
- log.info('忽略子模块:', path)
- pattern[#pattern+1] = path
- end
- end
- end
- -- config.workspace.useGitIgnore
- if config.config.workspace.useGitIgnore then
- local buf = io.load(self.root / '.gitignore')
- if buf then
- for line in buf:gmatch '[^\r\n]+' do
- pattern[#pattern+1] = line
- end
- end
- buf = io.load(self.root / '.git' / 'info' / 'exclude' )
- if buf then
- for line in buf:gmatch '[^\r\n]+' do
- pattern[#pattern+1] = line
- end
- end
- end
- -- config.workspace.library
- for path in pairs(config.config.workspace.library) do
- pattern[#pattern+1] = path
- end
-
- return pattern
-end
-
----@param options table
-function mt:buildLibraryRequests(options)
- local requests = {}
- for path, pattern in pairs(config.config.workspace.library) do
- requests[#requests+1] = {
- mode = 'library',
- root = fs.absolute(fs.path(path)):string(),
- pattern = pattern,
- options = options,
- }
- end
- return table.unpack(requests)
-end
-
-function mt:scanFiles()
- if self._scanRequest then
- log.info('Break scan.')
- self._scanRequest:push('stop')
- self._scanRequest = nil
- self._complete = false
- self:reset()
- end
-
- local pattern = self:buildScanPattern()
- local options = {
- ignoreCase = platform.OS == 'Windows',
- }
-
- self.gitignore = glob.gitignore(pattern, options)
- self._currentScanCompiled = {}
- local count = 0
- self._scanRequest = async.run('scanfiles', {
- {
- mode = 'workspace',
- root = self.root:string(),
- pattern = pattern,
- options = options,
- },
- self:buildLibraryRequests(options),
- }, function (mode, ...)
- if mode == 'ok' then
- log.info('Scan finish, got', count, 'files.')
- self._complete = true
- self._scanRequest = nil
- self:reset()
- return true
- elseif mode == 'log' then
- log.debug(...)
- elseif mode == 'workspace' then
- local path = fs.path(...)
- if not fn.isLuaFile(path) then
- return
- end
- self._loadFileRequest:push(path:string(), 'workspace')
- count = count + 1
- elseif mode == 'library' then
- local path = fs.path(...)
- if not fn.isLuaFile(path) then
- return
- end
- self._loadFileRequest:push(path:string(), 'library')
- count = count + 1
- elseif mode == 'stop' then
- log.info('Scan stoped.')
- return false
- end
- end)
-end
-
-function mt:init(rootUri)
- self.root = uric.decode(rootUri)
- self.uri = rootUri
- if not self.root then
- return
- end
- log.info('Workspace inited, root: ', self.root)
- log.info('Workspace inited, uri: ', rootUri)
- local logPath = ROOT / 'log' / (rootUri:gsub('[/:]+', '_') .. '.log')
- log.info('Log path: ', logPath)
- log.init(ROOT, logPath)
-end
-
-function mt:isComplete()
- return self._complete == true
-end
-
-function mt:addFile(path)
- if not fn.isLuaFile(path) then
- return
- end
- local name = fn.getFileName(path)
- local uri = uric.encode(path)
- self.files[name] = uri
- self.lsp:readText(self, uri, path)
-end
-
-function mt:removeFile(path)
- local name = fn.getFileName(path)
- if not self.files[name] then
- return
- end
- self.files[name] = nil
- local uri = uric.encode(path)
- self.lsp:removeText(uri)
-end
-
-function mt:findPath(baseUri, searchers)
- local results = {}
- local basePath = uric.decode(baseUri)
- if not basePath then
- return nil
- end
- local baseName = fn.getFileName(basePath)
- for filename, uri in pairs(self.files) do
- if filename ~= baseName then
- for _, searcher in ipairs(searchers) do
- if filename:sub(-#searcher) == searcher then
- local sep = filename:sub(-#searcher-1, -#searcher-1)
- if sep == '/' or sep == '\\' then
- results[#results+1] = uri
- end
- end
- end
- end
- end
-
- if #results == 0 then
- return nil
- end
- local uri
- if #results == 1 then
- uri = results[1]
- else
- table.sort(results, function (a, b)
- return fn.similarity(a, baseUri) > fn.similarity(b, baseUri)
- end)
- uri = results[1]
- end
- return uri
-end
-
-function mt:createCompiler(str)
- local state = {
- 'Main',
- }
- local function push(c)
- if state.Main then
- state.Main = state.Main * c
- else
- state.Main = c
- end
- end
- local count = 0
- local function code()
- count = count + 1
- local name = 'C' .. tostring(count)
- local nextName = 'C' .. tostring(count + 1)
- state[name] = ll.P(1) * (#ll.V(nextName) + ll.V(name))
- return ll.V(name)
- end
- local function static(c)
- count = count + 1
- local name = 'C' .. tostring(count)
- local nextName = 'C' .. tostring(count + 1)
- local catch = #ll.V(nextName)
- if platform.OS == 'Windows' then
- for i = #c, 1, -1 do
- local char = c:sub(i, i)
- local u = char:upper()
- local l = char:lower()
- if u == l then
- catch = ll.P(char) * catch
- else
- catch = (ll.P(u) + ll.P(l)) * catch
- end
- end
- else
- catch = ll.P(c) * catch
- end
- state[name] = catch
- return ll.V(name)
- end
- local function eof()
- count = count + 1
- local name = 'C' .. tostring(count)
- state[name] = ll.Cmt(ll.P(1) + ll.Cp(), function (_, _, c)
- return type(c) == 'number'
- end)
- return ll.V(name)
- end
- local isFirstCode = true
- local firstCode
- local compiler = ll.P {
- 'Result',
- Result = (ll.V'Code' + ll.V'Static')^1,
- Code = ll.P'?' / function ()
- if isFirstCode then
- isFirstCode = false
- push(ll.Cmt(ll.C(code()), function (_, pos, code)
- firstCode = code
- return pos, code
- end))
- else
- push(ll.Cmt(
- ll.C(code()),
- function (_, _, me)
- return firstCode == me
- end
- ))
- end
- end,
- Static = (1 - ll.P'?')^1 / function (c)
- push(static(c))
- end,
- }
- compiler:match(str)
- push(eof())
- return ll.P(state)
-end
-
-function mt:compileLuaPath()
- for i, luapath in ipairs(config.config.runtime.path) do
- self.pathMatcher[i] = self:createCompiler(luapath)
- end
-end
-
-function mt:convertPathAsRequire(filename, start)
- local list
- for _, matcher in ipairs(self.pathMatcher) do
- local str = matcher:match(filename:sub(start))
- if str then
- if not list then
- list = {}
- end
- list[#list+1] = str:gsub('/', '.')
- end
- end
- return list
-end
-
---- @param baseUri uri
---- @param input string
-function mt:matchPath(baseUri, input)
- local first = input:match '^[^%.]+'
- if not first then
- return nil
- end
- first = first:gsub('%W', '%%%1')
- local basePath = uric.decode(baseUri)
- if not basePath then
- return nil
- end
- local baseName = fn.getFileName(basePath)
- local rootLen = #self.root:string(basePath)
- local map = {}
- for filename in pairs(self.files) do
- if filename ~= baseName then
- local trueFilename = fn.getTrueName(filename)
- local start
- if platform.OS == 'Windows' then
- start = filename:find('[/\\]' .. first:lower(), rootLen + 1)
- else
- start = trueFilename:find('[/\\]' .. first, rootLen + 1)
- end
- if start then
- local list = self:convertPathAsRequire(trueFilename, start + 1)
- if list then
- for _, str in ipairs(list) do
- if #str >= #input and fn.fileNameEq(str:sub(1, #input), input) then
- if not map[str] then
- map[str] = trueFilename
- else
- local s1 = fn.similarity(trueFilename, baseName)
- local s2 = fn.similarity(map[str], baseName)
- if s1 > s2 then
- map[str] = trueFilename
- elseif s1 == s2 then
- if trueFilename < map[str] then
- map[str] = trueFilename
- end
- end
- end
- end
- end
- end
- end
- end
- end
-
- local list = {}
- for str in pairs(map) do
- list[#list+1] = str
- map[str] = map[str]:sub(rootLen + 2)
- end
- if #list == 0 then
- return nil
- end
- table.sort(list, function (a, b)
- local sa = fn.similarity(map[a], baseName)
- local sb = fn.similarity(map[b], baseName)
- if sa == sb then
- return a < b
- else
- return sa > sb
- end
- end)
- return list, map
-end
-
-function mt:searchPath(baseUri, str)
- str = fn.getFileName(fs.path(str))
- if self.searched[baseUri] and self.searched[baseUri][str] then
- return self.searched[baseUri][str]
- end
- str = str:gsub('%.', '/')
- :gsub('%%', '%%%%')
- local searchers = {}
- for i, luapath in ipairs(config.config.runtime.path) do
- searchers[i] = luapath:gsub('%?', str)
- end
-
- local uri = self:findPath(baseUri, searchers)
- if uri then
- if not self.searched[baseUri] then
- self.searched[baseUri] = {}
- end
- self.searched[baseUri][str] = uri
- end
- return uri
-end
-
-function mt:loadPath(baseUri, str)
- local ok, relative = pcall(fs.relative, fs.absolute(self.root / str), self.root)
- if not ok then
- return nil
- end
- str = fn.getFileName(relative)
- if self.loaded[str] then
- return self.loaded[str]
- end
-
- local searchers = { str }
-
- local uri = self:findPath(baseUri, searchers)
- if uri then
- self.loaded[str] = uri
- end
- return uri
-end
-
-function mt:reset()
- self.searched = {}
- self.loaded = {}
- self.lsp:reCompile()
-end
-
----@param uri uri
----@return path
-function mt:relativePathByUri(uri)
- local path = uric.decode(uri)
- if not path then
- return nil
- end
- local relate = fs.relative(path, self.root)
- return relate
-end
-
----@param uri uri
----@return path
-function mt:absolutePathByUri(uri)
- local path = uric.decode(uri)
- if not path then
- return nil
- end
- return fs.absolute(path)
-end
-
---- @param lsp LSP
---- @param name string
---- @return Workspace
-return function (lsp, name)
- local workspace = setmetatable({
- lsp = lsp,
- name = name,
- files = {},
- searched = {},
- loaded = {},
- pathMatcher = {}
- }, mt)
- workspace:compileLuaPath()
- workspace:listenLoadFile()
- return workspace
-end