summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/main.lua2
-rw-r--r--server/src/core/completion.lua83
-rw-r--r--server/src/core/definition.lua309
-rw-r--r--server/src/core/diagnostics.lua40
-rw-r--r--server/src/core/hover/hover.lua5
-rw-r--r--server/src/core/implementation.lua21
-rw-r--r--server/src/core/references.lua17
-rw-r--r--server/src/emmy/class.lua20
-rw-r--r--server/src/emmy/manager.lua53
-rw-r--r--server/src/emmy/type.lua55
-rw-r--r--server/src/emmy/typeUnit.lua51
-rw-r--r--server/src/method/initialize.lua1
-rw-r--r--server/src/method/textDocument/completion.lua54
-rw-r--r--server/src/method/textDocument/definition.lua40
-rw-r--r--server/src/method/textDocument/publishDiagnostics.lua5
-rw-r--r--server/src/method/textDocument/references.lua20
-rw-r--r--server/src/timer.lua4
-rw-r--r--server/src/vm/emmy.lua57
-rw-r--r--server/src/vm/local.lua9
-rw-r--r--server/src/vm/value.lua39
-rw-r--r--server/src/vm/vm.lua46
-rw-r--r--server/test/completion/init.lua122
-rw-r--r--server/test/crossfile/completion.lua31
-rw-r--r--server/test/crossfile/definition.lua99
-rw-r--r--server/test/crossfile/references.lua2
-rw-r--r--server/test/definition/bug.lua2
-rw-r--r--server/test/definition/emmy.lua63
-rw-r--r--server/test/definition/function.lua2
-rw-r--r--server/test/definition/init.lua4
-rw-r--r--server/test/definition/method.lua3
-rw-r--r--server/test/definition/set.lua2
-rw-r--r--server/test/definition/table.lua32
-rw-r--r--server/test/diagnostics/init.lua53
-rw-r--r--server/test/hover/init.lua78
-rw-r--r--server/test/references/init.lua26
35 files changed, 955 insertions, 495 deletions
diff --git a/server/main.lua b/server/main.lua
index ac7860c5..685fce01 100644
--- a/server/main.lua
+++ b/server/main.lua
@@ -22,7 +22,7 @@ local function tryDebugger()
log.info('Debugger startup, listen port: 11411')
end
---pcall(tryDebugger)
+pcall(tryDebugger)
require 'utility'
require 'global_protect'
diff --git a/server/src/core/completion.lua b/server/src/core/completion.lua
index 4a487441..d0da95cd 100644
--- a/server/src/core/completion.lua
+++ b/server/src/core/completion.lua
@@ -397,6 +397,14 @@ local function searchEmmyKeyword(vm, source, word, callback)
end
end
+local function searchEmmyClass(vm, source, word, callback)
+ vm.emmyMgr:eachClass(function (class)
+ if matchKey(word, class:getName()) then
+ callback(class:getName(), class:getSource(), CompletionItemKind.Class)
+ end
+ end)
+end
+
local function searchSource(vm, source, word, callback)
if source.type == 'keyword' then
searchAsKeyowrd(vm, source, word, callback)
@@ -430,6 +438,9 @@ local function searchSource(vm, source, word, callback)
searchEmmyKeyword(vm, source, word, callback)
return
end
+ if source:get 'target class' then
+ searchEmmyClass(vm, source, word, callback)
+ end
end
local function searchInRequire(vm, select, source, callback)
@@ -650,11 +661,9 @@ local function makeList(source, pos, word)
end, list
end
-local function searchToclose(text, word, callback, pos)
- if #word > 0 then
- pos = pos - 1
- end
- if text:sub(pos, pos) ~= '*' then
+local function searchToclose(text, source, word, callback)
+ local pos = source.start
+ if text:sub(pos-1, pos-1) ~= '*' then
return false
end
if not matchKey(word, 'toclose') then
@@ -685,7 +694,62 @@ local function keywordSource(vm, word, pos)
}
end
-return function (vm, text, pos, word, oldText)
+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 == '|' 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 = findSource(vm, pos, filter)
+ if source then
+ return source, pos, word
+ end
+ pos = findStartPos(pos, text)
+ source = findSource(vm, pos, filter)
+ or keywordSource(vm, word, pos)
+ return source, pos, word
+end
+
+return function (vm, text, pos, oldText)
local filter = {
['name'] = true,
['string'] = true,
@@ -695,10 +759,7 @@ return function (vm, text, pos, word, oldText)
['emmyIncomplete'] = true,
['call'] = true,
}
- local source = findSource(vm, pos, filter)
- or findSource(vm, pos-1, filter)
- or findSource(vm, pos+1, filter)
- or keywordSource(vm, word, pos)
+ local source, pos, word = getSource(vm, pos, text, filter)
if not source then
return nil
end
@@ -709,7 +770,7 @@ return function (vm, text, pos, word, oldText)
end
end
local callback, list = makeList(source, pos, word)
- if searchToclose(text, word, callback, pos) then
+ if searchToclose(text, source, word, callback) then
return list
end
searchSpecial(vm, source, word, callback, pos)
diff --git a/server/src/core/definition.lua b/server/src/core/definition.lua
index a7296bfb..f096fb99 100644
--- a/server/src/core/definition.lua
+++ b/server/src/core/definition.lua
@@ -1,9 +1,11 @@
-local function parseValueSimily(vm, source, lsp)
+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
- local positions = {}
vm:eachSource(function (other)
if other == source then
goto CONTINUE
@@ -11,210 +13,211 @@ local function parseValueSimily(vm, source, lsp)
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,
- }
+ 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)
- 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
+local function parseLocal(callback, vm, source)
+ ---@type Local
+ local loc = source:bindLocal()
+ loc:eachInfo(function (info, src)
+ if Mode == 'definition' then
+ if info.type == 'set' or info.type == 'local' then
+ if src.id < source.id then
+ callback(src)
+ 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)
- if #positions > 0 then
- return positions
- end
+end
+local function parseValueByValue(callback, vm, source, value, isGlobal)
value:eachInfo(function (info, src)
- if info.type == 'set' and src.uri == value.uri then
- positions[#positions+1] = {
- src.start,
- src.finish,
- value.uri,
- }
+ if Mode == 'definition' then
+ if info.type == 'set' or info.type == 'local' or info.type == 'return' then
+ if vm.uri == src:getUri() then
+ if isGlobal or source.id > src.id then
+ callback(src)
+ end
+ elseif value.uri == src:getUri() then
+ callback(src)
+ 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)
- if #positions > 0 then
- return positions
- end
+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,
- }
+local function parseValue(callback, vm, source)
+ local value = source:bindValue()
+ local isGlobal
+ if value then
+ isGlobal = value:isGlobal()
+ parseValueByValue(callback, vm, source, value, isGlobal)
+ 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
+ parseValueByValue(callback, vm, source, class:getValue(), isGlobal)
+ end
+ end)
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
+ 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 result = parseValueSimily(destVM, source, lsp)
- if result then
- for _, position in ipairs(result) do
- positions[#positions+1] = position
- position[3] = value.uri
+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
- if #positions > 0 then
- return positions
- end
+ end)
+end
- return nil
+local function jumpUri(callback, vm, source)
+ local uri = source:get 'target uri'
+ callback {
+ start = 0,
+ finish = 0,
+ uri = uri
+ }
end
-local function parseLocal(vm, source, lsp)
- local positions = {}
- local loc = source:bindLocal()
- local locSource = loc:getSource()
- if locSource:get 'arg' then
- positions[#positions+1] = {
- locSource.start,
- locSource.finish,
- locSource:getUri(),
- }
- return positions
- end
- local value = source:bindValue()
- if value and value.uri ~= '' and value.uri ~= vm.uri then
- local positions = parseValueCrossFile(vm, source, lsp)
- if positions and #positions > 0 then
- return positions
+local function parseClass(callback, vm, source)
+ local className = source:get 'target class'
+ vm.emmyMgr:eachClass(className, function (class)
+ if Mode == 'definition' then
+ if class.type == 'emmy.class' then
+ local src = class:getSource()
+ callback(src)
+ end
+ elseif Mode == 'reference' then
+ if class.type == 'emmy.class' or class.type == 'emmy.typeUnit' then
+ local src = class:getSource()
+ callback(src)
+ end
end
+ end)
+end
+
+local function parseFunction(callback, vm, source)
+ if 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
- positions[#positions+1] = {
- locSource.start,
- locSource.finish,
- locSource:getUri(),
- }
- if #positions == 0 then
- return nil
- end
- return positions
end
-local function parseValue(vm, source, lsp)
- local positions = {}
+local function makeList(source)
+ local list = {}
local mark = {}
-
- local function callback(src)
- if source == src then
- return
+ return list, function (src)
+ if Mode == 'definition' then
+ if source == src then
+ return
+ end
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] = {
+ list[#list+1] = {
src.start,
src.finish,
- uri,
+ src.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)
- 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
-return function (vm, source, lsp)
+return function (vm, pos, mode)
+ local source = findSource(vm, pos)
if not source then
return nil
end
+ Mode = mode
+ local list, callback = makeList(source)
+ local isGlobal
if source:bindLocal() then
- return parseLocal(vm, source, lsp)
+ parseLocal(callback, vm, source)
end
if source:bindValue() then
- return parseValue(vm, source, lsp)
- or parseValueSimily(vm, source, lsp)
+ isGlobal = parseValue(callback, vm, source)
end
if source:bindLabel() then
- return parseLabel(vm, source:bindLabel(), lsp)
+ parseLabel(callback, vm, source:bindLabel())
+ end
+ if source:bindFunction() then
+ parseFunction(callback, vm, source)
end
if source:get 'target uri' then
- return jumpUri(vm, source, lsp)
+ jumpUri(callback, vm, source)
end
if source:get 'in index' then
- return parseValue(vm, source, lsp)
- or parseValueSimily(vm, source, lsp)
+ isGlobal = parseValue(callback, vm, source)
+ end
+ if source:get 'target class' then
+ parseClass(callback, vm, source)
end
+
+ if #list == 0 then
+ parseValueSimily(callback, vm, source)
+ end
+
+ return list, isGlobal
end
diff --git a/server/src/core/diagnostics.lua b/server/src/core/diagnostics.lua
index d49f8da2..9f46d724 100644
--- a/server/src/core/diagnostics.lua
+++ b/server/src/core/diagnostics.lua
@@ -410,6 +410,9 @@ function mt:checkEmmyClass(source, callback)
local name = class:getName()
local related = {}
self.vm.emmyMgr:eachClass(name, function (class)
+ if class.type ~= 'emmy.class' then
+ return
+ end
local src = class:getSource()
if src ~= source then
related[#related+1] = {
@@ -428,7 +431,9 @@ function mt:checkEmmyClass(source, callback)
return
end
local parent = self.vm.emmyMgr:eachClass(extends, function (parent)
- return 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)
@@ -439,10 +444,6 @@ function mt:checkEmmyClass(source, callback)
local related = {}
local current = class
for _ = 1, 10 do
- if parent:getName() == class:getName() then
- callback(source.start, source.finish, lang.script.DIAG_CYCLIC_EXTENDS, related)
- break
- end
local extends = current.extends
if not extends then
break
@@ -452,13 +453,32 @@ function mt:checkEmmyClass(source, callback)
finish = current:getSource().finish,
uri = current:getSource().uri,
}
- current = parent
- parent = self.vm.emmyMgr:eachClass(extends, function (parent)
- return parent
+ current = self.vm.emmyMgr:eachClass(extends, function (parent)
+ if parent.type == 'emmy.class' then
+ return parent
+ end
end)
- if not parent then
+ 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
+ local name = tpsource[1]
+ local class = self.vm.emmyMgr:eachClass(name, function (class)
+ if class.type == 'emmy.class' then
+ return class
+ end
+ end)
+ if not class then
+ callback(tpsource.start, tpsource.finish, lang.script.DIAG_UNDEFINED_CLASS)
+ end
end
end
@@ -466,6 +486,8 @@ function mt:searchEmmyLua(callback)
self.vm:eachSource(function (source)
if source.type == 'emmyClass' then
self:checkEmmyClass(source, callback)
+ elseif source.type == 'emmyType' then
+ self:checkEmmyType(source, callback)
end
end)
end
diff --git a/server/src/core/hover/hover.lua b/server/src/core/hover/hover.lua
index a7fcca19..058ddd4a 100644
--- a/server/src/core/hover/hover.lua
+++ b/server/src/core/hover/hover.lua
@@ -17,6 +17,11 @@ local OriginTypes = {
}
local function findClass(value)
+ -- 检查是否有emmy
+ local emmy = value:getEmmy()
+ if emmy then
+ return emmy:getType()
+ end
-- 检查对象元表
local metaValue = value:getMetaTable()
if not metaValue then
diff --git a/server/src/core/implementation.lua b/server/src/core/implementation.lua
index 1b8006b1..450b9a0c 100644
--- a/server/src/core/implementation.lua
+++ b/server/src/core/implementation.lua
@@ -166,6 +166,20 @@ local function jumpUri(vm, source, lsp)
return positions
end
+local function parseClass(vm, source)
+ local className = source:get 'target 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
@@ -180,4 +194,11 @@ return function (vm, source, lsp)
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 'target class' then
+ return parseClass(vm, source)
+ end
end
diff --git a/server/src/core/references.lua b/server/src/core/references.lua
index 10d8b2df..33b38fec 100644
--- a/server/src/core/references.lua
+++ b/server/src/core/references.lua
@@ -1,13 +1,13 @@
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)
- return
end
if source:bindLocal() then
local loc = source:bindLocal()
@@ -22,7 +22,6 @@ local function parseResult(vm, source, declarat, callback)
callback(src)
end
end)
- return
end
if source:bindFunction() then
if declarat then
@@ -40,6 +39,9 @@ local function parseResult(vm, source, declarat, callback)
callback(src)
end
end)
+ if source:bindValue():isGlobal() then
+ isGlobal = true
+ end
end
local parent = source:get 'parent'
if parent then
@@ -51,6 +53,13 @@ local function parseResult(vm, source, declarat, callback)
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)
@@ -60,7 +69,7 @@ return function (vm, pos, declarat)
end
local positions = {}
local mark = {}
- parseResult(vm, source, declarat, function (src)
+ local isGlobal = parseResult(vm, source, declarat, function (src)
if mark[src] then
return
end
@@ -78,5 +87,5 @@ return function (vm, pos, declarat)
uri,
}
end)
- return positions
+ return positions, isGlobal
end
diff --git a/server/src/emmy/class.lua b/server/src/emmy/class.lua
index f1101e91..84854fc5 100644
--- a/server/src/emmy/class.lua
+++ b/server/src/emmy/class.lua
@@ -1,5 +1,6 @@
local listMgr = require 'vm.list'
+---@class EmmyClass
local mt = {}
mt.__index = mt
mt.type = 'emmy.class'
@@ -16,11 +17,28 @@ function mt:getSource()
return listMgr.get(self.source)
end
-return function (source)
+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
+
+return function (manager, source)
local self = setmetatable({
name = source[1][1],
source = source.id,
extends = source[2] and source[2][1],
+ _manager = manager,
}, mt)
return self
end
diff --git a/server/src/emmy/manager.lua b/server/src/emmy/manager.lua
index 99b46420..b86da6d2 100644
--- a/server/src/emmy/manager.lua
+++ b/server/src/emmy/manager.lua
@@ -1,5 +1,7 @@
local listMgr = require 'vm.list'
local newClass = require 'emmy.class'
+local newType = require 'emmy.type'
+local newTypeUnit = require 'emmy.typeUnit'
local mt = {}
mt.__index = mt
@@ -26,7 +28,7 @@ function mt:flushClass(name)
list.version = version
end
-function mt:eachClass(name, callback)
+function mt:eachClassByName(name, callback)
self:flushClass(name)
local list = self._class[name]
if not list then
@@ -42,27 +44,64 @@ function mt:eachClass(name, callback)
end
end
-function mt:addClass(source)
- local className = source[1][1]
- self:flushClass(className)
- local list = self._class[className]
+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[className] = list
+ self._class[name] = list
end
- list[source.id] = newClass(source)
+ return list
+end
+
+function mt:addClass(source)
+ local className = source[1][1]
+ local list = self:getClass(className)
+ list[source.id] = newClass(self, source)
return list[source.id]
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]
+ local list = self:getClass(className)
+ typeUnit:setParent(typeObj)
+ list[source.id] = typeUnit
+ typeObj._childs[i] = typeUnit
+ obj:set('emmy.typeUnit', typeUnit)
+ end
+ return typeObj
+end
+
function mt:remove()
end
return function ()
+ ---@class emmyMgr
local self = setmetatable({
_class = {},
+ _type = {},
}, mt)
return self
end
diff --git a/server/src/emmy/type.lua b/server/src/emmy/type.lua
new file mode 100644
index 00000000..b7e451b2
--- /dev/null
+++ b/server/src/emmy/type.lua
@@ -0,0 +1,55 @@
+local listMgr = require 'vm.list'
+
+local function buildName(source)
+ local names = {}
+ for i, type in ipairs(source) do
+ names[i] = type[1]
+ 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/server/src/emmy/typeUnit.lua b/server/src/emmy/typeUnit.lua
new file mode 100644
index 00000000..73d7ea6b
--- /dev/null
+++ b/server/src/emmy/typeUnit.lua
@@ -0,0 +1,51 @@
+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/server/src/method/initialize.lua b/server/src/method/initialize.lua
index 82ff9640..5478e744 100644
--- a/server/src/method/initialize.lua
+++ b/server/src/method/initialize.lua
@@ -13,7 +13,6 @@ return function (lsp)
capabilities = {
hoverProvider = true,
definitionProvider = true,
- implementationProvider = true,
referencesProvider = true,
renameProvider = true,
documentSymbolProvider = true,
diff --git a/server/src/method/textDocument/completion.lua b/server/src/method/textDocument/completion.lua
index 7f60708b..1b9aa73f 100644
--- a/server/src/method/textDocument/completion.lua
+++ b/server/src/method/textDocument/completion.lua
@@ -16,70 +16,27 @@ local function posToRange(lines, start, finish)
}
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 == ':' then
- res = i
- 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 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 word = findWord(position, text)
- local startPos = findStartPos(position, text)
local vm = lsp:getVM(uri)
- if not vm or not startPos then
+ if not vm then
vm = lsp:loadVM(uri)
if not vm then
return nil
end
end
- startPos = startPos or position
- local items = core.completion(vm, text, startPos, word, oldText)
+ 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
- startPos = startPos or position
- items = core.completion(vm, text, startPos, word)
+ items = core.completion(vm, text, position)
if not items or #items == 0 then
return nil
end
@@ -93,16 +50,13 @@ local function finishCompletion(lsp, params, lines)
local text = lsp:getText(uri)
-- lua是从1开始的,因此都要+1
local position = lines:positionAsChar(params.position.line + 1, params.position.character)
- local word = findWord(position, text)
- local startPos = findStartPos(position, text)
local vm = lsp:loadVM(uri)
if not vm then
return nil
end
- startPos = startPos or position
- local items = core.completion(vm, text, startPos, word)
+ local items = core.completion(vm, text, position)
if not items or #items == 0 then
return nil
end
diff --git a/server/src/method/textDocument/definition.lua b/server/src/method/textDocument/definition.lua
index fe8d38a5..d33fd739 100644
--- a/server/src/method/textDocument/definition.lua
+++ b/server/src/method/textDocument/definition.lua
@@ -1,15 +1,5 @@
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)
@@ -24,11 +14,9 @@ local function findResult(lsp, params)
return nil
end
- checkWorkSpaceComplete(lsp, source)
-
- local positions = core.definition(vm, source, lsp)
+ local positions, isGlobal = core.definition(vm, position, 'definition', lsp)
if not positions then
- return nil
+ return nil, isGlobal
end
local locations = {}
@@ -70,10 +58,10 @@ local function findResult(lsp, params)
end
if #locations == 0 then
- return nil
+ return nil, isGlobal
end
- return locations
+ return locations, isGlobal
end
local LastTask
@@ -83,27 +71,17 @@ return function (lsp, params)
LastTask:remove()
LastTask = nil
end
- local result = findResult(lsp, params)
- if result then
- return result
- end
return function (response)
- local count = 0
+ local clock = os.clock()
LastTask = ac.loop(0.1, function ()
- local result = findResult(lsp, params)
- if result then
- response(result)
- LastTask:remove()
- LastTask = nil
- return
- end
- count = count + 1
- if lsp:isWaitingCompile() and count < 10 then
+ local result, isGlobal = findResult(lsp, params)
+ if isGlobal and lsp:isWaitingCompile() and os.clock() - clock < 1 then
return
end
- response(nil)
+ response(result)
LastTask:remove()
LastTask = nil
end)
+ LastTask:onTimer()
end
end
diff --git a/server/src/method/textDocument/publishDiagnostics.lua b/server/src/method/textDocument/publishDiagnostics.lua
index fbe65bb3..3a4e4658 100644
--- a/server/src/method/textDocument/publishDiagnostics.lua
+++ b/server/src/method/textDocument/publishDiagnostics.lua
@@ -43,7 +43,7 @@ local function getRange(start, finish, lines)
}
end
-local function createInfo(data, lines)
+local function createInfo(lsp, data, lines)
local diagnostic = {
source = lang.script.DIAG_DIAGNOSTICS,
range = getRange(data.start, data.finish, lines),
@@ -54,6 +54,7 @@ local function createInfo(data, lines)
if data.related then
local related = {}
for i, info in ipairs(data.related) do
+ local _, lines = lsp:getVM(info.uri)
local message = info.message
if not message then
local start_line = lines:rowcol(info.start)
@@ -144,7 +145,7 @@ return function (lsp, params)
if vm then
local datas = core.diagnostics(vm, lines, uri)
for _, data in ipairs(datas) do
- diagnostics[#diagnostics+1] = createInfo(data, lines)
+ diagnostics[#diagnostics+1] = createInfo(lsp, data, lines)
end
end
if errs then
diff --git a/server/src/method/textDocument/references.lua b/server/src/method/textDocument/references.lua
index d969e919..ca4bed87 100644
--- a/server/src/method/textDocument/references.lua
+++ b/server/src/method/textDocument/references.lua
@@ -4,7 +4,7 @@ local LastTask
local function findReferences(lsp, uri, position, declarat)
local vm = lsp:getVM(uri)
- local positions = core.references(vm, position, declarat)
+ local positions, isGlobal = core.definition(vm, position, 'reference')
if not positions then
return nil
end
@@ -48,10 +48,10 @@ local function findReferences(lsp, uri, position, declarat)
end
if #locations == 0 then
- return nil
+ return nil, isGlobal
end
- return locations
+ return locations, isGlobal
end
return function (lsp, params)
@@ -71,20 +71,16 @@ return function (lsp, params)
local position = lines:positionAsChar(params.position.line + 1, params.position.character)
return function (response)
+ local clock = os.clock()
LastTask = ac.loop(0.1, function (t)
- local positions = findReferences(lsp, uri, position, declarat)
- if positions then
- response(positions)
- t:remove()
- LastTask = nil
- return
- end
- if lsp:isWaitingCompile() then
+ local positions, isGlobal = findReferences(lsp, uri, position, declarat)
+ if isGlobal and lsp:isWaitingCompile() and os.clock() - clock < 5 then
return
end
- response(nil)
+ response(positions)
t:remove()
LastTask = nil
end)
+ LastTask:onTimer()
end
end
diff --git a/server/src/timer.lua b/server/src/timer.lua
index 0ae5094f..f477dccf 100644
--- a/server/src/timer.lua
+++ b/server/src/timer.lua
@@ -193,6 +193,10 @@ 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),
diff --git a/server/src/vm/emmy.lua b/server/src/vm/emmy.lua
index 6e5c2b13..1cd52a8a 100644
--- a/server/src/vm/emmy.lua
+++ b/server/src/vm/emmy.lua
@@ -1,15 +1,70 @@
local mt = require 'vm.manager'
+function mt:clearEmmy()
+ self._emmy = 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
+ elseif tp == 'emmyParam' then
+ elseif tp == 'emmyReturn' then
+ elseif tp == 'emmyField' then
+ elseif tp == 'emmyGeneric' then
+ elseif tp == 'emmyVararg' then
+ elseif tp == 'emmyLanguage' then
+ elseif tp == 'emmyArrayType' then
+ elseif tp == 'emmyTableType' then
+ elseif tp == 'emmyFunctionType' then
+ elseif tp == 'emmySee' then
+ elseif tp == 'emmyIncomplete' then
+ self:doEmmyIncomplete(action)
+ end
+end
+
+function mt:getEmmy()
+ local emmy = self._emmy
+ self._emmy = nil
+ return emmy
+end
+
function mt:doEmmyClass(action)
+ ---@type emmyMgr
local emmyMgr = self.emmyMgr
self:instantSource(action)
+ self:instantSource(action[1])
local class = emmyMgr:addClass(action)
- self.emmy = class
+ action:set('target class', class:getName())
+ action[1]:set('target class', class:getName())
+ local extends = action[2]
+ if extends then
+ self:instantSource(extends)
+ extends:set('target 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:doEmmyType(action)
+ ---@type emmyMgr
local emmyMgr = self.emmyMgr
+ self:instantSource(action)
+ for _, obj in ipairs(action) do
+ self:instantSource(obj)
+ obj:set('target class', obj[1])
+ end
+ local type = emmyMgr:addType(action)
+ self._emmy = type
+ if self.lsp then
+ self.lsp.global:markGet(self:getUri())
+ end
end
function mt:doEmmyIncomplete(action)
diff --git a/server/src/vm/local.lua b/server/src/vm/local.lua
index e75ed149..c20224de 100644
--- a/server/src/vm/local.lua
+++ b/server/src/vm/local.lua
@@ -3,6 +3,7 @@ local listMgr = require 'vm.list'
local Sort = 0
local Watch = setmetatable({}, {__mode = 'kv'})
+---@class Local
local mt = {}
mt.__index = mt
mt.type = 'local'
@@ -111,6 +112,13 @@ function mt:shadow(old)
end
group[#group+1] = self
self._shadow = group
+
+ if not self:getSource() then
+ log.debug('not self:getSource()', table.dump(self))
+ end
+ if not old.close then
+ log.debug('not old.close', table.dump(old))
+ end
old:close(self:getSource().start - 1)
end
@@ -136,7 +144,6 @@ function mt:setEmmy(emmy)
if emmy.type ~= 'emmy.class' and emmy.type ~= 'emmy.type' then
return
end
- self._emmy = emmy
if self.value then
self.value:setEmmy(emmy)
end
diff --git a/server/src/vm/value.lua b/server/src/vm/value.lua
index 76b173a9..8a0b0861 100644
--- a/server/src/vm/value.lua
+++ b/server/src/vm/value.lua
@@ -225,10 +225,10 @@ function mt:flushChild()
local count = 0
for srcId, info in pairs(infos) do
local src = listMgr.get(srcId)
- if src
- and (info.type == 'set child' or info.type == 'get child')
- then
- alived[info[1]] = true
+ if src then
+ if info.type == 'set child' or info.type == 'get child' then
+ alived[info[1]] = true
+ end
count = count + 1
else
infos[srcId] = nil
@@ -322,7 +322,7 @@ function mt:mergeValue(value)
if not value then
return
end
- local global = self._global
+ local global = self._global or value._global
local list = {self, value}
local pos = 1
while true do
@@ -351,13 +351,14 @@ function mt:mergeValue(value)
list[#list+1] = bc
end
end
- if global then
- bc:markGlobal()
- 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
@@ -528,10 +529,30 @@ function mt:setEmmy(emmy)
if not emmy then
return
end
- if emmy.type ~= 'emmy.class' and emmy.type ~= 'emmy.type' then
+ 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)
+ 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)
+ else
return
end
self._emmy = emmy
+ self:markGlobal()
end
function mt:getEmmy()
diff --git a/server/src/vm/vm.lua b/server/src/vm/vm.lua
index 9ca661bd..a6d5f9ea 100644
--- a/server/src/vm/vm.lua
+++ b/server/src/vm/vm.lua
@@ -41,12 +41,14 @@ function mt:buildTable(source)
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]))
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)
@@ -59,6 +61,7 @@ function mt:buildTable(source)
end
end
elseif obj.type:sub(1, 4) == 'emmy' then
+ self:doEmmy(obj)
else
local value = self:getExp(obj)
if value.type == 'multi' then
@@ -779,10 +782,11 @@ function mt:doGoTo(source)
end
end
-function mt:setOne(var, value)
+function mt:setOne(var, value, emmy)
if not value then
value = valueMgr.create('nil', self:getDefaultSource())
end
+ value:setEmmy(emmy)
self:instantSource(var)
if var.type == 'name' then
self:setName(var[1], var, value)
@@ -806,6 +810,7 @@ function mt:setOne(var, value)
end
function mt:doSet(action)
+ local emmy = self:getEmmy()
if not action[2] then
return
end
@@ -825,11 +830,12 @@ function mt:doSet(action)
local i = 0
self:forList(vars, function (var)
i = i + 1
- self:setOne(var, values[i])
+ self:setOne(var, values[i], emmy)
end)
end
function mt:doLocal(action)
+ local emmy = self:getEmmy()
self:instantSource(action)
local vars = action[1]
local exps = action[2]
@@ -852,9 +858,8 @@ function mt:doLocal(action)
if values then
value = values[i]
end
- self:createLocal(key[1], key, value)
+ self:createLocal(key[1], key, value, emmy)
end)
- self.emmy = nil
end
function mt:doIf(action)
@@ -934,11 +939,9 @@ function mt:doFunction(action)
if source.type == 'index' then
local index = self:getIndex(source)
parent:setChild(index, value, source[1])
- parent:addInfo('set child', source, index)
elseif source.type == 'name' then
local index = source[1]
parent:setChild(index, value, source)
- parent:addInfo('set child', source, index)
end
source:bindValue(value, 'set')
@@ -963,11 +966,9 @@ function mt:doFunction(action)
if source.type == 'index' then
local index = self:getIndex(source)
parent:setChild(index, value, source[1])
- parent:addInfo('set child', source, index)
elseif source.type == 'name' then
local index = source[1]
parent:setChild(index, value, source)
- parent:addInfo('set child', source, index)
end
source:bindValue(value, 'set')
end
@@ -1011,6 +1012,10 @@ function mt:doAction(action)
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
@@ -1042,27 +1047,11 @@ function mt:doAction(action)
self:doFunction(action)
elseif tp == 'localfunction' then
self:doLocalFunction(action)
- elseif tp == 'emmyClass' then
- --self:doEmmyClass(action)
- elseif tp == 'emmyType' then
- --self:doEmmyType(action)
- elseif tp == 'emmyAlias' then
- elseif tp == 'emmyParam' then
- elseif tp == 'emmyReturn' then
- elseif tp == 'emmyField' then
- elseif tp == 'emmyGeneric' then
- elseif tp == 'emmyVararg' then
- elseif tp == 'emmyLanguage' then
- elseif tp == 'emmyArrayType' then
- elseif tp == 'emmyTableType' then
- elseif tp == 'emmyFunctionType' then
- elseif tp == 'emmySee' then
- elseif tp == 'emmyIncomplete' then
- self:doEmmyIncomplete(action)
else
self:getExp(action)
action:set('as action', true)
end
+ self:clearEmmy()
end
function mt:doActions(actions)
@@ -1164,20 +1153,20 @@ function mt:bindLabel(source, label, action)
end
end
-function mt:createLocal(key, source, value)
+function mt:createLocal(key, source, value, emmy)
local loc = self:bindLocal(source)
if not value then
value = self:createValue('nil', source)
end
if loc then
loc:setValue(value)
- loc:setEmmy(self.emmy)
+ loc:setEmmy(emmy)
self:saveLocal(key, loc)
return loc
end
loc = localMgr.create(key, source, value)
- loc:setEmmy(self.emmy)
+ loc:setEmmy(emmy)
self:saveLocal(key, loc)
self:bindLocal(source, loc, 'local')
value:addInfo('local', source)
@@ -1284,6 +1273,7 @@ return function (ast, lsp, uri)
main = nil,
env = nil,
emmy = nil,
+ ---@type emmyMgr
emmyMgr = lsp and lsp.emmy or emmyMgr(),
lsp = lsp,
uri = uri or '',
diff --git a/server/test/completion/init.lua b/server/test/completion/init.lua
index 6dc998c7..560f6fa4 100644
--- a/server/test/completion/init.lua
+++ b/server/test/completion/init.lua
@@ -58,57 +58,16 @@ local function eq(a, b)
return a == b
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 == ':' then
- res = i
- 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
-
rawset(_G, 'TEST', true)
function TEST(script)
return function (expect)
local pos = script:find('$', 1, true) - 1
- local new_script = script:gsub('%$', ' ')
+ local new_script = script:gsub('%$', '')
local ast = parser:ast(new_script, 'lua', 'Lua 5.4')
local vm = buildVM(ast)
assert(vm)
- local word = findWord(pos, new_script)
- local startPos = findStartPos(pos, new_script)
- local result = core.completion(vm, new_script, startPos, word)
+ local result = core.completion(vm, new_script, pos)
if expect then
assert(result)
assert(eq(expect, result))
@@ -414,7 +373,7 @@ collectgarbage('$')
documentation = EXISTS,
textEdit = {
start = 16,
- finish = 18,
+ finish = 17,
newText = '"collect"',
},
},
@@ -424,7 +383,7 @@ collectgarbage('$')
documentation = EXISTS,
textEdit = {
start = 16,
- finish = 18,
+ finish = 17,
newText = '"stop"',
},
},
@@ -434,7 +393,7 @@ collectgarbage('$')
documentation = EXISTS,
textEdit = {
start = 16,
- finish = 18,
+ finish = 17,
newText = '"restart"',
},
},
@@ -444,7 +403,7 @@ collectgarbage('$')
documentation = EXISTS,
textEdit = {
start = 16,
- finish = 18,
+ finish = 17,
newText = '"count"',
},
},
@@ -454,7 +413,7 @@ collectgarbage('$')
documentation = EXISTS,
textEdit = {
start = 16,
- finish = 18,
+ finish = 17,
newText = '"step"',
},
},
@@ -464,7 +423,7 @@ collectgarbage('$')
documentation = EXISTS,
textEdit = {
start = 16,
- finish = 18,
+ finish = 17,
newText = '"setpause"',
},
},
@@ -474,7 +433,7 @@ collectgarbage('$')
documentation = EXISTS,
textEdit = {
start = 16,
- finish = 18,
+ finish = 17,
newText = '"setstepmul"',
},
},
@@ -484,7 +443,7 @@ collectgarbage('$')
documentation = EXISTS,
textEdit = {
start = 16,
- finish = 18,
+ finish = 17,
newText = '"isrunning"',
},
},
@@ -536,7 +495,7 @@ self.results.list[#$]
kind = CompletionItemKind.Snippet,
textEdit = {
start = 20,
- finish = 21,
+ finish = 20,
newText = 'self.results.list+1] = ',
},
},
@@ -551,7 +510,7 @@ self.results.list[#self.re$]
kind = CompletionItemKind.Snippet,
textEdit = {
start = 20,
- finish = 28,
+ finish = 27,
newText = 'self.results.list+1] = ',
},
},
@@ -570,7 +529,7 @@ fff[#ff$]
kind = CompletionItemKind.Snippet,
textEdit = {
start = 6,
- finish = 9,
+ finish = 8,
newText = 'fff+1] = ',
},
},
@@ -589,7 +548,7 @@ local _ = fff.kkk[#$]
kind = CompletionItemKind.Snippet,
textEdit = {
start = 20,
- finish = 21,
+ finish = 20,
newText = 'fff.kkk]',
},
},
@@ -736,25 +695,25 @@ end
(nil)
require 'config' .config.runtime.version = 'Lua 5.4'
-TEST [[
-local *$
-]]
-{
- {
- label = 'toclose',
- kind = CompletionItemKind.Keyword,
- }
-}
-
-TEST [[
-local *tocl$
-]]
-{
- {
- label = 'toclose',
- kind = CompletionItemKind.Keyword,
- }
-}
+--TEST [[
+--local *$
+--]]
+--{
+-- {
+-- label = 'toclose',
+-- kind = CompletionItemKind.Keyword,
+-- }
+--}
+
+--TEST [[
+--local *tocl$
+--]]
+--{
+-- {
+-- label = 'toclose',
+-- kind = CompletionItemKind.Keyword,
+-- }
+--}
TEST [[
local mt = {}
@@ -810,3 +769,18 @@ TEST [[
kind = CompletionItemKind.Keyword
}
}
+
+TEST [[
+---@class ABC
+---@class BBC : $
+]]
+{
+ {
+ label = 'ABC',
+ kind = CompletionItemKind.Class,
+ },
+ {
+ label = 'BBC',
+ kind = CompletionItemKind.Class,
+ },
+}
diff --git a/server/test/crossfile/completion.lua b/server/test/crossfile/completion.lua
index c982607b..cc31ed5d 100644
--- a/server/test/crossfile/completion.lua
+++ b/server/test/crossfile/completion.lua
@@ -61,31 +61,6 @@ local function eq(a, b)
return a == b
end
-local function findStartPos(pos, buf)
- local res = nil
- for i = pos-1, 1, -1 do
- local c = buf:sub(i, i)
- if c:find '%a' then
- res = i
- else
- break
- end
- end
- return res
-end
-
-local function findWord(position, text)
- local word = text
- for i = position-1, 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
-
function TEST(data)
local lsp = service()
local ws = workspace(lsp, 'test')
@@ -99,7 +74,7 @@ function TEST(data)
local uri = ws:uriEncode(fs.path(info.path))
local script = info.content
if info.main then
- pos = script:find('$', 1, true)
+ pos = script:find('$', 1, true) - 1
script = script:gsub('%$', '')
mainUri = uri
mainBuf = script
@@ -114,9 +89,7 @@ function TEST(data)
local vm = lsp:loadVM(mainUri)
assert(vm)
- local word = findWord(pos, mainBuf)
- local startPos = findStartPos(pos, mainBuf) or pos
- local result = core.completion(vm, mainBuf, startPos, word)
+ local result = core.completion(vm, mainBuf, pos)
local expect = data.completion
if expect then
assert(result)
diff --git a/server/test/crossfile/definition.lua b/server/test/crossfile/definition.lua
index aac08802..9310c323 100644
--- a/server/test/crossfile/definition.lua
+++ b/server/test/crossfile/definition.lua
@@ -22,24 +22,53 @@ local function catch_target(script, sep)
return new_script, list
end
+local function founded(targets, results)
+ if #targets ~= #results then
+ return false
+ end
+ for _, target in ipairs(targets) do
+ for _, result in ipairs(results) do
+ if target[1] == result[1]
+ and target[2] == result[2]
+ and target[3] == result[3]
+ then
+ goto NEXT
+ end
+ end
+ do return false end
+ ::NEXT::
+ end
+ return true
+end
+
function TEST(datas)
local lsp = service()
local ws = workspace(lsp, 'test')
lsp.workspace = ws
local compiled = {}
- local targetList, targetUri, sourceList, sourceUri
+ local targetList = {}
+ local sourceList, sourceUri
for i, data in ipairs(datas) do
local uri = ws:uriEncode(fs.path(data.path))
local new, list = catch_target(data.content, '!')
if new ~= data.content or data.target then
if data.target then
- targetList = data.target
+ targetList[#targetList+1] = {
+ data.target[1],
+ data.target[2],
+ uri
+ }
else
- targetList = list[1]
+ for _, position in ipairs(list) do
+ targetList[#targetList+1] = {
+ position[1],
+ position[2],
+ uri
+ }
+ end
end
- targetUri = uri
data.content = new
end
new, list = catch_target(data.content, '?')
@@ -60,13 +89,12 @@ function TEST(datas)
local sourceVM = lsp:getVM(sourceUri)
assert(sourceVM)
local sourcePos = (sourceList[1][1] + sourceList[1][2]) // 2
- local source = core.findSource(sourceVM, sourcePos)
- local positions = core.definition(sourceVM, source, lsp)
- assert(positions and positions[1])
- local start, finish, valueUri = positions[1][1], positions[1][2], positions[1][3]
- assert(valueUri == targetUri)
- assert(start == targetList[1])
- assert(finish == targetList[2])
+ local positions = core.definition(sourceVM, sourcePos, 'definition')
+ if positions then
+ assert(founded(targetList, positions))
+ else
+ assert(#targetList == 0)
+ end
end
TEST {
@@ -84,6 +112,17 @@ TEST {
TEST {
{
path = 'a.lua',
+ content = 'local <!t!> = 1; return <!t!>',
+ },
+ {
+ path = 'b.lua',
+ content = 'local <?t?> = require "a"',
+ },
+}
+
+TEST {
+ {
+ path = 'a.lua',
content = '',
target = {0, 0},
},
@@ -96,7 +135,7 @@ TEST {
TEST {
{
path = 'a.lua',
- content = 'local <!t!> = 1; return t',
+ content = 'local <!t!> = 1; return <!t!>',
},
{
path = 'b.lua',
@@ -152,7 +191,7 @@ TEST {
{
path = 'b.lua',
content = [[
- local f = require "a"
+ local <!f!> = require "a"
<?f?>()
]],
},
@@ -258,3 +297,37 @@ TEST {
]],
},
}
+
+TEST {
+ {
+ path = 'a.lua',
+ content = [[
+ ---@class Class
+ local <!obj!>
+ ]]
+ },
+ {
+ path = 'b.lua',
+ content = [[
+ ---@type Class
+ local <?obj?>
+ ]]
+ },
+}
+
+TEST {
+ {
+ path = 'a.lua',
+ content = [[
+ ---@type Class
+ local <?obj?>
+ ]]
+ },
+ {
+ path = 'b.lua',
+ content = [[
+ ---@class Class
+ local <!obj!>
+ ]]
+ },
+}
diff --git a/server/test/crossfile/references.lua b/server/test/crossfile/references.lua
index 5f29eb25..4163a321 100644
--- a/server/test/crossfile/references.lua
+++ b/server/test/crossfile/references.lua
@@ -109,7 +109,7 @@ function TEST(data)
compileAll(lsp)
assert(vm)
- local result = core.references(vm, pos, true)
+ local result = core.definition(vm, pos, 'reference')
if expect then
assert(result)
assert(founded(expect, result))
diff --git a/server/test/definition/bug.lua b/server/test/definition/bug.lua
index 1d3ab02c..ee414ef6 100644
--- a/server/test/definition/bug.lua
+++ b/server/test/definition/bug.lua
@@ -86,5 +86,5 @@ return f(), <?b?>
TEST [[
local a = os.clock()
-local <?<!b!>?> = os.clock()
+local <?b?> = os.clock()
]]
diff --git a/server/test/definition/emmy.lua b/server/test/definition/emmy.lua
new file mode 100644
index 00000000..82bac7a8
--- /dev/null
+++ b/server/test/definition/emmy.lua
@@ -0,0 +1,63 @@
+TEST [[
+---@class <!A!>
+---@class B : <?A?>
+]]
+
+TEST [[
+---@class <!A!>
+---@type B|<?A?>
+]]
+
+TEST [[
+---@class A
+local mt = {}
+function mt:<!cast!>()
+end
+
+---@type A
+local obj
+obj:<?cast?>()
+]]
+
+TEST [[
+---@class A
+local <!mt!> = {}
+function mt:cast()
+end
+
+---@type A
+local <!obj!>
+<?obj?>:cast()
+]]
+
+TEST [[
+---@type A
+local <?obj?>
+
+---@class A
+local <!mt!>
+]]
+
+TEST [[
+---@type A
+local obj
+obj:<?func?>()
+
+---@class A
+local mt
+function mt:<!func!>()
+end
+]]
+
+TEST [[
+---@type A
+local obj
+obj:<?func?>()
+
+local mt = {}
+mt.__index = mt
+function mt:<!func!>()
+end
+---@class A
+local obj = setmetatable({}, mt)
+]]
diff --git a/server/test/definition/function.lua b/server/test/definition/function.lua
index aae5786f..ce20f50b 100644
--- a/server/test/definition/function.lua
+++ b/server/test/definition/function.lua
@@ -18,7 +18,7 @@ end
TEST [[
local <!x!>
-function x()
+function <!x!>()
end
<?x?>()
]]
diff --git a/server/test/definition/init.lua b/server/test/definition/init.lua
index e095e474..1ff5881e 100644
--- a/server/test/definition/init.lua
+++ b/server/test/definition/init.lua
@@ -45,8 +45,7 @@ function TEST(script)
local vm = buildVM(ast)
assert(vm)
- local source = core.findSource(vm, pos)
- local positions = core.definition(vm, source, nil)
+ local positions = core.definition(vm, pos, 'definition')
if positions then
assert(founded(target, positions))
else
@@ -62,3 +61,4 @@ require 'definition.table'
require 'definition.method'
require 'definition.label'
require 'definition.bug'
+require 'definition.emmy'
diff --git a/server/test/definition/method.lua b/server/test/definition/method.lua
index 40c5f127..08b56f61 100644
--- a/server/test/definition/method.lua
+++ b/server/test/definition/method.lua
@@ -77,6 +77,7 @@ end
local obj = setmetatable({}, { __index = mt })
obj.<!init!> = 1
+obj:x()
]]
TEST [[
@@ -86,6 +87,7 @@ function mt:x()
end
local obj = setmetatable({ <!init!> = 1 }, { __index = mt })
+obj:x()
]]
TEST [[
@@ -99,6 +101,7 @@ local obj = setmetatable({
<!out!> = 1,
}
}, { __index = mt })
+obj:x()
]]
TEST [[
diff --git a/server/test/definition/set.lua b/server/test/definition/set.lua
index 54df1e7a..44277d33 100644
--- a/server/test/definition/set.lua
+++ b/server/test/definition/set.lua
@@ -23,7 +23,7 @@ x = 1
do
local <!x!> = 1
do
- x = 2
+ <!x!> = 2
end
<?x?>()
end
diff --git a/server/test/definition/table.lua b/server/test/definition/table.lua
index 1b6d011e..90e7926f 100644
--- a/server/test/definition/table.lua
+++ b/server/test/definition/table.lua
@@ -96,19 +96,19 @@ local t = {
t.<?insert?>()
]]
-TEST[[
-local t = {
- <!insert!> = 1,
-}
-y.<?insert?>()
-]]
-
-TEST[[
-local t = {
- <!insert!> = 1,
-}
-local y = {
- insert = 1,
-}
-t.<?insert?>()
-]]
+--TEST[[
+--local t = {
+-- <!insert!> = 1,
+--}
+--y.<?insert?>()
+--]]
+
+--TEST[[
+--local t = {
+-- <!insert!> = 1,
+--}
+--local y = {
+-- insert = 1,
+--}
+--t.<?insert?>()
+--]]
diff --git a/server/test/diagnostics/init.lua b/server/test/diagnostics/init.lua
index 7a092803..4b762122 100644
--- a/server/test/diagnostics/init.lua
+++ b/server/test/diagnostics/init.lua
@@ -334,25 +334,34 @@ local function x()
end
]]
---TEST [[
------@class <!Class!>
------@class <!Class!>
---]]
---
---TEST [[
------@class A : <!B!>
---]]
---
---TEST [[
------@class <!A : B!>
------@class <!B : C!>
------@class <!C : D!>
------@class <!D : A!>
---]]
---
---TEST [[
------@class A : B
------@class B : C
------@class C : D
------@class D
---]]
+TEST [[
+---@class <!Class!>
+---@class <!Class!>
+]]
+
+TEST [[
+---@class A : <!B!>
+]]
+
+TEST [[
+---@class <!A : B!>
+---@class <!B : C!>
+---@class <!C : D!>
+---@class <!D : A!>
+]]
+
+TEST [[
+---@class A : B
+---@class B : C
+---@class C : D
+---@class D
+]]
+
+TEST [[
+---@type <!A!>
+]]
+
+TEST [[
+---@class A
+---@type A|<!B!>|<!C!>
+]]
diff --git a/server/test/hover/init.lua b/server/test/hover/init.lua
index fbbbeb82..79904e6c 100644
--- a/server/test/hover/init.lua
+++ b/server/test/hover/init.lua
@@ -465,10 +465,74 @@ function n<next>(table: table [, index: any])
-> key: any, value: any
]]
---TEST[[
------@class Class
---local <?x?> = class()
---]]
---[[
---local x: *Class
---]]
+TEST[[
+---@class Class
+local <?x?> = class()
+]]
+[[
+local x: *Class {}
+]]
+
+TEST[[
+---@class Class
+<?x?> = class()
+]]
+[[
+global x: *Class {}
+]]
+
+TEST[[
+local t = {
+ ---@class Class
+ <?x?> = class()
+}
+]]
+[[
+field x: *Class {}
+]]
+
+TEST[[
+---@type Class
+local <?x?> = class()
+]]
+[[
+local x: *Class {}
+]]
+
+TEST[[
+---@type Class
+<?x?> = class()
+]]
+[[
+global x: *Class {}
+]]
+
+TEST[[
+local t = {
+ ---@type Class
+ <?x?> = class()
+}
+]]
+[[
+field x: *Class {}
+]]
+
+TEST[[
+---@type A|B|C
+local <?x?> = class()
+]]
+[[
+local x: *A|B|C {}
+]]
+
+TEST[[
+---@class Class
+local <?x?> = {
+ b = 1
+}
+]]
+[[
+local x: *Class {
+ b: number = 1,
+}
+]]
diff --git a/server/test/references/init.lua b/server/test/references/init.lua
index e5d0b1cc..d1eab422 100644
--- a/server/test/references/init.lua
+++ b/server/test/references/init.lua
@@ -43,7 +43,7 @@ function TEST(script)
local vm = buildVM(ast)
assert(vm)
- local positions = core.references(vm, pos, true)
+ local positions = core.definition(vm, pos, 'reference')
if positions then
assert(founded(target, positions))
else
@@ -52,13 +52,13 @@ function TEST(script)
end
TEST [[
-local <!a!> = 1
-<?a?> = <?a?>
+local <?a?> = 1
+<!a!> = <!a!>
]]
TEST [[
-t.<!a!> = 1
-t.<?a?> = t.<?a?>
+t.<?a?> = 1
+t.<!a!> = t.<!a!>
]]
TEST [[
@@ -68,8 +68,8 @@ goto <?LABEL?>
TEST [[
local a = 1
-local <!a!> = 1
-<?a?> = <?a?>
+local <?a?> = 1
+<!a!> = <!a!>
]]
TEST [[
@@ -100,3 +100,15 @@ table.<!dump!>()
function table.<?dump?>()
end
]]
+
+TEST [[
+---@class <!Class!>
+---@type <?Class?>
+---@type <!Class!>
+]]
+
+TEST [[
+---@class <?Class?>
+---@type <!Class!>
+---@type <!Class!>
+]]