summaryrefslogtreecommitdiff
path: root/server/src/lsp.lua
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/lsp.lua')
-rw-r--r--server/src/lsp.lua155
1 files changed, 155 insertions, 0 deletions
diff --git a/server/src/lsp.lua b/server/src/lsp.lua
new file mode 100644
index 00000000..4a88aba6
--- /dev/null
+++ b/server/src/lsp.lua
@@ -0,0 +1,155 @@
+local json = require 'json'
+local parser = require 'parser'
+
+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._method = nil
+mt._file = nil
+
+function mt:_callback(method, params)
+ local f = self._method
+ if f then
+ return f(method, params)
+ end
+ return nil, '没有注册method'
+end
+
+function mt:_send(data)
+ local f = self._output
+ if not f then
+ return
+ end
+ data.jsonrpc = '2.0'
+ local content = json.encode(data)
+ local buf = ('Content-Length: %d\r\n\r\n%s'):format(#content, 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+2)
+ local suc, res = pcall(json.decode, buf)
+ if not suc then
+ log.error('错误的协议:', buf)
+ return
+ end
+ local id = res.id
+ local method = res.method
+ local params = res.params
+ local response, err = self:_callback(method, params)
+ if id then
+ if response then
+ self:_send {
+ id = id,
+ result = response,
+ }
+ else
+ self:_send {
+ id = id,
+ error = {
+ code = ErrorCodes.UnknownErrorCode,
+ message = err or ('没有回应:' .. method),
+ },
+ }
+ end
+ end
+ if not response then
+ log.error(err or ('没有回应:' .. method))
+ end
+ -- 运行时不清理垃圾,在回复前端之后清理垃圾
+ collectgarbage()
+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:saveText(uri, version, text)
+ local obj = self._file[uri]
+ if obj then
+ if obj.version >= version then
+ return
+ end
+ obj.version = version
+ obj.text = text
+ else
+ self._file[uri] = {
+ version = version,
+ text = text,
+ }
+ end
+end
+
+function mt:loadText(uri)
+ local obj = self._file[uri]
+ if not obj then
+ return nil
+ end
+ return obj.text
+end
+
+function mt:removeText(uri)
+ self._file[uri] = nil
+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({
+ _file = {},
+ }, mt)
+end