From 7463c6284cf8e9611fa7ba38732cf89a03d33960 Mon Sep 17 00:00:00 2001
From: Yegappan Lakshmanan <yegappan@yahoo.com>
Date: Sun, 10 Jan 2021 14:43:02 -0800
Subject: [PATCH] Add commands for jumping to first/next/previous diagnostic
 message and rename the command to show all the diagnostics

---
 README.md        |  6 +++-
 autoload/lsp.vim | 85 +++++++++++++++++++++++++++++++++++++++++++++++-
 doc/lsp.txt      | 11 +++++--
 plugin/lsp.vim   |  6 +++-
 4 files changed, 103 insertions(+), 5 deletions(-)

diff --git a/README.md b/README.md
index 401ac02..75c98a9 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,11 @@ Command|Description
 :LspGotoTypeDef|Go to the type definition of the keyword under cursor
 :LspGotoImpl|Go to the implementation of the keyword under cursor
 :LspShowSignature|Display the signature of the keyword under cursor
-:LspShowDiagnostics|Display the diagnostics messages from the LSP server for the current buffer
+:LspDiagShow|Display the diagnostics messages from the LSP server for the current buffer
+:LspDiagFirst|Display the first diagnostic message for the current buffer
+:LspDiagNext|Display the next diagnostic message after the current line
+:LspDiagPrev|Display the previous diagnostic message before the current line
+:LspDiagCurrent|Display the diagnostic message for the current line
 :LspShowReferences|Display the list of references to the keyword under cursor in a new quickfix list.
 :LspHighlight|Highlight all the matches for the keyword under cursor
 :LspHighlightClear|Clear all the matches highlighted by :LspHighlight
diff --git a/autoload/lsp.vim b/autoload/lsp.vim
index b33d5a5..d765204 100644
--- a/autoload/lsp.vim
+++ b/autoload/lsp.vim
@@ -377,6 +377,7 @@ def s:lspDiagSevToQfType(severity: number): string
 enddef
 
 # Display the diagnostic messages from the LSP server for the current buffer
+# in a quickfix list
 def lsp#showDiagnostics(): void
   var ftype = &filetype
   if ftype == ''
@@ -419,6 +420,88 @@ def lsp#showDiagnostics(): void
   :copen
 enddef
 
+# Show the diagnostic message for the current line
+def lsp#showCurrentDiag()
+  var ftype = &filetype
+  if ftype == ''
+    return
+  endif
+
+  var lspserver: dict<any> = s:lspGetServer(ftype)
+  if lspserver->empty()
+    ErrMsg('Error: LSP server for "' .. ftype .. '" filetype is not found')
+    return
+  endif
+  if !lspserver.running
+    ErrMsg('Error: LSP server for "' .. ftype .. '" filetype is not running')
+    return
+  endif
+
+  var bnr: number = bufnr()
+  var lnum: number = line('.')
+  var diag: dict<any> = lspserver.getDiagByLine(bnr, lnum)
+  if diag->empty()
+    WarnMsg('No diagnostic messages found for current line')
+  else
+    echo diag.message
+  endif
+enddef
+
+# sort the diaganostics messages for a buffer by line number
+def s:getSortedDiagLines(lspserver: dict<any>, bnr: number): list<number>
+  var lnums: list<number> =
+		lspserver.diagsMap[bnr]->keys()->map((_, v) => str2nr(v))
+  return lnums->sort((a, b) => a - b)
+enddef
+
+def lsp#jumpToDiag(which: string): void
+  var ftype = &filetype
+  if ftype == ''
+    return
+  endif
+
+  var lspserver: dict<any> = s:lspGetServer(ftype)
+  if lspserver->empty()
+    ErrMsg('Error: LSP server for "' .. ftype .. '" filetype is not found')
+    return
+  endif
+  if !lspserver.running
+    ErrMsg('Error: LSP server for "' .. ftype .. '" filetype is not running')
+    return
+  endif
+
+  var fname: string = expand('%:p')
+  if fname == ''
+    return
+  endif
+  var bnr: number = bufnr()
+
+  if !lspserver.diagsMap->has_key(bnr) || lspserver.diagsMap[bnr]->empty()
+    WarnMsg('No diagnostic messages found for ' .. fname)
+    return
+  endif
+
+  # sort the diagnostics by line number
+  var sortedDiags: list<number> = s:getSortedDiagLines(lspserver, bnr)
+
+  if which == 'first'
+    cursor(sortedDiags[0], 1)
+    return
+  endif
+
+  # Find the entry just before the current line (binary search)
+  var curlnum: number = line('.')
+  for lnum in (which == 'next') ? sortedDiags : reverse(sortedDiags)
+    if (which == 'next' && lnum > curlnum)
+	  || (which == 'prev' && lnum < curlnum)
+      call cursor(lnum, 1)
+      return
+    endif
+  endfor
+
+  WarnMsg('Error: No more diagnostics found')
+enddef
+
 # Insert mode completion handler
 def lsp#completeFunc(findstart: number, base: string): any
   var ftype: string = &filetype
