-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlsp.tl
More file actions
148 lines (122 loc) · 3.6 KB
/
lsp.tl
File metadata and controls
148 lines (122 loc) · 3.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
local unistd = require("posix.unistd")
local wait = require("posix.sys.wait")
local json = require("cjson")
local record lsp
record Server
new: function(lang: string, pathname: string, args: {string}): Server, string
send: function(Server, cmd: {string:any}): integer
recv: function(Server): {string:any}, string
done: function(Server)
initialize: function(Server, params: {string:any})
method: function(Server, method: string, params: {string:any})
queue: {{string:any}}
pop: function(Server): {string:any}
lang: string
stdin: integer
stdout: integer
stderr: integer
msgid: integer
pid: integer
end
null: json.Null
end
lsp.null = json.null
function lsp.Server.new(lang: string, pathname: string, args: {string}): lsp.Server, string
local self: lsp.Server = {}
local stdin_r, stdin_w = unistd.pipe()
if stdin_w is string then
return nil, "failed creating pipe: " .. stdin_w
end
local stdout_r, stdout_w = unistd.pipe()
if stdout_w is string then
return nil, "failed creating pipe: " .. stdout_w
end
local stderr_r, stderr_w = unistd.pipe()
if stderr_w is string then
return nil, "failed creating pipe: " .. stderr_w
end
local pid = unistd.fork()
if pid == 0 then
unistd.close(stdin_w)
unistd.close(stdout_r)
unistd.close(stderr_r)
unistd.dup2(stdin_r, unistd.STDIN_FILENO)
unistd.dup2(stdout_w, unistd.STDOUT_FILENO)
unistd.dup2(stderr_w, unistd.STDERR_FILENO)
unistd.exec(pathname, args or {})
end
unistd.close(stdin_r)
unistd.close(stdout_w)
unistd.close(stderr_w)
self.lang = lang
self.stdin = stdin_w
self.stdout = stdout_r
self.stderr = stderr_r
self.pid = pid
self.msgid = 1
self.queue = {}
return setmetatable(self, { __index = lsp.Server })
end
function lsp.Server:send(cmd: {string:any}): integer
local id = self.msgid
self.msgid = self.msgid + 1
cmd.jsonrpc = "2.0"
cmd.id = id
local j = json.encode(cmd)
local out: {string} = {}
table.insert(out, "Content-Length: " .. #j)
table.insert(out, "")
table.insert(out, j)
unistd.write(self.stdin, table.concat(out, "\r\n"))
return id
end
function lsp.Server:recv(): {string:any}, string
local msg, err = unistd.read(self.stdout, 50)
if #msg > 0 then
local l, rest = msg:match("^[Cc]ontent%-[Ll]ength: ?(%d+)\r\n\r\n(.*)$")
local len = math.tointeger(tonumber(l))
if len > #rest then
local more = unistd.read(self.stdout, len - #rest)
if more then
msg = rest .. more
end
else
msg = rest
end
return json.decode(msg)
else
return nil, err
end
end
local function recv_until(self: lsp.Server, id: integer): {string:any}
local brk = false
while not brk do
local r = self:recv()
if r.id == id then
return r
else
table.insert(self.queue, r)
end
end
end
function lsp.Server:initialize(params: {string:any})
params.processId = unistd.getpid()
if not params.rootUri then
params.rootUri = json.null
end
self:method("initialize", params)
self:method("initialized", {})
end
function lsp.Server:method(method: string, params: {string:any}): {string:any}
local i = self:send({ method = method, params = params })
return recv_until(self, i)
end
function lsp.Server:pop(): {string:any}
return table.remove(self.queue)
end
function lsp.Server:done()
unistd.close(self.stdin)
unistd.close(self.stdout)
wait.wait(self.pid)
end
return lsp