@@ -4,12 +4,12 @@ import std/[syncio, os, json, strutils, strformat, streams, oids, sequtils, time
4
4
import ls, utils
5
5
import protocol/ types, chronos/ threadsync
6
6
7
- type
7
+ type
8
8
LspClientResponse* = object
9
9
jsonrpc* : JsonRPC2
10
10
id* : string
11
11
result * : JsonNode
12
-
12
+
13
13
Rpc* = proc (params: RequestParamsRx): Future[JsonString] {.gcsafe, raises: [].}
14
14
15
15
template flavorUsesAutomaticObjectSerialization(T: type JrpcSys): bool =
@@ -40,14 +40,13 @@ proc toJson*(params: RequestParamsRx): JsonNode =
40
40
for p in params.positional:
41
41
result .add parseJson($ p)
42
42
43
- proc wrapRpc* [T](
44
- fn: proc(params: T): Future[auto ] {.gcsafe, raises: [].}
45
- ): Rpc =
43
+ proc wrapRpc* [T](fn: proc(params: T): Future[auto ] {.gcsafe, raises: [].}): Rpc =
46
44
return proc(params: RequestParamsRx): Future[JsonString] {.gcsafe, async.} =
47
45
var val = params.to(T)
48
46
when typeof(fn(val)) is Future[void ]: #Notification
49
47
await fn(val)
50
- return JsonString("{}") #Client doesnt expect a response. Handled in processMessage
48
+ return
49
+ JsonString("{}") #Client doesnt expect a response. Handled in processMessage
51
50
else:
52
51
let res = await fn(val)
53
52
return JsonString($(%*res))
@@ -66,21 +65,23 @@ proc wrapRpc*[T](
66
65
return JsonString($(%*res))
67
66
68
67
proc addRpcToCancellable*(ls: LanguageServer, rpc: Rpc): Rpc =
69
- return proc(params: RequestParamsRx): Future[JsonString] {.gcsafe, raises:[].} =
68
+ return proc(params: RequestParamsRx): Future[JsonString] {.gcsafe, raises: [].} =
70
69
try:
71
70
let idRequest = get[uint ](params, "idRequest")
72
71
let name = get[string ](params, "method")
73
- ls.pendingRequests[idRequest] = PendingRequest(id: idRequest, name: name, startTime: now(), state: prsOnGoing)
72
+ ls.pendingRequests[idRequest] =
73
+ PendingRequest(id: idRequest, name: name, startTime: now(), state: prsOnGoing)
74
74
ls.sendStatusChanged
75
75
var fut = rpc(params)
76
- ls.pendingRequests[idRequest].request = fut #we need to add it before because the rpc may access to the pendingRequest to set the projectFile
77
- fut.addCallback proc (d: pointer ) =
76
+ ls.pendingRequests[idRequest].request = fut
77
+ #we need to add it before because the rpc may access to the pendingRequest to set the projectFile
78
+ fut.addCallback proc(d: pointer ) =
78
79
try:
79
80
ls.pendingRequests[idRequest].state = prsComplete
80
81
ls.pendingRequests[idRequest].endTime = now()
81
82
ls.sendStatusChanged
82
83
except KeyError:
83
- error "Error completing pending requests. Id not found in pending requests"
84
+ error "Error completing pending requests. Id not found in pending requests"
84
85
return fut
85
86
except KeyError as ex:
86
87
error "IdRequest not found in the request params"
@@ -89,36 +90,36 @@ proc addRpcToCancellable*(ls: LanguageServer, rpc: Rpc): Rpc =
89
90
error "Error adding request to cancellable requests"
90
91
writeStackTrace(ex)
91
92
92
-
93
93
proc processContentLength*(inputStream: FileStream): string =
94
94
result = inputStream.readLine()
95
95
if result .startsWith(CONTENT_LENGTH):
96
96
let parts = result .split(" ")
97
97
let length = parseInt(parts[ 1])
98
98
discard inputStream.readLine() # skip the \r\n
99
99
result = newString(length)
100
- for i in 0 ..< length:
100
+ for i in 0 ..< length:
101
101
result [i] = inputStream.readChar()
102
102
else :
103
103
error " No content length \n "
104
104
105
- proc processContentLength* (transport: StreamTransport, error: bool = true ): Future[string ] {.async:(raises:[]).} =
105
+ proc processContentLength* (
106
+ transport: StreamTransport, error: bool = true
107
+ ): Future[string ] {.async: (raises: []).} =
106
108
try :
107
109
result = await transport.readLine()
108
110
if result .startsWith(CONTENT_LENGTH):
109
111
let parts = result .split(" " )
110
112
let length = parseInt(parts[1 ])
111
113
discard await transport.readLine() # skip the \r\n
112
114
result = (await transport.read(length)).mapIt($ (it.char )).join()
113
-
114
115
else :
115
116
if error:
116
117
error " No content length \n "
117
118
except TransportError as ex:
118
119
if error:
119
120
error " Error reading content length" , msg = ex.msg
120
121
except CatchableError as ex:
121
- if error:
122
+ if error:
122
123
error " Error reading content length" , msg = ex.msg
123
124
124
125
proc readStdin* (ctx: ptr ReadStdinContext) {.thread.} =
@@ -130,14 +131,14 @@ proc readStdin*(ctx: ptr ReadStdinContext) {.thread.} =
130
131
discard ctx.onStdReadSignal.fireSync()
131
132
discard ctx.onMainReadSignal.waitSync()
132
133
133
- proc wrapContentWithContentLenght* (content: string ): string =
134
+ proc wrapContentWithContentLenght* (content: string ): string =
134
135
let contentLenght = content.len + 1
135
- & " { CONTENT_LENGTH} { contentLenght} { CRLF} { CRLF} { content} \n "
136
+ & " { CONTENT_LENGTH} { contentLenght} { CRLF} { CRLF} { content} \n "
136
137
137
138
proc writeOutput* (ls: LanguageServer, content: JsonNode) =
138
139
let res = wrapContentWithContentLenght($ content)
139
140
try :
140
- case ls.transportMode:
141
+ case ls.transportMode
141
142
of stdio:
142
143
ls.outStream.write(res)
143
144
ls.outStream.flush()
@@ -150,7 +151,7 @@ proc runRpc(ls: LanguageServer, req: RequestRx, rpc: RpcProc): Future[void] {.as
150
151
try :
151
152
let res = await rpc(req.params)
152
153
if res.string in [" " , " {}" ]:
153
- return # Notification (see wrapRpc). The client doesnt expect a response
154
+ return # Notification (see wrapRpc). The client doesnt expect a response
154
155
var json = newJObject()
155
156
json[" jsonrpc" ] = %* " 2.0"
156
157
if req.id.kind == riNumber:
@@ -163,18 +164,23 @@ proc runRpc(ls: LanguageServer, req: RequestRx, rpc: RpcProc): Future[void] {.as
163
164
error " [RunRPC] " , msg = ex.msg, req = req.`method`
164
165
writeStackTrace(ex = ex)
165
166
166
- proc processMessage(ls: LanguageServer, message: string ) {.raises:[].} =
167
+ proc processMessage(ls: LanguageServer, message: string ) {.raises: [].} =
167
168
try :
168
- let contentJson = parseJson(message) # OPT oportunity reuse the same JSON already parsed
169
+ let contentJson = parseJson(message)
170
+ # OPT oportunity reuse the same JSON already parsed
169
171
let isReq = " method" in contentJson
170
172
if isReq:
171
173
debug " [Processsing Message]" , request = contentJson[" method" ]
172
174
var fut = Future[JsonString]()
173
175
var req = JrpcSys.decode(message, RequestRx)
174
176
if req.params.kind == rpNamed and req.id.kind == riNumber:
175
177
#Some requests have no id but for others we need to pass the id to the wrapRpc as the id information is lost in the rpc proc
176
- req.params.named.add ParamDescNamed(name: "idRequest", value: JsonString($(%req.id.num)))
177
- req.params.named.add ParamDescNamed(name: "method", value: JsonString($(contentJson["method " ])))
178
+ req.params.named.add ParamDescNamed(
179
+ name: "idRequest", value: JsonString($(%req.id.num))
180
+ )
181
+ req.params.named.add ParamDescNamed(
182
+ name: "method", value: JsonString($(contentJson["method " ]))
183
+ )
178
184
let rpc = ls.srv.router.procs.getOrDefault(req.meth.get)
179
185
if rpc.isNil:
180
186
error " [Processsing Message] rpc method not found: " , msg = req.meth.get
@@ -184,15 +190,15 @@ proc processMessage(ls: LanguageServer, message: string) {.raises:[].} =
184
190
let response = JrpcSys.decode(message, LspClientResponse)
185
191
let id = response.id
186
192
if id notin ls.responseMap:
187
- error " Id not found in responseMap" , id = id # TODO we should store the call name we are trying to responde to here
193
+ error " Id not found in responseMap" , id = id
194
+ # TODO we should store the call name we are trying to responde to here
188
195
if response.result == nil :
189
196
ls.responseMap[id].complete(newJObject())
190
197
ls.responseMap.del id
191
198
else :
192
199
let r = response.result
193
200
ls.responseMap[id].complete(r)
194
201
ls.responseMap.del id
195
-
196
202
except JsonParsingError as ex:
197
203
error " [Processsing Message] Error parsing message" , message = message
198
204
writeStackTrace(ex)
@@ -202,8 +208,8 @@ proc processMessage(ls: LanguageServer, message: string) {.raises:[].} =
202
208
203
209
proc initActions* (ls: LanguageServer) =
204
210
let onExit: OnExitCallback = proc () {.async.} =
205
- case ls.transportMode:
206
- of stdio:
211
+ case ls.transportMode
212
+ of stdio:
207
213
ls.outStream.close()
208
214
freeShared(ls.stdinContext)
209
215
of socket:
@@ -216,10 +222,10 @@ proc initActions*(ls: LanguageServer) =
216
222
json[" params" ] = params
217
223
218
224
let notifyAction: NotifyAction = proc (name: string , params: JsonNode) =
219
- genJsonAction()
220
- ls.writeOutput(json)
225
+ genJsonAction()
226
+ ls.writeOutput(json)
221
227
222
- let callAction: CallAction = proc (name: string , params: JsonNode): Future[JsonNode] =
228
+ let callAction: CallAction = proc (name: string , params: JsonNode): Future[JsonNode] =
223
229
let id = $ genOid()
224
230
genJsonAction()
225
231
json[" id" ] = %* id
@@ -232,7 +238,6 @@ proc initActions*(ls: LanguageServer) =
232
238
ls.notify = notifyAction
233
239
ls.onExit = onExit
234
240
235
-
236
241
#start and loop functions belows are the only difference between transports
237
242
proc startStdioLoop*(ls: LanguageServer): Future[void ] {.async.} =
238
243
while true:
@@ -242,14 +247,14 @@ proc startStdioLoop*(ls: LanguageServer): Future[void] {.async.} =
242
247
await ls.stdinContext.onMainReadSignal.fire()
243
248
if msg == " " :
244
249
error " Client discconected"
245
- break
250
+ break
246
251
ls.processMessage(msg)
247
252
248
253
proc startStdioServer* (ls: LanguageServer) =
249
254
# Holds the responses from the client done via the callAction. Likely this is only needed for stdio
250
255
debug " Starting stdio server"
251
256
ls.srv = newRpcSocketServer()
252
- ls.initActions()
257
+ ls.initActions()
253
258
ls.outStream = newFileStream(stdout)
254
259
var stdinThread {.global.}: Thread[ptr ReadStdinContext]
255
260
ls.stdinContext = createShared(ReadStdinContext)
@@ -258,7 +263,9 @@ proc startStdioServer*(ls: LanguageServer) =
258
263
createThread(stdinThread, readStdin, ls.stdinContext)
259
264
asyncSpawn ls.startStdioLoop()
260
265
261
- proc processClientLoop* (ls: LanguageServer, server: StreamServer, transport: StreamTransport) {.async: (raises: []), gcsafe.} =
266
+ proc processClientLoop* (
267
+ ls: LanguageServer, server: StreamServer, transport: StreamTransport
268
+ ) {.async: (raises: []), gcsafe.} =
262
269
ls.socketTransport = transport
263
270
while true :
264
271
let msg = await processContentLength(transport)
@@ -270,8 +277,7 @@ proc processClientLoop*(ls: LanguageServer, server: StreamServer, transport: Str
270
277
ls.processMessage(msg)
271
278
272
279
proc startSocketServer* (ls: LanguageServer, port: Port) =
273
- ls.srv = newRpcSocketServer(partial(processClientLoop, ls))
274
- ls.initActions()
275
- ls.srv.addStreamServer(" localhost" , port)
276
- ls.srv.start
277
-
280
+ ls.srv = newRpcSocketServer(partial(processClientLoop, ls))
281
+ ls.initActions()
282
+ ls.srv.addStreamServer(" localhost" , port)
283
+ ls.srv.start
0 commit comments