README.md | 16 ++++++++-------- autoload/lsp/diag.vim | 88 +++++++++++++++++++++++++++++++++++++++-------------- autoload/lsp/inlayhints.vim | 12 +++++++----- autoload/lsp/lsp.vim | 94 +++++++++++++++++++++++++++++++++++++++++++++-------- autoload/lsp/options.vim | 2 +- doc/lsp.txt | 95 +++++++++++++++++++++++++++-------------------------- plugin/lsp.vim | 17 ++++++++--------- test/clangd_offsetencoding.vim | 6 +++--- test/clangd_tests.vim | 246 ++++++++++++++++++++++++++++++++++++++--------------- test/common.vim | 2 +- diff --git a/README.md b/README.md index 4657d6ce56cee81ae699c6ae931a87fb9830875d..106a2b2aad0caa99ed6437e0665a1fe5da83162b 100644 --- a/README.md +++ b/README.md @@ -167,14 +167,14 @@ Command|Description -------|----------- :LspCodeAction|Apply the code action supplied by the language server to the diagnostic in the current line. :LspCodeLens|Display a list of code lens commands and apply a selected code lens command to the current file. -:LspDiagCurrent|Display the diagnostic message for the current line. -:LspDiagFirst|Jump to the first diagnostic message for the current buffer. -:LspDiagHere|Jump to the next diagnostic message in the current line. -:LspDiagHighlightDisable|Disable diagnostic message highlights. -:LspDiagHighlightEnable|Enable diagnostic message highlights. -:LspDiagNext|Jump to the next diagnostic message after the current position. -:LspDiagPrev|Jump to the previous diagnostic message before the current position. -:LspDiagShow|Display the diagnostics messages from the language server for the current buffer in a new location list. +:LspDiag current|Display the diagnostic message for the current line. +:LspDiag first|Jump to the first diagnostic message for the current buffer. +:LspDiag here|Jump to the next diagnostic message in the current line. +:LspDiag highlight disable|Disable diagnostic message highlights. +:LspDiag highlight enable|Enable diagnostic message highlights. +:LspDiag next|Jump to the next diagnostic message after the current position. +:LspDiag prev|Jump to the previous diagnostic message before the current position. +:LspDiag show|Display the diagnostics messages from the language server for the current buffer in a new location list. :LspDocumentSymbol|Display the symbols in the current file in a popup menu and jump to the selected symbol. :LspFold|Fold the current file. :LspFormat|Format a range of lines in the current file using the language server. The **shiftwidth** and **expandtab** values set for the current buffer are used when format is applied. The default range is the entire file. diff --git a/autoload/lsp/diag.vim b/autoload/lsp/diag.vim index 263c07c8d35a826432362fb9a39f3c3f4c6bb59b..a17598abe6cd03d5ad1b5d3e5d6ef2b753eb666f 100644 --- a/autoload/lsp/diag.vim +++ b/autoload/lsp/diag.vim @@ -100,6 +100,12 @@ {highlight: 'LspDiagVirtualTextInfo', override: true}) prop_type_add('LspDiagVirtualTextHint', {highlight: 'LspDiagVirtualTextHint', override: true}) + autocmd_add([{group: 'LspOptionsChanged', + event: 'User', + pattern: '*', + cmd: 'LspDiagsOptionsChanged()'}]) + + # ALE plugin support if opt.lspOptions.aleSupport autocmd_add([ { @@ -182,14 +188,14 @@ return typeMap[severity - 1] enddef # Remove signs and text properties for diagnostics in buffer -def RemoveDiagVisualsForBuffer(bnr: number) +def RemoveDiagVisualsForBuffer(bnr: number, all: bool = false) var lspOpts = opt.lspOptions - if lspOpts.showDiagWithSign + if lspOpts.showDiagWithSign || all # Remove all the existing diagnostic signs sign_unplace('LSPDiag', {buffer: bnr}) endif - if lspOpts.showDiagWithVirtualText + if lspOpts.showDiagWithVirtualText || all # Remove all the existing virtual text prop_remove({type: 'LspDiagVirtualTextError', bufnr: bnr, all: true}) prop_remove({type: 'LspDiagVirtualTextWarning', bufnr: bnr, all: true}) @@ -197,7 +203,7 @@ prop_remove({type: 'LspDiagVirtualTextInfo', bufnr: bnr, all: true}) prop_remove({type: 'LspDiagVirtualTextHint', bufnr: bnr, all: true}) endif - if lspOpts.highlightDiagInline + if lspOpts.highlightDiagInline || all # Remove all the existing virtual text prop_remove({type: 'LspDiagInlineError', bufnr: bnr, all: true}) prop_remove({type: 'LspDiagInlineWarning', bufnr: bnr, all: true}) @@ -208,10 +214,10 @@ enddef # Refresh the placed diagnostics in buffer "bnr" # This inline signs, inline props, and virtual text diagnostics -export def DiagsRefresh(bnr: number) +export def DiagsRefresh(bnr: number, all: bool = false) :silent! bnr->bufload() - RemoveDiagVisualsForBuffer(bnr) + RemoveDiagVisualsForBuffer(bnr, all) if !diagsMap->has_key(bnr) || diagsMap[bnr].sortedDiagnostics->empty() @@ -481,7 +487,7 @@ var LspQfId: number = bnr->getbufvar('LspQfId', 0) if LspQfId == 0 && !opt.lspOptions.autoPopulateDiags && !calledByCmd # Diags location list is not present. Create the location list only if - # the 'autoPopulateDiags' option is set or the :LspDiagShow command is + # the 'autoPopulateDiags' option is set or the ":LspDiag show" command is # invoked. return false endif @@ -777,33 +783,69 @@ util.WarnMsg('No more diagnostics found') endif enddef -# Disable the LSP diagnostics highlighting in all the buffers -export def DiagsHighlightDisable() - # turn off all diags highlight - opt.lspOptions.autoHighlightDiags = false - for binfo in getbufinfo({bufloaded: true}) - RemoveDiagVisualsForBuffer(binfo.bufnr) - endfor +# Return the sorted diagnostics for buffer "bnr".  Default is the current +# buffer.  A copy of the diagnostics is returned so that the caller can modify +# the diagnostics. +export def GetDiagsForBuf(bnr: number = bufnr()): list> + if !diagsMap->has_key(bnr) || + diagsMap[bnr].sortedDiagnostics->empty() + return [] + endif + + return diagsMap[bnr].sortedDiagnostics->deepcopy() enddef + +# Track the current diagnostics auto highlight enabled/disabled state. Used +# when the "autoHighlightDiags" option value is changed. +var save_autoHighlightDiags = opt.lspOptions.autoHighlightDiags +var save_highlightDiagInline = opt.lspOptions.highlightDiagInline +var save_showDiagWithSign = opt.lspOptions.showDiagWithSign +var save_showDiagWithVirtualText = opt.lspOptions.showDiagWithVirtualText # Enable the LSP diagnostics highlighting export def DiagsHighlightEnable() opt.lspOptions.autoHighlightDiags = true + save_autoHighlightDiags = true for binfo in getbufinfo({bufloaded: true}) - DiagsRefresh(binfo.bufnr) + if diagsMap->has_key(binfo.bufnr) + DiagsRefresh(binfo.bufnr) + endif endfor enddef -# Return the sorted diagnostics for buffer "bnr".  Default is the current -# buffer.  A copy of the diagnostics is returned so that the caller can modify -# the diagnostics. -export def GetDiagsForBuf(bnr: number = bufnr()): list> - if !diagsMap->has_key(bnr) || - diagsMap[bnr].sortedDiagnostics->empty() - return [] +# Disable the LSP diagnostics highlighting in all the buffers +export def DiagsHighlightDisable() + # turn off all diags highlight + opt.lspOptions.autoHighlightDiags = false + save_autoHighlightDiags = false + for binfo in getbufinfo() + if diagsMap->has_key(binfo.bufnr) + RemoveDiagVisualsForBuffer(binfo.bufnr) + endif + endfor +enddef + +# Some options are changed. If 'autoHighlightDiags' option is changed, then +# either enable or disable diags auto highlight. +export def LspDiagsOptionsChanged() + if save_autoHighlightDiags && !opt.lspOptions.autoHighlightDiags + DiagsHighlightDisable() + elseif !save_autoHighlightDiags && opt.lspOptions.autoHighlightDiags + DiagsHighlightEnable() endif - return diagsMap[bnr].sortedDiagnostics->deepcopy() + if save_highlightDiagInline != opt.lspOptions.highlightDiagInline + || save_showDiagWithSign != opt.lspOptions.showDiagWithSign + || save_showDiagWithVirtualText != opt.lspOptions.showDiagWithVirtualText + save_highlightDiagInline = opt.lspOptions.highlightDiagInline + save_showDiagWithSign = opt.lspOptions.showDiagWithSign + save_showDiagWithVirtualText = opt.lspOptions.showDiagWithVirtualText + for binfo in getbufinfo({bufloaded: true}) + if diagsMap->has_key(binfo.bufnr) + DiagsRefresh(binfo.bufnr, true) + endif + endfor + endif enddef # vim: tabstop=8 shiftwidth=2 softtabstop=2 diff --git a/autoload/lsp/inlayhints.vim b/autoload/lsp/inlayhints.vim index bff0e93ea34e741b45ceefd3db689d97ec1fdf91..1bbd331dec38e9b1aa1bcc1a26b6e88441d8d4c5 100644 --- a/autoload/lsp/inlayhints.vim +++ b/autoload/lsp/inlayhints.vim @@ -160,7 +160,9 @@ autocmd_add(acmds) enddef -var inlayHintsEnabled = opt.lspOptions.showInlayHints +# Track the current inlay hints enabled/disabled state. Used when the +# "showInlayHints" option value is changed. +var save_showInlayHints = opt.lspOptions.showInlayHints # Enable inlay hints. For all the buffers with an attached language server # that supports inlay hints, refresh the inlay hints. @@ -180,7 +182,7 @@ BufferInit(lspserver, binfo.bufnr) LspInlayHintsUpdateNow(binfo.bufnr) endfor endfor - inlayHintsEnabled = true + save_showInlayHints = true enddef # Disable inlay hints for the current Vim session. Clear the inlay hints in @@ -196,15 +198,15 @@ LspInlayHintsUpdateStop(binfo.bufnr) :silent! autocmd_delete([{bufnr: binfo.bufnr, group: 'LspInlayHints'}]) InlayHintsClear(binfo.bufnr) endfor - inlayHintsEnabled = false + save_showInlayHints = false enddef # Some options are changed. If 'showInlayHints' option is changed, then # either enable or disable inlay hints. export def LspInlayHintsOptionsChanged() - if inlayHintsEnabled && !opt.lspOptions.showInlayHints + if save_showInlayHints && !opt.lspOptions.showInlayHints InlayHintsDisable() - elseif !inlayHintsEnabled && opt.lspOptions.showInlayHints + elseif !save_showInlayHints && opt.lspOptions.showInlayHints InlayHintsEnable() endif enddef diff --git a/autoload/lsp/lsp.vim b/autoload/lsp/lsp.vim index 66387ca3051a5e3b52c2bb8e62d8fe3d056181d0..a2e0e83177ff470a9fff7e688879166c513ec282 100644 --- a/autoload/lsp/lsp.vim +++ b/autoload/lsp/lsp.vim @@ -543,7 +543,9 @@ lspserver.textdocDidChange(bnr, 0, 0, 0, []) endif endfor # Refresh the displayed diags visuals - diag.DiagsRefresh(bnr) + if opt.lspOptions.autoHighlightDiags + diag.DiagsRefresh(bnr) + endif enddef # Stop all the LSP servers @@ -1162,8 +1164,9 @@ export def RegisterCmdHandler(cmd: string, Handler: func) codeaction.RegisterCmdHandler(cmd, Handler) enddef -# Command-line completion for the ":LspServer " sub command -def LspServerSubCmdComplete(cmds: list, arglead: string, cmdline: string, cursorPos: number): list +# Command-line completion for the ":LspServer " and ":LspDiag " sub +# commands +def LspSubCmdComplete(cmds: list, arglead: string, cmdline: string, cursorPos: number): list var wordBegin = cmdline->match('\s\+\zs\S', cursorPos) if wordBegin == -1 return cmds @@ -1178,23 +1181,88 @@ return [] enddef +# Command-line completion for the ":LspDiag highlight" command +def LspDiagHighlightComplete(arglead: string, cmdline: string, cursorPos: number): list + return LspSubCmdComplete(['enable', 'disable'], arglead, cmdline, cursorPos) +enddef + +# Command-line completion for the ":LspDiag" command +export def LspDiagComplete(arglead: string, cmdline: string, cursorPos: number): list + var wordBegin = -1 + var wordEnd = -1 + var l = ['first', 'current', 'here', 'highlight', 'last', 'next', 'prev', + 'show'] + + # Skip the command name + var i = cmdline->stridx(' ', 0) + wordBegin = cmdline->match('\s\+\zs\S', i) + if wordBegin == -1 + return l + endif + + wordEnd = cmdline->stridx(' ', wordBegin) + if wordEnd == -1 + return filter(l, (_, val) => val =~ $'^{arglead}') + endif + + var cmd = cmdline->strpart(wordBegin, wordEnd - wordBegin) + if cmd == 'highlight' + return LspDiagHighlightComplete(arglead, cmdline, wordEnd) + endif + + return [] +enddef + +# ":LspDiag" command handler +export def LspDiagCmd(args: string, cmdCount: number, force: bool) + if args->stridx('highlight') == 0 + if args[9] == ' ' + var subcmd = args[10 : ]->trim() + if subcmd == 'enable' + diag.DiagsHighlightEnable() + elseif subcmd == 'disable' + diag.DiagsHighlightDisable() + else + util.ErrMsg($':LspDiag highlight - Unsupported argument "{subcmd}"') + endif + else + util.ErrMsg('Argument required for ":LspDiag highlight"') + endif + elseif args == 'first' + diag.LspDiagsJump('first', 0) + elseif args == 'current' + LspShowCurrentDiag(force) + elseif args == 'here' + diag.LspDiagsJump('here', 0) + elseif args == 'last' + diag.LspDiagsJump('last', 0) + elseif args == 'next' + diag.LspDiagsJump('next', cmdCount) + elseif args == 'prev' + diag.LspDiagsJump('prev', cmdCount) + elseif args == 'show' + ShowDiagnostics() + else + util.ErrMsg($':LspDiag - Unsupported argument "{args}"') + endif +enddef + # Command-line completion for the ":LspServer debug" command def LspServerDebugComplete(arglead: string, cmdline: string, cursorPos: number): list - return LspServerSubCmdComplete(['errors', 'messages', 'off', 'on'], - arglead, cmdline, cursorPos) + return LspSubCmdComplete(['errors', 'messages', 'off', 'on'], arglead, + cmdline, cursorPos) enddef # Command-line completion for the ":LspServer show" command def LspServerShowComplete(arglead: string, cmdline: string, cursorPos: number): list - return LspServerSubCmdComplete(['capabilities', 'initializeRequest', - 'messages', 'status'], arglead, cmdline, - cursorPos) + return LspSubCmdComplete(['capabilities', 'initializeRequest', 'messages', + 'status'], arglead, cmdline, cursorPos) enddef # Command-line completion for the ":LspServer trace" command def LspServerTraceComplete(arglead: string, cmdline: string, cursorPos: number): list - return LspServerSubCmdComplete(['messages', 'off', 'verbose'], - arglead, cmdline, cursorPos) + return LspSubCmdComplete(['messages', 'off', 'verbose'], arglead, cmdline, + cursorPos) enddef # Command-line completion for the ":LspServer" command @@ -1235,7 +1303,7 @@ if args[5] == ' ' var subcmd = args[6 : ]->trim() ServerDebug(subcmd) else - util.ErrMsg('Argument required') + util.ErrMsg('Argument required for ":LspServer debug"') endif elseif args == 'restart' RestartServer() @@ -1244,14 +1312,14 @@ if args[4] == ' ' var subcmd = args[5 : ]->trim() ShowServer(subcmd) else - util.ErrMsg('Argument required') + util.ErrMsg('Argument required for ":LspServer show"') endif elseif args->stridx('trace') == 0 if args[5] == ' ' var subcmd = args[6 : ]->trim() ServerTraceSet(subcmd) else - util.ErrMsg('Argument required') + util.ErrMsg('Argument required for ":LspServer trace"') endif else util.ErrMsg($'LspServer - Unsupported argument "{args}"') diff --git a/autoload/lsp/options.vim b/autoload/lsp/options.vim index 754a6fb9f656a0091b7c45f783455fa90097c91d..7e5c8d6f508171830840ae3dbf812d0cf3ee1f0c 100644 --- a/autoload/lsp/options.vim +++ b/autoload/lsp/options.vim @@ -42,7 +42,7 @@ # Show the symbol documentation in the preview window instead of in a popup hoverInPreview: false, # Don't print message when a configured language server is missing. ignoreMissingServer: false, - # Focus on the location list window after LspDiagShow + # Focus on the location list window after ":LspDiag show" keepFocusInDiags: true, # Focus on the location list window after LspShowReferences keepFocusInReferences: true, diff --git a/doc/lsp.txt b/doc/lsp.txt index b530f5b03b93e413d6637849cec2572a320ede38..81be3a560b909768e4d0f9526466f73ca414dc2d 100644 --- a/doc/lsp.txt +++ b/doc/lsp.txt @@ -73,23 +73,24 @@ :LspCodeAction Apply the code action supplied by the language server to the diagnostic in the current line. :LspCodeLens Display all the code lens commands available for the current file and apply the selected command. -:LspDiagCurrent Display the diagnostic message for the current line. -:LspDiagFirst Jump to the first diagnostic message for the current +:LspDiag current Display the diagnostic message for the current line. +:LspDiag first Jump to the first diagnostic message for the current buffer. -:LspDiagHere Jump to the next diagnostic message in the current +:LspDiag here Jump to the next diagnostic message in the current line. -:LspDiagHighlightDisable +:LspDiag highlight disable Disable highlighting lines with a diagnostic message for the current Vim session. -:LspDiagHighlightEnable Enable highlighting lines with a diagnostic message +:LspDiag highlight enable + Enable highlighting lines with a diagnostic message for the current Vim session. -:LspDiagLast Jump to the last diagnostic message for the current +:LspDiag last Jump to the last diagnostic message for the current buffer. -:LspDiagNext Jump to the next diagnostic message for the current +:LspDiag next Jump to the next diagnostic message for the current buffer after the current cursor position. -:LspDiagPrev Jump to the previous diagnostic message for the +:LspDiag prev Jump to the previous diagnostic message for the current buffer before the current current position. -:LspDiagShow Display the diagnostics messages from the language +:LspDiag show Display the diagnostics messages from the language server for the current buffer in a location list. :LspDocumentSymbol Display the symbols in the current file in a popup menu and jump to the location of a selected symbol. @@ -538,7 +539,7 @@ server executable. By default this is set to false. *lsp-opt-keepFocusInDiags* keepFocusInDiags |Boolean| option. Focus on the location list window - after LspDiagShow. + after ":LspDiag show". By default this is set to true. *lsp-opt-keepFocusInReferences* @@ -566,7 +567,7 @@ outlineWinSize |Number| option. The size of the symbol Outline window. By default this is set to 20. *lsp-opt-showDiagInPopup* -showDiagInPopup |Boolean| option. When using the |:LspDiagCurrent| +showDiagInPopup |Boolean| option. When using the ":LspDiag current" command to display the diagnostic message for the current line, use a popup window to display the message instead of echoing in the status area. @@ -662,7 +663,7 @@ *:LspCodeAction* :LspCodeAction [query] Apply the code action supplied by the language server to the diagnostic in the current line. This works only if there is a diagnostic message for the current line. - You can use the |:LspDiagCurrent| command to display + You can use the ":LspDiag current" command to display the diagnostic for the current line. When [query] is given the code action starting with @@ -679,64 +680,64 @@ :LspCodeLens Display a list of code lens commands available for the current buffer and apply the selected code lens command. - *:LspDiagCurrent* -:LspDiagCurrent Displays the diagnostic message (if any) for the + *:LspDiag-current* +:LspDiag current Displays the diagnostic message (if any) for the current line. If the option 'showDiagInPopup' is set to true (default), then the message is displayed in a popup window. Otherwise the message is displayed in the status message area. -:LspDiagCurrent! Only display a diagnostic message if it's directly +:LspDiag! current Only display a diagnostic message if it's directly under the cursor. Otherwise works exactly like - |:LspDiagCurrent| + ":LspDiag current" To show the current diagnotic under the cursor while moving around the following autocmd can be used: > augroup LspCustom - au! - au CursorMoved * silent! LspDiagCurrent! + au! + au CursorMoved * silent! LspDiag! current augroup END < - *:LspDiagFirst* -:LspDiagFirst Jumps to the location of the first diagnostic message + *:LspDiag-first* +:LspDiag first Jumps to the location of the first diagnostic message for the current file. - *:LspDiagHere* -:LspDiagHere Jumps to the location of the diagnostic message in + *:LspDiag-here* +:LspDiag here Jumps to the location of the diagnostic message in the current line (start from current column). -:LspDiagHighlightDisable *:LspDiagHighlightDisable* +:LspDiag highlight disable *:LspDiag-highlight-disable* Disable highlighting lines with a diagnostic message for the current Vim session. To always disable the highlighting, set the autoHighlightDiags option to false. - *:LspDiagHighlightEnable* -:LspDiagHighlightEnable Enable highlighting lines with a diagnostic message +:LspDiag highlight enable *:LspDiag-highlight-enable* + Enable highlighting lines with a diagnostic message for the current Vim session. Note that highlighting lines with a diagnostic message is enabled by default. - *:LspDiagLast* -:LspDiagLast Jumps to the location of the first diagnostic message + *:LspDiag-last* +:LspDiag last Jumps to the location of the first diagnostic message for the current file. - *:LspDiagNext* -:[count]LspDiagNext Go to the [count] diagnostic message after the current + *:LspDiag-next* +:[count]LspDiag next Go to the [count] diagnostic message after the current cursor position. If [count] is omitted, then 1 is used. If [count] exceeds the number of diagnostics after the current position, then the last diagnostic is selected. - *:LspDiagPrev* -:[count]LspDiagPrev Go to the [count] diagnostic message before the + *:LspDiag-prev* +:[count]LspDiag prev Go to the [count] diagnostic message before the current cursor position. If [count] is omitted, then 1 is used. If [count] exceeds the number of diagnostics before the current position, then first last diagnostic is selected. - *:LspDiagShow* -:LspDiagShow Creates a new location list with the diagnostics + *:LspDiag-show* +:LspDiag show Creates a new location list with the diagnostics messages (if any) from the language server for the current file and opens the location list window. You can use the Vim location list commands to browse the @@ -1253,15 +1254,15 @@ 7. Diagnostics *lsp-diagnostics* When a source file has syntax errors or warnings or static analysis warnings, the LSP plugin highlights them by placing |signs| in the |sign-column|. You -can use the |:LspDiagShow| command to display all the diagnostic messages for -the current file in a |location-list-window|. You can use the |:LspDiagFirst| -command to jump to the line with the first diagnostic message, the -|:LspDiagNext| command to jump to the next nearest line with the diagnostic -message, the |:LspDiagPrev| command to jump to the previous nearest line with -the diagnostic message, the |:LspDiagHere| command to jump to the diagnostic -message in the current line. You can use the |:LspDiagCurrent| command to -display the entire diagnostic message from the language server for the current -line. +can use the ":LspDiag show" command to display all the diagnostic messages for +the current file in a |location-list-window|. You can use the +":LspDiag first" command to jump to the line with the first diagnostic +message, the ":LspDiag next" command to jump to the next nearest line with the +diagnostic message, the ":LspDiag prev" command to jump to the previous +nearest line with the diagnostic message, the ":LspDiag here" command to jump +to the diagnostic message in the current line. You can use the ":LspDiag +current" command to display the entire diagnostic message from the language +server for the current line. By default, the lines with a diagnostic message have a sign placed on them and are highlighted. You can disable the automatic sign placement by setting the @@ -1283,8 +1284,8 @@ "LspDiagInlineHint" or "LspDiagInlineInfo" or "LspDiagInlineWarning" highlight group. You can temporarily disable the automatic diagnostic highlighting for the -current Vim session using the |:LspDiagHighlightDisable| command and re-enable -them using the |:LspDiagHighlightEnable| command. +current Vim session using the ":LspDiag highlight disable" command and +re-enable them using the ":LspDiag highlight enable" command. To disable the automatic highlighting of the diagnostics, you can set the 'autoHighlightDiags' option to v:false: > @@ -1305,7 +1306,7 @@ automatic fix. To apply this fix, you can use the |:LspCodeAction| command. This command applies the action provided by the language server (if any) for the current line. -The |:LspDiagShow| command creates a new location list with the current list +The ":LspDiag show" command creates a new location list with the current list of diagnostics for the current buffer. To automatically add the diagnostics messages to the location list, you can set the 'autoPopulateDiags' option to true.  By default this option is set to false.  When new diagnostics are @@ -1324,8 +1325,8 @@ To display the diagnostic message for the current line in the status area, you can set the 'showDiagOnStatusLine' option to true. By default, this option is set to false. -By default, the |:LspDiagCurrent| command displays the diagnostic message for -the current line in a popup window. To display the message in the status +By default, the ":LspDiag current" command displays the diagnostic message +for the current line in a popup window. To display the message in the status message area instead, you can set the 'showDiagInPopup' option to false. By default this is set to true. diff --git a/plugin/lsp.vim b/plugin/lsp.vim index 382ae49e49edcc5c925a008a6250c0e8a314a34c..36b7ef4a927b71293b02fee4f71158c9ebe87721 100644 --- a/plugin/lsp.vim +++ b/plugin/lsp.vim @@ -59,10 +59,9 @@ # LSP commands command! -nargs=? -bar -range LspCodeAction lsp.CodeAction(, , ) command! -nargs=0 -bar LspCodeLens lsp.CodeLens() +command! -nargs=+ -bar -bang -count -complete=customlist,lsp.LspDiagComplete LspDiag lsp.LspDiagCmd(, , false) command! -nargs=0 -bar -bang LspDiagCurrent lsp.LspShowCurrentDiag(false) command! -nargs=0 -bar LspDiagFirst lsp.JumpToDiag('first') -command! -nargs=0 -bar LspDiagHighlightDisable lsp.DiagHighlightDisable() -command! -nargs=0 -bar LspDiagHighlightEnable lsp.DiagHighlightEnable() command! -nargs=0 -bar LspDiagLast lsp.JumpToDiag('last') command! -nargs=0 -bar -count=1 LspDiagNext lsp.JumpToDiag('next', ) command! -nargs=0 -bar -count=1 LspDiagPrev lsp.JumpToDiag('prev', ) @@ -127,13 +126,13 @@ anoremenu L&sp.Highlight\ Symbol :LspHighlight anoremenu L&sp.Highlight\ Clear :LspHighlightClear # Diagnostics - anoremenu L&sp.Diagnostics.Current :LspDiagCurrent - anoremenu L&sp.Diagnostics.Show\ All :LspDiagShow - anoremenu L&sp.Diagnostics.First :LspDiagFirst - anoremenu L&sp.Diagnostics.Last :LspDiagLast - anoremenu L&sp.Diagnostics.Next :LspDiagNext - anoremenu L&sp.Diagnostics.Prev :LspDiagPrev - anoremenu L&sp.Diagnostics.This :LspDiagHere + anoremenu L&sp.Diagnostics.Current :LspDiag current + anoremenu L&sp.Diagnostics.Show\ All :LspDiag show + anoremenu L&sp.Diagnostics.First :LspDiag first + anoremenu L&sp.Diagnostics.Last :LspDiag last + anoremenu L&sp.Diagnostics.Next :LspDiag next + anoremenu L&sp.Diagnostics.Previous :LspDiag prev + anoremenu L&sp.Diagnostics.This :LspDiag here if &mousemodel =~ 'popup' anoremenu PopUp.L&sp.Go\ to\ Definition diff --git a/test/clangd_offsetencoding.vim b/test/clangd_offsetencoding.vim index 0918ab91a3dc77ca9749cdc4301667c8256a893d..397f4593e16bbc1f89346c20d2acdacdecf4b17a 100644 --- a/test/clangd_offsetencoding.vim +++ b/test/clangd_offsetencoding.vim @@ -16,7 +16,7 @@ # Need patch 9.0.1629 to properly encode/decode the UTF-16 offsets finish endif -var lspOpts = {autoComplete: false, highlightDiagInline: true} +var lspOpts = {autoComplete: false} g:LspOptionsSet(lspOpts) var lspServers = [{ @@ -62,7 +62,7 @@ :%bw! enddef -# Test for :LspDiagShow when using multibyte and composing characters +# Test for ":LspDiag show" when using multibyte and composing characters def g:Test_LspDiagShow_multibyte() :silent! edit XLspDiagShow_mb.c sleep 200m @@ -79,7 +79,7 @@ END setline(1, lines) g:WaitForServerFileLoad(3) :redraw! - :LspDiagShow + :LspDiag show var qfl: list> = getloclist(0) assert_equal([5, 37], [qfl[0].lnum, qfl[0].col]) assert_equal([6, 33], [qfl[1].lnum, qfl[1].col]) diff --git a/test/clangd_tests.vim b/test/clangd_tests.vim index 5ad93b7b3b1ab2bfc6200d20456fd3e1007b7708..5e18f94e888cadbbb7db9d1277e91df23d9ba043 100644 --- a/test/clangd_tests.vim +++ b/test/clangd_tests.vim @@ -3,7 +3,7 @@ # Unit tests for Vim Language Server Protocol (LSP) clangd client source common.vim -var lspOpts = {autoComplete: false, highlightDiagInline: true} +var lspOpts = {autoComplete: false} g:LspOptionsSet(lspOpts) g:LSPTest_modifyDiags = false @@ -194,7 +194,7 @@ assert_equal([1, 5], [loclist[0].lnum, loclist[0].col]) :lclose # Test for opening in qf list - g:LspOptionsSet({ useQuickfixForLocations: true }) + g:LspOptionsSet({useQuickfixForLocations: true}) cursor(5, 2) :LspShowReferences sleep 100m @@ -212,14 +212,14 @@ assert_equal(1, getqflist()->len()) qfl = getqflist() assert_equal([1, 5], [qfl[0].lnum, qfl[0].col]) :cclose - g:LspOptionsSet({ useQuickfixForLocations: false }) + g:LspOptionsSet({useQuickfixForLocations: false}) # Test for maintaining buffer focus - g:LspOptionsSet({ keepFocusInReferences: false }) + g:LspOptionsSet({keepFocusInReferences: false}) :LspShowReferences assert_equal('', getwinvar(0, '&buftype')) :lclose - g:LspOptionsSet({ keepFocusInReferences: true }) + g:LspOptionsSet({keepFocusInReferences: true}) # Test for LspPeekReferences @@ -286,7 +286,7 @@ setline(1, lines) g:WaitForServerFileLoad(1) var bnr: number = bufnr() :redraw! - :LspDiagShow + :LspDiag show var qfl: list> = getloclist(0) assert_equal('quickfix', getwinvar(winnr('$'), '&buftype')) assert_equal(bnr, qfl[0].bufnr) @@ -297,54 +297,54 @@ assert_equal([7, 2, 'W'], [qfl[2].lnum, qfl[2].col, qfl[2].type]) close g:LspOptionsSet({showDiagInPopup: false}) normal gg - var output = execute('LspDiagCurrent')->split("\n") + var output = execute('LspDiag current')->split("\n") assert_equal('Warn: No diagnostic messages found for current line', output[0]) - :LspDiagFirst + :LspDiag first assert_equal([3, 14], [line('.'), col('.')]) - output = execute('LspDiagCurrent')->split("\n") + output = execute('LspDiag current')->split("\n") assert_equal("Expected ';' at end of declaration (fix available)", output[0]) :normal! 0 - :LspDiagHere + :LspDiag here assert_equal([3, 14], [line('.'), col('.')]) - :LspDiagNext + :LspDiag next assert_equal([5, 2], [line('.'), col('.')]) - :LspDiagNext + :LspDiag next assert_equal([7, 2], [line('.'), col('.')]) - output = execute('LspDiagNext')->split("\n") + output = execute('LspDiag next')->split("\n") assert_equal('Warn: No more diagnostics found', output[0]) - :LspDiagPrev - :LspDiagPrev - :LspDiagPrev - output = execute('LspDiagPrev')->split("\n") + :LspDiag prev + :LspDiag prev + :LspDiag prev + output = execute('LspDiag prev')->split("\n") assert_equal('Warn: No more diagnostics found', output[0]) # Test for maintaining buffer focus - g:LspOptionsSet({ keepFocusInDiags: false }) - :LspDiagShow + g:LspOptionsSet({keepFocusInDiags: false}) + :LspDiag show assert_equal('', getwinvar(0, '&buftype')) :lclose - g:LspOptionsSet({ keepFocusInDiags: true }) + g:LspOptionsSet({keepFocusInDiags: true}) - # :[count]LspDiagNext + # :[count]LspDiag next cursor(3, 1) - :2LspDiagNext + :2LspDiag next assert_equal([5, 2], [line('.'), col('.')]) - :2LspDiagNext + :2LspDiag next assert_equal([7, 2], [line('.'), col('.')]) - output = execute(':2LspDiagNext')->split("\n") + output = execute(':2LspDiag next')->split("\n") assert_equal('Warn: No more diagnostics found', output[0]) - # :[count]LspDiagPrev + # :[count]LspDiag prev cursor(7, 2) - :4LspDiagPrev + :4LspDiag prev assert_equal([3, 14], [line('.'), col('.')]) - output = execute(':4LspDiagPrev')->split("\n") + output = execute(':4LspDiag prev')->split("\n") assert_equal('Warn: No more diagnostics found', output[0]) :%d setline(1, ['void blueFunc()', '{', '}']) g:WaitForDiags(0) - output = execute('LspDiagShow')->split("\n") + output = execute('LspDiag show')->split("\n") assert_match('Warn: No diagnostic messages found for', output[0]) g:LspOptionsSet({showDiagInPopup: true}) @@ -370,10 +370,10 @@ g:WaitForServerFileLoad(1) :redraw! normal gg - :LspDiagFirst + :LspDiag first assert_equal([3, 14], [line('.'), col('.')]) - var output = execute('LspDiagCurrent')->split("\n") + var output = execute('LspDiag current')->split("\n") assert_equal("this is overridden", output[0]) g:LspOptionsSet({showDiagInPopup: true}) @@ -399,7 +399,7 @@ var d = lsp#diag#GetDiagsForBuf()[0] assert_equal({start: {line: 0, character: 5}, end: {line: 0, character: 6}}, d.range) - :LspDiagShow + :LspDiag show assert_equal(1, line('$')) wincmd w setline(2, 'int j:') @@ -479,7 +479,7 @@ g:WaitForServerFileLoad(3) else g:WaitForServerFileLoad(2) endif - :LspDiagShow + :LspDiag show var qfl: list> = getloclist(0) assert_equal('quickfix', getwinvar(winnr('$'), '&buftype')) assert_equal(bnr, qfl[0].bufnr) @@ -495,35 +495,35 @@ close :sleep 100m cursor(2, 1) - assert_equal('', execute('LspDiagPrev')) + assert_equal('', execute('LspDiag prev')) assert_equal([1, 9], [line('.'), col('.')]) - assert_equal('', execute('LspDiagPrev')) + assert_equal('', execute('LspDiag prev')) assert_equal([1, 5], [line('.'), col('.')]) - var output = execute('LspDiagPrev')->split("\n") + var output = execute('LspDiag prev')->split("\n") assert_equal('Warn: No more diagnostics found', output[0]) cursor(2, 1) - assert_equal('', execute('LspDiagFirst')) + assert_equal('', execute('LspDiag first')) assert_equal([1, 5], [line('.'), col('.')]) - assert_equal('', execute('LspDiagNext')) + assert_equal('', execute('LspDiag next')) assert_equal([1, 9], [line('.'), col('.')]) cursor(1, 1) - assert_equal('', execute('LspDiagLast')) + assert_equal('', execute('LspDiag last')) assert_equal([2, 9], [line('.'), col('.')]) popup_clear() - # Test for :LspDiagHere on a line with multiple diagnostics + # Test for :LspDiag here on a line with multiple diagnostics cursor(1, 1) - :LspDiagHere + :LspDiag here assert_equal([1, 5], [line('.'), col('.')]) var ids = popup_list() assert_equal(1, ids->len()) assert_match('Incompatible pointer to integer', getbufline(ids[0]->winbufnr(), 1, '$')[0]) popup_clear() cursor(1, 6) - :LspDiagHere + :LspDiag here assert_equal([1, 9], [line('.'), col('.')]) ids = popup_list() assert_equal(1, ids->len()) @@ -532,83 +532,83 @@ popup_clear() # Line without diagnostics cursor(3, 1) - output = execute('LspDiagHere')->split("\n") + output = execute('LspDiag here')->split("\n") assert_equal('Warn: No more diagnostics found on this line', output[0]) g:LspOptionsSet({showDiagInPopup: false}) for i in range(1, 5) cursor(1, i) - output = execute('LspDiagCurrent')->split('\n') + output = execute('LspDiag current')->split('\n') assert_match('Incompatible pointer to integer', output[0]) endfor for i in range(6, 12) cursor(1, i) - output = execute('LspDiagCurrent')->split('\n') + output = execute('LspDiag current')->split('\n') assert_match('Initializer element is not ', output[0]) endfor g:LspOptionsSet({showDiagInPopup: true}) - # Check for exact diag ":LspDiagCurrent!" + # Check for exact diag ":LspDiag current!" g:LspOptionsSet({showDiagInPopup: false}) for i in range(1, 4) cursor(1, i) - output = execute('LspDiagCurrent!')->split('\n') + output = execute('LspDiag! current')->split('\n') assert_equal('Warn: No diagnostic messages found for current position', output[0]) endfor cursor(1, 5) - output = execute('LspDiagCurrent!')->split('\n') + output = execute('LspDiag! current')->split('\n') assert_match('Incompatible pointer to integer', output[0]) for i in range(6, 8) cursor(1, i) - output = execute('LspDiagCurrent!')->split('\n') + output = execute('LspDiag! current')->split('\n') assert_equal('Warn: No diagnostic messages found for current position', output[0]) endfor for i in range(9, 11) cursor(1, i) - output = execute('LspDiagCurrent!')->split('\n') + output = execute('LspDiag! current')->split('\n') assert_match('Initializer element is not ', output[0]) endfor for i in range(12, 12) cursor(1, i) - output = execute('LspDiagCurrent!')->split('\n') + output = execute('LspDiag! current')->split('\n') assert_equal('Warn: No diagnostic messages found for current position', output[0]) endfor g:LspOptionsSet({showDiagInPopup: true}) - # :[count]LspDiagNext + # :[count]LspDiag next g:LspOptionsSet({showDiagInPopup: false}) cursor(1, 1) - :2LspDiagNext + :2LspDiag next assert_equal([1, 9], [line('.'), col('.')]) - :2LspDiagNext + :2LspDiag next assert_equal([2, 9], [line('.'), col('.')]) - output = execute(':2LspDiagNext')->split("\n") + output = execute(':2LspDiag next')->split("\n") assert_equal('Warn: No more diagnostics found', output[0]) cursor(1, 1) - :99LspDiagNext + :99LspDiag next assert_equal([2, 9], [line('.'), col('.')]) g:LspOptionsSet({showDiagInPopup: true}) - # :[count]LspDiagPrev + # :[count]LspDiag prev g:LspOptionsSet({showDiagInPopup: false}) cursor(1, 1) - :2LspDiagPrev + :2LspDiag prev assert_equal('Warn: No more diagnostics found', output[0]) cursor(3, 3) - :2LspDiagPrev + :2LspDiag prev assert_equal([1, 9], [line('.'), col('.')]) - :2LspDiagPrev + :2LspDiag prev assert_equal([1, 5], [line('.'), col('.')]) - output = execute(':2LspDiagPrev')->split("\n") + output = execute(':2LspDiag prev')->split("\n") assert_equal('Warn: No more diagnostics found', output[0]) cursor(3, 3) - :99LspDiagPrev + :99LspDiag prev assert_equal([1, 5], [line('.'), col('.')]) g:LspOptionsSet({showDiagInPopup: true}) @@ -631,6 +631,8 @@ # TODO: Waiting count doesn't include Warning, Info, and Hint diags g:WaitForDiags(2) + g:LspOptionsSet({highlightDiagInline: true}) + var props = prop_list(1) assert_equal(0, props->len()) props = prop_list(2) @@ -647,6 +649,10 @@ props = prop_list(5) assert_equal(1, props->len()) assert_equal([{'id': 0, 'col': 5, 'type_bufnr': 0, 'end': 1, 'type': 'LspDiagInlineError', 'length': 6, 'start': 1}], props) props = prop_list(6) + assert_equal(0, props->len()) + + g:LspOptionsSet({highlightDiagInline: false}) + props = prop_list(1, {end_lnum: line('$')}) assert_equal(0, props->len()) :%bw! @@ -1117,7 +1123,7 @@ # Show current diagnostic as to open another popup. # Then we can test that LspHover closes all existing popups cursor(10, 6) - :LspDiagCurrent + :LspDiag current assert_equal(1, popup_list()->len()) :LspHover assert_equal(1, popup_list()->len()) @@ -1270,14 +1276,14 @@ execute $':{bnum}bw' # Validate position vert botright - g:LspOptionsSet({ outlineOnRight: true }) + g:LspOptionsSet({outlineOnRight: true}) :LspOutline assert_equal(2, winnr('$')) bnum = winbufnr(winid + 2) assert_equal('LSP-Outline', bufname(bnum)) assert_equal(['Function', ' aFuncOutline', ' bFuncOutline'], getbufline(bnum, 4, '$')) assert_equal(['row', [['leaf', winid], ['leaf', winid + 2]]], winlayout()) - g:LspOptionsSet({ outlineOnRight: false }) + g:LspOptionsSet({outlineOnRight: false}) execute $':{bnum}bw' # Validate position botright (below) @@ -1290,7 +1296,7 @@ assert_equal(['col', [['leaf', winid], ['leaf', winid + 3]]], winlayout()) execute $':{bnum}bw' # Validate that outlineWinSize works for LspOutline - g:LspOptionsSet({ outlineWinSize: 40 }) + g:LspOptionsSet({outlineWinSize: 40}) :LspOutline assert_equal(2, winnr('$')) bnum = winbufnr(winid + 4) @@ -1298,7 +1304,7 @@ assert_equal('LSP-Outline', bufname(bnum)) assert_equal(['Function', ' aFuncOutline', ' bFuncOutline'], getbufline(bnum, 4, '$')) assert_equal(40, winwidth(winid + 4)) execute $':{bnum}bw' - g:LspOptionsSet({ outlineWinSize: 20 }) + g:LspOptionsSet({outlineWinSize: 20}) # Validate that works for LspOutline :37LspOutline @@ -1573,6 +1579,40 @@ :%bw! delete('Xreloadbuffer.c') enddef +# Test for ":LspDiag" sub commands +def g:Test_LspDiagsSubcmd() + new XLspDiagsSubCmd.raku + + feedkeys(":LspDiag \\", 'xt') + assert_equal('LspDiag first current here highlight last next prev show', @:) + feedkeys(":LspDiag highlight \\", 'xt') + assert_equal('LspDiag highlight enable disable', @:) + assert_equal(['Error: :LspDiag - Unsupported argument "xyz"'], + execute('LspDiag xyz')->split("\n")) + assert_equal(['Error: :LspDiag - Unsupported argument "first xyz"'], + execute('LspDiag first xyz')->split("\n")) + assert_equal(['Error: :LspDiag - Unsupported argument "current xyz"'], + execute('LspDiag current xyz')->split("\n")) + assert_equal(['Error: :LspDiag - Unsupported argument "here xyz"'], + execute('LspDiag here xyz')->split("\n")) + assert_equal(['Error: Argument required for ":LspDiag highlight"'], + execute('LspDiag highlight')->split("\n")) + assert_equal(['Error: :LspDiag highlight - Unsupported argument "xyz"'], + execute('LspDiag highlight xyz')->split("\n")) + assert_equal(['Error: :LspDiag highlight - Unsupported argument "enable xyz"'], + execute('LspDiag highlight enable xyz')->split("\n")) + assert_equal(['Error: :LspDiag - Unsupported argument "last xyz"'], + execute('LspDiag last xyz')->split("\n")) + assert_equal(['Error: :LspDiag - Unsupported argument "next xyz"'], + execute('LspDiag next xyz')->split("\n")) + assert_equal(['Error: :LspDiag - Unsupported argument "prev xyz"'], + execute('LspDiag prev xyz')->split("\n")) + assert_equal(['Error: :LspDiag - Unsupported argument "show xyz"'], + execute('LspDiag show xyz')->split("\n")) + + :%bw! +enddef + # Test for the :LspServer command. def g:Test_LspServer() new a.raku @@ -1586,24 +1626,92 @@ assert_equal(['Warn: No Lsp servers found for "a.raku"'], execute('LspServer trace verbose')->split("\n")) assert_equal(['Error: LspServer - Unsupported argument "xyz"'], execute('LspServer xyz')->split("\n")) - assert_equal(['Error: Argument required'], + assert_equal(['Error: Argument required for ":LspServer debug"'], execute('LspServer debug')->split("\n")) assert_equal(['Error: Unsupported argument "xyz"'], execute('LspServer debug xyz')->split("\n")) assert_equal(['Error: Unsupported argument "on xyz"'], execute('LspServer debug on xyz')->split("\n")) - assert_equal(['Error: Argument required'], + assert_equal(['Error: Argument required for ":LspServer show"'], execute('LspServer show')->split("\n")) assert_equal(['Error: Unsupported argument "xyz"'], execute('LspServer show xyz')->split("\n")) assert_equal(['Error: Unsupported argument "status xyz"'], execute('LspServer show status xyz')->split("\n")) - assert_equal(['Error: Argument required'], + assert_equal(['Error: Argument required for ":LspServer trace"'], execute('LspServer trace')->split("\n")) assert_equal(['Error: Unsupported argument "xyz"'], execute('LspServer trace xyz')->split("\n")) assert_equal(['Error: Unsupported argument "verbose xyz"'], execute('LspServer trace verbose xyz')->split("\n")) + :%bw! +enddef + +# Test for the diagnostics virtual text text property +def g:Test_DiagVirtualText() + if !has('patch-9.0.1157') + # Doesn't support virtual text + return + endif + :silent! edit XdiagVirtualText.c + sleep 200m + var lines: list =<< trim END + void DiagVirtualTextFunc1() + { + int i: + } + END + setline(1, lines) + g:WaitForServerFileLoad(1) + redraw! + + var p = prop_list(1, {end_lnum: line('$')}) + assert_equal(0, p->len()) + + g:LspOptionsSet({showDiagWithVirtualText: true}) + p = prop_list(1, {end_lnum: line('$')}) + assert_equal(1, p->len()) + assert_equal([3, 8, 'LspDiagVirtualTextError'], [p[0].lnum, p[0].length, p[0].type]) + + g:LspOptionsSet({showDiagWithVirtualText: false}) + p = prop_list(1, {end_lnum: line('$')}) + assert_equal(0, p->len()) + + :%bw! +enddef + +# Test for enabling and disabling the "showDiagWithSign" option. +def g:Test_DiagSigns() + :silent! edit Xdiagsigns.c + sleep 200m + var lines: list =<< trim END + void DiagSignsFunc1(void) + { + int a: + } + END + setline(1, lines) + g:WaitForServerFileLoad(1) + redraw! + + var signs = sign_getplaced('%', {group: '*'})[0].signs + assert_equal([1, 3], [signs->len(), signs[0].lnum]) + + g:LspOptionsSet({showDiagWithSign: false}) + signs = sign_getplaced('%', {group: '*'})[0].signs + assert_equal([], signs) + g:LspOptionsSet({showDiagWithSign: true}) + signs = sign_getplaced('%', {group: '*'})[0].signs + assert_equal([1, 3], [signs->len(), signs[0].lnum]) + + # Test for enabling/disabling "autoHighlightDiags" + g:LspOptionsSet({autoHighlightDiags: false}) + signs = sign_getplaced('%', {group: '*'})[0].signs + assert_equal([], signs) + g:LspOptionsSet({autoHighlightDiags: true}) + signs = sign_getplaced('%', {group: '*'})[0].signs + assert_equal([1, 3], [signs->len(), signs[0].lnum]) + :%bw! enddef diff --git a/test/common.vim b/test/common.vim index 045642efa2c662dd35e2b08746de08646f07e64d..eeb852c854c1f08be89b7820c9581a9e6b03acf1 100644 --- a/test/common.vim +++ b/test/common.vim @@ -115,7 +115,7 @@ endwhile assert_equal(errCount, lsp#lsp#ErrorCount().Error) if lsp#lsp#ErrorCount().Error != errCount - :LspDiagShow + :LspDiag show assert_report(getloclist(0)->string()) :lclose endif