From 58a93a2b93302a9a63cb1df2ae3b1b1b88cbb1c5 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Mon, 10 Jul 2023 19:29:14 -0700 Subject: [PATCH] Add the ":LspDiag" command and add sub-commands for the various Lsp diagnostic functionality. Update the test scripts to use the new set of commands. Add support for dynamically changing the value of the diagnostic options. Remove the ":LspDiagHighlightEnable" and the ":LspDiagHighlightDisable" commands (these are replaced by the ":LspDiag highlight" command) --- README.md | 16 +-- autoload/lsp/diag.vim | 90 ++++++++---- 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 +- 10 files changed, 400 insertions(+), 180 deletions(-) diff --git a/README.md b/README.md index 4657d6c..106a2b2 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 263c07c..a17598a 100644 --- a/autoload/lsp/diag.vim +++ b/autoload/lsp/diag.vim @@ -100,6 +100,12 @@ export def InitOnce() 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 @@ def DiagSevToSymbolText(severity: number): string 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 @@ def RemoveDiagVisualsForBuffer(bnr: number) 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 @@ def DiagsUpdateLocList(bnr: number, calledByCmd: bool = false): bool 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,23 +783,6 @@ export def LspDiagsJump(which: string, a_count: number = 0): void 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 -enddef - -# Enable the LSP diagnostics highlighting -export def DiagsHighlightEnable() - opt.lspOptions.autoHighlightDiags = true - for binfo in getbufinfo({bufloaded: true}) - DiagsRefresh(binfo.bufnr) - 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. @@ -806,4 +795,57 @@ export def GetDiagsForBuf(bnr: number = bufnr()): list> 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}) + if diagsMap->has_key(binfo.bufnr) + DiagsRefresh(binfo.bufnr) + endif + endfor +enddef + +# 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 + + 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 bff0e93..1bbd331 100644 --- a/autoload/lsp/inlayhints.vim +++ b/autoload/lsp/inlayhints.vim @@ -160,7 +160,9 @@ export def BufferInit(lspserver: dict, bnr: number) 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 @@ export def InlayHintsEnable() 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 @@ export def InlayHintsDisable() :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 66387ca..a2e0e83 100644 --- a/autoload/lsp/lsp.vim +++ b/autoload/lsp/lsp.vim @@ -543,7 +543,9 @@ export def BufferLoadedInWin(bnr: number) 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 @@ def LspServerSubCmdComplete(cmds: list, arglead: string, cmdline: string 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 @@ export def LspServerCmd(args: string) 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 @@ export def LspServerCmd(args: string) 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 754a6fb..7e5c8d6 100644 --- a/autoload/lsp/options.vim +++ b/autoload/lsp/options.vim @@ -42,7 +42,7 @@ export var lspOptions: dict = { 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 b530f5b..81be3a5 100644 --- a/doc/lsp.txt +++ b/doc/lsp.txt @@ -73,23 +73,24 @@ The following commands are provided: 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 @@ ignoreMissingServer |Boolean| option. Do not print a missing language *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 @@ can map these commands to keys and make it easier to invoke them. :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 @@ can map these commands to keys and make it easier to invoke them. 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 @@ case insensitive or fuzzy comparison. 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 @@ is set to v:false. The text is highlighted using the "LspDiagInlineError" or 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 382ae49..36b7ef4 100644 --- a/plugin/lsp.vim +++ b/plugin/lsp.vim @@ -59,10 +59,9 @@ augroup END # 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 @@ if has('gui_running') 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 0918ab9..397f459 100644 --- a/test/clangd_offsetencoding.vim +++ b/test/clangd_offsetencoding.vim @@ -16,7 +16,7 @@ if !has('patch-9.0.1629') finish endif -var lspOpts = {autoComplete: false, highlightDiagInline: true} +var lspOpts = {autoComplete: false} g:LspOptionsSet(lspOpts) var lspServers = [{ @@ -62,7 +62,7 @@ def g:Test_LspCodeAction_multibyte() :%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 @@ def g:Test_LspDiagShow_multibyte() 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 5ad93b7..5e18f94 100644 --- a/test/clangd_tests.vim +++ b/test/clangd_tests.vim @@ -3,7 +3,7 @@ vim9script source common.vim -var lspOpts = {autoComplete: false, highlightDiagInline: true} +var lspOpts = {autoComplete: false} g:LspOptionsSet(lspOpts) g:LSPTest_modifyDiags = false @@ -194,7 +194,7 @@ def g:Test_LspShowReferences() :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 @@ def g:Test_LspShowReferences() 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 @@ def g:Test_LspDiag() 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 @@ def g:Test_LspDiag() 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 @@ def g:Test_LspProcessDiagHandler() :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 @@ def g:Test_DiagLocListAutoUpdate() 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 @@ def g:Test_LspDiag_Multi() 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 @@ def g:Test_LspDiag_Multi() :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 @@ def g:Test_LspDiag_Multi() # 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 @@ def g:Test_LspHighlightDiagInline() # 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) @@ -649,6 +651,10 @@ def g:Test_LspHighlightDiagInline() 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! enddef @@ -1117,7 +1123,7 @@ def g:Test_LspHover() # 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 @@ def g:Test_LspOutline() 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 @@ def g:Test_LspOutline() 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 @@ def g:Test_LspOutline() 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 @@ def g:Test_ReloadBufferWithDiags() 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,19 +1626,19 @@ def g:Test_LspServer() 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")) @@ -1607,6 +1647,74 @@ def g:Test_LspServer() :%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 + # TODO: # 1. Add a test for autocompletion with a single match while ignoring case. # After the full matched name is typed, the completion popup should still diff --git a/test/common.vim b/test/common.vim index 045642e..eeb852c 100644 --- a/test/common.vim +++ b/test/common.vim @@ -115,7 +115,7 @@ def g:WaitForDiags(errCount: number) assert_equal(errCount, lsp#lsp#ErrorCount().Error) if lsp#lsp#ErrorCount().Error != errCount - :LspDiagShow + :LspDiag show assert_report(getloclist(0)->string()) :lclose endif -- 2.44.0