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 lspInitializedOnce = true
55 # Returns the LSP servers for the a specific filetype. Based on how well there
56 # score, LSP servers with the same score are being returned.
57 # Returns an empty list if the servers is not found.
58 def LspGetServers(bnr: number, ftype: string): list<dict<any>>
59 if !ftypeServerMap->has_key(ftype)
63 var bufDir = bnr->bufname()->fnamemodify(':p:h')
65 return ftypeServerMap[ftype]->filter((key, lspserver) => {
66 # Don't run the server if no path is found
67 if !lspserver.runIfSearchFiles->empty()
68 var path = util.FindNearestRootDir(bufDir, lspserver.runIfSearchFiles)
75 # Don't run the server if a path is found
76 if !lspserver.runUnlessSearchFiles->empty()
77 var path = util.FindNearestRootDir(bufDir, lspserver.runUnlessSearchFiles)
89 # Add a LSP server for a filetype
90 def LspAddServer(ftype: string, lspsrv: dict<any>)
91 var lspsrvlst = ftypeServerMap->has_key(ftype) ? ftypeServerMap[ftype] : []
92 lspsrvlst->add(lspsrv)
93 ftypeServerMap[ftype] = lspsrvlst
96 # Enable/disable the logging of the language server protocol messages
97 def ServerDebug(arg: string)
98 if ['errors', 'messages', 'off', 'on']->index(arg) == -1
99 util.ErrMsg($'Unsupported argument "{arg}"')
103 var lspservers: list<dict<any>> = buf.CurbufGetServers()
104 if lspservers->empty()
105 util.WarnMsg($'No Lsp servers found for "{@%}"')
109 for lspserver in lspservers
111 util.ClearTraceLogs(lspserver.logfile)
112 util.ClearTraceLogs(lspserver.errfile)
113 lspserver.debug = true
115 lspserver.debug = false
116 elseif arg == 'messages'
117 util.ServerMessagesShow(lspserver.logfile)
119 util.ServerMessagesShow(lspserver.errfile)
124 # Show information about all the LSP servers
125 export def ShowAllServers()
127 # Add filetype to server mapping information
128 lines->add('Filetype Information')
129 lines->add('====================')
130 for [ftype, lspservers] in ftypeServerMap->items()
131 for lspserver in lspservers
132 lines->add($"Filetype: '{ftype}'")
133 lines->add($"Server Name: '{lspserver.name}'")
134 lines->add($"Server Path: '{lspserver.path}'")
135 lines->add($"Status: {lspserver.running ? 'Running' : 'Not running'}")
140 # Add buffer to server mapping information
141 lines->add('Buffer Information')
142 lines->add('==================')
143 for bnr in range(1, bufnr('$'))
144 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
145 if !lspservers->empty()
146 lines->add($"Buffer: '{bufname(bnr)}'")
147 for lspserver in lspservers
148 lines->add($"Server Path: '{lspserver.path}'")
149 lines->add($"Status: {lspserver.running ? 'Running' : 'Not running'}")
155 var wid = bufwinid('Language-Servers')
161 :new Language-Servers
162 :setlocal buftype=nofile
163 :setlocal bufhidden=wipe
165 :setlocal nonumber nornu
166 :setlocal fdc=0 signcolumn=no
170 :setlocal nomodifiable
173 # Create a new window containing the buffer "bname" or if the window is
174 # already present then jump to it.
175 def OpenScratchWindow(bname: string)
176 var wid = bufwinid(bname)
183 :setlocal buftype=nofile
184 :setlocal bufhidden=wipe
186 :setlocal nonumber nornu
187 :setlocal fdc=0 signcolumn=no
191 # Show the status of the LSP server for the current buffer
192 def ShowServer(arg: string)
193 if ['status', 'capabilities', 'initializeRequest', 'messages']->index(arg) == -1
194 util.ErrMsg($'Unsupported argument "{arg}"')
198 var lspservers: list<dict<any>> = buf.CurbufGetServers()
199 if lspservers->empty()
200 util.WarnMsg($'No Lsp servers found for "{@%}"')
204 var windowName: string = ''
205 var lines: list<string> = []
206 if arg->empty() || arg == 'status'
207 windowName = $'LangServer-Status'
208 for lspserver in lspservers
210 lines->extend(['', repeat('=', &columns), ''])
212 var msg = $"LSP server '{lspserver.name}' is "
216 msg ..= 'not running'
220 elseif arg == 'capabilities'
221 windowName = $'LangServer-Capabilities'
222 for lspserver in lspservers
224 lines->extend(['', repeat('=', &columns), ''])
226 lines->extend(lspserver.getCapabilities())
228 elseif arg == 'initializeRequest'
229 windowName = $'LangServer-InitializeRequest'
230 for lspserver in lspservers
232 lines->extend(['', repeat('=', &columns), ''])
234 lines->extend(lspserver.getInitializeRequest())
236 elseif arg == 'messages'
237 windowName = $'LangServer-Messages'
238 for lspserver in lspservers
240 lines->extend(['', repeat('=', &columns), ''])
242 lines->extend(lspserver.getMessages())
245 util.ErrMsg($'Unsupported argument "{arg}"')
250 OpenScratchWindow(windowName)
253 :setlocal nomodifiable
255 util.InfoMsg(lines[0])
259 # Get LSP server running status for filetype "ftype"
260 # Return true if running, or false if not found or not running
261 export def ServerRunning(ftype: string): bool
262 if ftypeServerMap->has_key(ftype)
263 var lspservers = ftypeServerMap[ftype]
264 for lspserver in lspservers
274 # Go to a definition using "textDocument/definition" LSP request
275 export def GotoDefinition(peek: bool, cmdmods: string, count: number)
276 var lspserver: dict<any> = buf.CurbufGetServerChecked('definition')
277 if lspserver->empty()
281 lspserver.gotoDefinition(peek, cmdmods, count)
284 # Go to a declaration using "textDocument/declaration" LSP request
285 export def GotoDeclaration(peek: bool, cmdmods: string, count: number)
286 var lspserver: dict<any> = buf.CurbufGetServerChecked('declaration')
287 if lspserver->empty()
291 lspserver.gotoDeclaration(peek, cmdmods, count)
294 # Go to a type definition using "textDocument/typeDefinition" LSP request
295 export def GotoTypedef(peek: bool, cmdmods: string, count: number)
296 var lspserver: dict<any> = buf.CurbufGetServerChecked('typeDefinition')
297 if lspserver->empty()
301 lspserver.gotoTypeDef(peek, cmdmods, count)
304 # Go to a implementation using "textDocument/implementation" LSP request
305 export def GotoImplementation(peek: bool, cmdmods: string, count: number)
306 var lspserver: dict<any> = buf.CurbufGetServerChecked('implementation')
307 if lspserver->empty()
311 lspserver.gotoImplementation(peek, cmdmods, count)
314 # Switch source header using "textDocument/switchSourceHeader" LSP request
315 # (Clangd specifc extension)
316 export def SwitchSourceHeader()
317 var lspserver: dict<any> = buf.CurbufGetServerChecked()
318 if lspserver->empty()
322 lspserver.switchSourceHeader()
325 # Show the signature using "textDocument/signatureHelp" LSP method
326 # Invoked from an insert-mode mapping, so return an empty string.
327 def g:LspShowSignature(): string
328 var lspserver: dict<any> = buf.CurbufGetServerChecked('signatureHelp')
329 if lspserver->empty()
333 # first send all the changes in the current buffer to the LSP server
335 lspserver.showSignature()
339 # A buffer is saved. Send the "textDocument/didSave" LSP notification
341 var bnr: number = expand('<abuf>')->str2nr()
342 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)->filter(
343 (key, lspsrv) => !lspsrv->empty() && lspsrv.running
346 if lspservers->empty()
350 for lspserver in lspservers
351 lspserver.didSaveFile(bnr)
355 # Called after leaving insert mode. Used to process diag messages (if any)
356 def LspLeftInsertMode(bnr: number)
357 var updatePending: bool = bnr->getbufvar('LspDiagsUpdatePending', false)
361 setbufvar(bnr, 'LspDiagsUpdatePending', false)
363 diag.ProcessNewDiags(bnr)
366 # Add buffer-local autocmds when attaching a LSP server to a buffer
367 def AddBufLocalAutocmds(lspserver: dict<any>, bnr: number): void
368 var acmds: list<dict<any>> = []
370 # file saved notification handler
371 acmds->add({bufnr: bnr,
372 event: 'BufWritePost',
373 group: 'LSPBufferAutocmds',
374 cmd: 'LspSavedFile()'})
376 # Update the diagnostics when insert mode is stopped
377 acmds->add({bufnr: bnr,
378 event: 'InsertLeave',
379 group: 'LSPBufferAutocmds',
380 cmd: $'LspLeftInsertMode({bnr})'})
382 # Auto highlight all the occurrences of the current keyword
383 if opt.lspOptions.autoHighlight &&
384 lspserver.isDocumentHighlightProvider
385 acmds->add({bufnr: bnr,
386 event: 'CursorMoved',
387 group: 'LSPBufferAutocmds',
388 cmd: 'call LspDocHighlightClear() | call LspDocHighlight("silent")'})
391 # Show diagnostics on the status line
392 if opt.lspOptions.showDiagOnStatusLine
393 acmds->add({bufnr: bnr,
394 event: 'CursorMoved',
395 group: 'LSPBufferAutocmds',
396 cmd: 'LspShowCurrentDiagInStatusLine()'})
402 def BufferInit(lspserverId: number, bnr: number): void
403 var lspserver = buf.BufLspServerGetById(bnr, lspserverId)
404 if lspserver->empty() || !lspserver.running
408 var ftype: string = bnr->getbufvar('&filetype')
409 lspserver.textdocDidOpen(bnr, ftype)
411 # add a listener to track changes to this buffer
412 listener_add((_bnr: number, start: number, end: number, added: number, changes: list<dict<number>>) => {
413 lspserver.textdocDidChange(bnr, start, end, added, changes)
416 AddBufLocalAutocmds(lspserver, bnr)
418 diag.BufferInit(lspserver, bnr)
419 signature.BufferInit(lspserver)
420 inlayhints.BufferInit(lspserver, bnr)
422 var allServersReady = true
423 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
424 for lspsrv in lspservers
426 allServersReady = false
432 for lspsrv in lspservers
433 # It's only possible to initialize completion when all server capabilities
435 var completionServer = buf.BufLspServerGet(bnr, 'completion')
436 if !completionServer->empty() && lspsrv.id == completionServer.id
437 completion.BufferInit(lspsrv, bnr, ftype)
441 if exists('#User#LspAttached')
442 doautocmd <nomodeline> User LspAttached
447 # A new buffer is opened. If LSP is supported for this buffer, then add it
448 export def AddFile(bnr: number): void
449 if buf.BufHasLspServer(bnr)
450 # LSP server for this buffer is already initialized and running
455 if util.LspUriRemote(bnr->bufname()->fnamemodify(':p'))
459 var ftype: string = bnr->getbufvar('&filetype')
463 var lspservers: list<dict<any>> = LspGetServers(bnr, ftype)
464 if lspservers->empty()
467 for lspserver in lspservers
468 if !lspserver.running
469 if !lspInitializedOnce
472 lspserver.startServer(bnr)
474 buf.BufLspServerSet(bnr, lspserver)
477 BufferInit(lspserver.id, bnr)
479 augroup LSPBufferAutocmds
480 exe $'autocmd User LspServerReady_{lspserver.id} ++once BufferInit({lspserver.id}, {bnr})'
486 # Notify LSP server to remove a file
487 export def RemoveFile(bnr: number): void
488 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
489 for lspserver in lspservers->copy()
490 if lspserver->empty()
494 lspserver.textdocDidClose(bnr)
496 diag.DiagRemoveFile(bnr)
497 buf.BufLspServerRemove(bnr, lspserver)
501 # Buffer 'bnr' is loaded in a window, send the latest buffer contents to the
503 export def BufferLoadedInWin(bnr: number)
504 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
505 if lspservers->empty()
506 # No language servers for this buffer
509 for lspserver in lspservers
510 if !lspserver->empty() && lspserver.ready
511 lspserver.textdocDidChange(bnr, 0, 0, 0, [])
514 # Refresh the displayed diags visuals
515 if opt.lspOptions.autoHighlightDiags
516 diag.DiagsRefresh(bnr)
520 # Stop all the LSP servers
521 export def StopAllServers()
522 for lspserver in LSPServers
524 lspserver.stopServer()
529 # Add all the buffers with 'filetype' set to "ftype" to the language server.
530 def AddBuffersToLsp(ftype: string)
531 # Add all the buffers with the same file type as the current buffer
532 for binfo in getbufinfo({bufloaded: 1})
533 if binfo.bufnr->getbufvar('&filetype') == ftype
539 # Restart the LSP server for the current buffer
541 var lspservers: list<dict<any>> = buf.CurbufGetServers()
542 if lspservers->empty()
543 util.WarnMsg($'No Lsp servers found for "{@%}"')
547 # Remove all the buffers with the same file type as the current buffer
548 var ftype: string = &filetype
549 for binfo in getbufinfo()
550 if binfo.bufnr->getbufvar('&filetype') == ftype
551 RemoveFile(binfo.bufnr)
555 for lspserver in lspservers
556 # Stop the server (if running)
558 lspserver.stopServer()
561 # Start the server again
562 lspserver.startServer(bufnr())
565 AddBuffersToLsp(ftype)
568 # Add the LSP server for files with 'filetype' as "ftype".
569 def AddServerForFiltype(lspserver: dict<any>, ftype: string, omnicompl: bool)
570 LspAddServer(ftype, lspserver)
571 completion.OmniComplSet(ftype, omnicompl)
573 # If a buffer of this file type is already present, then send it to the LSP
575 AddBuffersToLsp(ftype)
578 # Register a LSP server for one or more file types
579 export def AddServer(serverList: list<dict<any>>)
580 for server in serverList
581 if !server->has_key('filetype') || !server->has_key('path')
582 util.ErrMsg('LSP server information is missing filetype or path')
585 # Enable omni-completion by default
586 server.omnicompl = server->get('omnicompl', true)
588 if !server.path->executable()
589 if !opt.lspOptions.ignoreMissingServer
590 util.ErrMsg($'LSP server {server.path} is not found')
594 if server->has_key('args')
595 if server.args->type() != v:t_list
596 util.ErrMsg($'Arguments for LSP server {server.args} is not a List')
603 if !server->has_key('initializationOptions')
604 || server.initializationOptions->type() != v:t_dict
605 server.initializationOptions = {}
608 if !server->has_key('forceOffsetEncoding')
609 || server.forceOffsetEncoding->type() != v:t_string
610 || (server.forceOffsetEncoding != 'utf-8'
611 && server.forceOffsetEncoding != 'utf-16'
612 && server.forceOffsetEncoding != 'utf-32')
613 server.forceOffsetEncoding = ''
616 if !server->has_key('customNotificationHandlers')
617 || server.customNotificationHandlers->type() != v:t_dict
618 server.customNotificationHandlers = {}
621 if server->has_key('processDiagHandler')
622 if server.processDiagHandler->type() != v:t_func
623 util.ErrMsg($'Setting of processDiagHandler {server.processDiagHandler} is not a Funcref nor lambda')
627 server.processDiagHandler = null_function
630 if !server->has_key('customRequestHandlers')
631 || server.customRequestHandlers->type() != v:t_dict
632 server.customRequestHandlers = {}
635 if !server->has_key('features') || server.features->type() != v:t_dict
639 if server.omnicompl->type() != v:t_bool
640 util.ErrMsg($'Setting of omnicompl {server.omnicompl} is not a Boolean')
644 if !server->has_key('syncInit')
645 server.syncInit = false
648 if !server->has_key('name') || server.name->type() != v:t_string
649 || server.name->empty()
650 # Use the executable name (without the extension) as the language server
652 server.name = server.path->fnamemodify(':t:r')
655 if !server->has_key('debug') || server.debug->type() != v:t_bool
659 if !server->has_key('traceLevel')
660 || server->type() != v:t_string
661 || (server.traceLevel != 'off' && server.traceLevel != 'debug'
662 && server.traceLevel != 'verbose')
663 server.traceLevel = 'off'
666 if !server->has_key('workspaceConfig')
667 || server.workspaceConfig->type() != v:t_dict
668 server.workspaceConfig = {}
671 if !server->has_key('rootSearch') || server.rootSearch->type() != v:t_list
672 server.rootSearch = []
675 if !server->has_key('runIfSearch') ||
676 server.runIfSearch->type() != v:t_list
677 server.runIfSearch = []
680 if !server->has_key('runUnlessSearch') ||
681 server.runUnlessSearch->type() != v:t_list
682 server.runUnlessSearch = []
685 var lspserver: dict<any> = lserver.NewLspServer(server)
687 var ftypes = server.filetype
688 if ftypes->type() == v:t_string
689 AddServerForFiltype(lspserver, ftypes, server.omnicompl)
690 elseif ftypes->type() == v:t_list
692 AddServerForFiltype(lspserver, ftype, server.omnicompl)
695 util.ErrMsg($'Unsupported file type information "{ftypes->string()}" in LSP server registration')
701 # The LSP server is considered ready when the server capabilities are
702 # received ("initialize" LSP reply message)
703 export def ServerReady(): bool
704 var fname: string = @%
709 var lspservers: list<dict<any>> = buf.CurbufGetServers()
710 if lspservers->empty()
714 for lspserver in lspservers
723 # set the LSP server trace level for the current buffer
724 # Params: SetTraceParams
725 def ServerTraceSet(traceVal: string)
726 if ['off', 'messages', 'verbose']->index(traceVal) == -1
727 util.ErrMsg($'Unsupported argument "{traceVal}"')
731 var lspservers: list<dict<any>> = buf.CurbufGetServers()
732 if lspservers->empty()
733 util.WarnMsg($'No Lsp servers found for "{@%}"')
737 for lspserver in lspservers
738 lspserver.setTrace(traceVal)
742 # Display the diagnostic messages from the LSP server for the current buffer
744 export def ShowDiagnostics(): void
748 # Show the diagnostic message for the current line
749 export def LspShowCurrentDiag(atPos: bool)
750 diag.ShowCurrentDiag(atPos)
753 # Display the diagnostics for the current line in the status line.
754 export def LspShowCurrentDiagInStatusLine()
755 var fname: string = @%
760 diag.ShowCurrentDiagInStatusLine()
763 # get the count of diagnostics in the current buffer
764 export def ErrorCount(): dict<number>
765 var res = {Error: 0, Warn: 0, Info: 0, Hint: 0}
766 var fname: string = @%
771 return diag.DiagsGetErrorCount(bufnr())
774 # jump to the next/previous/first diagnostic message in the current buffer
775 export def JumpToDiag(which: string, count: number = 0): void
776 diag.LspDiagsJump(which, count)
779 # Display the hover message from the LSP server for the current cursor
781 export def Hover(cmdmods: string)
782 var lspserver: dict<any> = buf.CurbufGetServerChecked('hover')
783 if lspserver->empty()
787 lspserver.hover(cmdmods)
790 # Enable or disable inlay hints
791 export def InlayHints(ctl: string)
793 inlayhints.InlayHintsEnable()
794 elseif ctl == 'disable'
795 inlayhints.InlayHintsDisable()
797 util.ErrMsg($'LspInlayHints - Unsupported argument "{ctl}"')
801 # Command-line completion for the ":LspInlayHints" command
802 export def LspInlayHintsComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
803 var l = ['enable', 'disable']
804 return filter(l, (_, val) => val =~ $'^{arglead}')
807 # show symbol references
808 export def ShowReferences(peek: bool)
809 var lspserver: dict<any> = buf.CurbufGetServerChecked('references')
810 if lspserver->empty()
814 lspserver.showReferences(peek)
817 # highlight all the places where a symbol is referenced
818 def g:LspDocHighlight(cmdmods: string = '')
819 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentHighlight')
820 if lspserver->empty()
824 lspserver.docHighlight(cmdmods)
827 # clear the symbol reference highlight
828 def g:LspDocHighlightClear()
829 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentHighlight')
830 if lspserver->empty()
834 if has('patch-9.0.0233')
835 prop_remove({types: ['LspTextRef', 'LspReadRef', 'LspWriteRef'], all: true})
837 prop_remove({type: 'LspTextRef', all: true})
838 prop_remove({type: 'LspReadRef', all: true})
839 prop_remove({type: 'LspWriteRef', all: true})
843 def g:LspRequestDocSymbols()
844 if outline.SkipOutlineRefresh()
848 var fname: string = @%
853 var lspserver: dict<any> = buf.CurbufGetServer('documentSymbol')
854 if lspserver->empty() || !lspserver.running || !lspserver.ready
858 lspserver.getDocSymbols(fname, true)
861 # open a window and display all the symbols in a file (outline)
862 export def Outline(cmdmods: string, winsize: number)
863 var fname: string = @%
868 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentSymbol')
869 if lspserver->empty() || !lspserver.running || !lspserver.ready
873 outline.OpenOutlineWindow(cmdmods, winsize)
874 g:LspRequestDocSymbols()
877 # show all the symbols in a file in a popup menu
878 export def ShowDocSymbols()
879 var fname: string = @%
884 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentSymbol')
885 if lspserver->empty() || !lspserver.running || !lspserver.ready
889 lspserver.getDocSymbols(fname, false)
892 # Format the entire file
893 export def TextDocFormat(range_args: number, line1: number, line2: number)
895 util.ErrMsg('Current file is not a modifiable file')
899 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentFormatting')
900 if lspserver->empty()
904 var fname: string = @%
906 lspserver.textDocFormat(fname, true, line1, line2)
908 lspserver.textDocFormat(fname, false, 0, 0)
912 # TODO: Add support for textDocument.onTypeFormatting?
913 # Will this slow down Vim?
915 # Display all the locations where the current symbol is called from.
916 # Uses LSP "callHierarchy/incomingCalls" request
917 export def IncomingCalls()
918 var lspserver: dict<any> = buf.CurbufGetServerChecked('callHierarchy')
919 if lspserver->empty()
923 lspserver.incomingCalls(@%)
926 # Display all the symbols used by the current symbol.
927 # Uses LSP "callHierarchy/outgoingCalls" request
928 export def OutgoingCalls()
929 var lspserver: dict<any> = buf.CurbufGetServerChecked('callHierarchy')
930 if lspserver->empty()
934 lspserver.outgoingCalls(@%)
937 # Display the type hierarchy for the current symbol. Direction is 0 for
938 # sub types and 1 for super types.
939 export def TypeHierarchy(direction: number)
940 var lspserver: dict<any> = buf.CurbufGetServerChecked('typeHierarchy')
941 if lspserver->empty()
945 lspserver.typeHierarchy(direction)
949 # Uses LSP "textDocument/rename" request
950 export def Rename(a_newName: string)
951 var lspserver: dict<any> = buf.CurbufGetServerChecked('rename')
952 if lspserver->empty()
956 var newName: string = a_newName
958 var sym: string = expand('<cword>')
959 newName = input($"Rename symbol '{sym}' to: ", sym)
964 # clear the input prompt
968 lspserver.renameSymbol(newName)
971 # Perform a code action
972 # Uses LSP "textDocument/codeAction" request
973 export def CodeAction(line1: number, line2: number, query: string)
974 var lspserver: dict<any> = buf.CurbufGetServerChecked('codeAction')
975 if lspserver->empty()
979 var fname: string = @%
980 lspserver.codeAction(fname, line1, line2, query)
984 # Uses LSP "textDocument/codeLens" request
985 export def CodeLens()
986 var lspserver: dict<any> = buf.CurbufGetServerChecked('codeLens')
987 if lspserver->empty()
991 lspserver.codeLens(@%)
994 # Perform a workspace wide symbol lookup
995 # Uses LSP "workspace/symbol" request
996 export def SymbolSearch(queryArg: string, cmdmods: string)
997 var lspserver: dict<any> = buf.CurbufGetServerChecked('workspaceSymbol')
998 if lspserver->empty()
1002 var query: string = queryArg
1004 query = input('Lookup symbol: ', expand('<cword>'))
1011 lspserver.workspaceQuery(query, true, cmdmods)
1014 # Display the list of workspace folders
1015 export def ListWorkspaceFolders()
1016 var lspservers: list<dict<any>> = buf.CurbufGetServers()
1017 for lspserver in lspservers
1018 util.InfoMsg($'Workspace Folders: "{lspserver.name}" {lspserver.workspaceFolders->string()}')
1022 # Add a workspace folder. Default is to use the current folder.
1023 export def AddWorkspaceFolder(dirArg: string)
1024 var dirName: string = dirArg
1026 dirName = input('Add Workspace Folder: ', getcwd(), 'dir')
1032 if !dirName->isdirectory()
1033 util.ErrMsg($'{dirName} is not a directory')
1037 var lspservers: list<dict<any>> = buf.CurbufGetServers()
1039 for lspserver in lspservers
1040 lspserver.addWorkspaceFolder(dirName)
1044 # Remove a workspace folder. Default is to use the current folder.
1045 export def RemoveWorkspaceFolder(dirArg: string)
1046 var dirName: string = dirArg
1048 dirName = input('Remove Workspace Folder: ', getcwd(), 'dir')
1054 if !dirName->isdirectory()
1055 util.ErrMsg($'{dirName} is not a directory')
1059 var lspservers: list<dict<any>> = buf.CurbufGetServers()
1060 for lspserver in lspservers
1061 lspserver.removeWorkspaceFolder(dirName)
1065 # expand the previous selection or start a new selection
1066 export def SelectionExpand()
1067 var lspserver: dict<any> = buf.CurbufGetServerChecked('selectionRange')
1068 if lspserver->empty()
1072 lspserver.selectionExpand()
1075 # shrink the previous selection or start a new selection
1076 export def SelectionShrink()
1077 var lspserver: dict<any> = buf.CurbufGetServerChecked('selectionRange')
1078 if lspserver->empty()
1082 lspserver.selectionShrink()
1085 # fold the entire document
1086 export def FoldDocument()
1087 var lspserver: dict<any> = buf.CurbufGetServerChecked('foldingRange')
1088 if lspserver->empty()
1092 if &foldmethod != 'manual'
1093 util.ErrMsg("Only works when 'foldmethod' is 'manual'")
1097 var fname: string = @%
1098 lspserver.foldRange(fname)
1101 # Enable diagnostic highlighting for all the buffers
1102 export def DiagHighlightEnable()
1103 diag.DiagsHighlightEnable()
1106 # Disable diagnostic highlighting for all the buffers
1107 export def DiagHighlightDisable()
1108 diag.DiagsHighlightDisable()
1111 # Function to use with the 'tagfunc' option.
1112 export def TagFunc(pat: string, flags: string, info: dict<any>): any
1113 var lspserver: dict<any> = buf.CurbufGetServerChecked('definition')
1114 if lspserver->empty()
1118 return lspserver.tagFunc(pat, flags, info)
1121 # Function to use with the 'formatexpr' option.
1122 export def FormatExpr(): number
1123 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentFormatting')
1124 if lspserver->empty()
1128 lspserver.textDocFormat(@%, true, v:lnum, v:lnum + v:count - 1)
1132 export def RegisterCmdHandler(cmd: string, Handler: func)
1133 codeaction.RegisterCmdHandler(cmd, Handler)
1136 # Command-line completion for the ":LspServer <cmd>" and ":LspDiag <cmd>" sub
1138 def LspSubCmdComplete(cmds: list<string>, arglead: string, cmdline: string, cursorPos: number): list<string>
1139 var wordBegin = cmdline->match('\s\+\zs\S', cursorPos)
1144 # Make sure there are no additional sub-commands
1145 var wordEnd = cmdline->stridx(' ', wordBegin)
1147 return cmds->filter((_, val) => val =~ $'^{arglead}')
1153 # Command-line completion for the ":LspDiag highlight" command
1154 def LspDiagHighlightComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1155 return LspSubCmdComplete(['enable', 'disable'], arglead, cmdline, cursorPos)
1158 # Command-line completion for the ":LspDiag" command
1159 export def LspDiagComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1162 var l = ['first', 'current', 'here', 'highlight', 'last', 'next', 'prev',
1165 # Skip the command name
1166 var i = cmdline->stridx(' ', 0)
1167 wordBegin = cmdline->match('\s\+\zs\S', i)
1172 wordEnd = cmdline->stridx(' ', wordBegin)
1174 return filter(l, (_, val) => val =~ $'^{arglead}')
1177 var cmd = cmdline->strpart(wordBegin, wordEnd - wordBegin)
1178 if cmd == 'highlight'
1179 return LspDiagHighlightComplete(arglead, cmdline, wordEnd)
1185 # ":LspDiag" command handler
1186 export def LspDiagCmd(args: string, cmdCount: number, force: bool)
1187 if args->stridx('highlight') == 0
1189 var subcmd = args[10 : ]->trim()
1190 if subcmd == 'enable'
1191 diag.DiagsHighlightEnable()
1192 elseif subcmd == 'disable'
1193 diag.DiagsHighlightDisable()
1195 util.ErrMsg($':LspDiag highlight - Unsupported argument "{subcmd}"')
1198 util.ErrMsg('Argument required for ":LspDiag highlight"')
1200 elseif args == 'first'
1201 diag.LspDiagsJump('first', 0)
1202 elseif args == 'current'
1203 LspShowCurrentDiag(force)
1204 elseif args == 'here'
1205 diag.LspDiagsJump('here', 0)
1206 elseif args == 'last'
1207 diag.LspDiagsJump('last', 0)
1208 elseif args == 'next'
1209 diag.LspDiagsJump('next', cmdCount)
1210 elseif args == 'prev'
1211 diag.LspDiagsJump('prev', cmdCount)
1212 elseif args == 'show'
1215 util.ErrMsg($':LspDiag - Unsupported argument "{args}"')
1219 # Command-line completion for the ":LspServer debug" command
1220 def LspServerDebugComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1221 return LspSubCmdComplete(['errors', 'messages', 'off', 'on'], arglead,
1225 # Command-line completion for the ":LspServer show" command
1226 def LspServerShowComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1227 return LspSubCmdComplete(['capabilities', 'initializeRequest', 'messages',
1228 'status'], arglead, cmdline, cursorPos)
1231 # Command-line completion for the ":LspServer trace" command
1232 def LspServerTraceComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1233 return LspSubCmdComplete(['messages', 'off', 'verbose'], arglead, cmdline,
1237 # Command-line completion for the ":LspServer" command
1238 export def LspServerComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1241 var l = ['debug', 'restart', 'show', 'trace']
1243 # Skip the command name
1244 var i = cmdline->stridx(' ', 0)
1245 wordBegin = cmdline->match('\s\+\zs\S', i)
1250 wordEnd = cmdline->stridx(' ', wordBegin)
1252 return filter(l, (_, val) => val =~ $'^{arglead}')
1255 var cmd = cmdline->strpart(wordBegin, wordEnd - wordBegin)
1257 return LspServerDebugComplete(arglead, cmdline, wordEnd)
1258 elseif cmd == 'restart'
1259 elseif cmd == 'show'
1260 return LspServerShowComplete(arglead, cmdline, wordEnd)
1261 elseif cmd == 'trace'
1262 return LspServerTraceComplete(arglead, cmdline, wordEnd)
1268 # ":LspServer" command handler
1269 export def LspServerCmd(args: string)
1270 if args->stridx('debug') == 0
1272 var subcmd = args[6 : ]->trim()
1275 util.ErrMsg('Argument required for ":LspServer debug"')
1277 elseif args == 'restart'
1279 elseif args->stridx('show') == 0
1281 var subcmd = args[5 : ]->trim()
1284 util.ErrMsg('Argument required for ":LspServer show"')
1286 elseif args->stridx('trace') == 0
1288 var subcmd = args[6 : ]->trim()
1289 ServerTraceSet(subcmd)
1291 util.ErrMsg('Argument required for ":LspServer trace"')
1294 util.ErrMsg($'LspServer - Unsupported argument "{args}"')
1298 # vim: tabstop=8 shiftwidth=2 softtabstop=2 noexpandtab