]> Sergey Matveev's repositories - vim-lsp.git/commitdiff
Add support for diagnostics from multiple servers
authorAndreas Louv <andreas@louv.dk>
Tue, 11 Apr 2023 14:40:02 +0000 (16:40 +0200)
committerAndreas Louv <andreas@louv.dk>
Thu, 13 Apr 2023 22:01:20 +0000 (00:01 +0200)
This will join diagnostics from all running language servers.

autoload/lsp/diag.vim
autoload/lsp/lspserver.vim

index 293051c0fa80653b68bd51d5579128f17c9c6a7b..12c699c4dd55640b28a55fd9f973a42f0c8e36b2 100644 (file)
@@ -6,6 +6,19 @@ import './options.vim' as opt
 import './buffer.vim' as buf
 import './util.vim'
 
+# [bnr] = {
+#   serverDiagnostics: {
+#     lspServer1Id: [diag, diag, diag]
+#     lspServer2Id: [diag, diag, diag]
+#   },
+#   serverDiagnosticsByLnum: {
+#     lspServer1Id: { [lnum]: [diag, diag diag] },
+#     lspServer2Id: { [lnum]: [diag, diag diag] },
+#   },
+#   sortedDiagnostics: [lspServer1.diags, ...lspServer2.diags]->sort()
+# }
+var diagsMap: dict<dict<any>> = {}
+
 # Initialize the signs and the text property type used for diagnostics.
 export def InitOnce()
   # Signs used for LSP diagnostics
@@ -75,9 +88,9 @@ def SortDiags(diags: list<dict<any>>): list<dict<any>>
 enddef
 
 # Remove the diagnostics stored for buffer 'bnr'
-export def DiagRemoveFile(lspserver: dict<any>, bnr: number)
-  if lspserver.diagsMap->has_key(bnr)
-    lspserver.diagsMap->remove(bnr)
+export def DiagRemoveFile(bnr: number)
+  if diagsMap->has_key(bnr)
+    diagsMap->remove(bnr)
   endif
 enddef
 
@@ -123,13 +136,13 @@ def DiagsRefresh(lspserver: dict<any>, bnr: number)
     prop_remove({type: 'LspDiagInlineHint', bufnr: bnr, all: true})
   endif
 
-  if !lspserver.diagsMap->has_key(bnr) ||
-      lspserver.diagsMap[bnr].sortedDiagnostics->empty()
+  if !diagsMap->has_key(bnr) ||
+      diagsMap[bnr].sortedDiagnostics->empty()
     return
   endif
 
   var signs: list<dict<any>> = []
-  var diags = lspserver.diagsMap[bnr].sortedDiagnostics
+  var diags: list<dict<any>> = diagsMap[bnr].sortedDiagnostics
   for diag in diags
     # TODO: prioritize most important severity if there are multiple diagnostics
     # from the same line
@@ -179,7 +192,7 @@ export def ProcessNewDiags(lspserver: dict<any>, bnr: number)
     return
   endif
 
-  if bnr == -1 || !lspserver.diagsMap->has_key(bnr)
+  if bnr == -1 || !diagsMap->has_key(bnr)
     return
   endif
 
@@ -197,7 +210,7 @@ enddef
 # process a diagnostic notification message from the LSP server
 # Notification: textDocument/publishDiagnostics
 # Param: PublishDiagnosticsParams
-export def DiagNotification(lspserver: dict<any>, uri: string, diags: list<dict<any>>): void
+export def DiagNotification(lspserver: dict<any>, uri: string, newDiags: list<dict<any>>): void
   var fname: string = util.LspUriToFile(uri)
   var bnr: number = fname->bufnr()
   if bnr == -1
