aboutsummaryrefslogtreecommitdiff
path: root/parser.lua
diff options
context:
space:
mode:
authorFlorian Fischer <florian.fischer@muhq.space>2021-08-16 14:01:15 +0200
committerFlorian Fischer <florian.fischer@muhq.space>2021-08-16 14:01:15 +0200
commit483ed22f686010cb9fe6f8ec4512263dd8d95706 (patch)
treebacfe45fce2e3783e0f25659e836918afde53212 /parser.lua
parentbcab92186e8bd66e6841f33275d261af00e43a25 (diff)
downloadvis-lspc-483ed22f686010cb9fe6f8ec4512263dd8d95706.tar.gz
vis-lspc-483ed22f686010cb9fe6f8ec4512263dd8d95706.zip
move and fix parser code
Move LSP message parser code into parser.lua. Fix parsing when message headers are split between to data junks. Simplify parser code using one string buffer and consuming already parsed message parts.
Diffstat (limited to 'parser.lua')
-rw-r--r--parser.lua75
1 files changed, 75 insertions, 0 deletions
diff --git a/parser.lua b/parser.lua
new file mode 100644
index 0000000..f1b09f5
--- /dev/null
+++ b/parser.lua
@@ -0,0 +1,75 @@
+-- Copyright (c) 2021 Florian Fischer. All rights reserved.
+-- Use of this source code is governed by a MIT license found in the LICENSE file.
+
+-- Parse the data send by a language server
+-- Note the chunks received may not end with the end of a message.
+-- In the worst case a data chunk contains two partial messages on at the beginning
+-- and one at the end
+
+local function add(p, data)
+ p.data = p.data .. data
+
+ -- we have not seen a complete header in data yet
+ if not p.exp_len then
+ -- search for the end of a header
+ -- LSP message format: 'header\r\n\r\nbody'
+ local header_end, content_start = p.data:find('\r\n\r\n')
+
+ -- header is not complete yet -> save data and wait for more
+ if not header_end then
+ return
+ end
+
+ -- got a complete header
+ local header = p.data:sub(1, header_end)
+
+ -- extract content length from the header
+ p.exp_len = tonumber(header:match('%d+'))
+ if not p.exp_len then
+ return 'invalid header in data: ' .. p.data
+ end
+
+ -- consume header from data
+ p.data = p.data:sub(content_start + 1)
+ end
+
+ local data_avail = string.len(p.data)
+ -- we have no complete message yet -> await more data
+ if p.exp_len > data_avail then
+ return
+ end
+
+ local complete_msg = p.data:sub(1, p.exp_len)
+ table.insert(p.msgs, complete_msg)
+
+ -- consume complete_msg from data
+ p.data = p.data:sub(p.exp_len + 1)
+
+ local leftover = data_avail - p.exp_len
+
+ -- reset exp_len to search for a new header
+ p.exp_len = nil
+
+ if leftover > 0 then
+ return p:add('')
+ end
+end
+
+local function get_msgs(p)
+ local msgs = p.msgs
+ p.msgs = {}
+ return msgs
+end
+
+local function Parser()
+ local parser = {
+ add = add,
+ get_msgs = get_msgs,
+ exp_len = nil,
+ data = '',
+ msgs = {},
+ }
+ return parser
+end
+
+return {Parser = Parser}