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})
49 :set ballooneval balloonevalterm
50 lspInitializedOnce = true
53 # Returns the LSP servers for the a specific filetype. Based on how well there
54 # score, LSP servers with the same score are being returned.
55 # Returns an empty list if the servers is not found.
56 def LspGetServers(bnr: number, ftype: string): list<dict<any>>
57 if !ftypeServerMap->has_key(ftype)
61 var bufDir = bnr->bufname()->fnamemodify(':p:h')
63 return ftypeServerMap[ftype]->filter((key, lspserver) => {
64 # Don't run the server if no path is found
65 if !lspserver.runIfSearchFiles->empty()
66 var path = util.FindNearestRootDir(bufDir, lspserver.runIfSearchFiles)
73 # Don't run the server if a path is found
74 if !lspserver.runUnlessSearchFiles->empty()
75 var path = util.FindNearestRootDir(bufDir, lspserver.runUnlessSearchFiles)
87 # Add a LSP server for a filetype
88 def LspAddServer(ftype: string, lspsrv: dict<any>)
89 var lspsrvlst = ftypeServerMap->has_key(ftype) ? ftypeServerMap[ftype] : []
90 lspsrvlst->add(lspsrv)
91 ftypeServerMap[ftype] = lspsrvlst
94 # Enable/disable the logging of the language server protocol messages
95 def ServerDebug(arg: string)
96 if ['errors', 'messages', 'off', 'on']->index(arg) == -1
97 util.ErrMsg($'Unsupported argument "{arg}"')
101 var lspservers: list<dict<any>> = buf.CurbufGetServers()
102 if lspservers->empty()
103 util.WarnMsg($'No Lsp servers found for "{@%}"')
107 for lspserver in lspservers
109 util.ClearTraceLogs(lspserver.logfile)
110 util.ClearTraceLogs(lspserver.errfile)
111 lspserver.debug = true
113 lspserver.debug = false
114 elseif arg == 'messages'
115 util.ServerMessagesShow(lspserver.logfile)
117 util.ServerMessagesShow(lspserver.errfile)
122 # Show information about all the LSP servers
123 export def ShowAllServers()
125 # Add filetype to server mapping information
126 lines->add('Filetype Information')
127 lines->add('====================')
128 for [ftype, lspservers] in ftypeServerMap->items()
129 for lspserver in lspservers
130 lines->add($"Filetype: '{ftype}'")
131 lines->add($"Server Name: '{lspserver.name}'")
132 lines->add($"Server Path: '{lspserver.path}'")
133 lines->add($"Status: {lspserver.running ? 'Running' : 'Not running'}")
138 # Add buffer to server mapping information
139 lines->add('Buffer Information')
140 lines->add('==================')
141 for bnr in range(1, bufnr('$'))
142 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
143 if !lspservers->empty()
144 lines->add($"Buffer: '{bufname(bnr)}'")
145 for lspserver in lspservers
146 lines->add($"Server Path: '{lspserver.path}'")
147 lines->add($"Status: {lspserver.running ? 'Running' : 'Not running'}")
153 var wid = bufwinid('Language-Servers')
159 :new Language-Servers
160 :setlocal buftype=nofile
161 :setlocal bufhidden=wipe
163 :setlocal nonumber nornu
164 :setlocal fdc=0 signcolumn=no
168 :setlocal nomodifiable
171 # Create a new window containing the buffer "bname" or if the window is
172 # already present then jump to it.
173 def OpenScratchWindow(bname: string)
174 var wid = bufwinid(bname)
181 :setlocal buftype=nofile
182 :setlocal bufhidden=wipe
184 :setlocal nonumber nornu
185 :setlocal fdc=0 signcolumn=no
189 # Show the status of the LSP server for the current buffer
190 def ShowServer(arg: string)
191 if ['status', 'capabilities', 'initializeRequest', 'messages']->index(arg) == -1
192 util.ErrMsg($'Unsupported argument "{arg}"')
196 var lspservers: list<dict<any>> = buf.CurbufGetServers()
197 if lspservers->empty()
198 util.WarnMsg($'No Lsp servers found for "{@%}"')
202 var windowName: string = ''
203 var lines: list<string> = []
204 if arg->empty() || arg == 'status'
205 windowName = $'LangServer-Status'
206 for lspserver in lspservers
208 lines->extend(['', repeat('=', &columns), ''])
210 var msg = $"LSP server '{lspserver.name}' is "
214 msg ..= 'not running'
218 elseif arg == 'capabilities'
219 windowName = $'LangServer-Capabilities'
220 for lspserver in lspservers
222 lines->extend(['', repeat('=', &columns), ''])
224 lines->extend(lspserver.getCapabilities())
226 elseif arg == 'initializeRequest'
227 windowName = $'LangServer-InitializeRequest'
228 for lspserver in lspservers
230 lines->extend(['', repeat('=', &columns), ''])
232 lines->extend(lspserver.getInitializeRequest())
234 elseif arg == 'messages'
235 windowName = $'LangServer-Messages'
236 for lspserver in lspservers
238 lines->extend(['', repeat('=', &columns), ''])
240 lines->extend(lspserver.getMessages())
243 util.ErrMsg($'Unsupported argument "{arg}"')
248 OpenScratchWindow(windowName)
251 :setlocal nomodifiable
253 util.InfoMsg(lines[0])
257 # Get LSP server running status for filetype "ftype"
258 # Return true if running, or false if not found or not running
259 export def ServerRunning(ftype: string): bool
260 if ftypeServerMap->has_key(ftype)
261 var lspservers = ftypeServerMap[ftype]
262 for lspserver in lspservers
272 # Go to a definition using "textDocument/definition" LSP request
273 export def GotoDefinition(peek: bool, cmdmods: string, count: number)
274 var lspserver: dict<any> = buf.CurbufGetServerChecked('definition')
275 if lspserver->empty()
279 lspserver.gotoDefinition(peek, cmdmods, count)
282 # Go to a declaration using "textDocument/declaration" LSP request
283 export def GotoDeclaration(peek: bool, cmdmods: string, count: number)
284 var lspserver: dict<any> = buf.CurbufGetServerChecked('declaration')
285 if lspserver->empty()
289 lspserver.gotoDeclaration(peek, cmdmods, count)
292 # Go to a type definition using "textDocument/typeDefinition" LSP request
293 export def GotoTypedef(peek: bool, cmdmods: string, count: number)
294 var lspserver: dict<any> = buf.CurbufGetServerChecked('typeDefinition')
295 if lspserver->empty()
299 lspserver.gotoTypeDef(peek, cmdmods, count)
302 # Go to a implementation using "textDocument/implementation" LSP request
303 export def GotoImplementation(peek: bool, cmdmods: string, count: number)
304 var lspserver: dict<any> = buf.CurbufGetServerChecked('implementation')
305 if lspserver->empty()
309 lspserver.gotoImplementation(peek, cmdmods, count)
312 # Switch source header using "textDocument/switchSourceHeader" LSP request
313 # (Clangd specifc extension)
314 export def SwitchSourceHeader()
315 var lspserver: dict<any> = buf.CurbufGetServerChecked()
316 if lspserver->empty()
320 lspserver.switchSourceHeader()
323 # Show the signature using "textDocument/signatureHelp" LSP method
324 # Invoked from an insert-mode mapping, so return an empty string.
325 def g:LspShowSignature(): string
326 var lspserver: dict<any> = buf.CurbufGetServerChecked()
327 if lspserver->empty()
331 # first send all the changes in the current buffer to the LSP server
333 lspserver.showSignature()
337 # A buffer is saved. Send the "textDocument/didSave" LSP notification
339 var bnr: number = expand('<abuf>')->str2nr()
340 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)->filter(
341 (key, lspsrv) => !lspsrv->empty() && lspsrv.running
344 if lspservers->empty()
348 for lspserver in lspservers
349 lspserver.didSaveFile(bnr)
353 # Return the diagnostic text from the LSP server for the current mouse line to
354 # display in a balloon
355 var lspDiagPopupID: number = 0
356 var lspDiagPopupInfo: dict<any> = {}
357 def g:LspDiagExpr(): any
358 # Display the diagnostic message only if the mouse is over the gutter for
360 if opt.lspOptions.noDiagHoverOnLine && v:beval_col >= 2
364 var diagsInfo: list<dict<any>> = diag.GetDiagsByLine(
368 if diagsInfo->empty()
369 # No diagnostic for the current cursor location
373 # Include all diagnostics from the current line in the message
374 var message: list<string> = []
375 for diag in diagsInfo
376 message->extend(diag.message->split("\n"))
382 # Called after leaving insert mode. Used to process diag messages (if any)
383 def LspLeftInsertMode()
384 if !exists('b:LspDiagsUpdatePending')
387 :unlet b:LspDiagsUpdatePending
389 var bnr: number = bufnr()
390 diag.ProcessNewDiags(bnr)
393 # Add buffer-local autocmds when attaching a LSP server to a buffer
394 def AddBufLocalAutocmds(lspserver: dict<any>, bnr: number): void
395 var acmds: list<dict<any>> = []
397 # file saved notification handler
398 acmds->add({bufnr: bnr,
399 event: 'BufWritePost',
400 group: 'LSPBufferAutocmds',
401 cmd: 'LspSavedFile()'})
403 # Update the diagnostics when insert mode is stopped
404 acmds->add({bufnr: bnr,
405 event: 'InsertLeave',
406 group: 'LSPBufferAutocmds',
407 cmd: 'LspLeftInsertMode()'})
409 # Auto highlight all the occurrences of the current keyword
410 if opt.lspOptions.autoHighlight &&
411 lspserver.isDocumentHighlightProvider
412 acmds->add({bufnr: bnr,
413 event: 'CursorMoved',
414 group: 'LSPBufferAutocmds',
415 cmd: 'call LspDocHighlightClear() | call LspDocHighlight("silent")'})
418 # Show diagnostics on the status line
419 if opt.lspOptions.showDiagOnStatusLine
420 acmds->add({bufnr: bnr,
421 event: 'CursorMoved',
422 group: 'LSPBufferAutocmds',
423 cmd: 'LspShowCurrentDiagInStatusLine()'})
429 def BufferInit(lspserverId: number, bnr: number): void
430 var lspserver = buf.BufLspServerGetById(bnr, lspserverId)
431 if lspserver->empty() || !lspserver.running
435 var ftype: string = bnr->getbufvar('&filetype')
436 lspserver.textdocDidOpen(bnr, ftype)
438 # add a listener to track changes to this buffer
439 listener_add((_bnr: number, start: number, end: number, added: number, changes: list<dict<number>>) => {
440 lspserver.textdocDidChange(bnr, start, end, added, changes)
443 AddBufLocalAutocmds(lspserver, bnr)
445 setbufvar(bnr, '&balloonexpr', 'g:LspDiagExpr()')
447 signature.BufferInit(lspserver)
448 inlayhints.BufferInit(lspserver, bnr)
450 var allServersReady = true
451 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
452 for lspsrv in lspservers
454 allServersReady = false
460 for lspsrv in lspservers
461 # It's only possible to initialize completion when all server capabilities
463 var completionServer = buf.BufLspServerGet(bnr, 'completion')
464 if !completionServer->empty() && lspsrv.id == completionServer.id
465 completion.BufferInit(lspsrv, bnr, ftype)
469 if exists('#User#LspAttached')
470 doautocmd <nomodeline> User LspAttached
475 # A new buffer is opened. If LSP is supported for this buffer, then add it
476 export def AddFile(bnr: number): void
477 if buf.BufHasLspServer(bnr)
478 # LSP server for this buffer is already initialized and running
483 if util.LspUriRemote(bnr->bufname()->fnamemodify(':p'))
487 var ftype: string = bnr->getbufvar('&filetype')
491 var lspservers: list<dict<any>> = LspGetServers(bnr, ftype)
492 if lspservers->empty()
495 for lspserver in lspservers
496 if !lspserver.running
497 if !lspInitializedOnce
500 lspserver.startServer(bnr)
502 buf.BufLspServerSet(bnr, lspserver)
505 BufferInit(lspserver.id, bnr)
507 augroup LSPBufferAutocmds
508 exe $'autocmd User LspServerReady_{lspserver.id} ++once BufferInit({lspserver.id}, {bnr})'
514 # Notify LSP server to remove a file
515 export def RemoveFile(bnr: number): void
516 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
517 for lspserver in lspservers->copy()
518 if lspserver->empty()
522 lspserver.textdocDidClose(bnr)
524 diag.DiagRemoveFile(bnr)
525 buf.BufLspServerRemove(bnr, lspserver)
529 # Stop all the LSP servers
530 export def StopAllServers()
531 for lspserver in LSPServers
533 lspserver.stopServer()
538 # Add all the buffers with 'filetype' set to "ftype" to the language server.
539 def AddBuffersToLsp(ftype: string)
540 # Add all the buffers with the same file type as the current buffer
541 for binfo in getbufinfo({bufloaded: 1})
542 if binfo.bufnr->getbufvar('&filetype') == ftype
548 # Restart the LSP server for the current buffer
550 var lspservers: list<dict<any>> = buf.CurbufGetServers()
551 if lspservers->empty()
552 util.WarnMsg($'No Lsp servers found for "{@%}"')
556 # Remove all the buffers with the same file type as the current buffer
557 var ftype: string = &filetype
558 for binfo in getbufinfo()
559 if binfo.bufnr->getbufvar('&filetype') == ftype
560 RemoveFile(binfo.bufnr)
564 for lspserver in lspservers
565 # Stop the server (if running)
567 lspserver.stopServer()
570 # Start the server again
571 lspserver.startServer(bufnr(''))
574 AddBuffersToLsp(ftype)
577 # Add the LSP server for files with 'filetype' as "ftype".
578 def AddServerForFiltype(lspserver: dict<any>, ftype: string, omnicompl: bool)
579 LspAddServer(ftype, lspserver)
580 completion.OmniComplSet(ftype, omnicompl)
582 # If a buffer of this file type is already present, then send it to the LSP
584 AddBuffersToLsp(ftype)
587 # Register a LSP server for one or more file types
588 export def AddServer(serverList: list<dict<any>>)
589 for server in serverList
590 if !server->has_key('filetype') || !server->has_key('path')
591 util.ErrMsg('LSP server information is missing filetype or path')
594 # Enable omni-completion by default
595 server.omnicompl = get(server, 'omnicompl', v:true)
597 if !server.path->executable()
598 if !opt.lspOptions.ignoreMissingServer
599 util.ErrMsg($'LSP server {server.path} is not found')
603 if server->has_key('args')
604 if server.args->type() != v:t_list
605 util.ErrMsg($'Arguments for LSP server {server.args} is not a List')
612 if !server->has_key('initializationOptions')
613 || server.initializationOptions->type() != v:t_dict
614 server.initializationOptions = {}
617 if !server->has_key('customNotificationHandlers')
618 || server.customNotificationHandlers->type() != v:t_dict
619 server.customNotificationHandlers = {}
622 if server->has_key('processDiagHandler')
623 if server.processDiagHandler->type() != v:t_func
624 util.ErrMsg($'Setting of processDiagHandler {server.processDiagHandler} is not a Funcref nor lambda')
628 server.processDiagHandler = null_function
631 if !server->has_key('customRequestHandlers')
632 || server.customRequestHandlers->type() != v:t_dict
633 server.customRequestHandlers = {}
636 if !server->has_key('features') || server.features->type() != v:t_dict
640 if server.omnicompl->type() != v:t_bool
641 util.ErrMsg($'Setting of omnicompl {server.omnicompl} is not a Boolean')
645 if !server->has_key('syncInit')
646 server.syncInit = v:false
649 if !server->has_key('name') || server.name->type() != v:t_string
650 || server.name->empty()
651 # Use the executable name (without the extension) as the language server
653 server.name = server.path->fnamemodify(':t:r')
656 if !server->has_key('debug') || server.debug->type() != v:t_bool
660 if !server->has_key('traceLevel')
661 || server->type() != v:t_string
662 || (server.traceLevel != 'off' && server.traceLevel != 'debug'
663 && server.traceLevel != 'verbose')
664 server.traceLevel = 'off'
667 if !server->has_key('workspaceConfig')
668 || server.workspaceConfig->type() != v:t_dict
669 server.workspaceConfig = {}
672 if !server->has_key('rootSearch') || server.rootSearch->type() != v:t_list
673 server.rootSearch = []
676 if !server->has_key('runIfSearch') ||
677 server.runIfSearch->type() != v:t_list
678 server.runIfSearch = []
681 if !server->has_key('runUnlessSearch') ||
682 server.runUnlessSearch->type() != v:t_list
683 server.runUnlessSearch = []
686 var lspserver: dict<any> = lserver.NewLspServer(server)
688 var ftypes = server.filetype
689 if ftypes->type() == v:t_string
690 AddServerForFiltype(lspserver, ftypes, server.omnicompl)
691 elseif ftypes->type() == v:t_list
693 AddServerForFiltype(lspserver, ftype, server.omnicompl)
696 util.ErrMsg($'Unsupported file type information "{ftypes->string()}" in LSP server registration')
702 # The LSP server is considered ready when the server capabilities are
703 # received ("initialize" LSP reply message)
704 export def ServerReady(): bool
705 var fname: string = @%
710 var lspservers: list<dict<any>> = buf.CurbufGetServers()
711 if lspservers->empty()
715 for lspserver in lspservers
724 # set the LSP server trace level for the current buffer
725 # Params: SetTraceParams
726 def ServerTraceSet(traceVal: string)
727 if ['off', 'messages', 'verbose']->index(traceVal) == -1
728 util.ErrMsg($'Unsupported argument "{traceVal}"')
732 var lspservers: list<dict<any>> = buf.CurbufGetServers()
733 if lspservers->empty()
734 util.WarnMsg($'No Lsp servers found for "{@%}"')
738 for lspserver in lspservers
739 lspserver.setTrace(traceVal)
743 # Display the diagnostic messages from the LSP server for the current buffer
745 export def ShowDiagnostics(): void
749 # Show the diagnostic message for the current line
750 export def LspShowCurrentDiag(atPos: bool)
751 diag.ShowCurrentDiag(atPos)
754 # Display the diagnostics for the current line in the status line.
755 export def LspShowCurrentDiagInStatusLine()
756 var fname: string = @%
761 diag.ShowCurrentDiagInStatusLine()
764 # get the count of diagnostics in the current buffer
765 export def ErrorCount(): dict<number>
766 var res = {Error: 0, Warn: 0, Info: 0, Hint: 0}
767 var fname: string = @%
772 return diag.DiagsGetErrorCount()
775 # jump to the next/previous/first diagnostic message in the current buffer
776 export def JumpToDiag(which: string, count: number = 0): void
777 diag.LspDiagsJump(which, count)
780 # Display the hover message from the LSP server for the current cursor
782 export def Hover(cmdmods: string)
783 var lspserver: dict<any> = buf.CurbufGetServerChecked('hover')
784 if lspserver->empty()
788 lspserver.hover(cmdmods)
791 # show symbol references
792 export def ShowReferences(peek: bool)
793 var lspserver: dict<any> = buf.CurbufGetServerChecked('references')
794 if lspserver->empty()
798 lspserver.showReferences(peek)
801 # highlight all the places where a symbol is referenced
802 def g:LspDocHighlight(cmdmods: string = '')
803 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentHighlight')
804 if lspserver->empty()
808 lspserver.docHighlight(cmdmods)
811 # clear the symbol reference highlight
812 def g:LspDocHighlightClear()
813 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentHighlight')
814 if lspserver->empty()
818 if has('patch-9.0.0233')
819 prop_remove({types: ['LspTextRef', 'LspReadRef', 'LspWriteRef'], all: true})
821 prop_remove({type: 'LspTextRef', all: true})
822 prop_remove({type: 'LspReadRef', all: true})
823 prop_remove({type: 'LspWriteRef', all: true})
827 def g:LspRequestDocSymbols()
828 if outline.SkipOutlineRefresh()
832 var fname: string = @%
837 var lspserver: dict<any> = buf.CurbufGetServer()
838 if lspserver->empty() || !lspserver.running || !lspserver.ready
842 lspserver.getDocSymbols(fname)
845 # open a window and display all the symbols in a file (outline)
846 export def Outline(cmdmods: string, winsize: number)
847 outline.OpenOutlineWindow(cmdmods, winsize)
848 g:LspRequestDocSymbols()
851 # Format the entire file
852 export def TextDocFormat(range_args: number, line1: number, line2: number)
854 util.ErrMsg('Current file is not a modifiable file')
858 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentFormatting')
859 if lspserver->empty()
863 var fname: string = @%
865 lspserver.textDocFormat(fname, true, line1, line2)
867 lspserver.textDocFormat(fname, false, 0, 0)
871 # TODO: Add support for textDocument.onTypeFormatting?
872 # Will this slow down Vim?
874 # Display all the locations where the current symbol is called from.
875 # Uses LSP "callHierarchy/incomingCalls" request
876 export def IncomingCalls()
877 var lspserver: dict<any> = buf.CurbufGetServerChecked()
878 if lspserver->empty()
882 lspserver.incomingCalls(@%)
885 # Display all the symbols used by the current symbol.
886 # Uses LSP "callHierarchy/outgoingCalls" request
887 export def OutgoingCalls()
888 var lspserver: dict<any> = buf.CurbufGetServerChecked()
889 if lspserver->empty()
893 lspserver.outgoingCalls(@%)
896 # Display the type hierarchy for the current symbol. Direction is 0 for
897 # sub types and 1 for super types.
898 export def TypeHierarchy(direction: number)
899 var lspserver: dict<any> = buf.CurbufGetServerChecked()
900 if lspserver->empty()
904 lspserver.typeHierarchy(direction)
908 # Uses LSP "textDocument/rename" request
909 export def Rename(a_newName: string)
910 var lspserver: dict<any> = buf.CurbufGetServerChecked('rename')
911 if lspserver->empty()
915 var newName: string = a_newName
917 var sym: string = expand('<cword>')
918 newName = input($"Rename symbol '{sym}' to: ", sym)
923 # clear the input prompt
927 lspserver.renameSymbol(newName)
930 # Perform a code action
931 # Uses LSP "textDocument/codeAction" request
932 export def CodeAction(line1: number, line2: number, query: string)
933 var lspserver: dict<any> = buf.CurbufGetServerChecked('codeAction')
934 if lspserver->empty()
938 var fname: string = @%
939 lspserver.codeAction(fname, line1, line2, query)
943 # Uses LSP "textDocument/codeLens" request
944 export def CodeLens()
945 var lspserver: dict<any> = buf.CurbufGetServerChecked('codeLens')
946 if lspserver->empty()
950 lspserver.codeLens(@%)
953 # Perform a workspace wide symbol lookup
954 # Uses LSP "workspace/symbol" request
955 export def SymbolSearch(queryArg: string, cmdmods: string)
956 var lspserver: dict<any> = buf.CurbufGetServerChecked()
957 if lspserver->empty()
961 var query: string = queryArg
963 query = input('Lookup symbol: ', expand('<cword>'))
970 lspserver.workspaceQuery(query, true, cmdmods)
973 # Display the list of workspace folders
974 export def ListWorkspaceFolders()
975 var lspservers: list<dict<any>> = buf.CurbufGetServers()
976 for lspserver in lspservers
977 util.InfoMsg($'Workspace Folders: "{lspserver.name}" {lspserver.workspaceFolders->string()}')
981 # Add a workspace folder. Default is to use the current folder.
982 export def AddWorkspaceFolder(dirArg: string)
983 var dirName: string = dirArg
985 dirName = input('Add Workspace Folder: ', getcwd(), 'dir')
991 if !dirName->isdirectory()
992 util.ErrMsg($'{dirName} is not a directory')
996 var lspservers: list<dict<any>> = buf.CurbufGetServers()
998 for lspserver in lspservers
999 lspserver.addWorkspaceFolder(dirName)
1003 # Remove a workspace folder. Default is to use the current folder.
1004 export def RemoveWorkspaceFolder(dirArg: string)
1005 var dirName: string = dirArg
1007 dirName = input('Remove Workspace Folder: ', getcwd(), 'dir')
1013 if !dirName->isdirectory()
1014 util.ErrMsg($'{dirName} is not a directory')
1018 var lspservers: list<dict<any>> = buf.CurbufGetServers()
1019 for lspserver in lspservers
1020 lspserver.removeWorkspaceFolder(dirName)
1024 # expand the previous selection or start a new selection
1025 export def SelectionExpand()
1026 var lspserver: dict<any> = buf.CurbufGetServerChecked('selectionRange')
1027 if lspserver->empty()
1031 lspserver.selectionExpand()
1034 # shrink the previous selection or start a new selection
1035 export def SelectionShrink()
1036 var lspserver: dict<any> = buf.CurbufGetServerChecked('selectionRange')
1037 if lspserver->empty()
1041 lspserver.selectionShrink()
1044 # fold the entire document
1045 export def FoldDocument()
1046 var lspserver: dict<any> = buf.CurbufGetServerChecked('foldingRange')
1047 if lspserver->empty()
1051 if &foldmethod != 'manual'
1052 util.ErrMsg("Only works when 'foldmethod' is 'manual'")
1056 var fname: string = @%
1057 lspserver.foldRange(fname)
1060 # Enable diagnostic highlighting for all the buffers
1061 export def DiagHighlightEnable()
1062 diag.DiagsHighlightEnable()
1065 # Disable diagnostic highlighting for all the buffers
1066 export def DiagHighlightDisable()
1067 diag.DiagsHighlightDisable()
1070 # Function to use with the 'tagfunc' option.
1071 export def TagFunc(pat: string, flags: string, info: dict<any>): any
1072 var lspserver: dict<any> = buf.CurbufGetServerChecked('definition')
1073 if lspserver->empty()
1077 return lspserver.tagFunc(pat, flags, info)
1080 # Function to use with the 'formatexpr' option.
1081 export def FormatExpr(): number
1082 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentFormatting')
1083 if lspserver->empty()
1087 lspserver.textDocFormat(@%, true, v:lnum, v:lnum + v:count - 1)
1091 export def RegisterCmdHandler(cmd: string, Handler: func)
1092 codeaction.RegisterCmdHandler(cmd, Handler)
1095 # Command-line completion for the ":LspServer <cmd>" sub command
1096 def LspServerSubCmdComplete(cmds: list<string>, arglead: string, cmdline: string, cursorPos: number): list<string>
1097 var wordBegin = cmdline->match('\s\+\zs\S', cursorPos)
1102 # Make sure there are no additional sub-commands
1103 var wordEnd = cmdline->stridx(' ', wordBegin)
1105 return cmds->filter((_, val) => val =~ $'^{arglead}')
1111 # Command-line completion for the ":LspServer debug" command
1112 def LspServerDebugComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1113 return LspServerSubCmdComplete(['errors', 'messages', 'off', 'on'],
1114 arglead, cmdline, cursorPos)
1117 # Command-line completion for the ":LspServer show" command
1118 def LspServerShowComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1119 return LspServerSubCmdComplete(['capabilities', 'initializeRequest',
1120 'messages', 'status'], arglead, cmdline,
1124 # Command-line completion for the ":LspServer trace" command
1125 def LspServerTraceComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1126 return LspServerSubCmdComplete(['messages', 'off', 'verbose'],
1127 arglead, cmdline, cursorPos)
1130 # Command-line completion for the ":LspServer" command
1131 export def LspServerComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1134 var l = ['debug', 'restart', 'show', 'trace']
1136 # Skip the command name
1137 var i = cmdline->stridx(' ', 0)
1138 wordBegin = cmdline->match('\s\+\zs\S', i)
1143 wordEnd = cmdline->stridx(' ', wordBegin)
1145 return filter(l, (_, val) => val =~ $'^{arglead}')
1148 var cmd = cmdline->strpart(wordBegin, wordEnd - wordBegin)
1150 return LspServerDebugComplete(arglead, cmdline, wordEnd)
1151 elseif cmd == 'restart'
1152 elseif cmd == 'show'
1153 return LspServerShowComplete(arglead, cmdline, wordEnd)
1154 elseif cmd == 'trace'
1155 return LspServerTraceComplete(arglead, cmdline, wordEnd)
1161 # ":LspServer" command handler
1162 export def LspServerCmd(args: string)
1163 if args->stridx('debug') == 0
1165 var subcmd = args[6 : ]->trim()
1168 util.ErrMsg('Argument required')
1170 elseif args == 'restart'
1172 elseif args->stridx('show') == 0
1174 var subcmd = args[5 : ]->trim()
1177 util.ErrMsg('Argument required')
1179 elseif args->stridx('trace') == 0
1181 var subcmd = args[6 : ]->trim()
1182 ServerTraceSet(subcmd)
1184 util.ErrMsg('Argument required')
1187 util.ErrMsg($'LspServer - Unsupported argument "{args}"')
1191 # vim: tabstop=8 shiftwidth=2 softtabstop=2 noexpandtab