5 # The functions to send request messages to the language server are in this
8 # Refer to https://microsoft.github.io/language-server-protocol/specification
9 # for the Language Server Protocol (LSP) specificaiton.
11 import './options.vim' as opt
12 import './handlers.vim'
14 import './capabilities.vim'
17 import './selection.vim'
19 import './textedit.vim'
20 import './completion.vim'
22 import './signature.vim'
23 import './codeaction.vim'
24 import './codelens.vim'
25 import './callhierarchy.vim' as callhier
26 import './typehierarchy.vim' as typehier
27 import './inlayhints.vim'
29 # LSP server standard output handler
30 def Output_cb(lspserver: dict<any>, chan: channel, msg: any): void
31 lspserver.traceLog($'{strftime("%m/%d/%y %T")}: Received {msg->string()}')
33 lspserver.processMessages()
36 # LSP server error output handler
37 def Error_cb(lspserver: dict<any>, chan: channel, emsg: string): void
38 lspserver.errorLog(emsg)
41 # LSP server exit callback
42 def Exit_cb(lspserver: dict<any>, job: job, status: number): void
43 util.WarnMsg($'{strftime("%m/%d/%y %T")}: LSP server ({lspserver.name}) exited with status {status}')
44 lspserver.running = false
45 lspserver.ready = false
46 lspserver.requests = {}
51 def StartServer(lspserver: dict<any>, bnr: number): number
53 util.WarnMsg($'LSP server "{lspserver.name}" is already running')
57 var cmd = [lspserver.path]
58 cmd->extend(lspserver.args)
60 var opts = {in_mode: 'lsp',
64 out_cb: function(Output_cb, [lspserver]),
65 err_cb: function(Error_cb, [lspserver]),
66 exit_cb: function(Exit_cb, [lspserver])}
71 lspserver.requests = {}
72 lspserver.omniCompletePending = false
73 lspserver.completionLazyDoc = false
74 lspserver.completionTriggerChars = []
75 lspserver.signaturePopup = -1
77 var job = cmd->job_start(opts)
78 if job->job_status() == 'fail'
79 util.ErrMsg($'Failed to start LSP server {lspserver.path}')
83 # wait a little for the LSP server to start
87 lspserver.running = true
89 lspserver.initServer(bnr)
94 # process the 'initialize' method reply from the LSP server
95 # Result: InitializeResult
96 def ServerInitReply(lspserver: dict<any>, initResult: dict<any>): void
97 if initResult->empty()
101 var caps: dict<any> = initResult.capabilities
102 lspserver.caps = caps
104 for [key, val] in initResult->items()
105 if key == 'capabilities'
109 lspserver.caps[$'~additionalInitResult_{key}'] = val
112 capabilities.ProcessServerCaps(lspserver, caps)
114 if caps->has_key('completionProvider')
115 if opt.lspOptions.autoComplete
116 lspserver.completionTriggerChars =
117 caps.completionProvider->get('triggerCharacters', [])
119 lspserver.completionLazyDoc =
120 caps.completionProvider->get('resolveProvider', false)
123 # send a "initialized" notification to server
124 lspserver.sendInitializedNotif()
125 # send any workspace configuration (optional)
126 if !lspserver.workspaceConfig->empty()
127 lspserver.sendWorkspaceConfig()
129 lspserver.ready = true
130 if exists($'#User#LspServerReady{lspserver.name}')
131 exe $'doautocmd <nomodeline> User LspServerReady{lspserver.name}'
133 # Used internally, and shouldn't be used by users
134 if exists($'#User#LspServerReady_{lspserver.id}')
135 exe $'doautocmd <nomodeline> User LspServerReady_{lspserver.id}'
138 # if the outline window is opened, then request the symbols for the current
140 if bufwinid('LSP-Outline') != -1
141 lspserver.getDocSymbols(@%)
144 # Update the inlay hints (if enabled)
145 if opt.lspOptions.showInlayHints && (lspserver.isInlayHintProvider
146 || lspserver.isClangdInlayHintsProvider)
147 inlayhints.LspInlayHintsUpdateNow()
151 # Request: 'initialize'
152 # Param: InitializeParams
153 def InitServer(lspserver: dict<any>, bnr: number)
154 # interface 'InitializeParams'
155 var initparams: dict<any> = {}
156 initparams.processId = getpid()
157 initparams.clientInfo = {
159 version: v:versionlong->string(),
162 # Compute the rootpath (based on the directory of the buffer)
164 var rootSearchFiles = lspserver.rootSearchFiles
165 var bufDir = bnr->bufname()->fnamemodify(':p:h')
166 if !rootSearchFiles->empty()
167 rootPath = util.FindNearestRootDir(bufDir, rootSearchFiles)
172 # bufDir is within cwd
173 var bufDirPrefix = bufDir[0 : cwd->strcharlen() - 1]
175 ? bufDirPrefix ==? cwd
176 : bufDirPrefix == cwd
183 lspserver.workspaceFolders = [rootPath]
185 var rootUri = util.LspFileToUri(rootPath)
186 initparams.rootPath = rootPath
187 initparams.rootUri = rootUri
188 initparams.workspaceFolders = [{
189 name: rootPath->fnamemodify(':t'),
193 initparams.trace = 'off'
194 initparams.capabilities = capabilities.GetClientCaps()
195 if !lspserver.initializationOptions->empty()
196 initparams.initializationOptions = lspserver.initializationOptions
198 initparams.initializationOptions = {}
201 lspserver.rpcInitializeRequest = initparams
203 lspserver.rpc_a('initialize', initparams, ServerInitReply)
206 # Send a "initialized" notification to the language server
207 def SendInitializedNotif(lspserver: dict<any>)
208 # Notification: 'initialized'
209 # Params: InitializedParams
210 lspserver.sendNotification('initialized')
215 def ShutdownServer(lspserver: dict<any>): void
216 lspserver.rpc('shutdown', {})
219 # Send a 'exit' notification to the language server
220 def ExitServer(lspserver: dict<any>): void
221 # Notification: 'exit'
223 lspserver.sendNotification('exit')
227 def StopServer(lspserver: dict<any>): number
228 if !lspserver.running
229 util.WarnMsg($'LSP server {lspserver.name} is not running')
233 # Send the shutdown request to the server
234 lspserver.shutdownServer()
236 # Notify the server to exit
237 lspserver.exitServer()
239 # Wait for the server to process the exit notification and exit for a
240 # maximum of 2 seconds.
241 var maxCount: number = 1000
242 while lspserver.job->job_status() == 'run' && maxCount > 0
247 if lspserver.job->job_status() == 'run'
248 lspserver.job->job_stop()
250 lspserver.running = false
251 lspserver.ready = false
252 lspserver.requests = {}
256 # Set the language server trace level using the '$/setTrace' notification.
257 # Supported values for "traceVal" are "off", "messages" and "verbose".
258 def SetTrace(lspserver: dict<any>, traceVal: string)
259 # Notification: '$/setTrace'
260 # Params: SetTraceParams
261 var params = {value: traceVal}
262 lspserver.sendNotification('$/setTrace', params)
265 # Log a debug message to the LSP server debug file
266 def TraceLog(lspserver: dict<any>, msg: string)
268 util.TraceLog(lspserver.logfile, false, msg)
272 # Log an error message to the LSP server error file
273 def ErrorLog(lspserver: dict<any>, errmsg: string)
275 util.TraceLog(lspserver.errfile, true, errmsg)
279 # Return the next id for a LSP server request message
280 def NextReqID(lspserver: dict<any>): number
281 var id = lspserver.nextID
282 lspserver.nextID = id + 1
286 # create a LSP server request message
287 def CreateRequest(lspserver: dict<any>, method: string): dict<any>
290 req.id = lspserver.nextReqID()
294 # Save the request, so that the corresponding response can be processed
295 lspserver.requests->extend({[req.id->string()]: req})
300 # create a LSP server response message
301 def CreateResponse(lspserver: dict<any>, req_id: number): dict<any>
309 # create a LSP server notification message
310 def CreateNotification(lspserver: dict<any>, notif: string): dict<any>
319 # send a response message to the server
320 def SendResponse(lspserver: dict<any>, request: dict<any>, result: any, error: dict<any>)
321 if (request.id->type() == v:t_string
322 && (request.id->trim() =~ '[^[:digit:]]\+'
323 || request.id->trim()->empty()))
324 || (request.id->type() != v:t_string && request.id->type() != v:t_number)
325 util.ErrMsg('request.id of response to LSP server is not a correct number')
328 var resp: dict<any> = lspserver.createResponse(
329 request.id->type() == v:t_string ? request.id->str2nr() : request.id)
331 resp->extend({result: result})
333 resp->extend({error: error})
335 lspserver.sendMessage(resp)
338 # Send a request message to LSP server
339 def SendMessage(lspserver: dict<any>, content: dict<any>): void
340 var job = lspserver.job
341 if job->job_status() != 'run'
342 # LSP server has exited
345 job->ch_sendexpr(content)
346 if content->has_key('id')
347 lspserver.traceLog($'{strftime("%m/%d/%y %T")}: Sent {content->string()}')
351 # Send a notification message to the language server
352 def SendNotification(lspserver: dict<any>, method: string, params: any = {})
353 var notif: dict<any> = CreateNotification(lspserver, method)
354 notif.params->extend(params)
355 lspserver.sendMessage(notif)
358 # Translate an LSP error code into a readable string
359 def LspGetErrorMessage(errcode: number): string
361 -32001: 'UnknownErrorCode',
362 -32002: 'ServerNotInitialized',
363 -32600: 'InvalidRequest',
364 -32601: 'MethodNotFound',
365 -32602: 'InvalidParams',
366 -32603: 'InternalError',
367 -32700: 'ParseError',
368 -32800: 'RequestCancelled',
369 -32801: 'ContentModified',
370 -32802: 'ServerCancelled',
371 -32803: 'RequestFailed'
374 return errmap->get(errcode, errcode->string())
377 # Process a LSP server response error and display an error message.
378 def ProcessLspServerError(method: string, responseError: dict<any>)
380 var emsg: string = responseError.message
381 emsg ..= $', error = {LspGetErrorMessage(responseError.code)}'
382 if responseError->has_key('data')
383 emsg ..= $', data = {responseError.data->string()}'
385 util.ErrMsg($'request {method} failed ({emsg})')
388 # Send a sync RPC request message to the LSP server and return the received
389 # reply. In case of an error, an empty Dict is returned.
390 def Rpc(lspserver: dict<any>, method: string, params: any, handleError: bool = true): dict<any>
394 req.params->extend(params)
396 var job = lspserver.job
397 if job->job_status() != 'run'
398 # LSP server has exited
402 lspserver.traceLog($'{strftime("%m/%d/%y %T")}: Sent {req->string()}')
404 # Do the synchronous RPC call
405 var reply = job->ch_evalexpr(req)
407 lspserver.traceLog($'{strftime("%m/%d/%y %T")}: Received {reply->string()}')
409 if reply->has_key('result')
414 if reply->has_key('error') && handleError
416 ProcessLspServerError(method, reply.error)
422 # LSP server asynchronous RPC callback
423 def AsyncRpcCb(lspserver: dict<any>, method: string, RpcCb: func, chan: channel, reply: dict<any>)
424 lspserver.traceLog($'{strftime("%m/%d/%y %T")}: Received {reply->string()}')
430 if reply->has_key('error')
432 ProcessLspServerError(method, reply.error)
436 if !reply->has_key('result')
437 util.ErrMsg($'request {method} failed (no result)')
441 RpcCb(lspserver, reply.result)
444 # Send a async RPC request message to the LSP server with a callback function.
445 # Returns the LSP message id. This id can be used to cancel the RPC request
446 # (if needed). Returns -1 on error.
447 def AsyncRpc(lspserver: dict<any>, method: string, params: any, Cbfunc: func): number
451 req.params->extend(params)
453 var job = lspserver.job
454 if job->job_status() != 'run'
455 # LSP server has exited
459 lspserver.traceLog($'{strftime("%m/%d/%y %T")}: Sent {req->string()}')
461 # Do the asynchronous RPC call
462 var Fn = function('AsyncRpcCb', [lspserver, method, Cbfunc])
465 if get(g:, 'LSPTest')
466 # When running LSP tests, make this a synchronous RPC call
467 reply = Rpc(lspserver, method, params)
468 Fn(test_null_channel(), reply)
470 # Otherwise, make an asynchronous RPC call
471 reply = job->ch_sendexpr(req, {callback: Fn})
480 # Wait for a response message from the LSP server for the request "req"
481 # Waits for a maximum of 5 seconds
482 def WaitForResponse(lspserver: dict<any>, req: dict<any>)
483 var maxCount: number = 2500
484 var key: string = req.id->string()
486 while lspserver.requests->has_key(key) && maxCount > 0
492 # Retrieve the Workspace configuration asked by the server.
493 # Request: workspace/configuration
494 def WorkspaceConfigGet(lspserver: dict<any>, configItem: dict<any>): dict<any>
495 if lspserver.workspaceConfig->empty()
498 if !configItem->has_key('section') || configItem.section->empty()
499 return lspserver.workspaceConfig
501 var config: dict<any> = lspserver.workspaceConfig
502 for part in configItem.section->split('\.')
503 if !config->has_key(part)
506 config = config[part]
511 # Send a "workspace/didChangeConfiguration" notification to the language
513 def SendWorkspaceConfig(lspserver: dict<any>)
514 # Params: DidChangeConfigurationParams
515 var params = {settings: lspserver.workspaceConfig}
516 lspserver.sendNotification('workspace/didChangeConfiguration', params)
519 # Send a file/document opened notification to the language server.
520 def TextdocDidOpen(lspserver: dict<any>, bnr: number, ftype: string): void
521 # Notification: 'textDocument/didOpen'
522 # Params: DidOpenTextDocumentParams
524 tdi.uri = util.LspBufnrToUri(bnr)
525 tdi.languageId = ftype
527 tdi.text = bnr->getbufline(1, '$')->join("\n") .. "\n"
528 var params = {textDocument: tdi}
529 lspserver.sendNotification('textDocument/didOpen', params)
532 # Send a file/document closed notification to the language server.
533 def TextdocDidClose(lspserver: dict<any>, bnr: number): void
534 # Notification: 'textDocument/didClose'
535 # Params: DidCloseTextDocumentParams
537 tdid.uri = util.LspBufnrToUri(bnr)
538 var params = {textDocument: tdid}
539 lspserver.sendNotification('textDocument/didClose', params)
542 # Send a file/document change notification to the language server.
543 # Params: DidChangeTextDocumentParams
544 def TextdocDidChange(lspserver: dict<any>, bnr: number, start: number,
545 end: number, added: number,
546 changes: list<dict<number>>): void
547 # Notification: 'textDocument/didChange'
548 # Params: DidChangeTextDocumentParams
549 var vtdid: dict<any> = {}
550 vtdid.uri = util.LspBufnrToUri(bnr)
551 # Use Vim 'changedtick' as the LSP document version number
552 vtdid.version = bnr->getbufvar('changedtick')
554 var changeset: list<dict<any>>
556 ##### FIXME: Sending specific buffer changes to the LSP server doesn't
557 ##### work properly as the computed line range numbers is not correct.
558 ##### For now, send the entire buffer content to LSP server.
560 # for change in changes
562 # var start_lnum: number
563 # var end_lnum: number
564 # var start_col: number
565 # var end_col: number
566 # if change.added == 0
568 # start_lnum = change.lnum - 1
569 # end_lnum = change.end - 1
570 # lines = getbufline(bnr, change.lnum, change.end - 1)->join("\n") .. "\n"
573 # elseif change.added > 0
575 # start_lnum = change.lnum - 1
576 # end_lnum = change.lnum - 1
579 # lines = getbufline(bnr, change.lnum, change.lnum + change.added - 1)->join("\n") .. "\n"
582 # start_lnum = change.lnum - 1
583 # end_lnum = change.lnum + (-change.added) - 1
588 # var range: dict<dict<number>> = {'start': {'line': start_lnum, 'character': start_col}, 'end': {'line': end_lnum, 'character': end_col}}
589 # changeset->add({'range': range, 'text': lines})
592 changeset->add({text: bnr->getbufline(1, '$')->join("\n") .. "\n"})
593 var params = {textDocument: vtdid, contentChanges: changeset}
594 lspserver.sendNotification('textDocument/didChange', params)
597 # Return the current cursor position as a LSP position.
598 # find_ident will search for a identifier in front of the cursor, just like
599 # CTRL-] and c_CTRL-R_CTRL-W does.
601 # LSP line and column numbers start from zero, whereas Vim line and column
602 # numbers start from one. The LSP column number is the character index in the
603 # line and not the byte index in the line.
604 def GetPosition(lspserver: dict<any>, find_ident: bool): dict<number>
605 var lnum: number = line('.') - 1
606 var col: number = charcol('.') - 1
607 var line = getline('.')
610 # 1. skip to start of identifier
611 while line[col] != '' && line[col] !~ '\k'
615 # 2. back up to start of identifier
616 while col > 0 && line[col - 1] =~ '\k'
621 # Compute character index counting composing characters as separate
623 var pos = {line: lnum, character: util.GetCharIdxWithCompChar(line, col)}
624 lspserver.encodePosition(bufnr(), pos)
629 # Return the current file name and current cursor position as a LSP
630 # TextDocumentPositionParams structure
631 def GetTextDocPosition(lspserver: dict<any>, find_ident: bool): dict<dict<any>>
632 # interface TextDocumentIdentifier
634 return {textDocument: {uri: util.LspFileToUri(@%)},
635 position: lspserver.getPosition(find_ident)}
638 # Get a list of completion items.
639 # Request: "textDocument/completion"
640 # Param: CompletionParams
641 def GetCompletion(lspserver: dict<any>, triggerKind_arg: number, triggerChar: string): void
642 # Check whether LSP server supports completion
643 if !lspserver.isCompletionProvider
644 util.ErrMsg('LSP server does not support completion')
653 # interface CompletionParams
654 # interface TextDocumentPositionParams
655 var params = lspserver.getTextDocPosition(false)
656 # interface CompletionContext
657 params.context = {triggerKind: triggerKind_arg, triggerCharacter: triggerChar}
659 lspserver.rpc_a('textDocument/completion', params,
660 completion.CompletionReply)
663 # Get lazy properties for a completion item.
664 # Request: "completionItem/resolve"
665 # Param: CompletionItem
666 def ResolveCompletion(lspserver: dict<any>, item: dict<any>): void
667 # Check whether LSP server supports completion item resolve
668 if !lspserver.isCompletionResolveProvider
669 util.ErrMsg('LSP server does not support completion item resolve')
673 # interface CompletionItem
674 lspserver.rpc_a('completionItem/resolve', item,
675 completion.CompletionResolveReply)
678 # Jump to or peek a symbol location.
680 # Send 'msg' to a LSP server and process the reply. 'msg' is one of the
682 # textDocument/definition
683 # textDocument/declaration
684 # textDocument/typeDefinition
685 # textDocument/implementation
687 # Process the LSP server reply and jump to the symbol location. Before
688 # jumping to the symbol location, save the current cursor position in the tag
691 # If 'peekSymbol' is true, then display the symbol location in the preview
692 # window but don't jump to the symbol location.
694 # Result: Location | Location[] | LocationLink[] | null
695 def GotoSymbolLoc(lspserver: dict<any>, msg: string, peekSymbol: bool,
696 cmdmods: string, count: number)
697 var reply = lspserver.rpc(msg, lspserver.getTextDocPosition(true), false)
698 if reply->empty() || reply.result->empty()
700 if msg == 'textDocument/declaration'
701 emsg = 'symbol declaration is not found'
702 elseif msg == 'textDocument/typeDefinition'
703 emsg = 'symbol type definition is not found'
704 elseif msg == 'textDocument/implementation'
705 emsg = 'symbol implementation is not found'
707 emsg = 'symbol definition is not found'
714 var result = reply.result
715 var location: dict<any>
716 if result->type() == v:t_list
718 # When there are multiple symbol locations, and a specific one isn't
719 # requested with 'count', display the locations in a location list.
721 var title: string = ''
722 if msg == 'textDocument/declaration'
723 title = 'Declarations'
724 elseif msg == 'textDocument/typeDefinition'
725 title = 'Type Definitions'
726 elseif msg == 'textDocument/implementation'
727 title = 'Implementations'
729 title = 'Definitions'
732 if lspserver.needOffsetEncoding
733 # Decode the position encoding in all the symbol locations
734 result->map((_, loc) => {
735 lspserver.decodeLocation(loc)
740 symbol.ShowLocations(lspserver, result, peekSymbol, title)
745 # Select the location requested in 'count'
747 if idx >= result->len()
748 idx = result->len() - 1
750 location = result[idx]
754 lspserver.decodeLocation(location)
756 symbol.GotoSymbol(lspserver, location, peekSymbol, cmdmods)
759 # Request: "textDocument/definition"
760 # Param: DefinitionParams
761 def GotoDefinition(lspserver: dict<any>, peek: bool, cmdmods: string, count: number)
762 # Check whether LSP server supports jumping to a definition
763 if !lspserver.isDefinitionProvider
764 util.ErrMsg('Jumping to a symbol definition is not supported')
768 # interface DefinitionParams
769 # interface TextDocumentPositionParams
770 GotoSymbolLoc(lspserver, 'textDocument/definition', peek, cmdmods, count)
773 # Request: "textDocument/declaration"
774 # Param: DeclarationParams
775 def GotoDeclaration(lspserver: dict<any>, peek: bool, cmdmods: string, count: number)
776 # Check whether LSP server supports jumping to a declaration
777 if !lspserver.isDeclarationProvider
778 util.ErrMsg('Jumping to a symbol declaration is not supported')
782 # interface DeclarationParams
783 # interface TextDocumentPositionParams
784 GotoSymbolLoc(lspserver, 'textDocument/declaration', peek, cmdmods, count)
787 # Request: "textDocument/typeDefinition"
788 # Param: TypeDefinitionParams
789 def GotoTypeDef(lspserver: dict<any>, peek: bool, cmdmods: string, count: number)
790 # Check whether LSP server supports jumping to a type definition
791 if !lspserver.isTypeDefinitionProvider
792 util.ErrMsg('Jumping to a symbol type definition is not supported')
796 # interface TypeDefinitionParams
797 # interface TextDocumentPositionParams
798 GotoSymbolLoc(lspserver, 'textDocument/typeDefinition', peek, cmdmods, count)
801 # Request: "textDocument/implementation"
802 # Param: ImplementationParams
803 def GotoImplementation(lspserver: dict<any>, peek: bool, cmdmods: string, count: number)
804 # Check whether LSP server supports jumping to a implementation
805 if !lspserver.isImplementationProvider
806 util.ErrMsg('Jumping to a symbol implementation is not supported')
810 # interface ImplementationParams
811 # interface TextDocumentPositionParams
812 GotoSymbolLoc(lspserver, 'textDocument/implementation', peek, cmdmods, count)
815 # Request: "textDocument/switchSourceHeader"
816 # Param: TextDocumentIdentifier
817 # Clangd specific extension
818 def SwitchSourceHeader(lspserver: dict<any>)
820 param.uri = util.LspFileToUri(@%)
821 var reply = lspserver.rpc('textDocument/switchSourceHeader', param)
822 if reply->empty() || reply.result->empty()
823 util.WarnMsg('No alternate file found')
827 # process the 'textDocument/switchSourceHeader' reply from the LSP server
829 var fname = util.LspUriToFile(reply.result)
830 if (&modified && !&hidden) || &buftype != ''
831 # if the current buffer has unsaved changes and 'hidden' is not set,
832 # or if the current buffer is a special buffer, then ask to save changes
833 exe $'confirm edit {fname}'
839 # get symbol signature help.
840 # Request: "textDocument/signatureHelp"
841 # Param: SignatureHelpParams
842 def ShowSignature(lspserver: dict<any>): void
843 # Check whether LSP server supports signature help
844 if !lspserver.isSignatureHelpProvider
845 util.ErrMsg('LSP server does not support signature help')
849 # interface SignatureHelpParams
850 # interface TextDocumentPositionParams
851 var params = lspserver.getTextDocPosition(false)
852 lspserver.rpc_a('textDocument/signatureHelp', params,
853 signature.SignatureHelp)
856 # Send a file/document saved notification to the language server
857 def DidSaveFile(lspserver: dict<any>, bnr: number): void
858 # Check whether the LSP server supports the didSave notification
859 if !lspserver.supportsDidSave
860 # LSP server doesn't support text document synchronization
864 # Notification: 'textDocument/didSave'
865 # Params: DidSaveTextDocumentParams
866 var params = {textDocument: {uri: util.LspBufnrToUri(bnr)}}
867 # FIXME: Need to set "params.text" when
868 # 'lspserver.caps.textDocumentSync.save.includeText' is set to true.
869 lspserver.sendNotification('textDocument/didSave', params)
872 # get the hover information
873 # Request: "textDocument/hover"
875 def ShowHoverInfo(lspserver: dict<any>, cmdmods: string): void
876 # Check whether LSP server supports getting hover information.
877 # caps->hoverProvider can be a "boolean" or "HoverOptions"
878 if !lspserver.isHoverProvider
882 # interface HoverParams
883 # interface TextDocumentPositionParams
884 var params = lspserver.getTextDocPosition(false)
885 lspserver.rpc_a('textDocument/hover', params, (_, reply) => {
886 hover.HoverReply(lspserver, reply, cmdmods)
890 # Request: "textDocument/references"
891 # Param: ReferenceParams
892 def ShowReferences(lspserver: dict<any>, peek: bool): void
893 # Check whether LSP server supports getting reference information
894 if !lspserver.isReferencesProvider
895 util.ErrMsg('LSP server does not support showing references')
899 # interface ReferenceParams
900 # interface TextDocumentPositionParams
902 param = lspserver.getTextDocPosition(true)
903 param.context = {includeDeclaration: true}
904 var reply = lspserver.rpc('textDocument/references', param)
906 # Result: Location[] | null
907 if reply->empty() || reply.result->empty()
908 util.WarnMsg('No references found')
912 if lspserver.needOffsetEncoding
913 # Decode the position encoding in all the reference locations
914 reply.result->map((_, loc) => {
915 lspserver.decodeLocation(loc)
920 symbol.ShowLocations(lspserver, reply.result, peek, 'Symbol References')
923 # process the 'textDocument/documentHighlight' reply from the LSP server
924 # Result: DocumentHighlight[] | null
925 def DocHighlightReply(lspserver: dict<any>, docHighlightReply: any,
926 bnr: number, cmdmods: string): void
927 if docHighlightReply->empty()
928 if cmdmods !~ 'silent'
929 util.WarnMsg($'No highlight for the current position')
934 for docHL in docHighlightReply
935 lspserver.decodeRange(bnr, docHL.range)
936 var kind: number = docHL->get('kind', 1)
940 propName = 'LspReadRef'
943 propName = 'LspWriteRef'
946 propName = 'LspTextRef'
949 prop_add(docHL.range.start.line + 1,
950 util.GetLineByteFromPos(bnr, docHL.range.start) + 1,
951 {end_lnum: docHL.range.end.line + 1,
952 end_col: util.GetLineByteFromPos(bnr, docHL.range.end) + 1,
955 catch /E966\|E964/ # Invalid lnum | Invalid col
956 # Highlight replies arrive asynchronously and the document might have
957 # been modified in the mean time. As the reply is stale, ignore invalid
958 # line number and column number errors.
963 # Request: "textDocument/documentHighlight"
964 # Param: DocumentHighlightParams
965 def DocHighlight(lspserver: dict<any>, cmdmods: string): void
966 # Check whether LSP server supports getting highlight information
967 if !lspserver.isDocumentHighlightProvider
968 util.ErrMsg('LSP server does not support document highlight')
972 # interface DocumentHighlightParams
973 # interface TextDocumentPositionParams
974 var params = lspserver.getTextDocPosition(false)
975 lspserver.rpc_a('textDocument/documentHighlight', params, (_, reply) => {
976 DocHighlightReply(lspserver, reply, bufnr(), cmdmods)
980 # Request: "textDocument/documentSymbol"
981 # Param: DocumentSymbolParams
982 def GetDocSymbols(lspserver: dict<any>, fname: string): void
983 # Check whether LSP server supports getting document symbol information
984 if !lspserver.isDocumentSymbolProvider
985 util.ErrMsg('LSP server does not support getting list of symbols')
989 # interface DocumentSymbolParams
990 # interface TextDocumentIdentifier
991 var params = {textDocument: {uri: util.LspFileToUri(fname)}}
992 lspserver.rpc_a('textDocument/documentSymbol', params, (_, reply) => {
993 symbol.DocSymbolReply(lspserver, reply, fname)
997 # Request: "textDocument/formatting"
998 # Param: DocumentFormattingParams
1000 # Request: "textDocument/rangeFormatting"
1001 # Param: DocumentRangeFormattingParams
1002 def TextDocFormat(lspserver: dict<any>, fname: string, rangeFormat: bool,
1003 start_lnum: number, end_lnum: number)
1004 # Check whether LSP server supports formatting documents
1005 if !lspserver.isDocumentFormattingProvider
1006 util.ErrMsg('LSP server does not support formatting documents')
1012 cmd = 'textDocument/rangeFormatting'
1014 cmd = 'textDocument/formatting'
1017 # interface DocumentFormattingParams
1018 # interface TextDocumentIdentifier
1019 # interface FormattingOptions
1021 param.textDocument = {uri: util.LspFileToUri(fname)}
1022 var fmtopts: dict<any> = {
1023 tabSize: shiftwidth(),
1024 insertSpaces: &expandtab ? true : false,
1026 param.options = fmtopts
1029 var r: dict<dict<number>> = {
1030 start: {line: start_lnum - 1, character: 0},
1031 end: {line: end_lnum - 1, character: charcol([end_lnum, '$']) - 1}}
1035 var reply = lspserver.rpc(cmd, param)
1037 # result: TextEdit[] | null
1039 if reply->empty() || reply.result->empty()
1044 var bnr: number = fname->bufnr()
1046 # file is already removed
1050 if lspserver.needOffsetEncoding
1051 # Decode the position encoding in all the reference locations
1052 reply.result->map((_, textEdit) => {
1053 lspserver.decodeRange(bnr, textEdit.range)
1058 # interface TextEdit
1059 # Apply each of the text edit operations
1060 var save_cursor: list<number> = getcurpos()
1061 textedit.ApplyTextEdits(bnr, reply.result)
1062 save_cursor->setpos('.')
1065 # Request: "textDocument/prepareCallHierarchy"
1066 def PrepareCallHierarchy(lspserver: dict<any>): dict<any>
1067 # interface CallHierarchyPrepareParams
1068 # interface TextDocumentPositionParams
1069 var param: dict<any>
1070 param = lspserver.getTextDocPosition(false)
1071 var reply = lspserver.rpc('textDocument/prepareCallHierarchy', param)
1072 if reply->empty() || reply.result->empty()
1076 # Result: CallHierarchyItem[] | null
1077 var choice: number = 1
1078 if reply.result->len() > 1
1079 var items: list<string> = ['Select a Call Hierarchy Item:']
1080 for i in reply.result->len()->range()
1081 items->add(printf("%d. %s", i + 1, reply.result[i].name))
1083 choice = items->inputlist()
1084 if choice < 1 || choice > items->len()
1089 return reply.result[choice - 1]
1092 # Request: "callHierarchy/incomingCalls"
1093 def IncomingCalls(lspserver: dict<any>, fname: string)
1094 # Check whether LSP server supports call hierarchy
1095 if !lspserver.isCallHierarchyProvider
1096 util.ErrMsg('LSP server does not support call hierarchy')
1100 callhier.IncomingCalls(lspserver)
1103 def GetIncomingCalls(lspserver: dict<any>, item: dict<any>): any
1104 # Request: "callHierarchy/incomingCalls"
1105 # Param: CallHierarchyIncomingCallsParams
1108 var reply = lspserver.rpc('callHierarchy/incomingCalls', param)
1113 if lspserver.needOffsetEncoding
1114 # Decode the position encoding in all the incoming call locations
1115 var bnr = util.LspUriToBufnr(item.uri)
1116 reply.result->map((_, hierItem) => {
1117 lspserver.decodeRange(bnr, hierItem.from.range)
1125 # Request: "callHierarchy/outgoingCalls"
1126 def OutgoingCalls(lspserver: dict<any>, fname: string)
1127 # Check whether LSP server supports call hierarchy
1128 if !lspserver.isCallHierarchyProvider
1129 util.ErrMsg('LSP server does not support call hierarchy')
1133 callhier.OutgoingCalls(lspserver)
1136 def GetOutgoingCalls(lspserver: dict<any>, item: dict<any>): any
1137 # Request: "callHierarchy/outgoingCalls"
1138 # Param: CallHierarchyOutgoingCallsParams
1141 var reply = lspserver.rpc('callHierarchy/outgoingCalls', param)
1146 if lspserver.needOffsetEncoding
1147 # Decode the position encoding in all the outgoing call locations
1148 var bnr = util.LspUriToBufnr(item.uri)
1149 reply.result->map((_, hierItem) => {
1150 lspserver.decodeRange(bnr, hierItem.to.range)
1158 # Request: "textDocument/inlayHint"
1160 def InlayHintsShow(lspserver: dict<any>)
1161 # Check whether LSP server supports type hierarchy
1162 if !lspserver.isInlayHintProvider && !lspserver.isClangdInlayHintsProvider
1163 util.ErrMsg('LSP server does not support inlay hint')
1167 var lastlnum = line('$')
1169 textDocument: {uri: util.LspFileToUri(@%)},
1172 start: {line: 0, character: 0},
1173 end: {line: lastlnum - 1, character: charcol([lastlnum, '$']) - 1}
1177 lspserver.encodeRange(bufnr(), param.range)
1180 if lspserver.isClangdInlayHintsProvider
1181 # clangd-style inlay hints
1182 msg = 'clangd/inlayHints'
1184 msg = 'textDocument/inlayHint'
1186 var reply = lspserver.rpc_a(msg, param, inlayhints.InlayHintsReply)
1189 def DecodeTypeHierarchy(lspserver: dict<any>, isSuper: bool, typeHier: dict<any>)
1190 if !lspserver.needOffsetEncoding
1193 var bnr = util.LspUriToBufnr(typeHier.uri)
1194 lspserver.decodeRange(bnr, typeHier.range)
1195 lspserver.decodeRange(bnr, typeHier.selectionRange)
1196 var subType: list<dict<any>>
1198 subType = typeHier->get('parents', [])
1200 subType = typeHier->get('children', [])
1202 if !subType->empty()
1203 # Decode the position encoding in all the type hierarchy items
1204 subType->map((_, typeHierItem) => {
1205 DecodeTypeHierarchy(lspserver, isSuper, typeHierItem)
1211 # Request: "textDocument/typehierarchy"
1212 # Support the clangd version of type hierarchy retrieval method.
1213 # The method described in the LSP 3.17.0 standard is not supported as clangd
1214 # doesn't support that method.
1215 def TypeHiearchy(lspserver: dict<any>, direction: number)
1216 # Check whether LSP server supports type hierarchy
1217 if !lspserver.isTypeHierarchyProvider
1218 util.ErrMsg('LSP server does not support type hierarchy')
1222 # interface TypeHierarchy
1223 # interface TextDocumentPositionParams
1224 var param: dict<any>
1225 param = lspserver.getTextDocPosition(false)
1226 # 0: children, 1: parent, 2: both
1227 param.direction = direction
1229 var reply = lspserver.rpc('textDocument/typeHierarchy', param)
1230 if reply->empty() || reply.result->empty()
1231 util.WarnMsg('No type hierarchy available')
1235 var isSuper = (direction == 1)
1237 DecodeTypeHierarchy(lspserver, isSuper, reply.result)
1239 typehier.ShowTypeHierarchy(lspserver, isSuper, reply.result)
1242 # Decode the ranges in "WorkspaceEdit"
1243 def DecodeWorkspaceEdit(lspserver: dict<any>, workspaceEdit: dict<any>)
1244 if !lspserver.needOffsetEncoding
1247 if workspaceEdit->has_key('changes')
1248 for [uri, changes] in workspaceEdit.changes->items()
1249 var bnr: number = util.LspUriToBufnr(uri)
1253 # Decode the position encoding in all the text edit locations
1254 changes->map((_, textEdit) => {
1255 lspserver.decodeRange(bnr, textEdit.range)
1261 if workspaceEdit->has_key('documentChanges')
1262 for change in workspaceEdit.documentChanges
1263 if !change->has_key('kind')
1264 var bnr: number = util.LspUriToBufnr(change.textDocument.uri)
1268 # Decode the position encoding in all the text edit locations
1269 change.edits->map((_, textEdit) => {
1270 lspserver.decodeRange(bnr, textEdit.range)
1278 # Request: "textDocument/rename"
1279 # Param: RenameParams
1280 def RenameSymbol(lspserver: dict<any>, newName: string)
1281 # Check whether LSP server supports rename operation
1282 if !lspserver.isRenameProvider
1283 util.ErrMsg('LSP server does not support rename operation')
1287 # interface RenameParams
1288 # interface TextDocumentPositionParams
1289 var param: dict<any> = {}
1290 param = lspserver.getTextDocPosition(true)
1291 param.newName = newName
1293 var reply = lspserver.rpc('textDocument/rename', param)
1295 # Result: WorkspaceEdit | null
1296 if reply->empty() || reply.result->empty()
1301 # result: WorkspaceEdit
1302 DecodeWorkspaceEdit(lspserver, reply.result)
1303 textedit.ApplyWorkspaceEdit(reply.result)
1306 # Decode the range in "CodeAction"
1307 def DecodeCodeAction(lspserver: dict<any>, actionList: list<dict<any>>)
1308 if !lspserver.needOffsetEncoding
1311 actionList->map((_, act) => {
1312 if !act->has_key('disabled') && act->has_key('edit')
1313 DecodeWorkspaceEdit(lspserver, act.edit)
1319 # Request: "textDocument/codeAction"
1320 # Param: CodeActionParams
1321 def CodeAction(lspserver: dict<any>, fname_arg: string, line1: number,
1322 line2: number, query: string)
1323 # Check whether LSP server supports code action operation
1324 if !lspserver.isCodeActionProvider
1325 util.ErrMsg('LSP server does not support code action operation')
1329 # interface CodeActionParams
1330 var params: dict<any> = {}
1331 var fname: string = fname_arg->fnamemodify(':p')
1332 var bnr: number = fname_arg->bufnr()
1333 var r: dict<dict<number>> = {
1336 character: line1 == line2 ? util.GetCharIdxWithCompChar(getline('.'), charcol('.') - 1) : 0
1340 character: util.GetCharIdxWithCompChar(getline(line2), charcol([line2, '$']) - 1)
1343 lspserver.encodeRange(bnr, r)
1344 params->extend({textDocument: {uri: util.LspFileToUri(fname)}, range: r})
1345 var d: list<dict<any>> = []
1346 for lnum in range(line1, line2)
1347 var diagsInfo: list<dict<any>> = diag.GetDiagsByLine(bnr, lnum, lspserver)->deepcopy()
1348 if lspserver.needOffsetEncoding
1349 diagsInfo->map((_, di) => {
1350 lspserver.encodeRange(bnr, di.range)
1354 d->extend(diagsInfo)
1356 params->extend({context: {diagnostics: d, triggerKind: 1}})
1358 var reply = lspserver.rpc('textDocument/codeAction', params)
1360 # Result: (Command | CodeAction)[] | null
1361 if reply->empty() || reply.result->empty()
1362 # no action can be performed
1363 util.WarnMsg('No code action is available')
1367 DecodeCodeAction(lspserver, reply.result)
1369 codeaction.ApplyCodeAction(lspserver, reply.result, query)
1372 # Request: "textDocument/codeLens"
1373 # Param: CodeLensParams
1374 def CodeLens(lspserver: dict<any>, fname: string)
1375 # Check whether LSP server supports code lens operation
1376 if !lspserver.isCodeLensProvider
1377 util.ErrMsg('LSP server does not support code lens operation')
1381 var params = {textDocument: {uri: util.LspFileToUri(fname)}}
1382 var reply = lspserver.rpc('textDocument/codeLens', params)
1383 if reply->empty() || reply.result->empty()
1384 util.WarnMsg($'No code lens actions found for the current file')
1388 var bnr = fname->bufnr()
1390 # Decode the position encoding in all the code lens items
1391 if lspserver.needOffsetEncoding
1392 reply.result->map((_, codeLensItem) => {
1393 lspserver.decodeRange(bnr, codeLensItem.range)
1398 codelens.ProcessCodeLens(lspserver, bnr, reply.result)
1401 # Request: "codeLens/resolve"
1403 def ResolveCodeLens(lspserver: dict<any>, bnr: number,
1404 codeLens: dict<any>): dict<any>
1405 if !lspserver.isCodeLensResolveProvider
1409 if lspserver.needOffsetEncoding
1410 lspserver.encodeRange(bnr, codeLens.range)
1413 var reply = lspserver.rpc('codeLens/resolve', codeLens)
1418 var codeLensItem: dict<any> = reply.result
1420 # Decode the position encoding in the code lens item
1421 if lspserver.needOffsetEncoding
1422 lspserver.decodeRange(bnr, codeLensItem.range)
1428 # List project-wide symbols matching query string
1429 # Request: "workspace/symbol"
1430 # Param: WorkspaceSymbolParams
1431 def WorkspaceQuerySymbols(lspserver: dict<any>, query: string)
1432 # Check whether the LSP server supports listing workspace symbols
1433 if !lspserver.isWorkspaceSymbolProvider
1434 util.ErrMsg('LSP server does not support listing workspace symbols')
1438 # Param: WorkspaceSymbolParams
1441 var reply = lspserver.rpc('workspace/symbol', param)
1442 if reply->empty() || reply.result->empty()
1443 util.WarnMsg($'Symbol "{query}" is not found')
1447 symbol.WorkspaceSymbolPopup(lspserver, query, reply.result)
1450 # Add a workspace folder to the language server.
1451 def AddWorkspaceFolder(lspserver: dict<any>, dirName: string): void
1452 if !lspserver.caps->has_key('workspace')
1453 || !lspserver.caps.workspace->has_key('workspaceFolders')
1454 || !lspserver.caps.workspace.workspaceFolders->has_key('supported')
1455 || !lspserver.caps.workspace.workspaceFolders.supported
1456 util.ErrMsg('LSP server does not support workspace folders')
1460 if lspserver.workspaceFolders->index(dirName) != -1
1461 util.ErrMsg($'{dirName} is already part of this workspace')
1465 # Notification: 'workspace/didChangeWorkspaceFolders'
1466 # Params: DidChangeWorkspaceFoldersParams
1467 var params = {event: {added: [dirName], removed: []}}
1468 lspserver.sendNotification('workspace/didChangeWorkspaceFolders', params)
1470 lspserver.workspaceFolders->add(dirName)
1473 # Remove a workspace folder from the language server.
1474 def RemoveWorkspaceFolder(lspserver: dict<any>, dirName: string): void
1475 if !lspserver.caps->has_key('workspace')
1476 || !lspserver.caps.workspace->has_key('workspaceFolders')
1477 || !lspserver.caps.workspace.workspaceFolders->has_key('supported')
1478 || !lspserver.caps.workspace.workspaceFolders.supported
1479 util.ErrMsg('LSP server does not support workspace folders')
1483 var idx: number = lspserver.workspaceFolders->index(dirName)
1485 util.ErrMsg($'{dirName} is not currently part of this workspace')
1489 # Notification: "workspace/didChangeWorkspaceFolders"
1490 # Param: DidChangeWorkspaceFoldersParams
1491 var params = {event: {added: [], removed: [dirName]}}
1492 lspserver.sendNotification('workspace/didChangeWorkspaceFolders', params)
1494 lspserver.workspaceFolders->remove(idx)
1497 def DecodeSelectionRange(lspserver: dict<any>, bnr: number, selRange: dict<any>)
1498 lspserver.decodeRange(bnr, selRange.range)
1499 if selRange->has_key('parent')
1500 DecodeSelectionRange(lspserver, bnr, selRange.parent)
1504 # select the text around the current cursor location
1505 # Request: "textDocument/selectionRange"
1506 # Param: SelectionRangeParams
1507 def SelectionRange(lspserver: dict<any>, fname: string)
1508 # Check whether LSP server supports selection ranges
1509 if !lspserver.isSelectionRangeProvider
1510 util.ErrMsg('LSP server does not support selection ranges')
1514 # clear the previous selection reply
1515 lspserver.selection = {}
1517 # interface SelectionRangeParams
1518 # interface TextDocumentIdentifier
1520 param.textDocument = {}
1521 param.textDocument.uri = util.LspFileToUri(fname)
1522 param.positions = [lspserver.getPosition(false)]
1523 var reply = lspserver.rpc('textDocument/selectionRange', param)
1525 if reply->empty() || reply.result->empty()
1529 # Decode the position encoding in all the selection range items
1530 if lspserver.needOffsetEncoding
1531 var bnr = fname->bufnr()
1532 reply.result->map((_, selItem) => {
1533 DecodeSelectionRange(lspserver, bnr, selItem)
1538 selection.SelectionStart(lspserver, reply.result)
1541 # Expand the previous selection or start a new one
1542 def SelectionExpand(lspserver: dict<any>)
1543 # Check whether LSP server supports selection ranges
1544 if !lspserver.isSelectionRangeProvider
1545 util.ErrMsg('LSP server does not support selection ranges')
1549 selection.SelectionModify(lspserver, true)
1552 # Shrink the previous selection or start a new one
1553 def SelectionShrink(lspserver: dict<any>)
1554 # Check whether LSP server supports selection ranges
1555 if !lspserver.isSelectionRangeProvider
1556 util.ErrMsg('LSP server does not support selection ranges')
1560 selection.SelectionModify(lspserver, false)
1563 # fold the entire document
1564 # Request: "textDocument/foldingRange"
1565 # Param: FoldingRangeParams
1566 def FoldRange(lspserver: dict<any>, fname: string)
1567 # Check whether LSP server supports fold ranges
1568 if !lspserver.isFoldingRangeProvider
1569 util.ErrMsg('LSP server does not support folding')
1573 # interface FoldingRangeParams
1574 # interface TextDocumentIdentifier
1575 var params = {textDocument: {uri: util.LspFileToUri(fname)}}
1576 var reply = lspserver.rpc('textDocument/foldingRange', params)
1577 if reply->empty() || reply.result->empty()
1581 # result: FoldingRange[]
1582 var end_lnum: number
1583 var last_lnum: number = line('$')
1584 for foldRange in reply.result
1585 end_lnum = foldRange.endLine + 1
1586 if end_lnum < foldRange.startLine + 2
1587 end_lnum = foldRange.startLine + 2
1589 exe $':{foldRange.startLine + 2}, {end_lnum}fold'
1590 # Open all the folds, otherwise the subsequently created folds are not
1596 :setlocal foldcolumn=2
1600 # process the 'workspace/executeCommand' reply from the LSP server
1601 # Result: any | null
1602 def WorkspaceExecuteReply(lspserver: dict<any>, execReply: any)
1603 # Nothing to do for the reply
1606 # Request the LSP server to execute a command
1607 # Request: workspace/executeCommand
1608 # Params: ExecuteCommandParams
1609 def ExecuteCommand(lspserver: dict<any>, cmd: dict<any>)
1610 # Need to check for lspserver.caps.executeCommandProvider?
1612 lspserver.rpc_a('workspace/executeCommand', params, WorkspaceExecuteReply)
1615 # Display the LSP server capabilities (received during the initialization
1617 def GetCapabilities(lspserver: dict<any>): list<string>
1619 var heading = $"'{lspserver.path}' Language Server Capabilities"
1620 var underlines = repeat('=', heading->len())
1621 l->extend([heading, underlines])
1622 for k in lspserver.caps->keys()->sort()
1623 l->add($'{k}: {lspserver.caps[k]->string()}')
1628 # Display the LSP server initialize request and result
1629 def GetInitializeRequest(lspserver: dict<any>): list<string>
1631 var heading = $"'{lspserver.path}' Language Server Initialize Request"
1632 var underlines = repeat('=', heading->len())
1633 l->extend([heading, underlines])
1634 if lspserver->has_key('rpcInitializeRequest')
1635 for k in lspserver.rpcInitializeRequest->keys()->sort()
1636 l->add($'{k}: {lspserver.rpcInitializeRequest[k]->string()}')
1642 # Store a log or trace message received from the language server.
1643 def AddMessage(lspserver: dict<any>, msgType: string, newMsg: string)
1644 # A single message may contain multiple lines separate by newline
1645 var msgs = newMsg->split("\n")
1646 lspserver.messages->add($'{strftime("%m/%d/%y %T")}: [{msgType}]: {msgs[0]}')
1647 lspserver.messages->extend(msgs[1 : ])
1648 # Keep only the last 500 messages to reduce the memory usage
1649 if lspserver.messages->len() >= 600
1650 lspserver.messages = lspserver.messages[-500 : ]
1654 # Display the log messages received from the LSP server (window/logMessage)
1655 def GetMessages(lspserver: dict<any>): list<string>
1656 if lspserver.messages->empty()
1657 return [$'No messages received from "{lspserver.name}" server']
1661 var heading = $"'{lspserver.path}' Language Server Messages"
1662 var underlines = repeat('=', heading->len())
1663 l->extend([heading, underlines])
1664 l->extend(lspserver.messages)
1668 # Send a 'textDocument/definition' request to the LSP server to get the
1669 # location where the symbol under the cursor is defined and return a list of
1670 # Dicts in a format accepted by the 'tagfunc' option.
1671 # Returns null if the LSP server doesn't support getting the location of a
1672 # symbol definition or the symbol is not defined.
1673 def TagFunc(lspserver: dict<any>, pat: string, flags: string, info: dict<any>): any
1674 # Check whether LSP server supports getting the location of a definition
1675 if !lspserver.isDefinitionProvider
1679 # interface DefinitionParams
1680 # interface TextDocumentPositionParams
1681 var reply = lspserver.rpc('textDocument/definition',
1682 lspserver.getTextDocPosition(false))
1683 if reply->empty() || reply.result->empty()
1687 var taglocations: list<dict<any>>
1688 if reply.result->type() == v:t_list
1689 taglocations = reply.result
1691 taglocations = [reply.result]
1694 if lspserver.needOffsetEncoding
1695 # Decode the position encoding in all the reference locations
1696 taglocations->map((_, loc) => {
1697 lspserver.decodeLocation(loc)
1702 return symbol.TagFunc(lspserver, taglocations, pat)
1705 # Returns unique ID used for identifying the various servers
1706 var UniqueServerIdCounter = 0
1707 def GetUniqueServerId(): number
1708 UniqueServerIdCounter = UniqueServerIdCounter + 1
1709 return UniqueServerIdCounter
1712 export def NewLspServer(name_arg: string, path_arg: string, args: list<string>,
1713 isSync: bool, initializationOptions: any,
1714 workspaceConfig: dict<any>,
1715 rootSearchFiles: list<any>,
1716 runIfSearchFiles: list<any>,
1717 runUnlessSearchFiles: list<any>,
1718 customNotificationHandlers: dict<func>,
1719 customRequestHandlers: dict<func>,
1720 ProcessDiagHandler: func,
1721 features: dict<bool>, debug_arg: bool): dict<any>
1722 var lspserver: dict<any> = {
1723 id: GetUniqueServerId(),
1728 initializationOptions: initializationOptions,
1729 customNotificationHandlers: customNotificationHandlers,
1730 customRequestHandlers: customRequestHandlers,
1731 processDiagHandler: ProcessDiagHandler,
1740 rootSearchFiles: rootSearchFiles,
1741 runIfSearchFiles: runIfSearchFiles,
1742 runUnlessSearchFiles: runUnlessSearchFiles,
1743 omniCompletePending: false,
1744 completionTriggerChars: [],
1747 typeHierFilePopup: -1,
1748 workspaceSymbolPopup: -1,
1749 workspaceSymbolQuery: '',
1750 peekSymbolPopup: -1,
1751 peekSymbolFilePopup: -1,
1752 callHierarchyType: '',
1754 workspaceConfig: workspaceConfig,
1758 lspserver.logfile = $'lsp-{lspserver.name}.log'
1759 lspserver.errfile = $'lsp-{lspserver.name}.err'
1761 # Add the LSP server functions
1763 startServer: function(StartServer, [lspserver]),
1764 initServer: function(InitServer, [lspserver]),
1765 stopServer: function(StopServer, [lspserver]),
1766 shutdownServer: function(ShutdownServer, [lspserver]),
1767 exitServer: function(ExitServer, [lspserver]),
1768 setTrace: function(SetTrace, [lspserver]),
1769 traceLog: function(TraceLog, [lspserver]),
1770 errorLog: function(ErrorLog, [lspserver]),
1771 nextReqID: function(NextReqID, [lspserver]),
1772 createRequest: function(CreateRequest, [lspserver]),
1773 createResponse: function(CreateResponse, [lspserver]),
1774 sendResponse: function(SendResponse, [lspserver]),
1775 sendMessage: function(SendMessage, [lspserver]),
1776 sendNotification: function(SendNotification, [lspserver]),
1777 rpc: function(Rpc, [lspserver]),
1778 rpc_a: function(AsyncRpc, [lspserver]),
1779 waitForResponse: function(WaitForResponse, [lspserver]),
1780 processReply: function(handlers.ProcessReply, [lspserver]),
1781 processNotif: function(handlers.ProcessNotif, [lspserver]),
1782 processRequest: function(handlers.ProcessRequest, [lspserver]),
1783 processMessages: function(handlers.ProcessMessages, [lspserver]),
1784 encodePosition: function(offset.EncodePosition, [lspserver]),
1785 decodePosition: function(offset.DecodePosition, [lspserver]),
1786 encodeRange: function(offset.EncodeRange, [lspserver]),
1787 decodeRange: function(offset.DecodeRange, [lspserver]),
1788 encodeLocation: function(offset.EncodeLocation, [lspserver]),
1789 decodeLocation: function(offset.DecodeLocation, [lspserver]),
1790 getPosition: function(GetPosition, [lspserver]),
1791 getTextDocPosition: function(GetTextDocPosition, [lspserver]),
1792 textdocDidOpen: function(TextdocDidOpen, [lspserver]),
1793 textdocDidClose: function(TextdocDidClose, [lspserver]),
1794 textdocDidChange: function(TextdocDidChange, [lspserver]),
1795 sendInitializedNotif: function(SendInitializedNotif, [lspserver]),
1796 sendWorkspaceConfig: function(SendWorkspaceConfig, [lspserver]),
1797 getCompletion: function(GetCompletion, [lspserver]),
1798 resolveCompletion: function(ResolveCompletion, [lspserver]),
1799 gotoDefinition: function(GotoDefinition, [lspserver]),
1800 gotoDeclaration: function(GotoDeclaration, [lspserver]),
1801 gotoTypeDef: function(GotoTypeDef, [lspserver]),
1802 gotoImplementation: function(GotoImplementation, [lspserver]),
1803 tagFunc: function(TagFunc, [lspserver]),
1804 switchSourceHeader: function(SwitchSourceHeader, [lspserver]),
1805 showSignature: function(ShowSignature, [lspserver]),
1806 didSaveFile: function(DidSaveFile, [lspserver]),
1807 hover: function(ShowHoverInfo, [lspserver]),
1808 showReferences: function(ShowReferences, [lspserver]),
1809 docHighlight: function(DocHighlight, [lspserver]),
1810 getDocSymbols: function(GetDocSymbols, [lspserver]),
1811 textDocFormat: function(TextDocFormat, [lspserver]),
1812 prepareCallHierarchy: function(PrepareCallHierarchy, [lspserver]),
1813 incomingCalls: function(IncomingCalls, [lspserver]),
1814 getIncomingCalls: function(GetIncomingCalls, [lspserver]),
1815 outgoingCalls: function(OutgoingCalls, [lspserver]),
1816 getOutgoingCalls: function(GetOutgoingCalls, [lspserver]),
1817 inlayHintsShow: function(InlayHintsShow, [lspserver]),
1818 typeHierarchy: function(TypeHiearchy, [lspserver]),
1819 renameSymbol: function(RenameSymbol, [lspserver]),
1820 codeAction: function(CodeAction, [lspserver]),
1821 codeLens: function(CodeLens, [lspserver]),
1822 resolveCodeLens: function(ResolveCodeLens, [lspserver]),
1823 workspaceQuery: function(WorkspaceQuerySymbols, [lspserver]),
1824 addWorkspaceFolder: function(AddWorkspaceFolder, [lspserver]),
1825 removeWorkspaceFolder: function(RemoveWorkspaceFolder, [lspserver]),
1826 selectionRange: function(SelectionRange, [lspserver]),
1827 selectionExpand: function(SelectionExpand, [lspserver]),
1828 selectionShrink: function(SelectionShrink, [lspserver]),
1829 foldRange: function(FoldRange, [lspserver]),
1830 executeCommand: function(ExecuteCommand, [lspserver]),
1831 workspaceConfigGet: function(WorkspaceConfigGet, [lspserver]),
1832 getCapabilities: function(GetCapabilities, [lspserver]),
1833 getInitializeRequest: function(GetInitializeRequest, [lspserver]),
1834 addMessage: function(AddMessage, [lspserver]),
1835 getMessages: function(GetMessages, [lspserver])
1841 # vim: tabstop=8 shiftwidth=2 softtabstop=2