var kindName = kindMap[kind]
var kindValue = defaultKinds[kindName]
- if opt.lspOptions.customCompletionKinds
- && opt.lspOptions.completionKinds->has_key(kindName)
- kindValue = opt.lspOptions.completionKinds[kindName]
+ var lspOpts = opt.lspOptions
+ if lspOpts.customCompletionKinds &&
+ lspOpts.completionKinds->has_key(kindName)
+ kindValue = lspOpts.completionKinds[kindName]
endif
return kindValue
endif
endfor
# Check every 200 lines if timeout is exceeded
- if timeout > 0 && linenr % 200 == 0 && start->reltime()->reltimefloat() * 1000 > timeout
+ if timeout > 0 && linenr % 200 == 0 &&
+ start->reltime()->reltimefloat() * 1000 > timeout
break
endif
linenr += 1
lspserver.completeItemsIsIncomplete = cItems->get('isIncomplete', false)
endif
+ var lspOpts = opt.lspOptions
+
# Get the keyword prefix before the current cursor column.
var chcol = charcol('.')
var starttext = chcol == 1 ? '' : getline('.')[ : chcol - 2]
var [prefix, start_idx, end_idx] = starttext->matchstrpos('\k*$')
- if opt.lspOptions.completionMatcher == 'icase'
+ if lspOpts.completionMatcherValue == opt.COMPLETIONMATCHER_ICASE
prefix = prefix->tolower()
endif
var start_col = start_idx + 1
- if opt.lspOptions.ultisnipsSupport
+ if lspOpts.ultisnipsSupport
snippet.CompletionUltiSnips(prefix, items)
- elseif opt.lspOptions.vsnipSupport
+ elseif lspOpts.vsnipSupport
snippet.CompletionVsnip(items)
endif
- if opt.lspOptions.useBufferCompletion
+ if lspOpts.useBufferCompletion
CompletionFromBuffer(items)
endif
for item in items
var d: dict<any> = {}
- # TODO: Add proper support for item.textEdit.newText and item.textEdit.range
- # Keep in mind that item.textEdit.range can start be way before the typed
- # keyword.
- if item->has_key('textEdit') && opt.lspOptions.completionMatcher != 'fuzzy'
+ # TODO: Add proper support for item.textEdit.newText and
+ # item.textEdit.range Keep in mind that item.textEdit.range can start be
+ # way before the typed keyword.
+ if item->has_key('textEdit') &&
+ lspOpts.completionMatcherValue != opt.COMPLETIONMATCHER_FUZZY
var start_charcol: number
- if prefix != ''
+ if !prefix->empty()
start_charcol = charidx(starttext, start_idx) + 1
else
start_charcol = chcol
# snippet completion. Needs a snippet plugin to expand the snippet.
# Remove all the snippet placeholders
d.word = MakeValidWord(d.word)
- elseif !lspserver.completeItemsIsIncomplete || opt.lspOptions.useBufferCompletion
+ elseif !lspserver.completeItemsIsIncomplete || lspOpts.useBufferCompletion
# Filter items only when "isIncomplete" is set (otherwise server would
# have done the filtering) or when buffer completion is enabled
# plain text completion
- if prefix != ''
+ if !prefix->empty()
# If the completion item text doesn't start with the current (case
# ignored) keyword prefix, skip it.
var filterText: string = item->get('filterText', d.word)
- if opt.lspOptions.completionMatcher == 'icase'
+ if lspOpts.completionMatcherValue == opt.COMPLETIONMATCHER_ICASE
if filterText->tolower()->stridx(prefix) != 0
continue
endif
# If the completion item text doesn't fuzzy match with the current
# keyword prefix, skip it.
- elseif opt.lspOptions.completionMatcher == 'fuzzy'
+ elseif lspOpts.completionMatcherValue == opt.COMPLETIONMATCHER_FUZZY
if matchfuzzy([filterText], prefix)->empty()
continue
endif
d.abbr = item.label
d.dup = 1
- if opt.lspOptions.completionMatcher == 'icase'
+ if lspOpts.completionMatcherValue == opt.COMPLETIONMATCHER_ICASE
d.icase = 1
endif
if lspserver.completionLazyDoc
d.info = 'Lazy doc'
else
- if item->has_key('detail') && item.detail != ''
+ if item->has_key('detail') && !item.detail->empty()
# Solve a issue where if a server send a detail field
# with a "\n", on the menu will be everything joined with
# a "^@" separating it. (example: clangd)
d.menu = item.detail->split("\n")[0]
endif
if item->has_key('documentation')
- if item.documentation->type() == v:t_string && item.documentation != ''
- d.info = item.documentation
- elseif item.documentation->type() == v:t_dict
- && item.documentation.value->type() == v:t_string
- d.info = item.documentation.value
+ var itemDoc = item.documentation
+ if itemDoc->type() == v:t_string && !itemDoc->empty()
+ d.info = itemDoc
+ elseif itemDoc->type() == v:t_dict
+ && itemDoc.value->type() == v:t_string
+ d.info = itemDoc.value
endif
endif
endif
completeItems->add(d)
endfor
- if opt.lspOptions.completionMatcher != 'fuzzy'
+ if lspOpts.completionMatcherValue != opt.COMPLETIONMATCHER_FUZZY
# Lexographical sort (case-insensitive).
completeItems->sort((a, b) =>
a.score == b.score ? 0 : a.score >? b.score ? 1 : -1)
endif
- if opt.lspOptions.autoComplete && !lspserver.omniCompletePending
+ if lspOpts.autoComplete && !lspserver.omniCompletePending
if completeItems->empty()
# no matches
return
if !infoText->empty()
infoText->extend(['- - -'])
endif
- if cItem.documentation->type() == v:t_dict
+ var cItemDoc = cItem.documentation
+ if cItemDoc->type() == v:t_dict
# MarkupContent
- if cItem.documentation.kind == 'plaintext'
- infoText->extend(cItem.documentation.value->split("\n"))
+ if cItemDoc.kind == 'plaintext'
+ infoText->extend(cItemDoc.value->split("\n"))
infoKind = 'text'
- elseif cItem.documentation.kind == 'markdown'
- infoText->extend(cItem.documentation.value->split("\n"))
+ elseif cItemDoc.kind == 'markdown'
+ infoText->extend(cItemDoc.value->split("\n"))
infoKind = 'lspgfm'
else
- util.ErrMsg($'Unsupported documentation type ({cItem.documentation.kind})')
+ util.ErrMsg($'Unsupported documentation type ({cItemDoc.kind})')
return
endif
- elseif cItem.documentation->type() == v:t_string
- infoText->extend(cItem.documentation->split("\n"))
+ elseif cItemDoc->type() == v:t_string
+ infoText->extend(cItemDoc->split("\n"))
else
- util.ErrMsg($'Unsupported documentation ({cItem.documentation->string()})')
+ util.ErrMsg($'Unsupported documentation ({cItemDoc->string()})')
return
endif
endif
return res
endif
- if opt.lspOptions.completionMatcher == 'fuzzy'
+ var lspOpts = opt.lspOptions
+ if lspOpts.completionMatcherValue == opt.COMPLETIONMATCHER_FUZZY
return res->matchfuzzy(prefix, { key: 'word' })
endif
- if opt.lspOptions.completionMatcher == 'icase'
- return res->filter((i, v) => v.word->tolower()->stridx(prefix->tolower()) == 0)
+ if lspOpts.completionMatcherValue == opt.COMPLETIONMATCHER_ICASE
+ return res->filter((i, v) =>
+ v.word->tolower()->stridx(prefix->tolower()) == 0)
endif
return res->filter((i, v) => v.word->stridx(prefix) == 0)
else
setbufvar(bnr, '&completeopt', 'menuone,popup,noinsert,noselect')
endif
- setbufvar(bnr, '&completepopup', 'width:80,highlight:Pmenu,align:item,border:off')
+ setbufvar(bnr, '&completepopup',
+ 'width:80,highlight:Pmenu,align:item,border:off')
# <Enter> in insert mode stops completion and inserts a <Enter>
if !opt.lspOptions.noNewlineInCompletion
:inoremap <expr> <buffer> <CR> pumvisible() ? "\<C-Y>\<CR>" : "\<CR>"
{highlight: 'LspDiagVirtualTextHint', override: true})
if opt.lspOptions.aleSupport
- autocmd_add([{group: 'LspAleCmds', event: 'User', pattern: 'ALEWantResults', cmd: 'AleHook(g:ale_want_results_buffer)'}])
+ autocmd_add([
+ {
+ group: 'LspAleCmds',
+ event: 'User',
+ pattern: 'ALEWantResults',
+ cmd: 'AleHook(g:ale_want_results_buffer)'
+ }
+ ])
endif
enddef
enddef
def DiagSevToSymbolText(severity: number): string
+ var lspOpts = opt.lspOptions
var typeMap: list<string> = [
- opt.lspOptions.diagSignErrorText,
- opt.lspOptions.diagSignWarningText,
- opt.lspOptions.diagSignInfoText,
- opt.lspOptions.diagSignHintText
+ lspOpts.diagSignErrorText,
+ lspOpts.diagSignWarningText,
+ lspOpts.diagSignInfoText,
+ lspOpts.diagSignHintText
]
if severity > 4
- return opt.lspOptions.diagSignHintText
+ return lspOpts.diagSignHintText
endif
return typeMap[severity - 1]
enddef
# Remove signs and text properties for diagnostics in buffer
def RemoveDiagVisualsForBuffer(bnr: number)
- if opt.lspOptions.showDiagWithSign
+ var lspOpts = opt.lspOptions
+ if lspOpts.showDiagWithSign
# Remove all the existing diagnostic signs
sign_unplace('LSPDiag', {buffer: bnr})
endif
- if opt.lspOptions.showDiagWithVirtualText
+ if lspOpts.showDiagWithVirtualText
# Remove all the existing virtual text
prop_remove({type: 'LspDiagVirtualTextError', bufnr: bnr, all: true})
prop_remove({type: 'LspDiagVirtualTextWarning', bufnr: bnr, all: true})
prop_remove({type: 'LspDiagVirtualTextHint', bufnr: bnr, all: true})
endif
- if opt.lspOptions.highlightDiagInline
+ if lspOpts.highlightDiagInline
# Remove all the existing virtual text
prop_remove({type: 'LspDiagInlineError', bufnr: bnr, all: true})
prop_remove({type: 'LspDiagInlineWarning', bufnr: bnr, all: true})
var diag_align: string = 'above'
var diag_wrap: string = 'truncate'
var diag_symbol: string = '┌─'
+ var lspOpts = opt.lspOptions
- if opt.lspOptions.diagVirtualTextAlign == 'below'
+ if lspOpts.diagVirtualTextAlign == 'below'
diag_align = 'below'
diag_wrap = 'truncate'
diag_symbol = '└─'
- elseif opt.lspOptions.diagVirtualTextAlign == 'after'
+ elseif lspOpts.diagVirtualTextAlign == 'after'
diag_align = 'after'
diag_wrap = 'wrap'
diag_symbol = 'E>'
var signs: list<dict<any>> = []
var diags: list<dict<any>> = diagsMap[bnr].sortedDiagnostics
for diag in diags
- # TODO: prioritize most important severity if there are multiple diagnostics
- # from the same line
- var d_start = diag.range.start
- var d_end = diag.range.end
+ # TODO: prioritize most important severity if there are multiple
+ # diagnostics from the same line
+ var d_range = diag.range
+ var d_start = d_range.start
+ var d_end = d_range.end
var lnum = d_start.line + 1
- if opt.lspOptions.showDiagWithSign
+ if lspOpts.showDiagWithSign
signs->add({id: 0, buffer: bnr, group: 'LSPDiag',
lnum: lnum, name: DiagSevToSignName(diag.severity),
priority: 10 - diag.severity})
endif
try
- if opt.lspOptions.highlightDiagInline
+ if lspOpts.highlightDiagInline
prop_add(lnum, util.GetLineByteFromPos(bnr, d_start) + 1,
{end_lnum: d_end.line + 1,
end_col: util.GetLineByteFromPos(bnr, d_end) + 1,
type: DiagSevToInlineHLName(diag.severity)})
endif
- if opt.lspOptions.showDiagWithVirtualText
+ if lspOpts.showDiagWithVirtualText
var padding: number
var symbol: string = diag_symbol
text_padding_left: padding})
endif
catch /E966\|E964/ # Invalid lnum | Invalid col
- # Diagnostics arrive asynchronous and the document changed while they wore
- # send. Ignore this as new once will arrive shortly.
+ # Diagnostics arrive asynchronous and the document changed while they
+ # wore send. Ignore this as new once will arrive shortly.
endtry
endfor
- if opt.lspOptions.showDiagWithSign
+ if lspOpts.showDiagWithSign
signs->sign_placelist()
endif
enddef
export def ProcessNewDiags(bnr: number)
DiagsUpdateLocList(bnr)
- if opt.lspOptions.aleSupport
+ var lspOpts = opt.lspOptions
+ if lspOpts.aleSupport
SendAleDiags(bnr, -1)
return
- elseif !opt.lspOptions.autoHighlightDiags
+ elseif !lspOpts.autoHighlightDiags
return
endif
var diags = diagsMap[bnr].sortedDiagnostics
for diag in diags
- var d_start = diag.range.start
- var d_end = diag.range.end
+ var d_range = diag.range
+ var d_start = d_range.start
+ var d_end = d_range.end
text = diag.message->substitute("\n\\+", "\n", 'g')
qflist->add({filename: fname,
lnum: d_start.line + 1,
# Notification: textDocument/publishDiagnostics
# Param: PublishDiagnosticsParams
def ProcessDiagNotif(lspserver: dict<any>, reply: dict<any>): void
- diag.DiagNotification(lspserver, reply.params.uri, reply.params.diagnostics)
+ var params = reply.params
+ diag.DiagNotification(lspserver, params.uri, params.diagnostics)
enddef
# Convert LSP message type to a string
# Notification: window/showMessage
# Param: ShowMessageParams
def ProcessShowMsgNotif(lspserver: dict<any>, reply: dict<any>)
- if reply.params.type >= 4
+ var msgType = reply.params.type
+ if msgType >= 4
# ignore log messages from the LSP server (too chatty)
# TODO: Add a configuration to control the message level that will be
# displayed. Also store these messages and provide a command to display
# them.
return
endif
- if reply.params.type == 1
+ if msgType == 1
util.ErrMsg($'Lsp({lspserver.name}) {reply.params.message}')
- elseif reply.params.type == 2
+ elseif msgType == 2
util.WarnMsg($'Lsp({lspserver.name}) {reply.params.message}')
- elseif reply.params.type == 3
+ elseif msgType == 3
util.InfoMsg($'Lsp({lspserver.name}) {reply.params.message}')
endif
enddef
# Notification: window/logMessage
# Param: LogMessageParams
def ProcessLogMsgNotif(lspserver: dict<any>, reply: dict<any>)
- var mtype = LspMsgTypeToString(reply.params.type)
- lspserver.addMessage(mtype, reply.params.message)
+ var params = reply.params
+ var mtype = LspMsgTypeToString(params.type)
+ lspserver.addMessage(mtype, params.message)
enddef
# process the log trace notification messages
# create a LSP server request message
def CreateRequest(lspserver: dict<any>, method: string): dict<any>
- var req = {}
- req.jsonrpc = '2.0'
- req.id = lspserver.nextReqID()
- req.method = method
- req.params = {}
+ var req = {
+ jsonrpc: '2.0',
+ id: lspserver.nextReqID(),
+ method: method,
+ params: {}
+ }
# Save the request, so that the corresponding response can be processed
lspserver.requests->extend({[req.id->string()]: req})
# create a LSP server response message
def CreateResponse(lspserver: dict<any>, req_id: number): dict<any>
- var resp = {}
- resp.jsonrpc = '2.0'
- resp.id = req_id
-
+ var resp = {
+ jsonrpc: '2.0',
+ id: req_id
+ }
return resp
enddef
# create a LSP server notification message
def CreateNotification(lspserver: dict<any>, notif: string): dict<any>
- var req = {}
- req.jsonrpc = '2.0'
- req.method = notif
- req.params = {}
+ var req = {
+ jsonrpc: '2.0',
+ method: notif,
+ params: {}
+ }
return req
enddef
def TextdocDidOpen(lspserver: dict<any>, bnr: number, ftype: string): void
# Notification: 'textDocument/didOpen'
# Params: DidOpenTextDocumentParams
- var tdi = {}
- tdi.uri = util.LspBufnrToUri(bnr)
- tdi.languageId = ftype
- tdi.version = 1
- tdi.text = bnr->getbufline(1, '$')->join("\n") .. "\n"
- var params = {textDocument: tdi}
+ var params = {
+ textDocument: {
+ uri: util.LspBufnrToUri(bnr),
+ languageId: ftype,
+ version: 1,
+ text: bnr->getbufline(1, '$')->join("\n") .. "\n"
+ }
+ }
lspserver.sendNotification('textDocument/didOpen', params)
enddef
def TextdocDidClose(lspserver: dict<any>, bnr: number): void
# Notification: 'textDocument/didClose'
# Params: DidCloseTextDocumentParams
- var tdid = {}
- tdid.uri = util.LspBufnrToUri(bnr)
- var params = {textDocument: tdid}
+ var params = {
+ textDocument: {
+ uri: util.LspBufnrToUri(bnr)
+ }
+ }
lspserver.sendNotification('textDocument/didClose', params)
enddef
changes: list<dict<number>>): void
# Notification: 'textDocument/didChange'
# Params: DidChangeTextDocumentParams
- var vtdid: dict<any> = {}
- vtdid.uri = util.LspBufnrToUri(bnr)
- # Use Vim 'changedtick' as the LSP document version number
- vtdid.version = bnr->getbufvar('changedtick')
var changeset: list<dict<any>>
# endfor
changeset->add({text: bnr->getbufline(1, '$')->join("\n") .. "\n"})
- var params = {textDocument: vtdid, contentChanges: changeset}
+ var params = {
+ textDocument: {
+ uri: util.LspBufnrToUri(bnr),
+ # Use Vim 'changedtick' as the LSP document version number
+ version: bnr->getbufvar('changedtick')
+ },
+ contentChanges: changeset
+ }
lspserver.sendNotification('textDocument/didChange', params)
enddef
# Param: TextDocumentIdentifier
# Clangd specific extension
def SwitchSourceHeader(lspserver: dict<any>)
- var param = {}
- param.uri = util.LspFileToUri(@%)
+ var param = {
+ uri: util.LspFileToUri(@%)
+ }
var reply = lspserver.rpc('textDocument/switchSourceHeader', param)
if reply->empty() || reply.result->empty()
util.WarnMsg('Source/Header file is not found')
propName = 'LspTextRef'
endif
try
- var docHL_start = docHL.range.start
- var docHL_end = docHL.range.end
+ var docHL_range = docHL.range
+ var docHL_start = docHL_range.start
+ var docHL_end = docHL_range.end
prop_add(docHL_start.line + 1,
util.GetLineByteFromPos(bnr, docHL_start) + 1,
{end_lnum: docHL_end.line + 1,
# interface DocumentFormattingParams
# interface TextDocumentIdentifier
# interface FormattingOptions
- var param = {}
- param.textDocument = {uri: util.LspFileToUri(fname)}
var fmtopts: dict<any> = {
tabSize: shiftwidth(),
insertSpaces: &expandtab ? true : false,
}
- param.options = fmtopts
+ var param = {
+ textDocument: {
+ uri: util.LspFileToUri(fname)
+ },
+ options: fmtopts
+ }
if rangeFormat
var r: dict<dict<number>> = {
callhier.IncomingCalls(lspserver)
enddef
-def GetIncomingCalls(lspserver: dict<any>, item: dict<any>): any
+def GetIncomingCalls(lspserver: dict<any>, item_arg: dict<any>): any
# Request: "callHierarchy/incomingCalls"
# Param: CallHierarchyIncomingCallsParams
- var param = {}
- param.item = item
+ var param = {
+ item: item_arg
+ }
var reply = lspserver.rpc('callHierarchy/incomingCalls', param)
if reply->empty()
return null
if lspserver.needOffsetEncoding
# Decode the position encoding in all the incoming call locations
- var bnr = util.LspUriToBufnr(item.uri)
+ var bnr = util.LspUriToBufnr(item_arg.uri)
reply.result->map((_, hierItem) => {
lspserver.decodeRange(bnr, hierItem.from.range)
return hierItem
callhier.OutgoingCalls(lspserver)
enddef
-def GetOutgoingCalls(lspserver: dict<any>, item: dict<any>): any
+def GetOutgoingCalls(lspserver: dict<any>, item_arg: dict<any>): any
# Request: "callHierarchy/outgoingCalls"
# Param: CallHierarchyOutgoingCallsParams
- var param = {}
- param.item = item
+ var param = {
+ item: item_arg
+ }
var reply = lspserver.rpc('callHierarchy/outgoingCalls', param)
if reply->empty()
return null
if lspserver.needOffsetEncoding
# Decode the position encoding in all the outgoing call locations
- var bnr = util.LspUriToBufnr(item.uri)
+ var bnr = util.LspUriToBufnr(item_arg.uri)
reply.result->map((_, hierItem) => {
lspserver.decodeRange(bnr, hierItem.to.range)
return hierItem
endif
# Param: WorkspaceSymbolParams
- var param = {}
- param.query = query
+ var param = {
+ query: query
+ }
var reply = lspserver.rpc('workspace/symbol', param)
if reply->empty() || reply.result->empty()
util.WarnMsg($'Symbol "{query}" is not found')
# interface SelectionRangeParams
# interface TextDocumentIdentifier
- var param = {}
- param.textDocument = {}
- param.textDocument.uri = util.LspFileToUri(fname)
- param.positions = [lspserver.getPosition(false)]
+ var param = {
+ textDocument: {
+ uri: util.LspFileToUri(fname)
+ },
+ positions: [lspserver.getPosition(false)]
+ }
var reply = lspserver.rpc('textDocument/selectionRange', param)
if reply->empty() || reply.result->empty()
completionKinds: {}
}
+export const COMPLETIONMATCHER_CASE = 1
+export const COMPLETIONMATCHER_ICASE = 2
+export const COMPLETIONMATCHER_FUZZY = 3
+
# set the LSP plugin options from the user provided option values
export def OptionsSet(opts: dict<any>)
lspOptions->extend(opts)
if !has('patch-9.0.1157')
lspOptions.showDiagWithVirtualText = false
endif
+
+ # For faster comparison, convert the 'completionMatcher' option value from a
+ # string to a number.
+ lspOptions.completionMatcherValue = COMPLETIONMATCHER_CASE
+ if lspOptions.completionMatcher == 'icase'
+ lspOptions.completionMatcherValue = COMPLETIONMATCHER_ICASE
+ elseif lspOptions.completionMatcher == 'fuzzy'
+ lspOptions.completionMatcherValue = COMPLETIONMATCHER_FUZZY
+ endif
enddef
# return a copy of the LSP plugin options