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.WarnMsg($'Unsupported notification message received from the LSP server ({lspserver.name}), message = {reply->string()}')
76 # Dict to process telemetry notification messages only once per filetype
77 var telemetryProcessed: dict<bool> = {}
78 # process unsupported notification messages only once
79 def ProcessUnsupportedNotifOnce(lspserver: dict<any>, reply: dict<any>)
80 if !telemetryProcessed->get(&ft, false)
81 ProcessUnsupportedNotif(lspserver, reply)
82 telemetryProcessed->extend({[&ft]: 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.
116 # pyright language server notifications
117 'pyright/beginProgress',
118 'pyright/reportProgress',
119 'pyright/endProgress',
121 'taplo/didChangeSchemaAssociation'
124 if lsp_notif_handlers->has_key(reply.method)
125 lsp_notif_handlers[reply.method](lspserver, reply)
126 elseif lspserver.customNotificationHandlers->has_key(reply.method)
127 lspserver.customNotificationHandlers[reply.method](lspserver, reply)
128 elseif lsp_ignored_notif_handlers->index(reply.method) == -1
129 ProcessUnsupportedNotif(lspserver, reply)
133 # process the workspace/applyEdit LSP server request
134 # Request: "workspace/applyEdit"
135 # Param: ApplyWorkspaceEditParams
136 def ProcessApplyEditReq(lspserver: dict<any>, request: dict<any>)
137 # interface ApplyWorkspaceEditParams
138 if !request->has_key('params')
141 var workspaceEditParams: dict<any> = request.params
142 if workspaceEditParams->has_key('label')
143 util.InfoMsg($'Workspace edit {workspaceEditParams.label}')
145 textedit.ApplyWorkspaceEdit(workspaceEditParams.edit)
146 # TODO: Need to return the proper result of the edit operation
147 lspserver.sendResponse(request, {applied: true}, {})
150 # process the workspace/workspaceFolders LSP server request
151 # Request: "workspace/workspaceFolders"
153 def ProcessWorkspaceFoldersReq(lspserver: dict<any>, request: dict<any>)
154 if !lspserver->has_key('workspaceFolders')
155 lspserver.sendResponse(request, null, {})
158 if lspserver.workspaceFolders->empty()
159 lspserver.sendResponse(request, [], {})
161 lspserver.sendResponse(request,
162 \ lspserver.workspaceFolders->copy()->map('{name: v:val->fnamemodify(":t"), uri: util.LspFileToUri(v:val)}'),
167 # process the workspace/configuration LSP server request
168 # Request: "workspace/configuration"
169 # Param: ConfigurationParams
170 def ProcessWorkspaceConfiguration(lspserver: dict<any>, request: dict<any>)
171 var items = request.params.items
172 var response = items->map((_, item) => lspserver.workspaceConfigGet(item))
173 lspserver.sendResponse(request, response, {})
176 # process the window/workDoneProgress/create LSP server request
177 # Request: "window/workDoneProgress/create"
179 def ProcessWorkDoneProgressCreate(lspserver: dict<any>, request: dict<any>)
180 lspserver.sendResponse(request, {}, {})
183 # process the client/registerCapability LSP server request
184 # Request: "client/registerCapability"
185 # Param: RegistrationParams
186 def ProcessClientRegisterCap(lspserver: dict<any>, request: dict<any>)
187 lspserver.sendResponse(request, {}, {})
190 # process the client/unregisterCapability LSP server request
191 # Request: "client/unregisterCapability"
192 # Param: UnregistrationParams
193 def ProcessClientUnregisterCap(lspserver: dict<any>, request: dict<any>)
194 lspserver.sendResponse(request, {}, {})
197 # process a request message from the server
198 export def ProcessRequest(lspserver: dict<any>, request: dict<any>)
199 var lspRequestHandlers: dict<func> =
201 'client/registerCapability': ProcessClientRegisterCap,
202 'client/unregisterCapability': ProcessClientUnregisterCap,
203 'window/workDoneProgress/create': ProcessWorkDoneProgressCreate,
204 'workspace/applyEdit': ProcessApplyEditReq,
205 'workspace/configuration': ProcessWorkspaceConfiguration,
206 'workspace/workspaceFolders': ProcessWorkspaceFoldersReq
207 # TODO: Handle the following requests from the server:
208 # workspace/codeLens/refresh
209 # workspace/diagnostic/refresh
210 # workspace/inlayHint/refresh
211 # workspace/inlineValue/refresh
212 # workspace/semanticTokens/refresh
215 # Explicitly ignored requests
216 var lspIgnoredRequestHandlers: list<string> =
218 # Eclipse java language server sends the
219 # 'workspace/executeClientCommand' request (to reload bundles) which is
220 # not in the LSP specification.
221 'workspace/executeClientCommand',
224 if lspRequestHandlers->has_key(request.method)
225 lspRequestHandlers[request.method](lspserver, request)
226 elseif lspserver.customRequestHandlers->has_key(request.method)
227 lspserver.customRequestHandlers[request.method](lspserver, request)
228 elseif lspIgnoredRequestHandlers->index(request.method) == -1
229 util.ErrMsg($'Unsupported request message received from the LSP server ({lspserver.name}), message = {request->string()}')
233 # process one or more LSP server messages
234 export def ProcessMessages(lspserver: dict<any>): void
242 if msg->has_key('result') || msg->has_key('error')
243 # response message from the server
244 req = lspserver.requests->get(msg.id->string(), {})
246 # Remove the corresponding stored request message
247 lspserver.requests->remove(msg.id->string())
249 if msg->has_key('result')
250 lspserver.processReply(req, msg)
253 var emsg: string = msg.error.message
254 emsg ..= $', code = {msg.error.code}'
255 if msg.error->has_key('data')
256 emsg ..= $', data = {msg.error.data->string()}'
258 util.ErrMsg($'request {req.method} failed ({emsg})')
261 elseif msg->has_key('id') && msg->has_key('method')
262 # request message from the server
263 lspserver.processRequest(msg)
264 elseif msg->has_key('method')
265 # notification message from the server
266 lspserver.processNotif(msg)
268 util.ErrMsg($'Unsupported message ({msg->string()})')
272 # vim: tabstop=8 shiftwidth=2 softtabstop=2