summaryrefslogtreecommitdiff
path: root/script/src/method/textDocument
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2019-11-22 23:26:32 +0800
committer最萌小汐 <sumneko@hotmail.com>2019-11-22 23:26:32 +0800
commitd0ff66c9abe9d6abbca12fd811e0c3cb69c1033a (patch)
treebb34518d70b85de7656dbdbe958dfa221a3ff3b3 /script/src/method/textDocument
parent0a2c2ad15e1ec359171fb0dd4c72e57c5b66e9ba (diff)
downloadlua-language-server-d0ff66c9abe9d6abbca12fd811e0c3cb69c1033a.zip
整理一下目录结构
Diffstat (limited to 'script/src/method/textDocument')
-rw-r--r--script/src/method/textDocument/codeAction.lua23
-rw-r--r--script/src/method/textDocument/completion.lua104
-rw-r--r--script/src/method/textDocument/definition.lua88
-rw-r--r--script/src/method/textDocument/didChange.lua16
-rw-r--r--script/src/method/textDocument/didClose.lua5
-rw-r--r--script/src/method/textDocument/didOpen.lua5
-rw-r--r--script/src/method/textDocument/documentHighlight.lua37
-rw-r--r--script/src/method/textDocument/documentSymbol.lua72
-rw-r--r--script/src/method/textDocument/foldingRange.lua57
-rw-r--r--script/src/method/textDocument/hover.lua44
-rw-r--r--script/src/method/textDocument/implementation.lua108
-rw-r--r--script/src/method/textDocument/onTypeFormatting.lua14
-rw-r--r--script/src/method/textDocument/publishDiagnostics.lua163
-rw-r--r--script/src/method/textDocument/references.lua86
-rw-r--r--script/src/method/textDocument/rename.lua50
-rw-r--r--script/src/method/textDocument/signatureHelp.lua50
16 files changed, 922 insertions, 0 deletions
diff --git a/script/src/method/textDocument/codeAction.lua b/script/src/method/textDocument/codeAction.lua
new file mode 100644
index 00000000..3c6e8d49
--- /dev/null
+++ b/script/src/method/textDocument/codeAction.lua
@@ -0,0 +1,23 @@
+local core = require 'core'
+
+return function (lsp, params)
+ local uri = params.textDocument.uri
+ local vm, lines = lsp:getVM(uri)
+ if not vm then
+ return
+ end
+ local diagnostics = params.context.diagnostics
+ local range = params.range
+
+ local results = core.codeAction(lsp
+ , uri
+ , diagnostics
+ , range
+ )
+
+ if #results == 0 then
+ return nil
+ end
+
+ return results
+end
diff --git a/script/src/method/textDocument/completion.lua b/script/src/method/textDocument/completion.lua
new file mode 100644
index 00000000..4c7581df
--- /dev/null
+++ b/script/src/method/textDocument/completion.lua
@@ -0,0 +1,104 @@
+local core = require 'core'
+local parser = require 'parser'
+
+local function posToRange(lines, start, finish)
+ local start_row, start_col = lines:rowcol(start)
+ local finish_row, finish_col = lines:rowcol(finish)
+ return {
+ start = {
+ line = start_row - 1,
+ character = start_col - 1,
+ },
+ ['end'] = {
+ line = finish_row - 1,
+ character = finish_col,
+ },
+ }
+end
+
+local function fastCompletion(lsp, params, lines)
+ local uri = params.textDocument.uri
+ local text, oldText = lsp:getText(uri)
+ -- lua是从1开始的,因此都要+1
+ local position = lines:positionAsChar(params.position.line + 1, params.position.character)
+
+ local vm = lsp:getVM(uri)
+ if not vm then
+ vm = lsp:loadVM(uri)
+ if not vm then
+ return nil
+ end
+ end
+
+ local items = core.completion(vm, text, position, oldText)
+ if not items or #items == 0 then
+ vm = lsp:loadVM(uri)
+ if not vm then
+ return nil
+ end
+ items = core.completion(vm, text, position)
+ if not items or #items == 0 then
+ return nil
+ end
+ end
+
+ return items
+end
+
+local function finishCompletion(lsp, params, lines)
+ local uri = params.textDocument.uri
+ local text = lsp:getText(uri)
+ -- lua是从1开始的,因此都要+1
+ local position = lines:positionAsChar(params.position.line + 1, params.position.character)
+
+ local vm = lsp:loadVM(uri)
+ if not vm then
+ return nil
+ end
+
+ local items = core.completion(vm, text, position)
+ if not items or #items == 0 then
+ return nil
+ end
+
+ return items
+end
+
+return function (lsp, params)
+ local uri = params.textDocument.uri
+ local text, oldText = lsp:getText(uri)
+ if not text then
+ return nil
+ end
+
+ local lines = parser:lines(text, 'utf8')
+ local items = fastCompletion(lsp, params, lines)
+ --local items = finishCompletion(lsp, params, lines)
+ if not items then
+ return nil
+ end
+
+ for i, item in ipairs(items) do
+ item.sortText = ('%04d'):format(i)
+ item.insertTextFormat = 2
+ item.insertText = item.insertText or item.label
+ if item.textEdit then
+ item.textEdit.range = posToRange(lines, item.textEdit.start, item.textEdit.finish)
+ item.textEdit.start = nil
+ item.textEdit.finish = nil
+ end
+ if item.additionalTextEdits then
+ for _, textEdit in ipairs(item.additionalTextEdits) do
+ textEdit.range = posToRange(lines, textEdit.start, textEdit.finish)
+ textEdit.start = nil
+ textEdit.finish = nil
+ end
+ end
+ end
+
+ local response = {
+ isIncomplete = true,
+ items = items,
+ }
+ return response
+end
diff --git a/script/src/method/textDocument/definition.lua b/script/src/method/textDocument/definition.lua
new file mode 100644
index 00000000..dbf9e41c
--- /dev/null
+++ b/script/src/method/textDocument/definition.lua
@@ -0,0 +1,88 @@
+local core = require 'core'
+
+local function findResult(lsp, uri, position)
+ local vm = lsp:getVM(uri)
+
+ local positions, isGlobal = core.definition(vm, position, 'definition')
+ if not positions then
+ return nil, isGlobal
+ end
+
+ local locations = {}
+ for i, position in ipairs(positions) do
+ local start, finish, valueUri = position[1], position[2], (position[3] or uri)
+ local vm, valueLines = lsp:getVM(valueUri)
+ if valueLines then
+ local start_row, start_col = valueLines:rowcol(start)
+ local finish_row, finish_col = valueLines:rowcol(finish)
+ locations[#locations+1] = {
+ uri = valueUri,
+ range = {
+ start = {
+ line = start_row - 1,
+ character = start_col - 1,
+ },
+ ['end'] = {
+ line = finish_row - 1,
+ -- 这里不用-1,因为前端期待的是匹配完成后的位置
+ character = finish_col,
+ },
+ }
+ }
+ elseif vm then
+ locations[#locations+1] = {
+ uri = valueUri,
+ range = {
+ start = {
+ line = 0,
+ character = 0,
+ },
+ ['end'] = {
+ line = 0,
+ character = 0,
+ },
+ }
+ }
+ end
+ end
+
+ if #locations == 0 then
+ return nil, isGlobal
+ end
+
+ return locations, isGlobal
+end
+
+local LastTask
+
+---@param lsp LSP
+---@param params table
+return function (lsp, params)
+ local uri = params.textDocument.uri
+ local vm, lines = lsp:loadVM(uri)
+ if not vm then
+ return nil
+ end
+
+ if LastTask then
+ LastTask:remove()
+ LastTask = nil
+ end
+
+ -- lua是从1开始的,因此都要+1
+ local position = lines:positionAsChar(params.position.line + 1, params.position.character)
+
+ return function (response)
+ local clock = os.clock()
+ LastTask = ac.loop(0.1, function ()
+ local result, isGlobal = findResult(lsp, uri, position)
+ if isGlobal and lsp:isWaitingCompile() and os.clock() - clock < 1 then
+ return
+ end
+ response(result)
+ LastTask:remove()
+ LastTask = nil
+ end)
+ LastTask:onTimer()
+ end
+end
diff --git a/script/src/method/textDocument/didChange.lua b/script/src/method/textDocument/didChange.lua
new file mode 100644
index 00000000..82e6c096
--- /dev/null
+++ b/script/src/method/textDocument/didChange.lua
@@ -0,0 +1,16 @@
+return function (lsp, params)
+ local doc = params.textDocument
+ local change = params.contentChanges
+ if lsp.workspace then
+ local path = lsp.workspace:relativePathByUri(doc.uri)
+ if not path or not lsp.workspace:isLuaFile(path) then
+ return
+ end
+ if not lsp:isOpen(doc.uri) and lsp.workspace.gitignore(path:string()) then
+ return
+ end
+ end
+ -- TODO 支持差量更新
+ lsp:saveText(doc.uri, doc.version, change[1].text)
+ return true
+end
diff --git a/script/src/method/textDocument/didClose.lua b/script/src/method/textDocument/didClose.lua
new file mode 100644
index 00000000..589b212f
--- /dev/null
+++ b/script/src/method/textDocument/didClose.lua
@@ -0,0 +1,5 @@
+return function (lsp, params)
+ local doc = params.textDocument
+ lsp:close(doc.uri)
+ return true
+end
diff --git a/script/src/method/textDocument/didOpen.lua b/script/src/method/textDocument/didOpen.lua
new file mode 100644
index 00000000..e2a67fd2
--- /dev/null
+++ b/script/src/method/textDocument/didOpen.lua
@@ -0,0 +1,5 @@
+return function (lsp, params)
+ local doc = params.textDocument
+ lsp:open(doc.uri, doc.version, doc.text)
+ return true
+end
diff --git a/script/src/method/textDocument/documentHighlight.lua b/script/src/method/textDocument/documentHighlight.lua
new file mode 100644
index 00000000..377ffcdf
--- /dev/null
+++ b/script/src/method/textDocument/documentHighlight.lua
@@ -0,0 +1,37 @@
+local core = require 'core'
+
+return function (lsp, params)
+ local uri = params.textDocument.uri
+ local vm, lines = lsp:loadVM(uri)
+ if not vm then
+ return nil
+ end
+ local position = lines:positionAsChar(params.position.line + 1, params.position.character)
+ local positions = core.highlight(vm, position)
+ if not positions then
+ return nil
+ end
+
+ local result = {}
+ for i, position in ipairs(positions) do
+ local start, finish = position[1], position[2]
+ local start_row, start_col = lines:rowcol(start)
+ local finish_row, finish_col = lines:rowcol(finish)
+ result[i] = {
+ range = {
+ start = {
+ line = start_row - 1,
+ character = start_col - 1,
+ },
+ ['end'] = {
+ line = finish_row - 1,
+ -- 这里不用-1,因为前端期待的是匹配完成后的位置
+ character = finish_col,
+ },
+ },
+ kind = position[3],
+ }
+ end
+
+ return result
+end
diff --git a/script/src/method/textDocument/documentSymbol.lua b/script/src/method/textDocument/documentSymbol.lua
new file mode 100644
index 00000000..a4b0c3b7
--- /dev/null
+++ b/script/src/method/textDocument/documentSymbol.lua
@@ -0,0 +1,72 @@
+local core = require 'core'
+local lang = require 'language'
+
+local timerCache = {}
+
+local function posToRange(lines, start, finish)
+ local start_row, start_col = lines:rowcol(start)
+ local finish_row, finish_col = lines:rowcol(finish)
+ return {
+ start = {
+ line = start_row - 1,
+ character = start_col - 1,
+ },
+ ['end'] = {
+ line = finish_row - 1,
+ character = finish_col,
+ },
+ }
+end
+
+local function convertRange(lines, symbol)
+ symbol.range = posToRange(lines, symbol.range[1], symbol.range[2])
+ symbol.selectionRange = posToRange(lines, symbol.selectionRange[1], symbol.selectionRange[2])
+ if symbol.name == '' then
+ symbol.name = lang.script.SYMBOL_ANONYMOUS
+ end
+
+ if symbol.children then
+ for _, child in ipairs(symbol.children) do
+ convertRange(lines, child)
+ end
+ end
+end
+
+return function (lsp, params)
+ local uri = params.textDocument.uri
+
+ if timerCache[uri] then
+ timerCache[uri]:remove()
+ timerCache[uri] = nil
+ end
+
+ return function (response)
+ local clock = os.clock()
+ timerCache[uri] = ac.loop(0.1, function (t)
+ local vm, lines = lsp:getVM(uri)
+ if not vm then
+ if os.clock() - clock > 10 then
+ t:remove()
+ timerCache[uri] = nil
+ response(nil)
+ end
+ return
+ end
+
+ t:remove()
+ timerCache[uri] = nil
+
+ local symbols = core.documentSymbol(vm)
+ if not symbols then
+ response(nil)
+ return
+ end
+
+ for _, symbol in ipairs(symbols) do
+ convertRange(lines, symbol)
+ end
+
+ response(symbols)
+ end)
+ end
+end
diff --git a/script/src/method/textDocument/foldingRange.lua b/script/src/method/textDocument/foldingRange.lua
new file mode 100644
index 00000000..0320b422
--- /dev/null
+++ b/script/src/method/textDocument/foldingRange.lua
@@ -0,0 +1,57 @@
+local core = require 'core'
+
+local timerCache = {}
+
+local function convertRange(lines, range)
+ local start_row, start_col = lines:rowcol(range.start)
+ local finish_row, finish_col = lines:rowcol(range.finish)
+ local result = {
+ startLine = start_row - 1,
+ endLine = finish_row - 2,
+ kind = range.kind,
+ }
+ if result.startLine >= result.endLine then
+ return nil
+ end
+ return result
+end
+
+return function (lsp, params)
+ local uri = params.textDocument.uri
+ if timerCache[uri] then
+ timerCache[uri]:remove()
+ timerCache[uri] = nil
+ end
+
+ return function (response)
+ local clock = os.clock()
+ timerCache[uri] = ac.loop(0.1, function (t)
+ local vm, lines = lsp:getVM(uri)
+ if not vm then
+ if os.clock() - clock > 10 then
+ t:remove()
+ timerCache[uri] = nil
+ response(nil)
+ end
+ return
+ end
+
+ t:remove()
+ timerCache[uri] = nil
+
+ local comments = lsp:getComments(uri)
+ local ranges = core.foldingRange(vm, comments)
+ if not ranges then
+ response(nil)
+ return
+ end
+
+ local results = {}
+ for _, range in ipairs(ranges) do
+ results[#results+1] = convertRange(lines, range)
+ end
+
+ response(results)
+ end)
+ end
+end
diff --git a/script/src/method/textDocument/hover.lua b/script/src/method/textDocument/hover.lua
new file mode 100644
index 00000000..f8dba27c
--- /dev/null
+++ b/script/src/method/textDocument/hover.lua
@@ -0,0 +1,44 @@
+local core = require 'core'
+
+return function (lsp, params)
+ local uri = params.textDocument.uri
+ local vm, lines = lsp:loadVM(uri)
+ if not vm then
+ return nil
+ end
+ -- lua是从1开始的,因此都要+1
+ local position = lines:positionAsChar(params.position.line + 1, params.position.character)
+
+ local source = core.findSource(vm, position)
+ if not source then
+ return nil
+ end
+
+ local hover = core.hover(source, lsp)
+ if not hover then
+ return nil
+ end
+
+ local text = ([[
+```lua
+%s
+```
+```lua
+%s
+```
+%s
+```lua
+%s
+```
+%s
+]]):format(hover.label or '', hover.overloads or '', hover.description or '', hover.enum or '', hover.doc or '')
+
+ local response = {
+ contents = {
+ value = text:gsub("```lua\n\n```", ""),
+ kind = 'markdown',
+ }
+ }
+
+ return response
+end
diff --git a/script/src/method/textDocument/implementation.lua b/script/src/method/textDocument/implementation.lua
new file mode 100644
index 00000000..14e2f24c
--- /dev/null
+++ b/script/src/method/textDocument/implementation.lua
@@ -0,0 +1,108 @@
+local core = require 'core'
+
+local function checkWorkSpaceComplete(lsp, source)
+ if not source:bindValue() then
+ return
+ end
+ if not source:bindValue():get 'cross file' then
+ return
+ end
+ lsp:checkWorkSpaceComplete()
+end
+
+local function findResult(lsp, params)
+ local uri = params.textDocument.uri
+ local vm, lines = lsp:loadVM(uri)
+ if not vm then
+ return nil
+ end
+ -- lua是从1开始的,因此都要+1
+ local position = lines:positionAsChar(params.position.line + 1, params.position.character)
+ local source = core.findSource(vm, position)
+ if not source then
+ return nil
+ end
+
+ checkWorkSpaceComplete(lsp, source)
+
+ local positions = core.implementation(vm, source, lsp)
+ if not positions then
+ return nil
+ end
+
+ local locations = {}
+ for i, position in ipairs(positions) do
+ local start, finish, valueUri = position[1], position[2], (position[3] or uri)
+ local _, valueLines = lsp:loadVM(valueUri)
+ if valueLines then
+ local start_row, start_col = valueLines:rowcol(start)
+ local finish_row, finish_col = valueLines:rowcol(finish)
+ locations[#locations+1] = {
+ uri = valueUri,
+ range = {
+ start = {
+ line = start_row - 1,
+ character = start_col - 1,
+ },
+ ['end'] = {
+ line = finish_row - 1,
+ -- 这里不用-1,因为前端期待的是匹配完成后的位置
+ character = finish_col,
+ },
+ }
+ }
+ else
+ locations[#locations+1] = {
+ uri = valueUri,
+ range = {
+ start = {
+ line = 0,
+ character = 0,
+ },
+ ['end'] = {
+ line = 0,
+ character = 0,
+ },
+ }
+ }
+ end
+ end
+
+ if #locations == 0 then
+ return nil
+ end
+
+ return locations
+end
+
+local LastTask
+
+return function (lsp, params)
+ if LastTask then
+ LastTask:remove()
+ LastTask = nil
+ end
+ local result = findResult(lsp, params)
+ if result then
+ return result
+ end
+ return function (response)
+ local count = 0
+ LastTask = ac.loop(0.1, function ()
+ local result = findResult(lsp, params)
+ if result then
+ LastTask:remove()
+ LastTask = nil
+ response(result)
+ return
+ end
+ count = count + 1
+ if lsp:isWaitingCompile() and count < 10 then
+ return
+ end
+ LastTask:remove()
+ LastTask = nil
+ response(nil)
+ end)
+ end
+end
diff --git a/script/src/method/textDocument/onTypeFormatting.lua b/script/src/method/textDocument/onTypeFormatting.lua
new file mode 100644
index 00000000..fc9cbdc9
--- /dev/null
+++ b/script/src/method/textDocument/onTypeFormatting.lua
@@ -0,0 +1,14 @@
+return function (lsp, params)
+ local uri = params.textDocument.uri
+ local vm, lines = lsp:loadVM(uri)
+ --log.debug(table.dump(params))
+ if not vm then
+ return nil
+ end
+ local position = lines:position(params.position.line + 1, params.position.character)
+ local ch = params.ch
+ local options = params.options
+ local tabSize = options.tabSize
+ local insertSpaces = options.insertSpaces
+ return nil
+end
diff --git a/script/src/method/textDocument/publishDiagnostics.lua b/script/src/method/textDocument/publishDiagnostics.lua
new file mode 100644
index 00000000..c767e934
--- /dev/null
+++ b/script/src/method/textDocument/publishDiagnostics.lua
@@ -0,0 +1,163 @@
+local core = require 'core'
+local lang = require 'language'
+local config = require 'config'
+
+local DiagnosticSeverity = {
+ Error = 1,
+ Warning = 2,
+ Information = 3,
+ Hint = 4,
+}
+
+--[[
+/**
+ * Represents a related message and source code location for a diagnostic. This should be
+ * used to point to code locations that cause or related to a diagnostics, e.g when duplicating
+ * a symbol in a scope.
+ */
+export interface DiagnosticRelatedInformation {
+ /**
+ * The location of this related diagnostic information.
+ */
+ location: Location;
+
+ /**
+ * The message of this related diagnostic information.
+ */
+ message: string;
+}
+]]--
+
+local function getRange(start, finish, lines)
+ local start_row, start_col = lines:rowcol(start)
+ local finish_row, finish_col = lines:rowcol(finish)
+ return {
+ start = {
+ line = start_row - 1,
+ character = start_col - 1,
+ },
+ ['end'] = {
+ line = finish_row - 1,
+ -- 这里不用-1,因为前端期待的是匹配完成后的位置
+ character = finish_col,
+ },
+ }
+end
+
+local function createInfo(lsp, data, lines)
+ local diagnostic = {
+ source = lang.script.DIAG_DIAGNOSTICS,
+ range = getRange(data.start, data.finish, lines),
+ severity = data.level,
+ message = data.message,
+ code = data.code,
+ tags = data.tags,
+ }
+ if data.related then
+ local related = {}
+ for _, info in ipairs(data.related) do
+ local _, lines = lsp:getVM(info.uri)
+ if lines then
+ local message = info.message
+ if not message then
+ local start_line = lines:rowcol(info.start)
+ local finish_line = lines:rowcol(info.finish)
+ local chars = {}
+ for n = start_line, finish_line do
+ chars[#chars+1] = lines:line(n)
+ end
+ message = table.concat(chars, '\n')
+ end
+ related[#related+1] = {
+ message = message,
+ location = {
+ uri = info.uri,
+ range = getRange(info.start, info.finish, lines),
+ }
+ }
+ end
+ end
+ diagnostic.relatedInformation = related
+ end
+ return diagnostic
+end
+
+local function buildError(err, lines, uri)
+ local diagnostic = {
+ source = lang.script.DIAG_SYNTAX_CHECK,
+ message = lang.script('PARSER_'..err.type, err.info)
+ }
+ if err.version then
+ local currentVersion = err.info and err.info.version or config.config.runtime.version
+ if type(err.version) == 'table' then
+ diagnostic.message = ('%s(%s)'):format(diagnostic.message, lang.script('DIAG_NEED_VERSION', table.concat(err.version, '/'), currentVersion))
+ else
+ diagnostic.message = ('%s(%s)'):format(diagnostic.message, lang.script('DIAG_NEED_VERSION', err.version, currentVersion))
+ end
+ end
+ if err.level == 'error' then
+ diagnostic.severity = DiagnosticSeverity.Error
+ else
+ diagnostic.severity = DiagnosticSeverity.Warning
+ end
+ local startrow, startcol = lines:rowcol(err.start)
+ local endrow, endcol = lines:rowcol(err.finish)
+ if err.type == 'UNKNOWN' then
+ local _, max = lines:range(endrow)
+ endcol = max
+ end
+ local range = {
+ start = {
+ line = startrow - 1,
+ character = startcol - 1,
+ },
+ ['end'] = {
+ line = endrow - 1,
+ character = endcol,
+ },
+ }
+ diagnostic.range = range
+
+ local related = err.info and err.info.related
+ if related then
+ local start_line = lines:rowcol(related[1])
+ local finish_line = lines:rowcol(related[2])
+ local chars = {}
+ for n = start_line, finish_line do
+ chars[#chars+1] = lines:line(n)
+ end
+ local message = table.concat(chars, '\n')
+ diagnostic.relatedInformation = {
+ {
+ message = message,
+ location = {
+ uri = uri,
+ range = getRange(related[1], related[2], lines),
+ }
+ }
+ }
+ end
+ return diagnostic
+end
+
+return function (lsp, params)
+ local vm = params.vm
+ local lines = params.lines
+ local uri = params.uri
+ local errs = lsp:getAstErrors(uri)
+
+ local diagnostics = {}
+ if vm then
+ local datas = core.diagnostics(vm, lines, uri)
+ for _, data in ipairs(datas) do
+ diagnostics[#diagnostics+1] = createInfo(lsp, data, lines)
+ end
+ end
+ if errs then
+ for _, err in ipairs(errs) do
+ diagnostics[#diagnostics+1] = buildError(err, lines, uri)
+ end
+ end
+
+ return diagnostics
+end
diff --git a/script/src/method/textDocument/references.lua b/script/src/method/textDocument/references.lua
new file mode 100644
index 00000000..0a198323
--- /dev/null
+++ b/script/src/method/textDocument/references.lua
@@ -0,0 +1,86 @@
+local core = require 'core'
+local LastTask
+
+local function findReferences(lsp, uri, position)
+ local vm = lsp:getVM(uri)
+
+ local positions, isGlobal = core.definition(vm, position, 'reference')
+ if not positions then
+ return nil, isGlobal
+ end
+
+ local locations = {}
+ for i, position in ipairs(positions) do
+ local start, finish, valueUri = position[1], position[2], (position[3] or uri)
+ local vm, valueLines = lsp:getVM(valueUri)
+ if valueLines then
+ local start_row, start_col = valueLines:rowcol(start)
+ local finish_row, finish_col = valueLines:rowcol(finish)
+ locations[#locations+1] = {
+ uri = valueUri,
+ range = {
+ start = {
+ line = start_row - 1,
+ character = start_col - 1,
+ },
+ ['end'] = {
+ line = finish_row - 1,
+ -- 这里不用-1,因为前端期待的是匹配完成后的位置
+ character = finish_col,
+ },
+ }
+ }
+ elseif vm then
+ locations[#locations+1] = {
+ uri = valueUri,
+ range = {
+ start = {
+ line = 0,
+ character = 0,
+ },
+ ['end'] = {
+ line = 0,
+ character = 0,
+ },
+ }
+ }
+ end
+ end
+
+ if #locations == 0 then
+ return nil, isGlobal
+ end
+
+ return locations, isGlobal
+end
+
+return function (lsp, params)
+ local uri = params.textDocument.uri
+ local declarat = params.context.includeDeclaration
+ local vm, lines = lsp:loadVM(uri)
+ if not vm then
+ return nil
+ end
+
+ if LastTask then
+ LastTask:remove()
+ LastTask = nil
+ end
+
+ -- lua是从1开始的,因此都要+1
+ local position = lines:positionAsChar(params.position.line + 1, params.position.character)
+
+ return function (response)
+ local clock = os.clock()
+ LastTask = ac.loop(0.1, function ()
+ local positions, isGlobal = findReferences(lsp, uri, position)
+ if isGlobal and lsp:isWaitingCompile() and os.clock() - clock < 5 then
+ return
+ end
+ response(positions)
+ LastTask:remove()
+ LastTask = nil
+ end)
+ LastTask:onTimer()
+ end
+end
diff --git a/script/src/method/textDocument/rename.lua b/script/src/method/textDocument/rename.lua
new file mode 100644
index 00000000..6da9c721
--- /dev/null
+++ b/script/src/method/textDocument/rename.lua
@@ -0,0 +1,50 @@
+local core = require 'core'
+
+return function (lsp, params)
+ local uri = params.textDocument.uri
+ local newName = params.newName
+ local vm, lines = lsp:loadVM(uri)
+ if not vm then
+ return {}
+ end
+ local position = lines:positionAsChar(params.position.line + 1, params.position.character)
+ local positions = core.rename(vm, position, newName)
+ if not positions then
+ return {}
+ end
+
+ local changes = {}
+ for _, position in ipairs(positions) do
+ local start, finish, uri = position[1], position[2], position[3]
+ local _, lines = lsp:getVM(uri)
+ if not lines then
+ goto CONTINUE
+ end
+ local start_row, start_col = lines:rowcol(start)
+ local finish_row, finish_col = lines:rowcol(finish)
+ if not changes[uri] then
+ changes[uri] = {}
+ end
+ changes[uri][#changes[uri]+1] = {
+ newText = newName,
+ range = {
+ start = {
+ line = start_row - 1,
+ character = start_col - 1,
+ },
+ ['end'] = {
+ line = finish_row - 1,
+ -- 这里不用-1,因为前端期待的是匹配完成后的位置
+ character = finish_col,
+ },
+ }
+ }
+ ::CONTINUE::
+ end
+
+ local response = {
+ changes = changes,
+ }
+
+ return response
+end
diff --git a/script/src/method/textDocument/signatureHelp.lua b/script/src/method/textDocument/signatureHelp.lua
new file mode 100644
index 00000000..01d6289d
--- /dev/null
+++ b/script/src/method/textDocument/signatureHelp.lua
@@ -0,0 +1,50 @@
+local core = require 'core'
+
+return function (lsp, params)
+ local uri = params.textDocument.uri
+ local vm, lines = lsp:loadVM(uri)
+ if not vm then
+ return
+ end
+ local position = lines:position(params.position.line + 1, params.position.character + 1)
+ local hovers = core.signature(vm, position)
+ if not hovers then
+ return
+ end
+
+ local hover = hovers[1]
+ local desc = {}
+ desc[#desc+1] = hover.description
+ local active
+ local signatures = {}
+ for i, hover in ipairs(hovers) do
+ local signature = {
+ label = hover.label,
+ documentation = {
+ kind = 'markdown',
+ value = table.concat(desc, '\n'),
+ },
+ }
+ if hover.argLabel then
+ if not active then
+ active = i
+ end
+ signature.parameters = {
+ {
+ label = {
+ hover.argLabel[1] - 1,
+ hover.argLabel[2],
+ }
+ }
+ }
+ end
+ signatures[i] = signature
+ end
+
+ local response = {
+ signatures = signatures,
+ activeSignature = active and active - 1 or 0,
+ }
+
+ return response
+end