summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lsp/init.lua122
-rw-r--r--src/lsp/method.lua8
-rw-r--r--src/service/init.lua29
-rw-r--r--src/utility/init.lua2
-rw-r--r--src/utility/io.lua (renamed from src/utility.lua)0
-rw-r--r--src/utility/table.lua62
6 files changed, 220 insertions, 3 deletions
diff --git a/src/lsp/init.lua b/src/lsp/init.lua
new file mode 100644
index 00000000..5af4d876
--- /dev/null
+++ b/src/lsp/init.lua
@@ -0,0 +1,122 @@
+local json = require 'json'
+local Method = require 'lsp.method'
+
+local ErrorCodes = {
+ -- Defined by JSON RPC
+ ParseError = -32700,
+ InvalidRequest = -32600,
+ MethodNotFound = -32601,
+ InvalidParams = -32602,
+ InternalError = -32603,
+ serverErrorStart = -32099,
+ serverErrorEnd = -32000,
+ ServerNotInitialized = -32002,
+ UnknownErrorCode = -32001,
+
+ -- Defined by the protocol.
+ RequestCancelled = -32800,
+}
+
+local mt = {}
+mt.__index = mt
+
+mt._input = nil
+mt._output = nil
+mt._inited = false
+mt._method = nil
+
+function mt:_callback(method, params)
+ local f = self._method
+ if f then
+ return f(method, params)
+ end
+ return nil
+end
+
+function mt:_send(data)
+ local f = self._output
+ if not f then
+ return
+ end
+ local content = json.encode(data)
+ local buf = ('Content-Length: %d\r\n\r\n%s'):format(#content + 2, content)
+ f(buf)
+end
+
+function mt:_readAsContent(header)
+ local len = tonumber(header:match('%d+'))
+ if not len then
+ log.error('错误的协议头:', header)
+ return
+ end
+ local buf = self:read(len)
+ local suc, res = pcall(json.decode, buf)
+ if not suc then
+ log.error('错误的协议:', buf)
+ return
+ end
+ log.debug(table.dump(res))
+ local id = res.id
+ local method = res.method
+ local params = res.params
+ local f = Method[method]
+ if not f then
+ return
+ end
+ local response, err = f(self, params)
+ if response then
+ self._send {
+ id = id,
+ result = response,
+ }
+ else
+ self._send {
+ id = id,
+ error = {
+ code = ErrorCodes.UnknownErrorCode,
+ message = err,
+ },
+ }
+ end
+end
+
+function mt:setInput(input)
+ self._input = input
+end
+
+function mt:setOutput(output)
+ self._output = output
+end
+
+function mt:read(mode)
+ if not self._input then
+ return nil
+ end
+ return self._input(mode)
+end
+
+function mt:start(method)
+ self._method = method
+ while true do
+ local header = self:read 'L'
+ if not header then
+ return
+ end
+ if header:sub(1, #'Content-Length') == 'Content-Length' then
+ self:_readAsContent(header)
+ elseif header:sub(1, #'Content-Type') == 'Content-Type' then
+ else
+ log.error('错误的协议头:', header)
+ end
+ end
+ return true
+end
+
+function mt:stop()
+ self._input = nil
+ self._output = nil
+end
+
+return function ()
+ return setmetatable({}, mt)
+end
diff --git a/src/lsp/method.lua b/src/lsp/method.lua
new file mode 100644
index 00000000..64767f3b
--- /dev/null
+++ b/src/lsp/method.lua
@@ -0,0 +1,8 @@
+local Method = {}
+
+function Method:initialize(params)
+ self._inited = true
+ return self:_callback('initialize', params)
+end
+
+return Method
diff --git a/src/service/init.lua b/src/service/init.lua
index 53e6e4b3..5e54b5b3 100644
--- a/src/service/init.lua
+++ b/src/service/init.lua
@@ -1,5 +1,16 @@
local sleep = require 'ffi.sleep'
local ext = require 'process.ext'
+local lsp = require 'lsp'
+
+local Method = {}
+
+function Method.initialize()
+ return {
+ capabilities = {
+ definitionProvider = true,
+ }
+ }
+end
local function listen(self, input, output)
if input then
@@ -18,9 +29,21 @@ local function listen(self, input, output)
io.stdout:setvbuf 'no'
end
- for line in io.input():lines 'L' do
- log.debug('标准输入:\n', line)
- end
+ local session = lsp()
+ session:setInput(function (mode)
+ return io.read(mode)
+ end)
+ session:setOutput(function (buf)
+ io.write(buf)
+ log.debug(buf)
+ end)
+ session:start(function (method, params)
+ local f = Method[method]
+ if f then
+ return f(params)
+ end
+ return nil
+ end)
end
local mt = {
diff --git a/src/utility/init.lua b/src/utility/init.lua
new file mode 100644
index 00000000..55bd6b0d
--- /dev/null
+++ b/src/utility/init.lua
@@ -0,0 +1,2 @@
+require 'utility.io'
+require 'utility.table'
diff --git a/src/utility.lua b/src/utility/io.lua
index 73c65237..73c65237 100644
--- a/src/utility.lua
+++ b/src/utility/io.lua
diff --git a/src/utility/table.lua b/src/utility/table.lua
new file mode 100644
index 00000000..37a74632
--- /dev/null
+++ b/src/utility/table.lua
@@ -0,0 +1,62 @@
+local table_sort = table.sort
+local string_rep = string.rep
+local type = type
+local pairs = pairs
+local ipairs = ipairs
+local math_type = math.type
+
+local TAB = setmetatable({}, { __index = function (self, n)
+ self[n] = string_rep('\t', n)
+ return self[n]
+end})
+
+local KEY = {}
+
+function table.dump(tbl)
+ if type(tbl) ~= 'table' then
+ error('必须是表')
+ end
+ local table_mark = {}
+ local lines = {}
+ lines[#lines+1] = '{'
+ local function unpack(tbl, tab)
+ if table_mark[tbl] then
+ error('不能循环引用')
+ end
+ table_mark[tbl] = true
+ local keys = {}
+ for key in pairs(tbl) do
+ if type(key) == 'string' then
+ if key:find('[^%w_]') then
+ KEY[key] = ('[%q]'):format(key)
+ else
+ KEY[key] = key
+ end
+ elseif math_type(key) == 'integer' then
+ KEY[key] = ('[%d]'):format(key)
+ else
+ error('必须使用字符串或整数作为键')
+ end
+ keys[#keys+1] = key
+ end
+ table_sort(keys, function (a, b)
+ return KEY[a] < KEY[b]
+ end)
+ for _, key in ipairs(keys) do
+ local value = tbl[key]
+ local tp = type(value)
+ if tp == 'table' then
+ lines[#lines+1] = ('%s%s = {'):format(TAB[tab+1], KEY[key])
+ unpack(value, tab+1)
+ lines[#lines+1] = ('%s},'):format(TAB[tab+1])
+ elseif tp == 'string' or tp == 'number' or tp == 'boolean' then
+ lines[#lines+1] = ('%s%s = %q,'):format(TAB[tab+1], KEY[key], value)
+ else
+ error(('不支持的值类型[%s]'):format(tp))
+ end
+ end
+ end
+ unpack(tbl, 0)
+ lines[#lines+1] = '}'
+ return table.concat(lines, '\n')
+end