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
34 hlset([{name: 'LspTextRef', default: true, linksto: 'Search'}])
35 hlset([{name: 'LspReadRef', default: true, linksto: 'DiffChange'}])
36 hlset([{name: 'LspWriteRef', default: true, linksto: 'DiffDelete'}])
38 var override = &cursorline
39 && &cursorlineopt =~ '\<line\>\|\<screenline\>\|\<both\>'
41 prop_type_add('LspTextRef', {highlight: 'LspTextRef', override: override})
42 prop_type_add('LspReadRef', {highlight: 'LspReadRef', override: override})
43 prop_type_add('LspWriteRef', {highlight: 'LspWriteRef', override: override})
50 :set ballooneval balloonevalterm
51 lspInitializedOnce = true
54 # Returns the LSP servers for the a specific filetype. Based on how well there
55 # score, LSP servers with the same score are being returned.
56 # Returns an empty list if the servers is not found.
57 def LspGetServers(bnr: number, ftype: string): list<dict<any>>
58 if !ftypeServerMap->has_key(ftype)
62 var bufDir = bnr->bufname()->fnamemodify(':p:h')
64 return ftypeServerMap[ftype]->filter((key, lspserver) => {
65 # Don't run the server if no path is found
66 if !lspserver.runIfSearchFiles->empty()
67 var path = util.FindNearestRootDir(bufDir, lspserver.runIfSearchFiles)
74 # Don't run the server if a path is found
75 if !lspserver.runUnlessSearchFiles->empty()
76 var path = util.FindNearestRootDir(bufDir, lspserver.runUnlessSearchFiles)
88 # Add a LSP server for a filetype
89 def LspAddServer(ftype: string, lspsrv: dict<any>)
90 var lspsrvlst = ftypeServerMap->has_key(ftype) ? ftypeServerMap[ftype] : []
91 lspsrvlst->add(lspsrv)
92 ftypeServerMap[ftype] = lspsrvlst
95 # Enable/disable the logging of the language server protocol messages
96 def ServerDebug(arg: string)
97 if ['errors', 'messages', 'off', 'on']->index(arg) == -1
98 util.ErrMsg($'Unsupported argument "{arg}"')
102 var lspservers: list<dict<any>> = buf.CurbufGetServers()
103 if lspservers->empty()
104 util.WarnMsg($'No Lsp servers found for "{@%}"')
108 for lspserver in lspservers
110 util.ClearTraceLogs(lspserver.logfile)
111 util.ClearTraceLogs(lspserver.errfile)
112 lspserver.debug = true
114 lspserver.debug = false
115 elseif arg == 'messages'
116 util.ServerMessagesShow(lspserver.logfile)
118 util.ServerMessagesShow(lspserver.errfile)
123 # Show information about all the LSP servers
124 export def ShowAllServers()
126 # Add filetype to server mapping information
127 lines->add('Filetype Information')
128 lines->add('====================')
129 for [ftype, lspservers] in ftypeServerMap->items()
130 for lspserver in lspservers
131 lines->add($"Filetype: '{ftype}'")
132 lines->add($"Server Name: '{lspserver.name}'")
133 lines->add($"Server Path: '{lspserver.path}'")
134 lines->add($"Status: {lspserver.running ? 'Running' : 'Not running'}")
139 # Add buffer to server mapping information
140 lines->add('Buffer Information')
141 lines->add('==================')
142 for bnr in range(1, bufnr('$'))
143 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
144 if !lspservers->empty()
145 lines->add($"Buffer: '{bufname(bnr)}'")
146 for lspserver in lspservers
147 lines->add($"Server Path: '{lspserver.path}'")
148 lines->add($"Status: {lspserver.running ? 'Running' : 'Not running'}")
154 var wid = bufwinid('Language-Servers')
160 :new Language-Servers
161 :setlocal buftype=nofile
162 :setlocal bufhidden=wipe
164 :setlocal nonumber nornu
165 :setlocal fdc=0 signcolumn=no
169 :setlocal nomodifiable
172 # Create a new window containing the buffer "bname" or if the window is
173 # already present then jump to it.
174 def OpenScratchWindow(bname: string)
175 var wid = bufwinid(bname)
182 :setlocal buftype=nofile
183 :setlocal bufhidden=wipe
185 :setlocal nonumber nornu
186 :setlocal fdc=0 signcolumn=no
190 # Show the status of the LSP server for the current buffer
191 def ShowServer(arg: string)
192 if ['status', 'capabilities', 'initializeRequest', 'messages']->index(arg) == -1
193 util.ErrMsg($'Unsupported argument "{arg}"')
197 var lspservers: list<dict<any>> = buf.CurbufGetServers()
198 if lspservers->empty()
199 util.WarnMsg($'No Lsp servers found for "{@%}"')
203 var windowName: string = ''
204 var lines: list<string> = []
205 if arg->empty() || arg == 'status'
206 windowName = $'LangServer-Status'
207 for lspserver in lspservers
209 lines->extend(['', repeat('=', &columns), ''])
211 var msg = $"LSP server '{lspserver.name}' is "
215 msg ..= 'not running'
219 elseif arg == 'capabilities'
220 windowName = $'LangServer-Capabilities'
221 for lspserver in lspservers
223 lines->extend(['', repeat('=', &columns), ''])
225 lines->extend(lspserver.getCapabilities())
227 elseif arg == 'initializeRequest'
228 windowName = $'LangServer-InitializeRequest'
229 for lspserver in lspservers
231 lines->extend(['', repeat('=', &columns), ''])
233 lines->extend(lspserver.getInitializeRequest())
235 elseif arg == 'messages'
236 windowName = $'LangServer-Messages'
237 for lspserver in lspservers
239 lines->extend(['', repeat('=', &columns), ''])
241 lines->extend(lspserver.getMessages())
244 util.ErrMsg($'Unsupported argument "{arg}"')
249 OpenScratchWindow(windowName)
252 :setlocal nomodifiable
254 util.InfoMsg(lines[0])
258 # Get LSP server running status for filetype "ftype"
259 # Return true if running, or false if not found or not running
260 export def ServerRunning(ftype: string): bool
261 if ftypeServerMap->has_key(ftype)
262 var lspservers = ftypeServerMap[ftype]
263 for lspserver in lspservers
273 # Go to a definition using "textDocument/definition" LSP request
274 export def GotoDefinition(peek: bool, cmdmods: string, count: number)
275 var lspserver: dict<any> = buf.CurbufGetServerChecked('definition')
276 if lspserver->empty()
280 lspserver.gotoDefinition(peek, cmdmods, count)
283 # Go to a declaration using "textDocument/declaration" LSP request
284 export def GotoDeclaration(peek: bool, cmdmods: string, count: number)
285 var lspserver: dict<any> = buf.CurbufGetServerChecked('declaration')
286 if lspserver->empty()
290 lspserver.gotoDeclaration(peek, cmdmods, count)
293 # Go to a type definition using "textDocument/typeDefinition" LSP request
294 export def GotoTypedef(peek: bool, cmdmods: string, count: number)
295 var lspserver: dict<any> = buf.CurbufGetServerChecked('typeDefinition')
296 if lspserver->empty()
300 lspserver.gotoTypeDef(peek, cmdmods, count)
303 # Go to a implementation using "textDocument/implementation" LSP request
304 export def GotoImplementation(peek: bool, cmdmods: string, count: number)
305 var lspserver: dict<any> = buf.CurbufGetServerChecked('implementation')
306 if lspserver->empty()
310 lspserver.gotoImplementation(peek, cmdmods, count)
313 # Switch source header using "textDocument/switchSourceHeader" LSP request
314 # (Clangd specifc extension)
315 export def SwitchSourceHeader()
316 var lspserver: dict<any> = buf.CurbufGetServerChecked()
317 if lspserver->empty()
321 lspserver.switchSourceHeader()
324 # Show the signature using "textDocument/signatureHelp" LSP method
325 # Invoked from an insert-mode mapping, so return an empty string.
326 def g:LspShowSignature(): string
327 var lspserver: dict<any> = buf.CurbufGetServerChecked('signatureHelp')
328 if lspserver->empty()
332 # first send all the changes in the current buffer to the LSP server
334 lspserver.showSignature()
338 # A buffer is saved. Send the "textDocument/didSave" LSP notification
340 var bnr: number = expand('<abuf>')->str2nr()
341 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)->filter(
342 (key, lspsrv) => !lspsrv->empty() && lspsrv.running
345 if lspservers->empty()
349 for lspserver in lspservers
350 lspserver.didSaveFile(bnr)
354 # Return the diagnostic text from the LSP server for the current mouse line to
355 # display in a balloon
356 var lspDiagPopupID: number = 0
357 var lspDiagPopupInfo: dict<any> = {}
358 def g:LspDiagExpr(): any
359 # Display the diagnostic message only if the mouse is over the gutter for
361 if opt.lspOptions.noDiagHoverOnLine && v:beval_col >= 2
365 var diagsInfo: list<dict<any>> = diag.GetDiagsByLine(
369 if diagsInfo->empty()
370 # No diagnostic for the current cursor location
374 # Include all diagnostics from the current line in the message
375 var message: list<string> = []
376 for diag in diagsInfo
377 message->extend(diag.message->split("\n"))
383 # Called after leaving insert mode. Used to process diag messages (if any)
384 def LspLeftInsertMode()
385 if !exists('b:LspDiagsUpdatePending')
388 :unlet b:LspDiagsUpdatePending
390 var bnr: number = bufnr()
391 diag.ProcessNewDiags(bnr)
394 # Add buffer-local autocmds when attaching a LSP server to a buffer
395 def AddBufLocalAutocmds(lspserver: dict<any>, bnr: number): void
396 var acmds: list<dict<any>> = []
398 # file saved notification handler
399 acmds->add({bufnr: bnr,
400 event: 'BufWritePost',
401 group: 'LSPBufferAutocmds',
402 cmd: 'LspSavedFile()'})
404 # Update the diagnostics when insert mode is stopped
405 acmds->add({bufnr: bnr,
406 event: 'InsertLeave',
407 group: 'LSPBufferAutocmds',
408 cmd: 'LspLeftInsertMode()'})
410 # Auto highlight all the occurrences of the current keyword
411 if opt.lspOptions.autoHighlight &&
412 lspserver.isDocumentHighlightProvider
413 acmds->add({bufnr: bnr,
414 event: 'CursorMoved',
415 group: 'LSPBufferAutocmds',
416 cmd: 'call LspDocHighlightClear() | call LspDocHighlight("silent")'})
419 # Show diagnostics on the status line
420 if opt.lspOptions.showDiagOnStatusLine
421 acmds->add({bufnr: bnr,
422 event: 'CursorMoved',
423 group: 'LSPBufferAutocmds',
424 cmd: 'LspShowCurrentDiagInStatusLine()'})
430 def BufferInit(lspserverId: number, bnr: number): void
431 var lspserver = buf.BufLspServerGetById(bnr, lspserverId)
432 if lspserver->empty() || !lspserver.running
436 var ftype: string = bnr->getbufvar('&filetype')
437 lspserver.textdocDidOpen(bnr, ftype)
439 # add a listener to track changes to this buffer
440 listener_add((_bnr: number, start: number, end: number, added: number, changes: list<dict<number>>) => {
441 lspserver.textdocDidChange(bnr, start, end, added, changes)
444 AddBufLocalAutocmds(lspserver, bnr)
446 setbufvar(bnr, '&balloonexpr', 'g:LspDiagExpr()')
448 signature.BufferInit(lspserver)
449 inlayhints.BufferInit(lspserver, bnr)
451 var allServersReady = true
452 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
453 for lspsrv in lspservers
455 allServersReady = false
461 for lspsrv in lspservers
462 # It's only possible to initialize completion when all server capabilities
464 var completionServer = buf.BufLspServerGet(bnr, 'completion')
465 if !completionServer->empty() && lspsrv.id == completionServer.id
466 completion.BufferInit(lspsrv, bnr, ftype)
470 if exists('#User#LspAttached')
471 doautocmd <nomodeline> User LspAttached
476 # A new buffer is opened. If LSP is supported for this buffer, then add it
477 export def AddFile(bnr: number): void
478 if buf.BufHasLspServer(bnr)
479 # LSP server for this buffer is already initialized and running
484 if util.LspUriRemote(bnr->bufname()->fnamemodify(':p'))
488 var ftype: string = bnr->getbufvar('&filetype')
492 var lspservers: list<dict<any>> = LspGetServers(bnr, ftype)
493 if lspservers->empty()
496 for lspserver in lspservers
497 if !lspserver.running
498 if !lspInitializedOnce
501 lspserver.startServer(bnr)
503 buf.BufLspServerSet(bnr, lspserver)
506 BufferInit(lspserver.id, bnr)
508 augroup LSPBufferAutocmds
509 exe $'autocmd User LspServerReady_{lspserver.id} ++once BufferInit({lspserver.id}, {bnr})'
515 # Notify LSP server to remove a file
516 export def RemoveFile(bnr: number): void
517 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
518 for lspserver in lspservers->copy()
519 if lspserver->empty()
523 lspserver.textdocDidClose(bnr)
525 diag.DiagRemoveFile(bnr)
526 buf.BufLspServerRemove(bnr, lspserver)
530 # Stop all the LSP servers
531 export def StopAllServers()
532 for lspserver in LSPServers
534 lspserver.stopServer()
539 # Add all the buffers with 'filetype' set to "ftype" to the language server.
540 def AddBuffersToLsp(ftype: string)
541 # Add all the buffers with the same file type as the current buffer
542 for binfo in getbufinfo({bufloaded: 1})
543 if binfo.bufnr->getbufvar('&filetype') == ftype
549 # Restart the LSP server for the current buffer
551 var lspservers: list<dict<any>> = buf.CurbufGetServers()
552 if lspservers->empty()
553 util.WarnMsg($'No Lsp servers found for "{@%}"')
557 # Remove all the buffers with the same file type as the current buffer
558 var ftype: string = &filetype
559 for binfo in getbufinfo()
560 if binfo.bufnr->getbufvar('&filetype') == ftype
561 RemoveFile(binfo.bufnr)
565 for lspserver in lspservers
566 # Stop the server (if running)
568 lspserver.stopServer()
571 # Start the server again
572 lspserver.startServer(bufnr(''))
575 AddBuffersToLsp(ftype)
578 # Add the LSP server for files with 'filetype' as "ftype".
579 def AddServerForFiltype(lspserver: dict<any>, ftype: string, omnicompl: bool)
580 LspAddServer(ftype, lspserver)
581 completion.OmniComplSet(ftype, omnicompl)
583 # If a buffer of this file type is already present, then send it to the LSP
585 AddBuffersToLsp(ftype)
588 # Register a LSP server for one or more file types
589 export def AddServer(serverList: list<dict<any>>)
590 for server in serverList
591 if !server->has_key('filetype') || !server->has_key('path')
592 util.ErrMsg('LSP server information is missing filetype or path')
595 # Enable omni-completion by default
596 server.omnicompl = get(server, 'omnicompl', v:true)
598 if !server.path->executable()
599 if !opt.lspOptions.ignoreMissingServer
600 util.ErrMsg($'LSP server {server.path} is not found')
604 if server->has_key('args')
605 if server.args->type() != v:t_list
606 util.ErrMsg($'Arguments for LSP server {server.args} is not a List')
613 if !server->has_key('initializationOptions')
614 || server.initializationOptions->type() != v:t_dict
615 server.initializationOptions = {}
618 if !server->has_key('forceOffsetEncoding')
619 || server.forceOffsetEncoding->type() != v:t_string
620 || (server.forceOffsetEncoding != 'utf-8'
621 && server.forceOffsetEncoding != 'utf-16'
622 && server.forceOffsetEncoding != 'utf-32')
623 server.forceOffsetEncoding = ''
626 if !server->has_key('customNotificationHandlers')
627 || server.customNotificationHandlers->type() != v:t_dict
628 server.customNotificationHandlers = {}
631 if server->has_key('processDiagHandler')
632 if server.processDiagHandler->type() != v:t_func
633 util.ErrMsg($'Setting of processDiagHandler {server.processDiagHandler} is not a Funcref nor lambda')
637 server.processDiagHandler = null_function
640 if !server->has_key('customRequestHandlers')
641 || server.customRequestHandlers->type() != v:t_dict
642 server.customRequestHandlers = {}
645 if !server->has_key('features') || server.features->type() != v:t_dict
649 if server.omnicompl->type() != v:t_bool
650 util.ErrMsg($'Setting of omnicompl {server.omnicompl} is not a Boolean')
654 if !server->has_key('syncInit')
655 server.syncInit = v:false
658 if !server->has_key('name') || server.name->type() != v:t_string
659 || server.name->empty()
660 # Use the executable name (without the extension) as the language server
662 server.name = server.path->fnamemodify(':t:r')
665 if !server->has_key('debug') || server.debug->type() != v:t_bool
669 if !server->has_key('traceLevel')
670 || server->type() != v:t_string
671 || (server.traceLevel != 'off' && server.traceLevel != 'debug'
672 && server.traceLevel != 'verbose')
673 server.traceLevel = 'off'
676 if !server->has_key('workspaceConfig')
677 || server.workspaceConfig->type() != v:t_dict
678 server.workspaceConfig = {}
681 if !server->has_key('rootSearch') || server.rootSearch->type() != v:t_list
682 server.rootSearch = []
685 if !server->has_key('runIfSearch') ||
686 server.runIfSearch->type() != v:t_list
687 server.runIfSearch = []
690 if !server->has_key('runUnlessSearch') ||
691 server.runUnlessSearch->type() != v:t_list
692 server.runUnlessSearch = []
695 var lspserver: dict<any> = lserver.NewLspServer(server)
697 var ftypes = server.filetype
698 if ftypes->type() == v:t_string
699 AddServerForFiltype(lspserver, ftypes, server.omnicompl)
700 elseif ftypes->type() == v:t_list
702 AddServerForFiltype(lspserver, ftype, server.omnicompl)
705 util.ErrMsg($'Unsupported file type information "{ftypes->string()}" in LSP server registration')
711 # The LSP server is considered ready when the server capabilities are
712 # received ("initialize" LSP reply message)
713 export def ServerReady(): bool
714 var fname: string = @%
719 var lspservers: list<dict<any>> = buf.CurbufGetServers()
720 if lspservers->empty()
724 for lspserver in lspservers
733 # set the LSP server trace level for the current buffer
734 # Params: SetTraceParams
735 def ServerTraceSet(traceVal: string)
736 if ['off', 'messages', 'verbose']->index(traceVal) == -1
737 util.ErrMsg($'Unsupported argument "{traceVal}"')
741 var lspservers: list<dict<any>> = buf.CurbufGetServers()
742 if lspservers->empty()
743 util.WarnMsg($'No Lsp servers found for "{@%}"')
747 for lspserver in lspservers
748 lspserver.setTrace(traceVal)
752 # Display the diagnostic messages from the LSP server for the current buffer
754 export def ShowDiagnostics(): void
758 # Show the diagnostic message for the current line
759 export def LspShowCurrentDiag(atPos: bool)
760 diag.ShowCurrentDiag(atPos)
763 # Display the diagnostics for the current line in the status line.
764 export def LspShowCurrentDiagInStatusLine()
765 var fname: string = @%
770 diag.ShowCurrentDiagInStatusLine()
773 # get the count of diagnostics in the current buffer
774 export def ErrorCount(): dict<number>
775 var res = {Error: 0, Warn: 0, Info: 0, Hint: 0}
776 var fname: string = @%
781 return diag.DiagsGetErrorCount()
784 # jump to the next/previous/first diagnostic message in the current buffer
785 export def JumpToDiag(which: string, count: number = 0): void
786 diag.LspDiagsJump(which, count)
789 # Display the hover message from the LSP server for the current cursor
791 export def Hover(cmdmods: string)
792 var lspserver: dict<any> = buf.CurbufGetServerChecked('hover')
793 if lspserver->empty()
797 lspserver.hover(cmdmods)
800 # show symbol references
801 export def ShowReferences(peek: bool)
802 var lspserver: dict<any> = buf.CurbufGetServerChecked('references')
803 if lspserver->empty()
807 lspserver.showReferences(peek)
810 # highlight all the places where a symbol is referenced
811 def g:LspDocHighlight(cmdmods: string = '')
812 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentHighlight')
813 if lspserver->empty()
817 lspserver.docHighlight(cmdmods)
820 # clear the symbol reference highlight
821 def g:LspDocHighlightClear()
822 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentHighlight')
823 if lspserver->empty()
827 if has('patch-9.0.0233')
828 prop_remove({types: ['LspTextRef', 'LspReadRef', 'LspWriteRef'], all: true})
830 prop_remove({type: 'LspTextRef', all: true})
831 prop_remove({type: 'LspReadRef', all: true})
832 prop_remove({type: 'LspWriteRef', all: true})
836 def g:LspRequestDocSymbols()
837 if outline.SkipOutlineRefresh()
841 var fname: string = @%
846 var lspserver: dict<any> = buf.CurbufGetServer('documentSymbol')
847 if lspserver->empty() || !lspserver.running || !lspserver.ready
851 lspserver.getDocSymbols(fname, true)
854 # open a window and display all the symbols in a file (outline)
855 export def Outline(cmdmods: string, winsize: number)
856 outline.OpenOutlineWindow(cmdmods, winsize)
857 g:LspRequestDocSymbols()
860 # show all the symbols in a file in a popup menu
861 export def ShowDocSymbols()
862 var fname: string = @%
867 var lspserver: dict<any> = buf.CurbufGetServer('documentSymbol')
868 if lspserver->empty() || !lspserver.running || !lspserver.ready
872 lspserver.getDocSymbols(fname, false)
875 # Format the entire file
876 export def TextDocFormat(range_args: number, line1: number, line2: number)
878 util.ErrMsg('Current file is not a modifiable file')
882 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentFormatting')
883 if lspserver->empty()
887 var fname: string = @%
889 lspserver.textDocFormat(fname, true, line1, line2)
891 lspserver.textDocFormat(fname, false, 0, 0)
895 # TODO: Add support for textDocument.onTypeFormatting?
896 # Will this slow down Vim?
898 # Display all the locations where the current symbol is called from.
899 # Uses LSP "callHierarchy/incomingCalls" request
900 export def IncomingCalls()
901 var lspserver: dict<any> = buf.CurbufGetServerChecked('callHierarchy')
902 if lspserver->empty()
906 lspserver.incomingCalls(@%)
909 # Display all the symbols used by the current symbol.
910 # Uses LSP "callHierarchy/outgoingCalls" request
911 export def OutgoingCalls()
912 var lspserver: dict<any> = buf.CurbufGetServerChecked('callHierarchy')
913 if lspserver->empty()
917 lspserver.outgoingCalls(@%)
920 # Display the type hierarchy for the current symbol. Direction is 0 for
921 # sub types and 1 for super types.
922 export def TypeHierarchy(direction: number)
923 var lspserver: dict<any> = buf.CurbufGetServerChecked('typeHierarchy')
924 if lspserver->empty()
928 lspserver.typeHierarchy(direction)
932 # Uses LSP "textDocument/rename" request
933 export def Rename(a_newName: string)
934 var lspserver: dict<any> = buf.CurbufGetServerChecked('rename')
935 if lspserver->empty()
939 var newName: string = a_newName
941 var sym: string = expand('<cword>')
942 newName = input($"Rename symbol '{sym}' to: ", sym)
947 # clear the input prompt
951 lspserver.renameSymbol(newName)
954 # Perform a code action
955 # Uses LSP "textDocument/codeAction" request
956 export def CodeAction(line1: number, line2: number, query: string)
957 var lspserver: dict<any> = buf.CurbufGetServerChecked('codeAction')
958 if lspserver->empty()
962 var fname: string = @%
963 lspserver.codeAction(fname, line1, line2, query)
967 # Uses LSP "textDocument/codeLens" request
968 export def CodeLens()
969 var lspserver: dict<any> = buf.CurbufGetServerChecked('codeLens')
970 if lspserver->empty()
974 lspserver.codeLens(@%)
977 # Perform a workspace wide symbol lookup
978 # Uses LSP "workspace/symbol" request
979 export def SymbolSearch(queryArg: string, cmdmods: string)
980 var lspserver: dict<any> = buf.CurbufGetServerChecked('workspaceSymbol')
981 if lspserver->empty()
985 var query: string = queryArg
987 query = input('Lookup symbol: ', expand('<cword>'))
994 lspserver.workspaceQuery(query, true, cmdmods)
997 # Display the list of workspace folders
998 export def ListWorkspaceFolders()
999 var lspservers: list<dict<any>> = buf.CurbufGetServers()
1000 for lspserver in lspservers
1001 util.InfoMsg($'Workspace Folders: "{lspserver.name}" {lspserver.workspaceFolders->string()}')
1005 # Add a workspace folder. Default is to use the current folder.
1006 export def AddWorkspaceFolder(dirArg: string)
1007 var dirName: string = dirArg
1009 dirName = input('Add Workspace Folder: ', getcwd(), 'dir')
1015 if !dirName->isdirectory()
1016 util.ErrMsg($'{dirName} is not a directory')
1020 var lspservers: list<dict<any>> = buf.CurbufGetServers()
1022 for lspserver in lspservers
1023 lspserver.addWorkspaceFolder(dirName)
1027 # Remove a workspace folder. Default is to use the current folder.
1028 export def RemoveWorkspaceFolder(dirArg: string)
1029 var dirName: string = dirArg
1031 dirName = input('Remove Workspace Folder: ', getcwd(), 'dir')
1037 if !dirName->isdirectory()
1038 util.ErrMsg($'{dirName} is not a directory')
1042 var lspservers: list<dict<any>> = buf.CurbufGetServers()
1043 for lspserver in lspservers
1044 lspserver.removeWorkspaceFolder(dirName)
1048 # expand the previous selection or start a new selection
1049 export def SelectionExpand()
1050 var lspserver: dict<any> = buf.CurbufGetServerChecked('selectionRange')
1051 if lspserver->empty()
1055 lspserver.selectionExpand()
1058 # shrink the previous selection or start a new selection
1059 export def SelectionShrink()
1060 var lspserver: dict<any> = buf.CurbufGetServerChecked('selectionRange')
1061 if lspserver->empty()
1065 lspserver.selectionShrink()
1068 # fold the entire document
1069 export def FoldDocument()
1070 var lspserver: dict<any> = buf.CurbufGetServerChecked('foldingRange')
1071 if lspserver->empty()
1075 if &foldmethod != 'manual'
1076 util.ErrMsg("Only works when 'foldmethod' is 'manual'")
1080 var fname: string = @%
1081 lspserver.foldRange(fname)
1084 # Enable diagnostic highlighting for all the buffers
1085 export def DiagHighlightEnable()
1086 diag.DiagsHighlightEnable()
1089 # Disable diagnostic highlighting for all the buffers
1090 export def DiagHighlightDisable()
1091 diag.DiagsHighlightDisable()
1094 # Function to use with the 'tagfunc' option.
1095 export def TagFunc(pat: string, flags: string, info: dict<any>): any
1096 var lspserver: dict<any> = buf.CurbufGetServerChecked('definition')
1097 if lspserver->empty()
1101 return lspserver.tagFunc(pat, flags, info)
1104 # Function to use with the 'formatexpr' option.
1105 export def FormatExpr(): number
1106 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentFormatting')
1107 if lspserver->empty()
1111 lspserver.textDocFormat(@%, true, v:lnum, v:lnum + v:count - 1)
1115 export def RegisterCmdHandler(cmd: string, Handler: func)
1116 codeaction.RegisterCmdHandler(cmd, Handler)
1119 # Command-line completion for the ":LspServer <cmd>" sub command
1120 def LspServerSubCmdComplete(cmds: list<string>, arglead: string, cmdline: string, cursorPos: number): list<string>
1121 var wordBegin = cmdline->match('\s\+\zs\S', cursorPos)
1126 # Make sure there are no additional sub-commands
1127 var wordEnd = cmdline->stridx(' ', wordBegin)
1129 return cmds->filter((_, val) => val =~ $'^{arglead}')
1135 # Command-line completion for the ":LspServer debug" command
1136 def LspServerDebugComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1137 return LspServerSubCmdComplete(['errors', 'messages', 'off', 'on'],
1138 arglead, cmdline, cursorPos)
1141 # Command-line completion for the ":LspServer show" command
1142 def LspServerShowComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1143 return LspServerSubCmdComplete(['capabilities', 'initializeRequest',
1144 'messages', 'status'], arglead, cmdline,
1148 # Command-line completion for the ":LspServer trace" command
1149 def LspServerTraceComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1150 return LspServerSubCmdComplete(['messages', 'off', 'verbose'],
1151 arglead, cmdline, cursorPos)
1154 # Command-line completion for the ":LspServer" command
1155 export def LspServerComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1158 var l = ['debug', 'restart', 'show', 'trace']
1160 # Skip the command name
1161 var i = cmdline->stridx(' ', 0)
1162 wordBegin = cmdline->match('\s\+\zs\S', i)
1167 wordEnd = cmdline->stridx(' ', wordBegin)
1169 return filter(l, (_, val) => val =~ $'^{arglead}')
1172 var cmd = cmdline->strpart(wordBegin, wordEnd - wordBegin)
1174 return LspServerDebugComplete(arglead, cmdline, wordEnd)
1175 elseif cmd == 'restart'
1176 elseif cmd == 'show'
1177 return LspServerShowComplete(arglead, cmdline, wordEnd)
1178 elseif cmd == 'trace'
1179 return LspServerTraceComplete(arglead, cmdline, wordEnd)
1185 # ":LspServer" command handler
1186 export def LspServerCmd(args: string)
1187 if args->stridx('debug') == 0
1189 var subcmd = args[6 : ]->trim()
1192 util.ErrMsg('Argument required')
1194 elseif args == 'restart'
1196 elseif args->stridx('show') == 0
1198 var subcmd = args[5 : ]->trim()
1201 util.ErrMsg('Argument required')
1203 elseif args->stridx('trace') == 0
1205 var subcmd = args[6 : ]->trim()
1206 ServerTraceSet(subcmd)
1208 util.ErrMsg('Argument required')
1211 util.ErrMsg($'LspServer - Unsupported argument "{args}"')
1215 # vim: tabstop=8 shiftwidth=2 softtabstop=2 noexpandtab