@@ -207,33 +220,49 @@ export def DiagNotification(lspserver: dict<any>, uri: string, diags: list<dict<
 
   # TODO: Is the buffer (bnr) always a loaded buffer? Should we load it here?
   var lastlnum: number = bnr->getbufinfo()[0].linecount
+
   # store the diagnostic for each line separately
-  var diagByLnum: dict<list<dict<any>>> = {}
+  var diagsByLnum: dict<list<dict<any>>> = {}
+
   var diagWithinRange: list<dict<any>> = []
-  for diag in diags
+  for diag in newDiags
     if diag.range.start.line + 1 > lastlnum
       # Make sure the line number is a valid buffer line number
       diag.range.start.line = lastlnum - 1
     endif
 
     var lnum = diag.range.start.line + 1
-    if !diagByLnum->has_key(lnum)
-      diagByLnum[lnum] = []
+    if !diagsByLnum->has_key(lnum)
+      diagsByLnum[lnum] = []
     endif
-    diagByLnum[lnum]->add(diag)
+    diagsByLnum[lnum]->add(diag)
 
     diagWithinRange->add(diag)
   endfor
 
-  # sort the diagnostics by line number and column number
-  var sortedDiags = SortDiags(diagWithinRange)
+  var serverDiags: dict<list<any>> = diagsMap->has_key(bnr) ?
+      diagsMap[bnr].serverDiagnostics : {}
+  serverDiags[lspserver.id] = diagWithinRange
 
-  lspserver.diagsMap->extend({
-    [$'{bnr}']: {
-      sortedDiagnostics: sortedDiags,
-      diagnosticsByLnum: diagByLnum
-    }
-  })
+  var serverDiagsByLnum: dict<dict<list<any>>> = diagsMap->has_key(bnr) ?
+      diagsMap[bnr].serverDiagnosticsByLnum : {}
+  serverDiagsByLnum[lspserver.id] = diagsByLnum
+
+  # store the diagnostic for each line separately
+  var joinedServerDiags: list<dict<any>> = []
+  for diags in serverDiags->values()
+    for diag in diags
+      joinedServerDiags->add(diag)
+    endfor
+  endfor
+
+  var sortedDiags = SortDiags(joinedServerDiags)
+
+  diagsMap[bnr] = {
+    sortedDiagnostics: sortedDiags,
+    serverDiagnosticsByLnum: serverDiagsByLnum,
+    serverDiagnostics: serverDiags
+  }
 
   ProcessNewDiags(lspserver, bnr)
 
@@ -251,8 +280,8 @@ export def DiagsGetErrorCount(lspserver: dict<any>): dict<number>
   var hintCount = 0
 
   var bnr: number = bufnr()
-  if lspserver.diagsMap->has_key(bnr)
-    var diags = lspserver.diagsMap[bnr].sortedDiagnostics
+  if diagsMap->has_key(bnr)
+    var diags = diagsMap[bnr].sortedDiagnostics
     for diag in diags
       var severity = diag->get('severity', -1)
       if severity == 1
@@ -295,8 +324,8 @@ def DiagsUpdateLocList(lspserver: dict<any>, bnr: number): bool
     LspQfId = 0
   endif
 
-  if !lspserver.diagsMap->has_key(bnr) ||
-      lspserver.diagsMap[bnr].sortedDiagnostics->empty()
+  if !diagsMap->has_key(bnr) ||
+      diagsMap[bnr].sortedDiagnostics->empty()
     if LspQfId != 0
       setloclist(0, [], 'r', {id: LspQfId, items: []})
     endif
@@ -306,7 +335,7 @@ def DiagsUpdateLocList(lspserver: dict<any>, bnr: number): bool
   var qflist: list<dict<any>> = []
   var text: string
 
-  var diags = lspserver.diagsMap[bnr].sortedDiagnostics
+  var diags = diagsMap[bnr].sortedDiagnostics
   for diag in diags
     text = diag.message->substitute("\n\\+", "\n", 'g')
     qflist->add({filename: fname,
@@ -457,15 +486,23 @@ enddef
 
 # Get all diagnostics from the LSP server for a particular line in a file
 export def GetDiagsByLine(lspserver: dict<any>, bnr: number, lnum: number): list<dict<any>>
-  if lspserver.diagsMap->has_key(bnr)
-    var diagsbyLnum = lspserver.diagsMap[bnr].diagnosticsByLnum
-    if diagsbyLnum->has_key(lnum)
-      return diagsbyLnum[lnum]->sort((a, b) => {
-         return a.range.start.character - b.range.start.character
-       })
-    endif
+  if !diagsMap->has_key(bnr)
+    return []
   endif
-  return []
+
+  var diags: list<dict<any>> = []
+
+  var serverDiagsByLnum = diagsMap[bnr].serverDiagnosticsByLnum
+
+  for diagsByLnum in serverDiagsByLnum->values()
+    if diagsByLnum->has_key(lnum)
+      diags->extend(diagsByLnum[lnum])
+    endif
+  endfor
+
+  return diags->sort((a, b) => {
+    return a.range.start.character - b.range.start.character
+  })
 enddef
 
 # Utility function to do the actual jump
@@ -485,13 +522,13 @@ export def LspDiagsJump(lspserver: dict<any>, which: string, a_count: number = 0
   endif
   var bnr: number = bufnr()
 
-  if !lspserver.diagsMap->has_key(bnr) ||
-      lspserver.diagsMap[bnr].sortedDiagnostics->empty()
+  if !diagsMap->has_key(bnr) ||
+      diagsMap[bnr].sortedDiagnostics->empty()
     util.WarnMsg($'No diagnostic messages found for {fname}')
     return
   endif
 
-  var diags = lspserver.diagsMap[bnr].sortedDiagnostics
+  var diags = diagsMap[bnr].sortedDiagnostics
 
   if which == 'first'
     JumpDiag(diags[0])
index 74e0914762e07694d849d38140afb2353fba828a..5c50b232b00b226d5e105fa4a8dbd2b0a684cefa 100644 (file)
@@ -1498,7 +1498,6 @@ export def NewLspServer(name_arg: string, path_arg: string, args: list<string>,
     signaturePopup: -1,
     typeHierPopup: -1,
     typeHierFilePopup: -1,
-    diagsMap: {},
     workspaceSymbolPopup: -1,
     workspaceSymbolQuery: '',
     peekSymbolPopup: -1,