5 # The functions called by plugin/lsp.vim are in this file.
7 # Needs Vim 9.0 and higher
12 import './options.vim' as opt
13 import './lspserver.vim' as lserver
15 import './buffer.vim' as buf
16 import './completion.vim'
17 import './textedit.vim'
20 import './outline.vim'
21 import './signature.vim'
22 import './codeaction.vim'
23 import './inlayhints.vim'
25 # LSP server information
26 var LSPServers: list<dict<any>> = []
28 # filetype to LSP server map
29 var ftypeServerMap: dict<list<dict<any>>> = {}
31 var lspInitializedOnce = false
35 {name: 'LspTextRef', default: true, linksto: 'Search'},
36 {name: 'LspReadRef', default: true, linksto: 'DiffChange'},
37 {name: 'LspWriteRef', default: true, linksto: 'DiffDelete'}
40 var override = &cursorline
41 && &cursorlineopt =~ '\<line\>\|\<screenline\>\|\<both\>'
43 prop_type_add('LspTextRef', {highlight: 'LspTextRef', override: override})
44 prop_type_add('LspReadRef', {highlight: 'LspReadRef', override: override})
45 prop_type_add('LspWriteRef', {highlight: 'LspWriteRef', override: override})
52 :set ballooneval balloonevalterm
53 lspInitializedOnce = true
56 # Returns the LSP servers for the a specific filetype. Based on how well there
57 # score, LSP servers with the same score are being returned.
58 # Returns an empty list if the servers is not found.
59 def LspGetServers(bnr: number, ftype: string): list<dict<any>>
60 if !ftypeServerMap->has_key(ftype)
64 var bufDir = bnr->bufname()->fnamemodify(':p:h')
66 return ftypeServerMap[ftype]->filter((key, lspserver) => {
67 # Don't run the server if no path is found
68 if !lspserver.runIfSearchFiles->empty()
69 var path = util.FindNearestRootDir(bufDir, lspserver.runIfSearchFiles)
76 # Don't run the server if a path is found
77 if !lspserver.runUnlessSearchFiles->empty()
78 var path = util.FindNearestRootDir(bufDir, lspserver.runUnlessSearchFiles)
90 # Add a LSP server for a filetype
91 def LspAddServer(ftype: string, lspsrv: dict<any>)
92 var lspsrvlst = ftypeServerMap->has_key(ftype) ? ftypeServerMap[ftype] : []
93 lspsrvlst->add(lspsrv)
94 ftypeServerMap[ftype] = lspsrvlst
97 # Enable/disable the logging of the language server protocol messages
98 def ServerDebug(arg: string)
99 if ['errors', 'messages', 'off', 'on']->index(arg) == -1
100 util.ErrMsg($'Unsupported argument "{arg}"')
104 var lspservers: list<dict<any>> = buf.CurbufGetServers()
105 if lspservers->empty()
106 util.WarnMsg($'No Lsp servers found for "{@%}"')
110 for lspserver in lspservers
112 util.ClearTraceLogs(lspserver.logfile)
113 util.ClearTraceLogs(lspserver.errfile)
114 lspserver.debug = true
116 lspserver.debug = false
117 elseif arg == 'messages'
118 util.ServerMessagesShow(lspserver.logfile)
120 util.ServerMessagesShow(lspserver.errfile)
125 # Show information about all the LSP servers
126 export def ShowAllServers()
128 # Add filetype to server mapping information
129 lines->add('Filetype Information')
130 lines->add('====================')
131 for [ftype, lspservers] in ftypeServerMap->items()
132 for lspserver in lspservers
133 lines->add($"Filetype: '{ftype}'")
134 lines->add($"Server Name: '{lspserver.name}'")
135 lines->add($"Server Path: '{lspserver.path}'")
136 lines->add($"Status: {lspserver.running ? 'Running' : 'Not running'}")
141 # Add buffer to server mapping information
142 lines->add('Buffer Information')
143 lines->add('==================')
144 for bnr in range(1, bufnr('$'))
145 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
146 if !lspservers->empty()
147 lines->add($"Buffer: '{bufname(bnr)}'")
148 for lspserver in lspservers
149 lines->add($"Server Path: '{lspserver.path}'")
150 lines->add($"Status: {lspserver.running ? 'Running' : 'Not running'}")
156 var wid = bufwinid('Language-Servers')
162 :new Language-Servers
163 :setlocal buftype=nofile
164 :setlocal bufhidden=wipe
166 :setlocal nonumber nornu
167 :setlocal fdc=0 signcolumn=no
171 :setlocal nomodifiable
174 # Create a new window containing the buffer "bname" or if the window is
175 # already present then jump to it.
176 def OpenScratchWindow(bname: string)
177 var wid = bufwinid(bname)
184 :setlocal buftype=nofile
185 :setlocal bufhidden=wipe
187 :setlocal nonumber nornu
188 :setlocal fdc=0 signcolumn=no
192 # Show the status of the LSP server for the current buffer
193 def ShowServer(arg: string)
194 if ['status', 'capabilities', 'initializeRequest', 'messages']->index(arg) == -1
195 util.ErrMsg($'Unsupported argument "{arg}"')
199 var lspservers: list<dict<any>> = buf.CurbufGetServers()
200 if lspservers->empty()
201 util.WarnMsg($'No Lsp servers found for "{@%}"')
205 var windowName: string = ''
206 var lines: list<string> = []
207 if arg->empty() || arg == 'status'
208 windowName = $'LangServer-Status'
209 for lspserver in lspservers
211 lines->extend(['', repeat('=', &columns), ''])
213 var msg = $"LSP server '{lspserver.name}' is "
217 msg ..= 'not running'
221 elseif arg == 'capabilities'
222 windowName = $'LangServer-Capabilities'
223 for lspserver in lspservers
225 lines->extend(['', repeat('=', &columns), ''])
227 lines->extend(lspserver.getCapabilities())
229 elseif arg == 'initializeRequest'
230 windowName = $'LangServer-InitializeRequest'
231 for lspserver in lspservers
233 lines->extend(['', repeat('=', &columns), ''])
235 lines->extend(lspserver.getInitializeRequest())
237 elseif arg == 'messages'
238 windowName = $'LangServer-Messages'
239 for lspserver in lspservers
241 lines->extend(['', repeat('=', &columns), ''])
243 lines->extend(lspserver.getMessages())
246 util.ErrMsg($'Unsupported argument "{arg}"')
251 OpenScratchWindow(windowName)
254 :setlocal nomodifiable
256 util.InfoMsg(lines[0])
260 # Get LSP server running status for filetype "ftype"
261 # Return true if running, or false if not found or not running
262 export def ServerRunning(ftype: string): bool
263 if ftypeServerMap->has_key(ftype)
264 var lspservers = ftypeServerMap[ftype]
265 for lspserver in lspservers
275 # Go to a definition using "textDocument/definition" LSP request
276 export def GotoDefinition(peek: bool, cmdmods: string, count: number)
277 var lspserver: dict<any> = buf.CurbufGetServerChecked('definition')
278 if lspserver->empty()
282 lspserver.gotoDefinition(peek, cmdmods, count)
285 # Go to a declaration using "textDocument/declaration" LSP request
286 export def GotoDeclaration(peek: bool, cmdmods: string, count: number)
287 var lspserver: dict<any> = buf.CurbufGetServerChecked('declaration')
288 if lspserver->empty()
292 lspserver.gotoDeclaration(peek, cmdmods, count)
295 # Go to a type definition using "textDocument/typeDefinition" LSP request
296 export def GotoTypedef(peek: bool, cmdmods: string, count: number)
297 var lspserver: dict<any> = buf.CurbufGetServerChecked('typeDefinition')
298 if lspserver->empty()
302 lspserver.gotoTypeDef(peek, cmdmods, count)
305 # Go to a implementation using "textDocument/implementation" LSP request
306 export def GotoImplementation(peek: bool, cmdmods: string, count: number)
307 var lspserver: dict<any> = buf.CurbufGetServerChecked('implementation')
308 if lspserver->empty()
312 lspserver.gotoImplementation(peek, cmdmods, count)
315 # Switch source header using "textDocument/switchSourceHeader" LSP request
316 # (Clangd specifc extension)
317 export def SwitchSourceHeader()
318 var lspserver: dict<any> = buf.CurbufGetServerChecked()
319 if lspserver->empty()
323 lspserver.switchSourceHeader()
326 # Show the signature using "textDocument/signatureHelp" LSP method
327 # Invoked from an insert-mode mapping, so return an empty string.
328 def g:LspShowSignature(): string
329 var lspserver: dict<any> = buf.CurbufGetServerChecked('signatureHelp')
330 if lspserver->empty()
334 # first send all the changes in the current buffer to the LSP server
336 lspserver.showSignature()
340 # A buffer is saved. Send the "textDocument/didSave" LSP notification
342 var bnr: number = expand('<abuf>')->str2nr()
343 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)->filter(
344 (key, lspsrv) => !lspsrv->empty() && lspsrv.running
347 if lspservers->empty()
351 for lspserver in lspservers
352 lspserver.didSaveFile(bnr)
356 # Return the diagnostic text from the LSP server for the current mouse line to
357 # display in a balloon
358 var lspDiagPopupID: number = 0
359 var lspDiagPopupInfo: dict<any> = {}
360 def g:LspDiagExpr(): any
361 # Display the diagnostic message only if the mouse is over the gutter for
363 if opt.lspOptions.noDiagHoverOnLine && v:beval_col >= 2
367 var diagsInfo: list<dict<any>> = diag.GetDiagsByLine(
371 if diagsInfo->empty()
372 # No diagnostic for the current cursor location
376 # Include all diagnostics from the current line in the message
377 var message: list<string> = []
378 for diag in diagsInfo
379 message->extend(diag.message->split("\n"))
385 # Called after leaving insert mode. Used to process diag messages (if any)
386 def LspLeftInsertMode()
387 if !exists('b:LspDiagsUpdatePending')
390 :unlet b:LspDiagsUpdatePending
392 var bnr: number = bufnr()
393 diag.ProcessNewDiags(bnr)
396 # Add buffer-local autocmds when attaching a LSP server to a buffer
397 def AddBufLocalAutocmds(lspserver: dict<any>, bnr: number): void
398 var acmds: list<dict<any>> = []
400 # file saved notification handler
401 acmds->add({bufnr: bnr,
402 event: 'BufWritePost',
403 group: 'LSPBufferAutocmds',
404 cmd: 'LspSavedFile()'})
406 # Update the diagnostics when insert mode is stopped
407 acmds->add({bufnr: bnr,
408 event: 'InsertLeave',
409 group: 'LSPBufferAutocmds',
410 cmd: 'LspLeftInsertMode()'})
412 # Auto highlight all the occurrences of the current keyword
413 if opt.lspOptions.autoHighlight &&
414 lspserver.isDocumentHighlightProvider
415 acmds->add({bufnr: bnr,
416 event: 'CursorMoved',
417 group: 'LSPBufferAutocmds',
418 cmd: 'call LspDocHighlightClear() | call LspDocHighlight("silent")'})
421 # Show diagnostics on the status line
422 if opt.lspOptions.showDiagOnStatusLine
423 acmds->add({bufnr: bnr,
424 event: 'CursorMoved',
425 group: 'LSPBufferAutocmds',
426 cmd: 'LspShowCurrentDiagInStatusLine()'})
432 def BufferInit(lspserverId: number, bnr: number): void
433 var lspserver = buf.BufLspServerGetById(bnr, lspserverId)
434 if lspserver->empty() || !lspserver.running
438 var ftype: string = bnr->getbufvar('&filetype')
439 lspserver.textdocDidOpen(bnr, ftype)
441 # add a listener to track changes to this buffer
442 listener_add((_bnr: number, start: number, end: number, added: number, changes: list<dict<number>>) => {
443 lspserver.textdocDidChange(bnr, start, end, added, changes)
446 AddBufLocalAutocmds(lspserver, bnr)
448 setbufvar(bnr, '&balloonexpr', 'g:LspDiagExpr()')
450 signature.BufferInit(lspserver)
451 inlayhints.BufferInit(lspserver, bnr)
453 var allServersReady = true
454 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
455 for lspsrv in lspservers
457 allServersReady = false
463 for lspsrv in lspservers
464 # It's only possible to initialize completion when all server capabilities
466 var completionServer = buf.BufLspServerGet(bnr, 'completion')
467 if !completionServer->empty() && lspsrv.id == completionServer.id
468 completion.BufferInit(lspsrv, bnr, ftype)
472 if exists('#User#LspAttached')
473 doautocmd <nomodeline> User LspAttached
478 # A new buffer is opened. If LSP is supported for this buffer, then add it
479 export def AddFile(bnr: number): void
480 if buf.BufHasLspServer(bnr)
481 # LSP server for this buffer is already initialized and running
486 if util.LspUriRemote(bnr->bufname()->fnamemodify(':p'))
490 var ftype: string = bnr->getbufvar('&filetype')
494 var lspservers: list<dict<any>> = LspGetServers(bnr, ftype)
495 if lspservers->empty()
498 for lspserver in lspservers
499 if !lspserver.running
500 if !lspInitializedOnce
503 lspserver.startServer(bnr)
505 buf.BufLspServerSet(bnr, lspserver)
508 BufferInit(lspserver.id, bnr)
510 augroup LSPBufferAutocmds
511 exe $'autocmd User LspServerReady_{lspserver.id} ++once BufferInit({lspserver.id}, {bnr})'
517 # Notify LSP server to remove a file
518 export def RemoveFile(bnr: number): void
519 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
520 for lspserver in lspservers->copy()
521 if lspserver->empty()
525 lspserver.textdocDidClose(bnr)
527 diag.DiagRemoveFile(bnr)
528 buf.BufLspServerRemove(bnr, lspserver)
532 # Buffer 'bnr' is loaded in a window, send the latest buffer contents to the
534 export def BufferLoadedInWin(bnr: number)
535 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
536 if lspservers->empty()
537 # No language servers for this buffer
540 for lspserver in lspservers
541 if !lspserver->empty() && lspserver.ready
542 lspserver.textdocDidChange(bnr, 0, 0, 0, [])
545 # Refresh the displayed diags visuals
546 diag.DiagsRefresh(bnr)
549 # Stop all the LSP servers
550 export def StopAllServers()
551 for lspserver in LSPServers
553 lspserver.stopServer()
558 # Add all the buffers with 'filetype' set to "ftype" to the language server.
559 def AddBuffersToLsp(ftype: string)
560 # Add all the buffers with the same file type as the current buffer
561 for binfo in getbufinfo({bufloaded: 1})
562 if binfo.bufnr->getbufvar('&filetype') == ftype
568 # Restart the LSP server for the current buffer
570 var lspservers: list<dict<any>> = buf.CurbufGetServers()
571 if lspservers->empty()
572 util.WarnMsg($'No Lsp servers found for "{@%}"')
576 # Remove all the buffers with the same file type as the current buffer
577 var ftype: string = &filetype
578 for binfo in getbufinfo()
579 if binfo.bufnr->getbufvar('&filetype') == ftype
580 RemoveFile(binfo.bufnr)
584 for lspserver in lspservers
585 # Stop the server (if running)
587 lspserver.stopServer()
590 # Start the server again
591 lspserver.startServer(bufnr(''))
594 AddBuffersToLsp(ftype)
597 # Add the LSP server for files with 'filetype' as "ftype".
598 def AddServerForFiltype(lspserver: dict<any>, ftype: string, omnicompl: bool)
599 LspAddServer(ftype, lspserver)
600 completion.OmniComplSet(ftype, omnicompl)
602 # If a buffer of this file type is already present, then send it to the LSP
604 AddBuffersToLsp(ftype)
607 # Register a LSP server for one or more file types
608 export def AddServer(serverList: list<dict<any>>)
609 for server in serverList
610 if !server->has_key('filetype') || !server->has_key('path')
611 util.ErrMsg('LSP server information is missing filetype or path')
614 # Enable omni-completion by default
615 server.omnicompl = server->get('omnicompl', true)
617 if !server.path->executable()
618 if !opt.lspOptions.ignoreMissingServer
619 util.ErrMsg($'LSP server {server.path} is not found')
623 if server->has_key('args')
624 if server.args->type() != v:t_list
625 util.ErrMsg($'Arguments for LSP server {server.args} is not a List')
632 if !server->has_key('initializationOptions')
633 || server.initializationOptions->type() != v:t_dict
634 server.initializationOptions = {}
637 if !server->has_key('forceOffsetEncoding')
638 || server.forceOffsetEncoding->type() != v:t_string
639 || (server.forceOffsetEncoding != 'utf-8'
640 && server.forceOffsetEncoding != 'utf-16'
641 && server.forceOffsetEncoding != 'utf-32')
642 server.forceOffsetEncoding = ''
645 if !server->has_key('customNotificationHandlers')
646 || server.customNotificationHandlers->type() != v:t_dict
647 server.customNotificationHandlers = {}
650 if server->has_key('processDiagHandler')
651 if server.processDiagHandler->type() != v:t_func
652 util.ErrMsg($'Setting of processDiagHandler {server.processDiagHandler} is not a Funcref nor lambda')
656 server.processDiagHandler = null_function
659 if !server->has_key('customRequestHandlers')
660 || server.customRequestHandlers->type() != v:t_dict
661 server.customRequestHandlers = {}
664 if !server->has_key('features') || server.features->type() != v:t_dict
668 if server.omnicompl->type() != v:t_bool
669 util.ErrMsg($'Setting of omnicompl {server.omnicompl} is not a Boolean')
673 if !server->has_key('syncInit')
674 server.syncInit = false
677 if !server->has_key('name') || server.name->type() != v:t_string
678 || server.name->empty()
679 # Use the executable name (without the extension) as the language server
681 server.name = server.path->fnamemodify(':t:r')
684 if !server->has_key('debug') || server.debug->type() != v:t_bool
688 if !server->has_key('traceLevel')
689 || server->type() != v:t_string
690 || (server.traceLevel != 'off' && server.traceLevel != 'debug'
691 && server.traceLevel != 'verbose')
692 server.traceLevel = 'off'
695 if !server->has_key('workspaceConfig')
696 || server.workspaceConfig->type() != v:t_dict
697 server.workspaceConfig = {}
700 if !server->has_key('rootSearch') || server.rootSearch->type() != v:t_list
701 server.rootSearch = []
704 if !server->has_key('runIfSearch') ||
705 server.runIfSearch->type() != v:t_list
706 server.runIfSearch = []
709 if !server->has_key('runUnlessSearch') ||
710 server.runUnlessSearch->type() != v:t_list
711 server.runUnlessSearch = []
714 var lspserver: dict<any> = lserver.NewLspServer(server)
716 var ftypes = server.filetype
717 if ftypes->type() == v:t_string
718 AddServerForFiltype(lspserver, ftypes, server.omnicompl)
719 elseif ftypes->type() == v:t_list
721 AddServerForFiltype(lspserver, ftype, server.omnicompl)
724 util.ErrMsg($'Unsupported file type information "{ftypes->string()}" in LSP server registration')
730 # The LSP server is considered ready when the server capabilities are
731 # received ("initialize" LSP reply message)
732 export def ServerReady(): bool
733 var fname: string = @%
738 var lspservers: list<dict<any>> = buf.CurbufGetServers()
739 if lspservers->empty()
743 for lspserver in lspservers
752 # set the LSP server trace level for the current buffer
753 # Params: SetTraceParams
754 def ServerTraceSet(traceVal: string)
755 if ['off', 'messages', 'verbose']->index(traceVal) == -1
756 util.ErrMsg($'Unsupported argument "{traceVal}"')
760 var lspservers: list<dict<any>> = buf.CurbufGetServers()
761 if lspservers->empty()
762 util.WarnMsg($'No Lsp servers found for "{@%}"')
766 for lspserver in lspservers
767 lspserver.setTrace(traceVal)
771 # Display the diagnostic messages from the LSP server for the current buffer
773 export def ShowDiagnostics(): void
777 # Show the diagnostic message for the current line
778 export def LspShowCurrentDiag(atPos: bool)
779 diag.ShowCurrentDiag(atPos)
782 # Display the diagnostics for the current line in the status line.
783 export def LspShowCurrentDiagInStatusLine()
784 var fname: string = @%
789 diag.ShowCurrentDiagInStatusLine()
792 # get the count of diagnostics in the current buffer
793 export def ErrorCount(): dict<number>
794 var res = {Error: 0, Warn: 0, Info: 0, Hint: 0}
795 var fname: string = @%
800 return diag.DiagsGetErrorCount()
803 # jump to the next/previous/first diagnostic message in the current buffer
804 export def JumpToDiag(which: string, count: number = 0): void
805 diag.LspDiagsJump(which, count)
808 # Display the hover message from the LSP server for the current cursor
810 export def Hover(cmdmods: string)
811 var lspserver: dict<any> = buf.CurbufGetServerChecked('hover')
812 if lspserver->empty()
816 lspserver.hover(cmdmods)
819 # Enable or disable inlay hints
820 export def InlayHints(ctl: string)
822 inlayhints.InlayHintsEnable()
823 elseif ctl == 'disable'
824 inlayhints.InlayHintsDisable()
826 util.ErrMsg($'LspInlayHints - Unsupported argument "{ctl}"')
830 # Command-line completion for the ":LspInlayHints" command
831 export def LspInlayHintsComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
832 var l = ['enable', 'disable']
833 return filter(l, (_, val) => val =~ $'^{arglead}')
836 # show symbol references
837 export def ShowReferences(peek: bool)
838 var lspserver: dict<any> = buf.CurbufGetServerChecked('references')
839 if lspserver->empty()
843 lspserver.showReferences(peek)
846 # highlight all the places where a symbol is referenced
847 def g:LspDocHighlight(cmdmods: string = '')
848 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentHighlight')
849 if lspserver->empty()
853 lspserver.docHighlight(cmdmods)
856 # clear the symbol reference highlight
857 def g:LspDocHighlightClear()
858 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentHighlight')
859 if lspserver->empty()
863 if has('patch-9.0.0233')
864 prop_remove({types: ['LspTextRef', 'LspReadRef', 'LspWriteRef'], all: true})
866 prop_remove({type: 'LspTextRef', all: true})
867 prop_remove({type: 'LspReadRef', all: true})
868 prop_remove({type: 'LspWriteRef', all: true})
872 def g:LspRequestDocSymbols()
873 if outline.SkipOutlineRefresh()
877 var fname: string = @%
882 var lspserver: dict<any> = buf.CurbufGetServer('documentSymbol')
883 if lspserver->empty() || !lspserver.running || !lspserver.ready
887 lspserver.getDocSymbols(fname, true)
890 # open a window and display all the symbols in a file (outline)
891 export def Outline(cmdmods: string, winsize: number)
892 var fname: string = @%
897 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentSymbol')
898 if lspserver->empty() || !lspserver.running || !lspserver.ready
902 outline.OpenOutlineWindow(cmdmods, winsize)
903 g:LspRequestDocSymbols()
906 # show all the symbols in a file in a popup menu
907 export def ShowDocSymbols()
908 var fname: string = @%
913 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentSymbol')
914 if lspserver->empty() || !lspserver.running || !lspserver.ready
918 lspserver.getDocSymbols(fname, false)
921 # Format the entire file
922 export def TextDocFormat(range_args: number, line1: number, line2: number)
924 util.ErrMsg('Current file is not a modifiable file')
928 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentFormatting')
929 if lspserver->empty()
933 var fname: string = @%
935 lspserver.textDocFormat(fname, true, line1, line2)
937 lspserver.textDocFormat(fname, false, 0, 0)
941 # TODO: Add support for textDocument.onTypeFormatting?
942 # Will this slow down Vim?
944 # Display all the locations where the current symbol is called from.
945 # Uses LSP "callHierarchy/incomingCalls" request
946 export def IncomingCalls()
947 var lspserver: dict<any> = buf.CurbufGetServerChecked('callHierarchy')
948 if lspserver->empty()
952 lspserver.incomingCalls(@%)
955 # Display all the symbols used by the current symbol.
956 # Uses LSP "callHierarchy/outgoingCalls" request
957 export def OutgoingCalls()
958 var lspserver: dict<any> = buf.CurbufGetServerChecked('callHierarchy')
959 if lspserver->empty()
963 lspserver.outgoingCalls(@%)
966 # Display the type hierarchy for the current symbol. Direction is 0 for
967 # sub types and 1 for super types.
968 export def TypeHierarchy(direction: number)
969 var lspserver: dict<any> = buf.CurbufGetServerChecked('typeHierarchy')
970 if lspserver->empty()
974 lspserver.typeHierarchy(direction)
978 # Uses LSP "textDocument/rename" request
979 export def Rename(a_newName: string)
980 var lspserver: dict<any> = buf.CurbufGetServerChecked('rename')
981 if lspserver->empty()
985 var newName: string = a_newName
987 var sym: string = expand('<cword>')
988 newName = input($"Rename symbol '{sym}' to: ", sym)
993 # clear the input prompt
997 lspserver.renameSymbol(newName)
1000 # Perform a code action
1001 # Uses LSP "textDocument/codeAction" request
1002 export def CodeAction(line1: number, line2: number, query: string)
1003 var lspserver: dict<any> = buf.CurbufGetServerChecked('codeAction')
1004 if lspserver->empty()
1008 var fname: string = @%
1009 lspserver.codeAction(fname, line1, line2, query)
1013 # Uses LSP "textDocument/codeLens" request
1014 export def CodeLens()
1015 var lspserver: dict<any> = buf.CurbufGetServerChecked('codeLens')
1016 if lspserver->empty()
1020 lspserver.codeLens(@%)
1023 # Perform a workspace wide symbol lookup
1024 # Uses LSP "workspace/symbol" request
1025 export def SymbolSearch(queryArg: string, cmdmods: string)
1026 var lspserver: dict<any> = buf.CurbufGetServerChecked('workspaceSymbol')
1027 if lspserver->empty()
1031 var query: string = queryArg
1033 query = input('Lookup symbol: ', expand('<cword>'))
1040 lspserver.workspaceQuery(query, true, cmdmods)
1043 # Display the list of workspace folders
1044 export def ListWorkspaceFolders()
1045 var lspservers: list<dict<any>> = buf.CurbufGetServers()
1046 for lspserver in lspservers
1047 util.InfoMsg($'Workspace Folders: "{lspserver.name}" {lspserver.workspaceFolders->string()}')
1051 # Add a workspace folder. Default is to use the current folder.
1052 export def AddWorkspaceFolder(dirArg: string)
1053 var dirName: string = dirArg
1055 dirName = input('Add Workspace Folder: ', getcwd(), 'dir')
1061 if !dirName->isdirectory()
1062 util.ErrMsg($'{dirName} is not a directory')
1066 var lspservers: list<dict<any>> = buf.CurbufGetServers()
1068 for lspserver in lspservers
1069 lspserver.addWorkspaceFolder(dirName)
1073 # Remove a workspace folder. Default is to use the current folder.
1074 export def RemoveWorkspaceFolder(dirArg: string)
1075 var dirName: string = dirArg
1077 dirName = input('Remove Workspace Folder: ', getcwd(), 'dir')
1083 if !dirName->isdirectory()
1084 util.ErrMsg($'{dirName} is not a directory')
1088 var lspservers: list<dict<any>> = buf.CurbufGetServers()
1089 for lspserver in lspservers
1090 lspserver.removeWorkspaceFolder(dirName)
1094 # expand the previous selection or start a new selection
1095 export def SelectionExpand()
1096 var lspserver: dict<any> = buf.CurbufGetServerChecked('selectionRange')
1097 if lspserver->empty()
1101 lspserver.selectionExpand()
1104 # shrink the previous selection or start a new selection
1105 export def SelectionShrink()
1106 var lspserver: dict<any> = buf.CurbufGetServerChecked('selectionRange')
1107 if lspserver->empty()
1111 lspserver.selectionShrink()
1114 # fold the entire document
1115 export def FoldDocument()
1116 var lspserver: dict<any> = buf.CurbufGetServerChecked('foldingRange')
1117 if lspserver->empty()
1121 if &foldmethod != 'manual'
1122 util.ErrMsg("Only works when 'foldmethod' is 'manual'")
1126 var fname: string = @%
1127 lspserver.foldRange(fname)
1130 # Enable diagnostic highlighting for all the buffers
1131 export def DiagHighlightEnable()
1132 diag.DiagsHighlightEnable()
1135 # Disable diagnostic highlighting for all the buffers
1136 export def DiagHighlightDisable()
1137 diag.DiagsHighlightDisable()
1140 # Function to use with the 'tagfunc' option.
1141 export def TagFunc(pat: string, flags: string, info: dict<any>): any
1142 var lspserver: dict<any> = buf.CurbufGetServerChecked('definition')
1143 if lspserver->empty()
1147 return lspserver.tagFunc(pat, flags, info)
1150 # Function to use with the 'formatexpr' option.
1151 export def FormatExpr(): number
1152 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentFormatting')
1153 if lspserver->empty()
1157 lspserver.textDocFormat(@%, true, v:lnum, v:lnum + v:count - 1)
1161 export def RegisterCmdHandler(cmd: string, Handler: func)
1162 codeaction.RegisterCmdHandler(cmd, Handler)
1165 # Command-line completion for the ":LspServer <cmd>" sub command
1166 def LspServerSubCmdComplete(cmds: list<string>, arglead: string, cmdline: string, cursorPos: number): list<string>
1167 var wordBegin = cmdline->match('\s\+\zs\S', cursorPos)
1172 # Make sure there are no additional sub-commands
1173 var wordEnd = cmdline->stridx(' ', wordBegin)
1175 return cmds->filter((_, val) => val =~ $'^{arglead}')
1181 # Command-line completion for the ":LspServer debug" command
1182 def LspServerDebugComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1183 return LspServerSubCmdComplete(['errors', 'messages', 'off', 'on'],
1184 arglead, cmdline, cursorPos)
1187 # Command-line completion for the ":LspServer show" command
1188 def LspServerShowComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1189 return LspServerSubCmdComplete(['capabilities', 'initializeRequest',
1190 'messages', 'status'], arglead, cmdline,
1194 # Command-line completion for the ":LspServer trace" command
1195 def LspServerTraceComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1196 return LspServerSubCmdComplete(['messages', 'off', 'verbose'],
1197 arglead, cmdline, cursorPos)
1200 # Command-line completion for the ":LspServer" command
1201 export def LspServerComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1204 var l = ['debug', 'restart', 'show', 'trace']
1206 # Skip the command name
1207 var i = cmdline->stridx(' ', 0)
1208 wordBegin = cmdline->match('\s\+\zs\S', i)
1213 wordEnd = cmdline->stridx(' ', wordBegin)
1215 return filter(l, (_, val) => val =~ $'^{arglead}')
1218 var cmd = cmdline->strpart(wordBegin, wordEnd - wordBegin)
1220 return LspServerDebugComplete(arglead, cmdline, wordEnd)
1221 elseif cmd == 'restart'
1222 elseif cmd == 'show'
1223 return LspServerShowComplete(arglead, cmdline, wordEnd)
1224 elseif cmd == 'trace'
1225 return LspServerTraceComplete(arglead, cmdline, wordEnd)
1231 # ":LspServer" command handler
1232 export def LspServerCmd(args: string)
1233 if args->stridx('debug') == 0
1235 var subcmd = args[6 : ]->trim()
1238 util.ErrMsg('Argument required')
1240 elseif args == 'restart'
1242 elseif args->stridx('show') == 0
1244 var subcmd = args[5 : ]->trim()
1247 util.ErrMsg('Argument required')
1249 elseif args->stridx('trace') == 0
1251 var subcmd = args[6 : ]->trim()
1252 ServerTraceSet(subcmd)
1254 util.ErrMsg('Argument required')
1257 util.ErrMsg($'LspServer - Unsupported argument "{args}"')
1261 # vim: tabstop=8 shiftwidth=2 softtabstop=2 noexpandtab