summaryrefslogtreecommitdiff
path: root/server-beta
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2019-11-19 21:10:55 +0800
committer最萌小汐 <sumneko@hotmail.com>2019-11-19 21:10:55 +0800
commit3958723467b8ddb62e471c01c1bb38950ec3c28f (patch)
tree08dd3a8bdaf87a230327b8e0ddd09343bb0ed058 /server-beta
parent5d7bcc1fabcc97afd1151e3d49a22aedb0832659 (diff)
downloadlua-language-server-3958723467b8ddb62e471c01c1bb38950ec3c28f.zip
暂存
Diffstat (limited to 'server-beta')
-rw-r--r--server-beta/src/await.lua11
-rw-r--r--server-beta/src/core/diagnostics/init.lua2
-rw-r--r--server-beta/src/core/highlight.lua3
-rw-r--r--server-beta/src/searcher/eachRef.lua15
-rw-r--r--server-beta/src/searcher/getValue.lua454
-rw-r--r--server-beta/src/searcher/init.lua3
-rw-r--r--server-beta/src/searcher/isTrue.lua52
-rw-r--r--server-beta/src/searcher/searcher.lua6
-rw-r--r--server-beta/src/searcher/typeInference.lua253
-rw-r--r--server-beta/test/type_inference/init.lua4
10 files changed, 486 insertions, 317 deletions
diff --git a/server-beta/src/await.lua b/server-beta/src/await.lua
index c744e1be..5a960e96 100644
--- a/server-beta/src/await.lua
+++ b/server-beta/src/await.lua
@@ -31,18 +31,23 @@ end
--- 休眠一段时间
---@param time number
-function m.sleep(time, ...)
+function m.sleep(time, getVersion)
if not coroutine.isyieldable() then
if m.errorHandle then
m.errorHandle(debug.traceback('Cannot yield'))
end
return
end
+ local version = getVersion and getVersion()
local co = coroutine.running()
timer.wait(time, function ()
- return m.checkResult(co, coroutine.resume(co))
+ if version == (getVersion and getVersion()) then
+ return m.checkResult(co, coroutine.resume(co))
+ else
+ coroutine.close(co)
+ end
end)
- return coroutine.yield(...)
+ return coroutine.yield(getVersion)
end
--- 等待直到唤醒
diff --git a/server-beta/src/core/diagnostics/init.lua b/server-beta/src/core/diagnostics/init.lua
index 2724387d..c5abff25 100644
--- a/server-beta/src/core/diagnostics/init.lua
+++ b/server-beta/src/core/diagnostics/init.lua
@@ -16,7 +16,7 @@ local function check(uri, name, level, results)
results[#results+1] = result
end, name)
local passed = os.clock() - clock
- if passed >= 0.1 then
+ if passed >= 0.5 then
log.warn(('Diagnostics [%s] @ [%s] takes [%.3f] sec!'):format(name, uri, passed))
await.delay()
end
diff --git a/server-beta/src/core/highlight.lua b/server-beta/src/core/highlight.lua
index 9aaf62b8..7ae5333a 100644
--- a/server-beta/src/core/highlight.lua
+++ b/server-beta/src/core/highlight.lua
@@ -47,6 +47,9 @@ end
local function ofIndex(source, uri, callback)
local parent = source.parent
+ if not parent then
+ return
+ end
if parent.type == 'setindex'
or parent.type == 'getindex'
or parent.type == 'tableindex' then
diff --git a/server-beta/src/searcher/eachRef.lua b/server-beta/src/searcher/eachRef.lua
index 9a3fd9f0..d8e3bd6f 100644
--- a/server-beta/src/searcher/eachRef.lua
+++ b/server-beta/src/searcher/eachRef.lua
@@ -449,6 +449,21 @@ local function eachRef(source, callback)
asArg(source, callback)
end
+--- 判断2个对象是否拥有相同的引用
+function searcher.isSameRef(a, b)
+ local cache = searcher.cache.eachRef[a]
+ if cache then
+ -- 相同引用的source共享同一份cache
+ return cache == searcher.cache.eachRef[b]
+ else
+ return searcher.eachRef(a, function (info)
+ if info.source == b then
+ return true
+ end
+ end) or false
+ end
+end
+
--- 获取所有的引用
function searcher.eachRef(source, callback)
local cache = searcher.cache.eachRef[source]
diff --git a/server-beta/src/searcher/getValue.lua b/server-beta/src/searcher/getValue.lua
new file mode 100644
index 00000000..ae28b212
--- /dev/null
+++ b/server-beta/src/searcher/getValue.lua
@@ -0,0 +1,454 @@
+local searcher = require 'searcher.searcher'
+local guide = require 'parser.guide'
+local config = require 'config'
+
+local typeSort = {
+ ['boolean'] = 1,
+ ['string'] = 2,
+ ['integer'] = 3,
+ ['number'] = 4,
+ ['table'] = 5,
+ ['function'] = 6,
+ ['nil'] = math.maxinteger,
+}
+
+NIL = setmetatable({'<nil>'}, { __tostring = function () return 'nil' end })
+
+local function merge(a, b)
+ local t = {}
+ for i = 1, #a do
+ t[#t+1] = a[i]
+ end
+ for i = 1, #b do
+ t[#t+1] = b[i]
+ end
+ return t
+end
+
+local function checkLiteral(source)
+ if source.type == 'string' then
+ return {
+ type = 'string',
+ value = source[1],
+ source = source,
+ }
+ elseif source.type == 'nil' then
+ return {
+ type = 'nil',
+ value = NIL,
+ source = source,
+ }
+ elseif source.type == 'boolean' then
+ return {
+ type = 'boolean',
+ value = source[1],
+ source = source,
+ }
+ elseif source.type == 'number' then
+ if math.type(source[1]) == 'integer' then
+ return {
+ type = 'integer',
+ value = source[1],
+ source = source,
+ }
+ else
+ return {
+ type = 'number',
+ value = source[1],
+ source = source,
+ }
+ end
+ elseif source.type == 'table' then
+ return {
+ type = 'table',
+ source = source,
+ }
+ elseif source.type == 'function' then
+ return {
+ type = 'function',
+ source = source,
+ }
+ end
+end
+
+local function checkUnary(source)
+ if source.type ~= 'unary' then
+ return
+ end
+ local op = source.op
+ if op.type == 'not' then
+ local isTrue = searcher.isTrue(source[1])
+ local value = nil
+ if isTrue == true then
+ value = false
+ elseif isTrue == false then
+ value = true
+ end
+ return {
+ type = 'boolean',
+ value = value,
+ source = source,
+ }
+ elseif op.type == '#' then
+ return {
+ type = 'integer',
+ source = source,
+ }
+ elseif op.type == '~' then
+ local l = searcher.getLiteral(source[1], 'integer')
+ return {
+ type = 'integer',
+ value = l and ~l or nil,
+ source = source,
+ }
+ elseif op.type == '-' then
+ local v = searcher.getLiteral(source[1], 'integer')
+ if v then
+ return {
+ type = 'integer',
+ value = - v,
+ source = source,
+ }
+ end
+ v = searcher.getLiteral(source[1], 'number')
+ return {
+ type = 'number',
+ value = v and -v or nil,
+ source = source,
+ }
+ end
+end
+
+local function checkBinary(source)
+ if source.type ~= 'binary' then
+ return
+ end
+ local op = source.op
+ if op.type == 'and' then
+ local isTrue = searcher.checkTrue(source[1])
+ if isTrue == true then
+ return searcher.getValue(source[2])
+ elseif isTrue == false then
+ return searcher.getValue(source[1])
+ else
+ return merge(
+ searcher.getValue(source[1]),
+ searcher.getValue(source[2])
+ )
+ end
+ elseif op.type == 'or' then
+ local isTrue = searcher.checkTrue(source[1])
+ if isTrue == true then
+ return searcher.getValue(source[1])
+ elseif isTrue == false then
+ return searcher.getValue(source[2])
+ else
+ return merge(
+ searcher.getValue(source[1]),
+ searcher.getValue(source[2])
+ )
+ end
+ elseif op.type == '==' then
+ local value = searcher.isSameValue(source[1], source[2])
+ if value ~= nil then
+ return {
+ type = 'boolean',
+ value = value,
+ source = source,
+ }
+ end
+ local isSame = searcher.isSameRef(source[1], source[2])
+ if isSame == true then
+ value = true
+ else
+ value = nil
+ end
+ return {
+ type = 'boolean',
+ value = value,
+ source = source,
+ }
+ elseif op.type == '~=' then
+ local value = searcher.isSameValue(source[1], source[2])
+ if value ~= nil then
+ return {
+ type = 'boolean',
+ value = not value,
+ source = source,
+ }
+ end
+ local isSame = searcher.isSameRef(source[1], source[2])
+ if isSame == true then
+ value = false
+ else
+ value = nil
+ end
+ return {
+ type = 'boolean',
+ value = value,
+ source = source,
+ }
+ elseif op.type == '<=' then
+ elseif op.type == '>='
+ or op.type == '<'
+ or op.type == '>' then
+ return 'boolean'
+ end
+ if op.type == '|'
+ or op.type == '~'
+ or op.type == '&'
+ or op.type == '<<'
+ or op.type == '>>' then
+ return 'integer'
+ end
+ if op.type == '..' then
+ return 'string'
+ end
+ if op.type == '^'
+ or op.type == '/' then
+ return 'number'
+ end
+ -- 其他数学运算根据2侧的值决定,当2侧的值均为整数时返回整数
+ if op.type == '+'
+ or op.type == '-'
+ or op.type == '*'
+ or op.type == '%'
+ or op.type == '//' then
+ if hasType('integer', searcher.getValue(source[1]))
+ and hasType('integer', searcher.getValue(source[2])) then
+ return 'integer'
+ else
+ return 'number'
+ end
+ end
+end
+
+local function checkValue(source)
+ if source.value then
+ return searcher.getValue(source.value)
+ end
+end
+
+local function checkCall(result, source)
+ if not source.parent then
+ return
+ end
+ if source.parent.type ~= 'call' then
+ return
+ end
+ if source.parent.node == source then
+ merge(result, 'function')
+ return
+ end
+end
+
+local function checkNext(result, source)
+ local next = source.next
+ if not next then
+ return
+ end
+ if next.type == 'getfield'
+ or next.type == 'getindex'
+ or next.type == 'getmethod'
+ or next.type == 'setfield'
+ or next.type == 'setindex'
+ or next.type == 'setmethod' then
+ merge(result, 'table')
+ end
+end
+
+local function checkDef(result, source)
+ searcher.eachDef(source, function (info)
+ local src = info.source
+ local tp = searcher.getValue(src)
+ if tp then
+ merge(result, tp)
+ end
+ end)
+end
+
+local function typeInference(source)
+ local tp = checkLiteral(source)
+ or checkValue(source)
+ or checkUnary(source)
+ or checkBinary(source)
+ if tp then
+ return tp
+ end
+
+ local result = {}
+
+ checkCall(result, source)
+ checkNext(result, source)
+ checkDef(result, source)
+
+ return dump(result)
+end
+
+local function getValue(source)
+ local result = checkLiteral(source)
+ if result then
+ return { result }
+ end
+ local results = checkValue(source)
+ or checkUnary(source)
+ or checkBinary(source)
+ if results then
+ return results
+ end
+end
+
+function searcher.checkTrue(source)
+ local values = searcher.getValue(source)
+ if not values then
+ return
+ end
+ -- 当前认为的结果
+ local current
+ for i = 1, #values do
+ -- 新的结果
+ local new
+ local v = values[i]
+ if v.type == 'nil' then
+ new = false
+ elseif v.type == 'boolean' then
+ if v.value == true then
+ new = true
+ elseif v.value == false then
+ new = false
+ end
+ end
+ if new ~= nil then
+ if current == nil then
+ current = new
+ else
+ -- 如果2个结果完全相反,则返回 nil 表示不确定
+ if new ~= current then
+ return nil
+ end
+ end
+ end
+ end
+ return current
+end
+
+--- 拥有某个类型的值
+function searcher.eachValueType(source, type, callback)
+ local values = searcher.getValue(source)
+ if not values then
+ return
+ end
+ for i = 1, #values do
+ local v = values[i]
+ if v.type == type then
+ local res = callback(v)
+ if res ~= nil then
+ return res
+ end
+ end
+ end
+end
+
+--- 获取特定类型的字面量值
+function searcher.getLiteral(source, type)
+ local values = searcher.getValue(source)
+ if not values then
+ return nil
+ end
+ for i = 1, #values do
+ local v = values[i]
+ if v.type == type and v.value ~= nil then
+ return v.value
+ end
+ end
+ return nil
+end
+
+function searcher.isSameValue(a, b)
+ local valuesA = searcher.getValue(a)
+ local valuesB = searcher.getValue(b)
+ if valuesA == valuesB and valuesA ~= nil then
+ return true
+ end
+ local values = {}
+ for i = 1, #valuesA do
+ local value = valuesA[i]
+ local literal = value.value
+ if literal then
+ values[literal] = false
+ end
+ end
+ for i = 1, #valuesB do
+ local value = valuesA[i]
+ local literal = value.value
+ if literal then
+ if values[literal] == nil then
+ return false
+ end
+ values[literal] = true
+ end
+ end
+ for k, v in pairs(values) do
+ if v == false then
+ return false
+ end
+ end
+ return true
+end
+
+function searcher.typeInference(source)
+ local values = searcher.getValue(source)
+ if not values then
+ return 'any'
+ end
+ local types = {}
+ for _ = 1, #values do
+ local tp = values.type
+ if not types[tp] then
+ types[tp] = true
+ types[#types+1] = tp
+ end
+ end
+ if #types == 0 then
+ return 'any'
+ end
+ if #types == 1 then
+ return types[1]
+ end
+ table.sort(types, function (a, b)
+ local sa = typeSort[a]
+ local sb = typeSort[b]
+ if sa and sb then
+ return sa < sb
+ end
+ if not sa and not sb then
+ return a < b
+ end
+ if sa and not sb then
+ return true
+ end
+ if not sa and sb then
+ return false
+ end
+ return false
+ end)
+ return table.concat(types, '|')
+end
+
+function searcher.getValue(source)
+ if not source then
+ return
+ end
+ local cache = searcher.cache.getValue[source]
+ if cache ~= nil then
+ return cache
+ end
+ local unlock = searcher.lock('getValue', source)
+ if not unlock then
+ return
+ end
+ cache = getValue(source) or false
+ searcher.cache.getValue[source] = cache
+ unlock()
+ return cache
+end
diff --git a/server-beta/src/searcher/init.lua b/server-beta/src/searcher/init.lua
index 1734c2ea..65753ccd 100644
--- a/server-beta/src/searcher/init.lua
+++ b/server-beta/src/searcher/init.lua
@@ -6,6 +6,5 @@ require 'searcher.getGlobals'
require 'searcher.getLinks'
require 'searcher.getGlobal'
require 'searcher.getLibrary'
-require 'searcher.typeInference'
-require 'searcher.isTrue'
+require 'searcher.getValue'
return searcher
diff --git a/server-beta/src/searcher/isTrue.lua b/server-beta/src/searcher/isTrue.lua
deleted file mode 100644
index ba825d43..00000000
--- a/server-beta/src/searcher/isTrue.lua
+++ /dev/null
@@ -1,52 +0,0 @@
-local searcher = require 'searcher.searcher'
-
-local function checkLiteral(source)
- if source.type == 'boolean' then
- if source[1] == true then
- return 'true'
- else
- return 'false'
- end
- end
- if source.type == 'string' then
- return 'true'
- end
- if source.type == 'number' then
- return 'true'
- end
- if source.type == 'table' then
- return 'true'
- end
- if source.type == 'function' then
- return 'true'
- end
- if source.type == 'nil' then
- return 'false'
- end
-end
-
-local function isTrue(source)
- local res = checkLiteral(source)
- if res then
- return res
- end
- return 'unknown'
-end
-
-function searcher.isTrue(source)
- if not source then
- return
- end
- local cache = searcher.cache.isTrue[source]
- if cache ~= nil then
- return cache
- end
- local unlock = searcher.lock('isTrue', source)
- if not unlock then
- return
- end
- cache = isTrue(source) or false
- searcher.cache.isTrue[source] = cache
- unlock()
- return cache
-end
diff --git a/server-beta/src/searcher/searcher.lua b/server-beta/src/searcher/searcher.lua
index 3e12430b..e47e8c54 100644
--- a/server-beta/src/searcher/searcher.lua
+++ b/server-beta/src/searcher/searcher.lua
@@ -64,8 +64,7 @@ function m.refreshCache()
getGlobal = {},
specialName = {},
getLibrary = {},
- typeInfer = {},
- isTrue = {},
+ getValue = {},
specials = nil,
}
m.locked = {
@@ -74,8 +73,7 @@ function m.refreshCache()
getGlobals = {},
getLinks = {},
getLibrary = {},
- typeInfer = {},
- isTrue = {},
+ getValue = {},
}
m.cacheTracker[m.cache] = true
end
diff --git a/server-beta/src/searcher/typeInference.lua b/server-beta/src/searcher/typeInference.lua
deleted file mode 100644
index 56f4cc98..00000000
--- a/server-beta/src/searcher/typeInference.lua
+++ /dev/null
@@ -1,253 +0,0 @@
-local searcher = require 'searcher.searcher'
-local guide = require 'parser.guide'
-local config = require 'config'
-
-local typeSort = {
- ['boolean'] = 1,
- ['string'] = 2,
- ['integer'] = 3,
- ['number'] = 4,
- ['table'] = 5,
- ['function'] = 6,
- ['nil'] = math.maxinteger,
-}
-
-local function merge(result, tp)
- if result[tp] then
- return
- end
- if tp:find('|', 1, true) then
- for sub in tp:gmatch '[^|]+' do
- if not result[sub] then
- result[sub] = true
- result[#result+1] = sub
- end
- end
- else
- result[tp] = true
- result[#result+1] = tp
- end
-end
-
-local function hasType(tp, target)
- if not target then
- return false
- end
- for sub in target:gmatch '[^|]+' do
- if sub == tp then
- return true
- end
- end
- return false
-end
-
-local function dump(result)
- if #result <= 1 then
- return result[1]
- end
- table.sort(result, function (a, b)
- local sa = typeSort[a]
- local sb = typeSort[b]
- if sa and sb then
- return sa < sb
- end
- if not sa and not sb then
- return a < b
- end
- if sa and not sb then
- return true
- end
- if not sa and sb then
- return false
- end
- return false
- end)
- return table.concat(result, '|')
-end
-
-local function checkLiteral(source)
- if source.type == 'string' then
- return 'string'
- elseif source.type == 'nil' then
- return 'nil'
- elseif source.type == 'boolean' then
- return 'boolean'
- elseif source.type == 'number' then
- if math.type(source[1]) == 'integer' then
- return 'integer'
- else
- return 'number'
- end
- elseif source.type == 'table' then
- return 'table'
- elseif source.type == 'function' then
- return 'function'
- end
-end
-
-local function checkUnary(source)
- if source.type ~= 'unary' then
- return
- end
- local op = source.op
- if op.type == 'not' then
- return 'boolean'
- elseif op.type == '#'
- or op.type == '~' then
- return 'integer'
- elseif op.type == '-' then
- if hasType('integer', searcher.typeInference(source[1])) then
- return 'integer'
- else
- return 'number'
- end
- end
-end
-
-local function checkBinary(source)
- if source.type ~= 'binary' then
- return
- end
- local op = source.op
- if op.type == 'and' then
- local isTrue = searcher.isTrue(source[1])
- if isTrue == 'true' then
- return searcher.typeInference(source[2])
- elseif isTrue == 'false' then
- return searcher.typeInference(source[1])
- else
- local result = {}
- merge(result, searcher.typeInference(source[1]))
- merge(result, searcher.typeInference(source[2]))
- return dump(result)
- end
- end
- if op.type == 'or' then
- local isTrue = searcher.isTrue(source[1])
- if isTrue == 'true' then
- return searcher.typeInference(source[1])
- elseif isTrue == 'false' then
- return searcher.typeInference(source[2])
- else
- local result = {}
- merge(result, searcher.typeInference(source[1]))
- merge(result, searcher.typeInference(source[2]))
- return dump(result)
- end
- end
- if op.type == '=='
- or op.type == '~='
- or op.type == '<='
- or op.type == '>='
- or op.type == '<'
- or op.type == '>' then
- return 'boolean'
- end
- if op.type == '|'
- or op.type == '~'
- or op.type == '&'
- or op.type == '<<'
- or op.type == '>>' then
- return 'integer'
- end
- if op.type == '..' then
- return 'string'
- end
- if op.type == '^'
- or op.type == '/' then
- return 'number'
- end
- -- 其他数学运算根据2侧的值决定,当2侧的值均为整数时返回整数
- if op.type == '+'
- or op.type == '-'
- or op.type == '*'
- or op.type == '%'
- or op.type == '//' then
- if hasType('integer', searcher.typeInference(source[1]))
- and hasType('integer', searcher.typeInference(source[2])) then
- return 'integer'
- else
- return 'number'
- end
- end
-end
-
-local function checkValue(source)
- if source.value then
- return searcher.typeInference(source.value)
- end
-end
-
-local function checkCall(result, source)
- if not source.parent then
- return
- end
- if source.parent.type ~= 'call' then
- return
- end
- if source.parent.node == source then
- merge(result, 'function')
- return
- end
-end
-
-local function checkNext(result, source)
- local next = source.next
- if not next then
- return
- end
- if next.type == 'getfield'
- or next.type == 'getindex'
- or next.type == 'getmethod'
- or next.type == 'setfield'
- or next.type == 'setindex'
- or next.type == 'setmethod' then
- merge(result, 'table')
- end
-end
-
-local function checkDef(result, source)
- searcher.eachDef(source, function (info)
- local src = info.source
- local tp = searcher.typeInference(src)
- if tp then
- merge(result, tp)
- end
- end)
-end
-
-local function typeInference(source)
- local tp = checkLiteral(source)
- or checkValue(source)
- or checkUnary(source)
- or checkBinary(source)
- if tp then
- return tp
- end
-
- local result = {}
-
- checkCall(result, source)
- checkNext(result, source)
- checkDef(result, source)
-
- return dump(result)
-end
-
-function searcher.typeInference(source)
- if not source then
- return
- end
- local cache = searcher.cache.typeInfer[source]
- if cache ~= nil then
- return cache
- end
- local unlock = searcher.lock('typeInfer', source)
- if not unlock then
- return
- end
- cache = typeInference(source) or false
- searcher.cache.typeInfer[source] = cache
- unlock()
- return cache
-end
diff --git a/server-beta/test/type_inference/init.lua b/server-beta/test/type_inference/init.lua
index 4a9f30aa..fd98e544 100644
--- a/server-beta/test/type_inference/init.lua
+++ b/server-beta/test/type_inference/init.lua
@@ -30,7 +30,7 @@ function TEST(wanted)
files.setText('', newScript)
local source = getSource(pos)
assert(source)
- local result = searcher.typeInference(source) or 'any'
+ local result = searcher.getValue(source) or 'any'
assert(wanted == result)
end
end
@@ -113,7 +113,7 @@ TEST 'integer' [[
<?x?> = ~ y
]]
-TEST 'number' [[
+TEST 'integer' [[
local a = true
local b = 1
<?x?> = a and b