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
340 def LspSavedFile(bnr: number)
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 # Called after leaving insert mode. Used to process diag messages (if any)
355 def LspLeftInsertMode(bnr: number)
356 var updatePending: bool = bnr->getbufvar('LspDiagsUpdatePending', false)
360 setbufvar(bnr, 'LspDiagsUpdatePending', false)
362 diag.ProcessNewDiags(bnr)
365 # Add buffer-local autocmds when attaching a LSP server to a buffer
366 def AddBufLocalAutocmds(lspserver: dict<any>, bnr: number): void
367 var acmds: list<dict<any>> = []
369 # file saved notification handler
370 acmds->add({bufnr: bnr,
371 event: 'BufWritePost',
372 group: 'LSPBufferAutocmds',
373 cmd: $'LspSavedFile({bnr})'})
375 # Update the diagnostics when insert mode is stopped
376 acmds->add({bufnr: bnr,
377 event: 'InsertLeave',
378 group: 'LSPBufferAutocmds',
379 cmd: $'LspLeftInsertMode({bnr})'})
381 # Auto highlight all the occurrences of the current keyword
382 if opt.lspOptions.autoHighlight &&
383 lspserver.isDocumentHighlightProvider
384 acmds->add({bufnr: bnr,
385 event: 'CursorMoved',
386 group: 'LSPBufferAutocmds',
387 cmd: $'call LspDocHighlightClear({bnr}) | call LspDocHighlight({bnr}, "silent")'})
393 # The LSP server with ID "lspserverId" is ready, initialize the LSP features
395 def BufferInit(lspserverId: number, bnr: number): void
396 var lspserver = buf.BufLspServerGetById(bnr, lspserverId)
397 if lspserver->empty() || !lspserver.running
401 var ftype: string = bnr->getbufvar('&filetype')
402 lspserver.textdocDidOpen(bnr, ftype)
404 # add a listener to track changes to this buffer
405 listener_add((_bnr: number, start: number, end: number, added: number, changes: list<dict<number>>) => {
406 lspserver.textdocDidChange(bnr, start, end, added, changes)
409 AddBufLocalAutocmds(lspserver, bnr)
411 diag.BufferInit(lspserver, bnr)
412 signature.BufferInit(lspserver)
413 inlayhints.BufferInit(lspserver, bnr)
415 var allServersReady = true
416 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
417 for lspsrv in lspservers
419 allServersReady = false
425 for lspsrv in lspservers
426 # It's only possible to initialize completion when all server capabilities
428 var completionServer = buf.BufLspServerGet(bnr, 'completion')
429 if !completionServer->empty() && lspsrv.id == completionServer.id
430 completion.BufferInit(lspsrv, bnr, ftype)
434 if exists('#User#LspAttached')
435 doautocmd <nomodeline> User LspAttached
440 # A new buffer is opened. If LSP is supported for this buffer, then add it
441 export def AddFile(bnr: number): void
442 if buf.BufHasLspServer(bnr)
443 # LSP server for this buffer is already initialized and running
448 if util.LspUriRemote(bnr->bufname()->fnamemodify(':p'))
452 var ftype: string = bnr->getbufvar('&filetype')
456 var lspservers: list<dict<any>> = LspGetServers(bnr, ftype)
457 if lspservers->empty()
460 for lspserver in lspservers
461 if !lspserver.running
462 if !lspInitializedOnce
465 lspserver.startServer(bnr)
467 buf.BufLspServerSet(bnr, lspserver)
470 BufferInit(lspserver.id, bnr)
472 augroup LSPBufferAutocmds
473 exe $'autocmd User LspServerReady_{lspserver.id} ++once BufferInit({lspserver.id}, {bnr})'
479 # Notify LSP server to remove a file
480 export def RemoveFile(bnr: number): void
481 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
482 for lspserver in lspservers->copy()
483 if lspserver->empty()
487 lspserver.textdocDidClose(bnr)
489 diag.DiagRemoveFile(bnr)
490 buf.BufLspServerRemove(bnr, lspserver)
494 # Buffer 'bnr' is loaded in a window, send the latest buffer contents to the
496 export def BufferLoadedInWin(bnr: number)
497 var lspservers: list<dict<any>> = buf.BufLspServersGet(bnr)
498 if lspservers->empty()
499 # No language servers for this buffer
502 for lspserver in lspservers
503 if !lspserver->empty() && lspserver.ready
504 lspserver.textdocDidChange(bnr, 0, 0, 0, [])
507 # Refresh the displayed diags visuals
508 if opt.lspOptions.autoHighlightDiags
509 diag.DiagsRefresh(bnr)
511 completion.BufferLoadedInWin(bnr)
514 # Stop all the LSP servers
515 export def StopAllServers()
516 for lspserver in LSPServers
518 lspserver.stopServer()
523 # Add all the buffers with 'filetype' set to "ftype" to the language server.
524 def AddBuffersToLsp(ftype: string)
525 # Add all the buffers with the same file type as the current buffer
526 for binfo in getbufinfo({bufloaded: 1})
527 if binfo.bufnr->getbufvar('&filetype') == ftype
533 # Restart the LSP server for the current buffer
535 var lspservers: list<dict<any>> = buf.CurbufGetServers()
536 if lspservers->empty()
537 util.WarnMsg($'No Lsp servers found for "{@%}"')
541 # Remove all the buffers with the same file type as the current buffer
542 var ftype: string = &filetype
543 for binfo in getbufinfo()
544 if binfo.bufnr->getbufvar('&filetype') == ftype
545 RemoveFile(binfo.bufnr)
549 for lspserver in lspservers
550 # Stop the server (if running)
552 lspserver.stopServer()
555 # Start the server again
556 lspserver.startServer(bufnr())
559 AddBuffersToLsp(ftype)
562 # Add the LSP server for files with 'filetype' as "ftype".
563 def AddServerForFiltype(lspserver: dict<any>, ftype: string, omnicompl: bool)
564 LspAddServer(ftype, lspserver)
565 completion.OmniComplSet(ftype, omnicompl)
567 # If a buffer of this file type is already present, then send it to the LSP
569 AddBuffersToLsp(ftype)
572 # Register a LSP server for one or more file types
573 export def AddServer(serverList: list<dict<any>>)
574 for server in serverList
575 if !server->has_key('filetype') || !server->has_key('path')
576 util.ErrMsg('LSP server information is missing filetype or path')
579 # Enable omni-completion by default
580 var omnicompl_def: bool = false
581 if opt.lspOptions.omniComplete == true
582 || (opt.lspOptions.omniComplete == null
583 && !opt.lspOptions.autoComplete)
586 server.omnicompl = server->get('omnicompl', omnicompl_def)
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 # get the count of diagnostics in the current buffer
754 export def ErrorCount(): dict<number>
755 var res = {Error: 0, Warn: 0, Info: 0, Hint: 0}
756 var fname: string = @%
761 return diag.DiagsGetErrorCount(bufnr())
764 # jump to the next/previous/first diagnostic message in the current buffer
765 export def JumpToDiag(which: string, count: number = 0): void
766 diag.LspDiagsJump(which, count)
769 # Display the hover message from the LSP server for the current cursor
771 export def Hover(cmdmods: string)
772 var lspserver: dict<any> = buf.CurbufGetServerChecked('hover')
773 if lspserver->empty()
777 lspserver.hover(cmdmods)
780 # Enable or disable inlay hints
781 export def InlayHints(ctl: string)
783 inlayhints.InlayHintsEnable()
784 elseif ctl == 'disable'
785 inlayhints.InlayHintsDisable()
787 util.ErrMsg($'LspInlayHints - Unsupported argument "{ctl}"')
791 # Command-line completion for the ":LspInlayHints" command
792 export def LspInlayHintsComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
793 var l = ['enable', 'disable']
794 return filter(l, (_, val) => val =~ $'^{arglead}')
797 # show symbol references
798 export def ShowReferences(peek: bool)
799 var lspserver: dict<any> = buf.CurbufGetServerChecked('references')
800 if lspserver->empty()
804 lspserver.showReferences(peek)
807 # highlight all the places where a symbol is referenced
808 def g:LspDocHighlight(bnr: number = bufnr(), cmdmods: string = '')
809 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentHighlight')
810 if lspserver->empty()
814 lspserver.docHighlight(bnr, cmdmods)
817 # clear the symbol reference highlight
818 def g:LspDocHighlightClear(bnr: number = bufnr())
819 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentHighlight')
820 if lspserver->empty()
824 var propNames = ['LspTextRef', 'LspReadRef', 'LspWriteRef']
825 if has('patch-9.0.0233')
826 prop_remove({types: propNames, bufnr: bnr, all: true})
828 for propName in propNames
829 prop_remove({type: propName, bufnr: bnr, all: true})
834 def g:LspRequestDocSymbols()
835 if outline.SkipOutlineRefresh()
839 var fname: string = @%
844 var lspserver: dict<any> = buf.CurbufGetServer('documentSymbol')
845 if lspserver->empty() || !lspserver.running || !lspserver.ready
849 lspserver.getDocSymbols(fname, true)
852 # open a window and display all the symbols in a file (outline)
853 export def Outline(cmdmods: string, winsize: number)
854 var fname: string = @%
859 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentSymbol')
860 if lspserver->empty() || !lspserver.running || !lspserver.ready
864 outline.OpenOutlineWindow(cmdmods, winsize)
865 g:LspRequestDocSymbols()
868 # show all the symbols in a file in a popup menu
869 export def ShowDocSymbols()
870 var fname: string = @%
875 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentSymbol')
876 if lspserver->empty() || !lspserver.running || !lspserver.ready
880 lspserver.getDocSymbols(fname, false)
883 # Format the entire file
884 export def TextDocFormat(range_args: number, line1: number, line2: number)
886 util.ErrMsg('Current file is not a modifiable file')
890 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentFormatting')
891 if lspserver->empty()
895 var fname: string = @%
897 lspserver.textDocFormat(fname, true, line1, line2)
899 lspserver.textDocFormat(fname, false, 0, 0)
903 # TODO: Add support for textDocument.onTypeFormatting?
904 # Will this slow down Vim?
906 # Display all the locations where the current symbol is called from.
907 # Uses LSP "callHierarchy/incomingCalls" request
908 export def IncomingCalls()
909 var lspserver: dict<any> = buf.CurbufGetServerChecked('callHierarchy')
910 if lspserver->empty()
914 lspserver.incomingCalls(@%)
917 # Display all the symbols used by the current symbol.
918 # Uses LSP "callHierarchy/outgoingCalls" request
919 export def OutgoingCalls()
920 var lspserver: dict<any> = buf.CurbufGetServerChecked('callHierarchy')
921 if lspserver->empty()
925 lspserver.outgoingCalls(@%)
928 # Display the type hierarchy for the current symbol. Direction is 0 for
929 # sub types and 1 for super types.
930 export def TypeHierarchy(direction: number)
931 var lspserver: dict<any> = buf.CurbufGetServerChecked('typeHierarchy')
932 if lspserver->empty()
936 lspserver.typeHierarchy(direction)
940 # Uses LSP "textDocument/rename" request
941 export def Rename(a_newName: string)
942 var lspserver: dict<any> = buf.CurbufGetServerChecked('rename')
943 if lspserver->empty()
947 var newName: string = a_newName
949 var sym: string = expand('<cword>')
950 newName = input($"Rename symbol '{sym}' to: ", sym)
955 # clear the input prompt
959 lspserver.renameSymbol(newName)
962 # Perform a code action
963 # Uses LSP "textDocument/codeAction" request
964 export def CodeAction(line1: number, line2: number, query: string)
965 var lspserver: dict<any> = buf.CurbufGetServerChecked('codeAction')
966 if lspserver->empty()
970 var fname: string = @%
971 lspserver.codeAction(fname, line1, line2, query)
975 # Uses LSP "textDocument/codeLens" request
976 export def CodeLens()
977 var lspserver: dict<any> = buf.CurbufGetServerChecked('codeLens')
978 if lspserver->empty()
982 lspserver.codeLens(@%)
985 # Perform a workspace wide symbol lookup
986 # Uses LSP "workspace/symbol" request
987 export def SymbolSearch(queryArg: string, cmdmods: string)
988 var lspserver: dict<any> = buf.CurbufGetServerChecked('workspaceSymbol')
989 if lspserver->empty()
993 var query: string = queryArg
995 query = input('Lookup symbol: ', expand('<cword>'))
1002 lspserver.workspaceQuery(query, true, cmdmods)
1005 # Display the list of workspace folders
1006 export def ListWorkspaceFolders()
1007 var lspservers: list<dict<any>> = buf.CurbufGetServers()
1008 for lspserver in lspservers
1009 util.InfoMsg($'Workspace Folders: "{lspserver.name}" {lspserver.workspaceFolders->string()}')
1013 # Add a workspace folder. Default is to use the current folder.
1014 export def AddWorkspaceFolder(dirArg: string)
1015 var dirName: string = dirArg
1017 dirName = input('Add Workspace Folder: ', getcwd(), 'dir')
1023 if !dirName->isdirectory()
1024 util.ErrMsg($'{dirName} is not a directory')
1028 var lspservers: list<dict<any>> = buf.CurbufGetServers()
1030 for lspserver in lspservers
1031 lspserver.addWorkspaceFolder(dirName)
1035 # Remove a workspace folder. Default is to use the current folder.
1036 export def RemoveWorkspaceFolder(dirArg: string)
1037 var dirName: string = dirArg
1039 dirName = input('Remove Workspace Folder: ', getcwd(), 'dir')
1045 if !dirName->isdirectory()
1046 util.ErrMsg($'{dirName} is not a directory')
1050 var lspservers: list<dict<any>> = buf.CurbufGetServers()
1051 for lspserver in lspservers
1052 lspserver.removeWorkspaceFolder(dirName)
1056 # expand the previous selection or start a new selection
1057 export def SelectionExpand()
1058 var lspserver: dict<any> = buf.CurbufGetServerChecked('selectionRange')
1059 if lspserver->empty()
1063 lspserver.selectionExpand()
1066 # shrink the previous selection or start a new selection
1067 export def SelectionShrink()
1068 var lspserver: dict<any> = buf.CurbufGetServerChecked('selectionRange')
1069 if lspserver->empty()
1073 lspserver.selectionShrink()
1076 # fold the entire document
1077 export def FoldDocument()
1078 var lspserver: dict<any> = buf.CurbufGetServerChecked('foldingRange')
1079 if lspserver->empty()
1083 if &foldmethod != 'manual'
1084 util.ErrMsg("Only works when 'foldmethod' is 'manual'")
1088 var fname: string = @%
1089 lspserver.foldRange(fname)
1092 # Enable diagnostic highlighting for all the buffers
1093 export def DiagHighlightEnable()
1094 diag.DiagsHighlightEnable()
1097 # Disable diagnostic highlighting for all the buffers
1098 export def DiagHighlightDisable()
1099 diag.DiagsHighlightDisable()
1102 # Function to use with the 'tagfunc' option.
1103 export def TagFunc(pat: string, flags: string, info: dict<any>): any
1104 var lspserver: dict<any> = buf.CurbufGetServerChecked('definition')
1105 if lspserver->empty()
1109 return lspserver.tagFunc(pat, flags, info)
1112 # Function to use with the 'formatexpr' option.
1113 export def FormatExpr(): number
1114 var lspserver: dict<any> = buf.CurbufGetServerChecked('documentFormatting')
1115 if lspserver->empty()
1119 lspserver.textDocFormat(@%, true, v:lnum, v:lnum + v:count - 1)
1123 export def RegisterCmdHandler(cmd: string, Handler: func)
1124 codeaction.RegisterCmdHandler(cmd, Handler)
1127 # Command-line completion for the ":LspServer <cmd>" and ":LspDiag <cmd>" sub
1129 def LspSubCmdComplete(cmds: list<string>, arglead: string, cmdline: string, cursorPos: number): list<string>
1130 var wordBegin = cmdline->match('\s\+\zs\S', cursorPos)
1135 # Make sure there are no additional sub-commands
1136 var wordEnd = cmdline->stridx(' ', wordBegin)
1138 return cmds->filter((_, val) => val =~ $'^{arglead}')
1144 # Command-line completion for the ":LspDiag highlight" command
1145 def LspDiagHighlightComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1146 return LspSubCmdComplete(['enable', 'disable'], arglead, cmdline, cursorPos)
1149 # Command-line completion for the ":LspDiag" command
1150 export def LspDiagComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1153 var l = ['first', 'current', 'here', 'highlight', 'last', 'next', 'prev',
1156 # Skip the command name
1157 var i = cmdline->stridx(' ', 0)
1158 wordBegin = cmdline->match('\s\+\zs\S', i)
1163 wordEnd = cmdline->stridx(' ', wordBegin)
1165 return filter(l, (_, val) => val =~ $'^{arglead}')
1168 var cmd = cmdline->strpart(wordBegin, wordEnd - wordBegin)
1169 if cmd == 'highlight'
1170 return LspDiagHighlightComplete(arglead, cmdline, wordEnd)
1176 # ":LspDiag" command handler
1177 export def LspDiagCmd(args: string, cmdCount: number, force: bool)
1178 if args->stridx('highlight') == 0
1180 var subcmd = args[10 : ]->trim()
1181 if subcmd == 'enable'
1182 diag.DiagsHighlightEnable()
1183 elseif subcmd == 'disable'
1184 diag.DiagsHighlightDisable()
1186 util.ErrMsg($':LspDiag highlight - Unsupported argument "{subcmd}"')
1189 util.ErrMsg('Argument required for ":LspDiag highlight"')
1191 elseif args == 'first'
1192 diag.LspDiagsJump('first', 0)
1193 elseif args == 'current'
1194 LspShowCurrentDiag(force)
1195 elseif args == 'here'
1196 diag.LspDiagsJump('here', 0)
1197 elseif args == 'last'
1198 diag.LspDiagsJump('last', 0)
1199 elseif args == 'next'
1200 diag.LspDiagsJump('next', cmdCount)
1201 elseif args == 'prev'
1202 diag.LspDiagsJump('prev', cmdCount)
1203 elseif args == 'show'
1206 util.ErrMsg($':LspDiag - Unsupported argument "{args}"')
1210 # Command-line completion for the ":LspServer debug" command
1211 def LspServerDebugComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1212 return LspSubCmdComplete(['errors', 'messages', 'off', 'on'], arglead,
1216 # Command-line completion for the ":LspServer show" command
1217 def LspServerShowComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1218 return LspSubCmdComplete(['capabilities', 'initializeRequest', 'messages',
1219 'status'], arglead, cmdline, cursorPos)
1222 # Command-line completion for the ":LspServer trace" command
1223 def LspServerTraceComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1224 return LspSubCmdComplete(['messages', 'off', 'verbose'], arglead, cmdline,
1228 # Command-line completion for the ":LspServer" command
1229 export def LspServerComplete(arglead: string, cmdline: string, cursorPos: number): list<string>
1232 var l = ['debug', 'restart', 'show', 'trace']
1234 # Skip the command name
1235 var i = cmdline->stridx(' ', 0)
1236 wordBegin = cmdline->match('\s\+\zs\S', i)
1241 wordEnd = cmdline->stridx(' ', wordBegin)
1243 return filter(l, (_, val) => val =~ $'^{arglead}')
1246 var cmd = cmdline->strpart(wordBegin, wordEnd - wordBegin)
1248 return LspServerDebugComplete(arglead, cmdline, wordEnd)
1249 elseif cmd == 'restart'
1250 elseif cmd == 'show'
1251 return LspServerShowComplete(arglead, cmdline, wordEnd)
1252 elseif cmd == 'trace'
1253 return LspServerTraceComplete(arglead, cmdline, wordEnd)
1259 # ":LspServer" command handler
1260 export def LspServerCmd(args: string)
1261 if args->stridx('debug') == 0
1263 var subcmd = args[6 : ]->trim()
1266 util.ErrMsg('Argument required for ":LspServer debug"')
1268 elseif args == 'restart'
1270 elseif args->stridx('show') == 0
1272 var subcmd = args[5 : ]->trim()
1275 util.ErrMsg('Argument required for ":LspServer show"')
1277 elseif args->stridx('trace') == 0
1279 var subcmd = args[6 : ]->trim()
1280 ServerTraceSet(subcmd)
1282 util.ErrMsg('Argument required for ":LspServer trace"')
1285 util.ErrMsg($'LspServer - Unsupported argument "{args}"')
1289 # vim: tabstop=8 shiftwidth=2 softtabstop=2 noexpandtab