summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author最萌小汐 <sumneko@hotmail.com>2018-12-05 15:43:25 +0800
committer最萌小汐 <sumneko@hotmail.com>2018-12-05 15:43:25 +0800
commitde0deb7f5edd78d1710f7b2babdbf176d5703c2f (patch)
treeb0224dc060229565e9f90aff5e569504e10810cb
parent4fe8c95622242835230378ce42cde8d69ae6f5db (diff)
downloadlua-language-server-de0deb7f5edd78d1710f7b2babdbf176d5703c2f.zip
使用另一个线程来读取标准输入并解析为协议
-rw-r--r--server/src/lsp.lua54
-rw-r--r--server/src/proto.lua63
-rw-r--r--server/src/service.lua56
-rw-r--r--server/src/thread.lua60
4 files changed, 144 insertions, 89 deletions
diff --git a/server/src/lsp.lua b/server/src/lsp.lua
index e4bbb5f4..2b5f2c58 100644
--- a/server/src/lsp.lua
+++ b/server/src/lsp.lua
@@ -45,37 +45,10 @@ function mt:_send(data)
f(buf)
end
-function mt:_readProtoHead()
- local header = self:read 'l'
- if not header then
- return
- end
- if header:sub(1, #'Content-Length') == 'Content-Length' then
- return header
- elseif header:sub(1, #'Content-Type') == 'Content-Type' then
- else
- log.error('错误的协议头:', header)
- end
-end
-
-function mt:_readProtoContent(header)
- local len = tonumber(header:match('%d+'))
- if not len then
- log.error('错误的协议头:', header)
- return true
- end
- local buf = self:read(len+2)
- if not buf then
- return
- end
- local suc, res = pcall(json.decode, buf)
- if not suc then
- log.error('错误的协议:', buf)
- return true
- end
- local id = res.id
- local method = res.method
- local params = res.params
+function mt:_doProto(proto)
+ local id = proto.id
+ local method = proto.method
+ local params = proto.params
local response, err = self:_callback(method, params)
if id then
if response then
@@ -96,7 +69,6 @@ function mt:_readProtoContent(header)
if response == nil then
log.error(err or ('没有回应:' .. method))
end
- return true
end
function mt:_buildTextCache()
@@ -197,18 +169,12 @@ function mt:setMethod(method)
end
function mt:runStep()
- if not self._header then
- -- 如果没有协议头,则尝试读一条协议头
- self._header = self:_readProtoHead()
- end
- if self._header then
- -- 如果有协议头,则读取协议内容
- local suc = self:_readProtoContent(self._header)
- if suc then
- -- 协议内容读取成功后重置
- self._header = nil
- self._idle_clock = os.clock()
- end
+ local proto = self._input()
+ if proto then
+ -- 协议内容读取成功后重置
+ self._header = nil
+ self._idle_clock = os.clock()
+ self:_doProto(proto)
return
end
if os.clock() - self._idle_clock >= 1 then
diff --git a/server/src/proto.lua b/server/src/proto.lua
new file mode 100644
index 00000000..367abc02
--- /dev/null
+++ b/server/src/proto.lua
@@ -0,0 +1,63 @@
+local thread = require 'bee.thread'
+local json = require 'json'
+local response = thread.channel 'response'
+
+local log = setmetatable({}, {__index = function (self, level)
+ self[level] = function (...)
+ local t = table.pack(...)
+ for i = 1, t.n do
+ t[i] = tostring(t[i])
+ end
+ local buf = table.concat(t, '\t')
+ response:push('log', {
+ level = level,
+ buf = buf,
+ })
+ end
+end})
+
+local function readProtoHeader()
+ local header = io.read 'l'
+ if header:sub(1, #'Content-Length') == 'Content-Length' then
+ return header
+ elseif header:sub(1, #'Content-Type') == 'Content-Type' then
+ return nil
+ else
+ log.error('错误的协议头:', header)
+ return nil
+ end
+end
+
+local function readProtoContent(header)
+ local len = tonumber(header:match('%d+'))
+ if not len then
+ log.error('错误的协议头:', header)
+ return nil
+ end
+ local buf = io.read(len+2)
+ if not buf then
+ return nil
+ end
+ local suc, res = pcall(json.decode, buf)
+ if not suc then
+ log.error('错误的协议:', buf)
+ return nil
+ end
+ return res
+end
+
+local function readProto()
+ local header = readProtoHeader()
+ if not header then
+ return
+ end
+ local data = readProtoContent(header)
+ if not data then
+ return
+ end
+ response:push('proto', data)
+end
+
+while true do
+ readProto()
+end
diff --git a/server/src/service.lua b/server/src/service.lua
index 9376e15a..40769a28 100644
--- a/server/src/service.lua
+++ b/server/src/service.lua
@@ -1,55 +1,20 @@
local subprocess = require 'bee.subprocess'
-local thread = require 'bee.thread'
local lsp = require 'lsp'
local Method = require 'method'
local fs = require 'bee.filesystem'
+local thread = require 'thread'
-local function listen(self, input, output)
- if input then
- log.info('指定输入文件,路径为:', input)
- fs.create_directories(input:parent_path())
- io.input(io.open(input:string(), 'rb'))
- else
- subprocess.filemode(io.stdin, 'b')
- end
- if output then
- log.info('指定输出文件,路径为:', output)
- fs.create_directories(output:parent_path())
- io.output(io.open(output:string(), 'wb'))
- else
- subprocess.filemode(io.stdout, 'b')
- end
- io.input():setvbuf 'no'
- io.output():setvbuf 'no'
+local function listen(self)
+ subprocess.filemode(io.stdin, 'b')
+ subprocess.filemode(io.stdout, 'b')
+ io.stdin:setvbuf 'no'
+ io.stdout:setvbuf 'no'
+
+ thread.require 'proto'
local session = lsp()
- local cache = ''
- session:setInput(function (mode)
- local num = subprocess.peek(io.input())
- if num > 0 then
- cache = cache .. io.read(num)
- end
- if type(mode) == 'number' then
- if #cache < mode then
- return nil
- end
- local res = cache:sub(1, mode)
- cache = cache:sub(mode + 1)
- return res
- elseif mode == 'l' then
- local pos = cache:find '[\r\n]'
- if not pos then
- return nil
- end
- local res = cache:sub(1, pos - 1)
- if cache:sub(pos, pos + 1) == '\r\n' then
- cache = cache:sub(pos + 2)
- else
- cache = cache:sub(pos + 1)
- end
- return res
- end
- return nil
+ session:setInput(function ()
+ return thread.proto()
end)
session:setOutput(function (buf)
io.write(buf)
@@ -77,6 +42,7 @@ local function listen(self, input, output)
end)
while true do
+ thread.on_tick()
session:runStep()
thread.sleep(0.001)
end
diff --git a/server/src/thread.lua b/server/src/thread.lua
new file mode 100644
index 00000000..af206a50
--- /dev/null
+++ b/server/src/thread.lua
@@ -0,0 +1,60 @@
+local thread = require 'bee.thread'
+local response, errlog
+local proto_cache
+
+local api = {
+ log = function (data)
+ local level = data.level
+ local buf = data.buf
+ log[level](buf)
+ end,
+ proto = function (data)
+ if not proto_cache then
+ proto_cache = {}
+ end
+ proto_cache[#proto_cache+1] = data
+ end,
+}
+
+local function on_tick()
+ if not response then
+ return
+ end
+ local ok, msg = errlog:pop()
+ if ok then
+ log.error(msg)
+ end
+ local ok, who, data = response:pop()
+ if ok then
+ api[who](data)
+ end
+end
+
+local function require(name)
+ if not response then
+ thread.newchannel 'response'
+ response = thread.channel 'response'
+ errlog = thread.channel 'errlog'
+ end
+
+ local buf = ("\z
+ package.cpath = %q\n\z
+ package.path = %q\n\z
+ require %q\n\z
+ "):format(package.cpath, package.path, name)
+ return thread.thread(buf)
+end
+
+local function proto()
+ if not proto_cache then
+ return nil
+ end
+ return table.remove(proto_cache, 1)
+end
+
+return {
+ require = require,
+ on_tick = on_tick,
+ proto = proto,
+ sleep = thread.sleep,
+}