@@ -461,7 +544,7 @@ def lsp#completeFunc(findstart: number, base: string): any
     for item in lspserver.completeItems
       res->add(item)
     endfor
-    return res
+    return res->empty() ? v:none : res
   endif
 enddef
 
diff --git a/doc/lsp.txt b/doc/lsp.txt
index 9ef408a..5658f3b 100644
--- a/doc/lsp.txt
+++ b/doc/lsp.txt
@@ -2,7 +2,7 @@
 
 Author: Yegappan Lakshmanan  (yegappan AT yahoo DOT com)
 For Vim version 8.2.2082 and above
-Last change: Dec 30, 2020
+Last change: Jan 10, 2021
 
 ==============================================================================
 						*lsp-license*
@@ -74,8 +74,15 @@ The following commands are provided:
 :LspGotoTypeDef		Go to the type definition of the keyword under cursor
 :LspGotoImpl		Go to the implementation of the keyword under cursor
 :LspShowSignature	Display the signature of the keyword under cursor
-:LspShowDiagnostics	Display the diagnostics messages from the LSP server for
+:LspDiagShow		Display the diagnostics messages from the LSP server for
 			the current buffer
+:LspDiagFirst		Display the first diagnostic message for the current
+			buffer
+:LspDiagNext		Display the next diagnostic message for the current
+			buffer after the current line
+:LspDiagPrev		Display the previous diagnostic message for the current
+			buffer before the current line
+:LspDiagCurrent		Display the diagnostic message for the current line
 :LspShowReferences	Display the list of references to the keyword under
 			cursor in a new quickfix list.
 :LspHighlight		Highlight all the matches for the keyword under cursor
diff --git a/plugin/lsp.vim b/plugin/lsp.vim
index d60d781..0797813 100644
--- a/plugin/lsp.vim
+++ b/plugin/lsp.vim
@@ -25,7 +25,11 @@ command! -nargs=0 -bar LspGotoDeclaration call lsp#gotoDeclaration()
 command! -nargs=0 -bar LspGotoTypeDef call lsp#gotoTypedef()
 command! -nargs=0 -bar LspGotoImpl call lsp#gotoImplementation()
 command! -nargs=0 -bar LspShowSignature call lsp#showSignature()
-command! -nargs=0 -bar LspShowDiagnostics call lsp#showDiagnostics()
+command! -nargs=0 -bar LspDiagShow call lsp#showDiagnostics()
+command! -nargs=0 -bar LspDiagCurrent call lsp#showCurrentDiag()
+command! -nargs=0 -bar LspDiagFirst call lsp#jumpToDiag('first')
+command! -nargs=0 -bar LspDiagNext call lsp#jumpToDiag('next')
+command! -nargs=0 -bar LspDiagPrev call lsp#jumpToDiag('prev')
 command! -nargs=0 -bar LspShowReferences call lsp#showReferences()
 command! -nargs=0 -bar LspHighlight call lsp#docHighlight()
 command! -nargs=0 -bar LspHighlightClear call lsp#docHighlightClear()
-- 
2.51.0