diff options
author | 最萌小汐 <sumneko@hotmail.com> | 2018-12-05 15:43:25 +0800 |
---|---|---|
committer | 最萌小汐 <sumneko@hotmail.com> | 2018-12-05 15:43:25 +0800 |
commit | de0deb7f5edd78d1710f7b2babdbf176d5703c2f (patch) | |
tree | b0224dc060229565e9f90aff5e569504e10810cb | |
parent | 4fe8c95622242835230378ce42cde8d69ae6f5db (diff) | |
download | lua-language-server-de0deb7f5edd78d1710f7b2babdbf176d5703c2f.zip |
使用另一个线程来读取标准输入并解析为协议
-rw-r--r-- | server/src/lsp.lua | 54 | ||||
-rw-r--r-- | server/src/proto.lua | 63 | ||||
-rw-r--r-- | server/src/service.lua | 56 | ||||
-rw-r--r-- | server/src/thread.lua | 60 |
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, +} |