3 # Handlers for messages from the LSP server
4 # Refer to https://microsoft.github.io/language-server-protocol/specification
5 # for the Language Server Protocol (LSP) specification.
9 import './textedit.vim'
11 # Process various reply messages from the LSP server
12 export def ProcessReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>): void
13 util.ErrMsg($'Unsupported reply received from LSP server: {reply->string()} for request: {req->string()}')
16 # process a diagnostic notification message from the LSP server
17 # Notification: textDocument/publishDiagnostics
18 # Param: PublishDiagnosticsParams
19 def ProcessDiagNotif(lspserver: dict<any>, reply: dict<any>): void
20 var params = reply.params
21 diag.DiagNotification(lspserver, params.uri, params.diagnostics)
24 # Convert LSP message type to a string
25 def LspMsgTypeToString(lspMsgType: number): string
26 var msgStrMap: list<string> = ['', 'Error', 'Warning', 'Info', 'Log']
27 var mtype: string = 'Log'
28 if lspMsgType > 0 && lspMsgType < 5
29 mtype = msgStrMap[lspMsgType]
34 # process a show notification message from the LSP server
35 # Notification: window/showMessage
36 # Param: ShowMessageParams
37 def ProcessShowMsgNotif(lspserver: dict<any>, reply: dict<any>)
38 var msgType = reply.params.type
40 # ignore log messages from the LSP server (too chatty)
41 # TODO: Add a configuration to control the message level that will be
42 # displayed. Also store these messages and provide a command to display
47 util.ErrMsg($'Lsp({lspserver.name}) {reply.params.message}')
49 util.WarnMsg($'Lsp({lspserver.name}) {reply.params.message}')
51 util.InfoMsg($'Lsp({lspserver.name}) {reply.params.message}')
55 # process a log notification message from the LSP server
56 # Notification: window/logMessage
57 # Param: LogMessageParams
58 def ProcessLogMsgNotif(lspserver: dict<any>, reply: dict<any>)
59 var params = reply.params
60 var mtype = LspMsgTypeToString(params.type)
61 lspserver.addMessage(mtype, params.message)
64 # process the log trace notification messages
65 # Notification: $/logTrace
66 # Param: LogTraceParams
67 def ProcessLogTraceNotif(lspserver: dict<any>, reply: dict<any>)
68 lspserver.addMessage('trace', reply.params.message)
71 # process unsupported notification messages
72 def ProcessUnsupportedNotif(lspserver: dict<any>, reply: dict<any>)
73 util.ErrMsg($'Unsupported notification message received from the LSP server ({lspserver.path}), message = {reply->string()}')
76 # per-filetype private map inside to record if ntf once or not
77 var ftypeNtfOnceMap: dict<bool> = {}
78 # process unsupported notification messages but only notify once
79 def ProcessUnsupportedNotifOnce(lspserver: dict<any>, reply: dict<any>)
80 if !ftypeNtfOnceMap->get(&ft, v:false)
81 ProcessUnsupportedNotif(lspserver, reply)
82 ftypeNtfOnceMap->extend({[&ft]: v:true})
86 # process notification messages from the LSP server
87 export def ProcessNotif(lspserver: dict<any>, reply: dict<any>): void
88 var lsp_notif_handlers: dict<func> =
90 'window/showMessage': ProcessShowMsgNotif,
91 'window/logMessage': ProcessLogMsgNotif,
92 'textDocument/publishDiagnostics': ProcessDiagNotif,
93 '$/logTrace': ProcessLogTraceNotif,
94 'telemetry/event': ProcessUnsupportedNotifOnce,
97 # Explicitly ignored notification messages
98 var lsp_ignored_notif_handlers: list<string> =
103 # PHP intelephense server sends the "indexingStarted" and
104 # "indexingEnded" notifications which is not in the LSP specification.
107 # Java language server sends the 'language/status' notification which is
108 # not in the LSP specification.
110 # Typescript language server sends the '$/typescriptVersion'
111 # notification which is not in the LSP specification.
112 '$/typescriptVersion',
113 # Dart language server sends the '$/analyzerStatus' notification which
114 # is not in the LSP specification.
118 if lsp_notif_handlers->has_key(reply.method)
119 lsp_notif_handlers[reply.method](lspserver, reply)
120 elseif lspserver.customNotificationHandlers->has_key(reply.method)
121 lspserver.customNotificationHandlers[reply.method](lspserver, reply)
122 elseif lsp_ignored_notif_handlers->index(reply.method) == -1
123 util.ErrMsg($'Unsupported notification received from LSP server {reply->string()}')
127 # process the workspace/applyEdit LSP server request
128 # Request: "workspace/applyEdit"
129 # Param: ApplyWorkspaceEditParams
130 def ProcessApplyEditReq(lspserver: dict<any>, request: dict<any>)
131 # interface ApplyWorkspaceEditParams
132 if !request->has_key('params')
135 var workspaceEditParams: dict<any> = request.params
136 if workspaceEditParams->has_key('label')
137 util.InfoMsg($'Workspace edit {workspaceEditParams.label}')
139 textedit.ApplyWorkspaceEdit(workspaceEditParams.edit)
140 # TODO: Need to return the proper result of the edit operation
141 lspserver.sendResponse(request, {applied: true}, {})
144 # process the workspace/workspaceFolders LSP server request
145 # Request: "workspace/workspaceFolders"
147 def ProcessWorkspaceFoldersReq(lspserver: dict<any>, request: dict<any>)
148 if !lspserver->has_key('workspaceFolders')
149 lspserver.sendResponse(request, null, {})
152 if lspserver.workspaceFolders->empty()
153 lspserver.sendResponse(request, [], {})
155 lspserver.sendResponse(request,
156 \ lspserver.workspaceFolders->copy()->map('{name: v:val->fnamemodify(":t"), uri: util.LspFileToUri(v:val)}'),
161 # process the workspace/configuration LSP server request
162 # Request: "workspace/configuration"
163 # Param: ConfigurationParams
164 def ProcessWorkspaceConfiguration(lspserver: dict<any>, request: dict<any>)
165 var items = request.params.items
166 var response = items->map((_, item) => lspserver.workspaceConfigGet(item))
167 lspserver.sendResponse(request, response, {})
170 # process the window/workDoneProgress/create LSP server request
171 # Request: "window/workDoneProgress/create"
173 def ProcessWorkDoneProgressCreate(lspserver: dict<any>, request: dict<any>)
174 lspserver.sendResponse(request, {}, {})
177 # process the client/registerCapability LSP server request
178 # Request: "client/registerCapability"
179 # Param: RegistrationParams
180 def ProcessClientRegisterCap(lspserver: dict<any>, request: dict<any>)
181 lspserver.sendResponse(request, {}, {})
184 # process the client/unregisterCapability LSP server request
185 # Request: "client/unregisterCapability"
186 # Param: UnregistrationParams
187 def ProcessClientUnregisterCap(lspserver: dict<any>, request: dict<any>)
188 lspserver.sendResponse(request, {}, {})
191 # process a request message from the server
192 export def ProcessRequest(lspserver: dict<any>, request: dict<any>)
193 var lspRequestHandlers: dict<func> =
195 'client/registerCapability': ProcessClientRegisterCap,
196 'client/unregisterCapability': ProcessClientUnregisterCap,
197 'window/workDoneProgress/create': ProcessWorkDoneProgressCreate,
198 'workspace/applyEdit': ProcessApplyEditReq,
199 'workspace/configuration': ProcessWorkspaceConfiguration,
200 'workspace/workspaceFolders': ProcessWorkspaceFoldersReq
201 # TODO: Handle the following requests from the server:
202 # workspace/codeLens/refresh
203 # workspace/diagnostic/refresh
204 # workspace/inlayHint/refresh
205 # workspace/inlineValue/refresh
206 # workspace/semanticTokens/refresh
209 # Explicitly ignored requests
210 var lspIgnoredRequestHandlers: list<string> =
212 # Eclipse java language server sends the
213 # 'workspace/executeClientCommand' request (to reload bundles) which is
214 # not in the LSP specification.
215 'workspace/executeClientCommand',
218 if lspRequestHandlers->has_key(request.method)
219 lspRequestHandlers[request.method](lspserver, request)
220 elseif lspserver.customRequestHandlers->has_key(request.method)
221 lspserver.customRequestHandlers[request.method](lspserver, request)
222 elseif lspIgnoredRequestHandlers->index(request.method) == -1
223 util.ErrMsg($'Unsupported request message received from the LSP server ({lspserver.path}), message = {request->string()}')
227 # process one or more LSP server messages
228 export def ProcessMessages(lspserver: dict<any>): void
236 if msg->has_key('result') || msg->has_key('error')
237 # response message from the server
238 req = lspserver.requests->get(msg.id->string(), {})
240 # Remove the corresponding stored request message
241 lspserver.requests->remove(msg.id->string())
243 if msg->has_key('result')
244 lspserver.processReply(req, msg)
247 var emsg: string = msg.error.message
248 emsg ..= $', code = {msg.error.code}'
249 if msg.error->has_key('data')
250 emsg ..= $', data = {msg.error.data->string()}'
252 util.ErrMsg($'request {req.method} failed ({emsg})')
255 elseif msg->has_key('id') && msg->has_key('method')
256 # request message from the server
257 lspserver.processRequest(msg)
258 elseif msg->has_key('method')
259 # notification message from the server
260 lspserver.processNotif(msg)
262 util.ErrMsg($'Unsupported message ({msg->string()})')
266 # vim: tabstop=8 shiftwidth=2 softtabstop=2