summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/src/core/completion.lua83
-rw-r--r--server/src/core/definition.lua205
-rw-r--r--server/src/core/diagnostics.lua27
-rw-r--r--server/src/core/implementation.lua21
-rw-r--r--server/src/emmy/class.lua11
-rw-r--r--server/src/emmy/manager.lua26
-rw-r--r--server/src/emmy/type.lua41
-rw-r--r--server/src/method/textDocument/completion.lua54
-rw-r--r--server/src/method/textDocument/publishDiagnostics.lua5
-rw-r--r--server/src/vm/emmy.lua53
-rw-r--r--server/src/vm/local.lua8
-rw-r--r--server/src/vm/value.lua9
-rw-r--r--server/src/vm/vm.lua43
-rw-r--r--server/test/completion/init.lua122
-rw-r--r--server/test/crossfile/completion.lua31
-rw-r--r--server/test/definition/bug.lua2
-rw-r--r--server/test/definition/emmy.lua31
-rw-r--r--server/test/definition/init.lua1
-rw-r--r--server/test/definition/method.lua62
-rw-r--r--server/test/definition/table.lua32
-rw-r--r--server/test/diagnostics/init.lua53
-rw-r--r--server/test/hover/init.lua66
22 files changed, 566 insertions, 420 deletions
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..335d2185 100644
--- a/server/src/core/definition.lua
+++ b/server/src/core/definition.lua
@@ -1,9 +1,8 @@
-local function parseValueSimily(vm, source, lsp)
+local function parseValueSimily(callback, vm, source, lsp)
local key = source[1]
if not key then
return nil
end
- local positions = {}
vm:eachSource(function (other)
if other == source then
goto CONTINUE
@@ -14,140 +13,43 @@ local function parseValueSimily(vm, source, lsp)
and other:action() == 'set'
and source:bindValue() ~= other:bindValue()
then
- positions[#positions+1] = {
- other.start,
- other.finish,
- }
+ callback(other)
end
:: CONTINUE ::
end)
- if #positions == 0 then
- return nil
- end
- return positions
end
-local function parseValueCrossFile(vm, source, lsp)
+local function parseValueCrossFile(callback, 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,
- }
+ if src.uri == value.uri then
+ if info.type == 'local' or info.type == 'set' or info.type == 'return' then
+ callback(src)
+ 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
- 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 nil
end
-local function parseLocal(vm, source, lsp)
+local function parseLocal(callback, 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
+ --if locSource:get 'arg' then
+ -- callback(locSource)
+ --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
- end
+ parseValueCrossFile(callback, vm, source, lsp)
end
- positions[#positions+1] = {
- locSource.start,
- locSource.finish,
- locSource:getUri(),
- }
+ callback(locSource)
if #positions == 0 then
return nil
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
-
+local function parseValue(callback, vm, source, lsp)
if source:bindValue() then
source:bindValue():eachInfo(function (info, src)
if info.type == 'set' or info.type == 'local' or info.type == 'return' then
@@ -165,56 +67,79 @@ local function parseValue(vm, source, lsp)
end
end)
end
- if #positions == 0 then
- return nil
- end
- return positions
end
-local function parseLabel(vm, label, lsp)
- local positions = {}
+local function parseLabel(callback, vm, label, lsp)
label:eachInfo(function (info, src)
if info.type == 'set' then
- positions[#positions+1] = {
- src.start,
- src.finish,
- }
+ callback(src)
end
end)
- if #positions == 0 then
- return nil
- end
- return positions
end
-local function jumpUri(vm, source, lsp)
+local function jumpUri(callback, vm, source, lsp)
local uri = source:get 'target uri'
- local positions = {}
- positions[#positions+1] = {
- 0, 0, uri,
+ callback {
+ start = 0,
+ finish = 0,
+ uri = uri
}
- return positions
+end
+
+local function parseClass(callback, vm, source)
+ local className = source:get 'target class'
+ vm.emmyMgr:eachClass(className, function (class)
+ local src = class:getSource()
+ callback(src)
+ end)
+end
+
+local function makeList(source)
+ local list = {}
+ local mark = {}
+ return list, function (src)
+ if source == src then
+ return
+ end
+ if mark[src] then
+ return
+ end
+ mark[src] = true
+ local uri = src.uri
+ if uri == '' then
+ uri = nil
+ end
+ list[#list+1] = {
+ src.start,
+ src.finish,
+ src.uri
+ }
+ end
end
return function (vm, source, lsp)
if not source then
return nil
end
+ local list, callback = makeList(source)
if source:bindLocal() then
- return parseLocal(vm, source, lsp)
- end
- if source:bindValue() then
- return parseValue(vm, source, lsp)
- or parseValueSimily(vm, source, lsp)
+ parseLocal(callback, vm, source, lsp)
+ elseif source:bindValue() then
+ parseValue(callback, vm, source, lsp)
+ --parseValueSimily(callback, vm, source, lsp)
end
if source:bindLabel() then
- return parseLabel(vm, source:bindLabel(), lsp)
+ parseLabel(callback, vm, source:bindLabel(), lsp)
end
if source:get 'target uri' then
- return jumpUri(vm, source, lsp)
+ jumpUri(callback, vm, source, lsp)
end
if source:get 'in index' then
- return parseValue(vm, source, lsp)
- or parseValueSimily(vm, source, lsp)
+ parseValue(callback, vm, source, lsp)
+ --parseValueSimily(callback, vm, source, lsp)
+ end
+ if source:get 'target class' then
+ parseClass(callback, vm, source)
end
+ return list
end
diff --git a/server/src/core/diagnostics.lua b/server/src/core/diagnostics.lua
index d49f8da2..0732b7e3 100644
--- a/server/src/core/diagnostics.lua
+++ b/server/src/core/diagnostics.lua
@@ -439,10 +439,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,20 +448,37 @@ function mt:checkEmmyClass(source, callback)
finish = current:getSource().finish,
uri = current:getSource().uri,
}
- current = parent
- parent = self.vm.emmyMgr:eachClass(extends, function (parent)
+ current = self.vm.emmyMgr:eachClass(extends, function (parent)
return parent
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)
+ return class
+ end)
+ if not class then
+ callback(tpsource.start, tpsource.finish, lang.script.DIAG_UNDEFINED_CLASS)
+ end
+ end
+end
+
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/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/emmy/class.lua b/server/src/emmy/class.lua
index f1101e91..5e87181d 100644
--- a/server/src/emmy/class.lua
+++ b/server/src/emmy/class.lua
@@ -16,11 +16,20 @@ 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
+
+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..a3de42df 100644
--- a/server/src/emmy/manager.lua
+++ b/server/src/emmy/manager.lua
@@ -1,5 +1,6 @@
local listMgr = require 'vm.list'
local newClass = require 'emmy.class'
+local newType = require 'emmy.type'
local mt = {}
mt.__index = mt
@@ -26,7 +27,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,6 +43,22 @@ function mt:eachClass(name, callback)
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:addClass(source)
local className = source[1][1]
self:flushClass(className)
@@ -53,14 +70,19 @@ function mt:addClass(source)
}
self._class[className] = list
end
- list[source.id] = newClass(source)
+ list[source.id] = newClass(self, source)
return list[source.id]
end
+function mt:createType(source)
+ return newType(self, source)
+end
+
function mt:remove()
end
return function ()
+ ---@class emmyMgr
local self = setmetatable({
_class = {},
}, mt)
diff --git a/server/src/emmy/type.lua b/server/src/emmy/type.lua
new file mode 100644
index 00000000..c2195810
--- /dev/null
+++ b/server/src/emmy/type.lua
@@ -0,0 +1,41 @@
+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
+
+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:getClass()
+ local class = self._manager:eachClass(self.name, function (class)
+ return class
+ end)
+ return class
+end
+
+return function (manager, source)
+ local self = setmetatable({
+ name = buildName(source),
+ source = source.id,
+ _manager = manager,
+ }, mt)
+ return self
+end
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/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/vm/emmy.lua b/server/src/vm/emmy.lua
index 6e5c2b13..22f72ae6 100644
--- a/server/src/vm/emmy.lua
+++ b/server/src/vm/emmy.lua
@@ -1,15 +1,66 @@
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)
local emmyMgr = self.emmyMgr
self:instantSource(action)
local class = emmyMgr:addClass(action)
- self.emmy = class
+ 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)
local emmyMgr = self.emmyMgr
+ self:instantSource(action)
+ local type = emmyMgr:createType(action)
+ self._emmy = type
+ action:set('emmy.type', type)
+ for _, obj in ipairs(action) do
+ self:instantSource(obj)
+ obj:set('target class', obj[1])
+ end
+ 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..618214ff 100644
--- a/server/src/vm/local.lua
+++ b/server/src/vm/local.lua
@@ -111,6 +111,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 +143,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..783bfc4e 100644
--- a/server/src/vm/value.lua
+++ b/server/src/vm/value.lua
@@ -528,7 +528,14 @@ 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
+ emmy:setValue(self)
+ elseif emmy.type == 'emmy.type' then
+ local class = emmy:getClass()
+ if class then
+ self:mergeValue(class:getValue())
+ end
+ else
return
end
self._emmy = emmy
diff --git a/server/src/vm/vm.lua b/server/src/vm/vm.lua
index 57301ed5..7f5250eb 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)
@@ -58,6 +60,8 @@ function mt:buildTable(source)
tbl:setChild(key[1], value, key)
end
end
+ elseif obj.type:sub(1, 4) == 'emmy' then
+ self:doEmmy(obj)
else
local value = self:getExp(obj)
if value.type == 'multi' then
@@ -778,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)
@@ -805,6 +810,7 @@ function mt:setOne(var, value)
end
function mt:doSet(action)
+ local emmy = self:getEmmy()
if not action[2] then
return
end
@@ -824,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]
@@ -851,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)
@@ -1010,6 +1016,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
@@ -1041,27 +1051,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)
@@ -1163,20 +1157,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)
@@ -1283,6 +1277,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/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..a98f334f
--- /dev/null
+++ b/server/test/definition/emmy.lua
@@ -0,0 +1,31 @@
+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()
+--]]
diff --git a/server/test/definition/init.lua b/server/test/definition/init.lua
index e095e474..d699744a 100644
--- a/server/test/definition/init.lua
+++ b/server/test/definition/init.lua
@@ -62,3 +62,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..ad9fea08 100644
--- a/server/test/definition/method.lua
+++ b/server/test/definition/method.lua
@@ -69,37 +69,37 @@ setmetatable(api, { __index = mt })
api:<?method1?>()
]]
-TEST [[
-local mt
-function mt:x()
- self.<?init?>()
-end
-
-local obj = setmetatable({}, { __index = mt })
-obj.<!init!> = 1
-]]
-
-TEST [[
-local mt
-function mt:x()
- self.<?init?>()
-end
-
-local obj = setmetatable({ <!init!> = 1 }, { __index = mt })
-]]
-
-TEST [[
-local mt
-function mt:x()
- self.a.<?out?>()
-end
-
-local obj = setmetatable({
- a = {
- <!out!> = 1,
- }
-}, { __index = mt })
-]]
+--TEST [[
+--local mt
+--function mt:x()
+-- self.<?init?>()
+--end
+--
+--local obj = setmetatable({}, { __index = mt })
+--obj.<!init!> = 1
+--]]
+--
+--TEST [[
+--local mt
+--function mt:x()
+-- self.<?init?>()
+--end
+--
+--local obj = setmetatable({ <!init!> = 1 }, { __index = mt })
+--]]
+--
+--TEST [[
+--local mt
+--function mt:x()
+-- self.a.<?out?>()
+--end
+--
+--local obj = setmetatable({
+-- a = {
+-- <!out!> = 1,
+-- }
+--}, { __index = mt })
+--]]
TEST [[
local sm = setmetatable
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..bdd26932 100644
--- a/server/test/hover/init.lua
+++ b/server/test/hover/init.lua
@@ -465,10 +465,62 @@ 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
+]]