vim9script
-import {WarnMsg, ErrMsg, LspUriToFile} from './util.vim'
+# Handlers for messages from the LSP server
+# Refer to https://microsoft.github.io/language-server-protocol/specification
+# for the Language Server Protocol (LSP) specificaiton.
+
+import {WarnMsg, ErrMsg, TraceLog, LspUriToFile} from './util.vim'
# process the 'initialize' method reply from the LSP server
+# Result: InitializeResult
def s:processInitializeReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>): void
if reply.result->len() <= 0
return
endif
- # interface 'InitializeResult'
var caps: dict<any> = reply.result.capabilities
lspserver.caps = caps
# process the 'textDocument/definition' / 'textDocument/declaration' /
# 'textDocument/typeDefinition' and 'textDocument/implementation' replies from
# the LSP server
+# Result: Location | Location[] | LocationLink[] | null
def s:processDefDeclReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>): void
if reply.result->empty()
WarnMsg("Error: definition is not found")
enddef
# process the 'textDocument/signatureHelp' reply from the LSP server
+# Result: SignatureHelp | null
def s:processSignaturehelpReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>): void
var result: dict<any> = reply.result
if result.signatures->len() <= 0
enddef
# process the 'textDocument/completion' reply from the LSP server
+# Result: CompletionItem[] | CompletionList | null
def s:processCompletionReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>): void
var items: list<dict<any>>
enddef
# process the 'textDocument/hover' reply from the LSP server
+# Result: Hover | null
def s:processHoverReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>): void
if type(reply.result) == v:t_none
return
enddef
# process the 'textDocument/references' reply from the LSP server
+# Result: Location[] | null
def s:processReferencesReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>): void
if type(reply.result) == v:t_none || reply.result->empty()
WarnMsg('Error: No references found')
enddef
# process the 'textDocument/documentHighlight' reply from the LSP server
+# Result: DocumentHighlight[] | null
def s:processDocHighlightReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>): void
if reply.result->empty()
return
# process the 'textDocument/documentSymbol' reply from the LSP server
# Open a symbols window and display the symbols as a tree
+# Result: DocumentSymbol[] | SymbolInformation[] | null
def s:processDocSymbolReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>): void
if reply.result->empty()
WarnMsg('No symbols are found')
enddef
# process the 'textDocument/formatting' reply from the LSP server
+# Result: TextEdit[] | null
def s:processFormatReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>)
if reply.result->empty()
# nothing to format
save_cursor->setpos('.')
enddef
-# process the 'textDocument/rename' reply from the LSP server
+# Reply: 'textDocument/rename'
+# Result: Range | { range: Range, placeholder: string }
+# | { defaultBehavior: boolean } | null
def s:processRenameReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>)
if reply.result->empty()
# nothing to rename
s:applyWorkspaceEdit(reply.result)
enddef
-# interface ExecuteCommandParams
+# Request the LSP server to execute a command
+# Request: workspace/executeCommand
+# Params: ExecuteCommandParams
def s:executeCommand(lspserver: dict<any>, cmd: dict<any>)
var req = lspserver.createRequest('workspace/executeCommand')
req.params->extend(cmd)
enddef
# process the 'textDocument/codeAction' reply from the LSP server
-# params: interface Command[] | interface CodeAction[]
+# Result: (Command | CodeAction)[] | null
def s:processCodeActionReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>)
if reply.result->empty()
# no action can be performed
endif
enddef
-# process the 'textDocument/selectionRange' reply from the LSP server
+# Reply: 'textDocument/selectionRange'
+# Result: SelectionRange[] | null
def s:processSelectionRangeReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>)
if reply.result->empty()
return
:normal gv
enddef
-# process the 'textDocument/foldingRange' reply from the LSP server
+# Reply: 'textDocument/foldingRange'
+# Result: FoldingRange[] | null
def s:processFoldingRangeReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>)
if reply.result->empty()
return
# keep some characters at the beginning and end (equally).
# 6 spaces are used for "..." and " ()"
var dirsz = (popupWidth - flen - 6) / 2
- dirname = dirname[:dirsz] .. '...' .. dirname[-dirsz:]
+ dirname = dirname[: dirsz] .. '...' .. dirname[-dirsz : ]
endif
var str: string = filename
if dirname != '.'
enddef
# process the 'workspace/symbol' reply from the LSP server
+# Result: SymbolInformation[] | null
def s:processWorkspaceSymbolReply(lspserver: dict<any>, req: dict<any>, reply: dict<any>)
if reply.result->empty()
return
enddef
# process a diagnostic notification message from the LSP server
-# params: interface PublishDiagnosticsParams
+# Param: PublishDiagnosticsParams
def s:processDiagNotif(lspserver: dict<any>, reply: dict<any>): void
var fname: string = LspUriToFile(reply.params.uri)
lspserver.diagsMap->extend({[fname]: diag_by_lnum})
enddef
-# process a log notification message from the LSP server
-def s:processLogMsgNotif(lspserver: dict<any>, reply: dict<any>)
- # interface LogMessageParams
+# process a show notification message from the LSP server
+# Param: ShowMessageParams
+def s:processShowMsgNotif(lspserver: dict<any>, reply: dict<any>)
var msgType: list<string> = ['', 'Error: ', 'Warning: ', 'Info: ', 'Log: ']
if reply.params.type == 4
# ignore log messages from the LSP server (too chatty)
:echomsg 'Lsp ' .. mtype .. reply.params.message
enddef
+# process a log notification message from the LSP server
+# Param: LogMessageParams
+def s:processLogMsgNotif(lspserver: dict<any>, reply: dict<any>)
+ var msgType: list<string> = ['', 'Error: ', 'Warning: ', 'Info: ', 'Log: ']
+ var mtype: string = 'Log: '
+ if reply.params.type > 0 && reply.params.type < 5
+ mtype = msgType[reply.params.type]
+ endif
+
+ TraceLog(false, '[' .. mtype .. ']: ' .. reply.params.message)
+enddef
+
# process notification messages from the LSP server
export def ProcessNotif(lspserver: dict<any>, reply: dict<any>): void
var lsp_notif_handlers: dict<func> =
{
'textDocument/publishDiagnostics': function('s:processDiagNotif'),
- 'window/showMessage': function('s:processLogMsgNotif'),
+ 'window/showMessage': function('s:processShowMsgNotif'),
'window/logMessage': function('s:processLogMsgNotif')
}
enddef
# process the workspace/applyEdit LSP server request
+# Param: ApplyWorkspaceEditParams
def s:processApplyEditReq(lspserver: dict<any>, request: dict<any>)
# interface ApplyWorkspaceEditParams
if !request->has_key('params')
vim9script
# LSP server functions
+# Refer to https://microsoft.github.io/language-server-protocol/specification
+# for the Language Server Protocol (LSP) specificaiton.
import {ProcessReply, ProcessNotif, ProcessRequest, ProcessMessages} from './handlers.vim'
import {WarnMsg, ErrMsg, ClearTraceLogs, TraceLog, LspUriToFile, LspFileToUri} from './util.vim'
return 0
enddef
-# Send a "initialize" LSP request
+# Request: 'initialize'
+# Param: InitializeParams
def s:initServer(lspserver: dict<any>)
var req = lspserver.createRequest('initialize')
enddef
# Send a "initialized" LSP notification
+# Params: InitializedParams
def s:sendInitializedNotif(lspserver: dict<any>)
var notif: dict<any> = lspserver.createNotification('initialized')
lspserver.sendMessage(notif)
enddef
-# Send a 'shutdown' request to the LSP server
+# Request: shutdown
+# Param: void
def s:shutdownServer(lspserver: dict<any>): void
var req = lspserver.createRequest('shutdown')
lspserver.sendMessage(req)
enddef
# Send a 'exit' notification to the LSP server
+# Params: void
def s:exitServer(lspserver: dict<any>): void
var notif: dict<any> = lspserver.createNotification('exit')
lspserver.sendMessage(notif)
enddef
# Send a LSP "textDocument/didOpen" notification
+# Params: DidOpenTextDocumentParams
def s:textdocDidOpen(lspserver: dict<any>, bnr: number, ftype: string): void
var notif: dict<any> = lspserver.createNotification('textDocument/didOpen')
enddef
# Send a LSP "textDocument/didChange" notification
+# Params: DidChangeTextDocumentParams
def s:textdocDidChange(lspserver: dict<any>, bnr: number, start: number,
end: number, added: number,
changes: list<dict<number>>): void
position: s:getLspPosition()}
enddef
+# Get a list of completion items.
+# Request: "textDocument/completion"
+# Param: CompletionParams
def s:getCompletion(lspserver: dict<any>): void
# Check whether LSP server supports completion
if !lspserver.caps->has_key('completionProvider')
}]}, 'a')
enddef
+# Request: "textDocument/definition"
+# Param: DefinitionParams
def s:gotoDefinition(lspserver: dict<any>): void
# Check whether LSP server supports jumping to a definition
if !lspserver.caps->has_key('definitionProvider')
lspserver.sendMessage(req)
enddef
+# Request: "textDocument/declaration"
+# Param: DeclarationParams
def s:gotoDeclaration(lspserver: dict<any>): void
# Check whether LSP server supports jumping to a declaration
if !lspserver.caps->has_key('declarationProvider')
lspserver.sendMessage(req)
enddef
+# Request: "textDocument/typeDefinition"
+# Param: TypeDefinitionParams
def s:gotoTypeDef(lspserver: dict<any>): void
# Check whether LSP server supports jumping to a type definition
if !lspserver.caps->has_key('typeDefinitionProvider')
lspserver.sendMessage(req)
enddef
+# Request: "textDocument/implementation"
+# Param: ImplementationParams
def s:gotoImplementation(lspserver: dict<any>): void
# Check whether LSP server supports jumping to a implementation
if !lspserver.caps->has_key('implementationProvider')
lspserver.sendMessage(req)
enddef
+# get symbol signature help.
+# Request: "textDocument/signatureHelp"
+# Param: SignatureHelpParams
def s:showSignature(lspserver: dict<any>): void
# Check whether LSP server supports signature help
if !lspserver.caps->has_key('signatureHelpProvider')
lspserver.sendMessage(notif)
enddef
+# get the hover information
+# Request: "textDocument/hover"
+# Param: HoverParams
def s:hover(lspserver: dict<any>): void
# Check whether LSP server supports getting hover information
if !lspserver.caps->has_key('hoverProvider')
lspserver.sendMessage(req)
enddef
+# Request: "textDocument/references"
+# Param: ReferenceParams
def s:showReferences(lspserver: dict<any>): void
# Check whether LSP server supports getting reference information
if !lspserver.caps->has_key('referencesProvider')
lspserver.sendMessage(req)
enddef
+# Request: "textDocument/documentHighlight"
+# Param: DocumentHighlightParams
def s:docHighlight(lspserver: dict<any>): void
# Check whether LSP server supports getting highlight information
if !lspserver.caps->has_key('documentHighlightProvider')
lspserver.sendMessage(req)
enddef
+# Request: "textDocument/documentSymbol"
+# Param: DocumentSymbolParams
def s:getDocSymbols(lspserver: dict<any>, fname: string): void
# Check whether LSP server supports getting document symbol information
if !lspserver.caps->has_key('documentSymbolProvider')
lspserver.sendMessage(req)
enddef
+# Request: "textDocument/formatting"
+# Param: DocumentFormattingParams
+# or
+# Request: "textDocument/rangeFormatting"
+# Param: DocumentRangeFormattingParams
def s:textDocFormat(lspserver: dict<any>, fname: string, rangeFormat: bool,
start_lnum: number, end_lnum: number)
# Check whether LSP server supports formatting documents
lspserver.sendMessage(req)
enddef
+# Request: "textDocument/rename"
+# Param: RenameParams
def s:renameSymbol(lspserver: dict<any>, newName: string)
# Check whether LSP server supports rename operation
if !lspserver.caps->has_key('renameProvider')
lspserver.sendMessage(req)
enddef
+# Request: "textDocument/codeAction"
+# Param: CodeActionParams
def s:codeAction(lspserver: dict<any>, fname_arg: string)
# Check whether LSP server supports code action operation
if !lspserver.caps->has_key('codeActionProvider')
lspserver.sendMessage(req)
enddef
-def s:workspaceSymbols(lspserver: dict<any>, sym: string): bool
+# List project-wide symbols matching query string
+# Request: "workspace/symbol"
+# Param: WorkspaceSymbolParams
+def s:workspaceQuerySymbols(lspserver: dict<any>, query: string): bool
# Check whether the LSP server supports listing workspace symbols
if !lspserver.caps->has_key('workspaceSymbolProvider')
|| !lspserver.caps.workspaceSymbolProvider
endif
var req = lspserver.createRequest('workspace/symbol')
- req.params->extend({query: sym})
+ req.params->extend({query: query})
lspserver.sendMessage(req)
return true
enddef
+# Add a workspace folder to the LSP server.
+# Request: "workspace/didChangeWorkspaceFolders"
+# Param: DidChangeWorkspaceFoldersParams
def s:addWorkspaceFolder(lspserver: dict<any>, dirName: string): void
if !lspserver.caps->has_key('workspace')
|| !lspserver.caps.workspace->has_key('workspaceFolders')
lspserver.workspaceFolders->add(dirName)
enddef
+# Remove a workspace folder from the LSP server.
+# Request: "workspace/didChangeWorkspaceFolders"
+# Param: DidChangeWorkspaceFoldersParams
def s:removeWorkspaceFolder(lspserver: dict<any>, dirName: string): void
if !lspserver.caps->has_key('workspace')
|| !lspserver.caps.workspace->has_key('workspaceFolders')
enddef
# select the text around the current cursor location
+# Request: "textDocument/selectionRange"
+# Param: SelectionRangeParams
def s:selectionRange(lspserver: dict<any>, fname: string)
# Check whether LSP server supports selection ranges
if !lspserver.caps->has_key('selectionRangeProvider')
enddef
# fold the entire document
+# Request: "textDocument/foldingRange"
+# Param: FoldingRangeParams
def s:foldRange(lspserver: dict<any>, fname: string)
# Check whether LSP server supports fold ranges
if !lspserver.caps->has_key('foldingRangeProvider')
textDocFormat: function('s:textDocFormat', [lspserver]),
renameSymbol: function('s:renameSymbol', [lspserver]),
codeAction: function('s:codeAction', [lspserver]),
- workspaceSymbols: function('s:workspaceSymbols', [lspserver]),
+ workspaceQuery: function('s:workspaceQuerySymbols', [lspserver]),
addWorkspaceFolder: function('s:addWorkspaceFolder', [lspserver]),
removeWorkspaceFolder: function('s:removeWorkspaceFolder', [lspserver]),
selectionRange: function('s:selectionRange', [lspserver]),