From 66e53fe8620e1d6a2d442f94b1ee27c04d0689f3 Mon Sep 17 00:00:00 2001
From: Yegappan Lakshmanan <yegappan@yahoo.com>
Date: Thu, 13 Apr 2023 07:18:47 -0700
Subject: [PATCH] Handle exceptions when starting the language server in tests

---
 autoload/lsp/callhierarchy.vim |   2 +-
 autoload/lsp/capabilities.vim  |   4 +-
 autoload/lsp/codeaction.vim    |  30 ++++----
 autoload/lsp/completion.vim    | 134 ++++++++++++++++-----------------
 test/runner.vim                |  22 ++++--
 5 files changed, 100 insertions(+), 92 deletions(-)

diff --git a/autoload/lsp/callhierarchy.vim b/autoload/lsp/callhierarchy.vim
index 6e57571..e91d08b 100644
--- a/autoload/lsp/callhierarchy.vim
+++ b/autoload/lsp/callhierarchy.vim
@@ -39,7 +39,7 @@ def CallHierarchyTreeItemRefresh(idx: number)
     if !reply->empty()
       for item in reply
 	treeItem.children->add({item: w:LspCallHierIncoming ? item.from :
-			       item.to, open: false})
+				item.to, open: false})
       endfor
     endif
   endif
diff --git a/autoload/lsp/capabilities.vim b/autoload/lsp/capabilities.vim
index 9aefc53..15a9766 100644
--- a/autoload/lsp/capabilities.vim
+++ b/autoload/lsp/capabilities.vim
@@ -286,7 +286,7 @@ export def GetClientCaps(): dict<any>
 			'source.organizeImports']
 	  }
 	},
-        isPreferredSupport: true,
+	isPreferredSupport: true,
 	disabledSupport: true
       },
       codeLens: {
@@ -307,7 +307,7 @@ export def GetClientCaps(): dict<any>
 	symbolKind: {valueSet: range(1, 25)}
       },
       hover: {
-        contentFormat: ['plaintext', 'markdown']
+	contentFormat: ['plaintext', 'markdown']
       },
       foldingRange: {lineFoldingOnly: true},
       inlayHint: {dynamicRegistration: false},
diff --git a/autoload/lsp/codeaction.vim b/autoload/lsp/codeaction.vim
index 81a146f..faa11bd 100644
--- a/autoload/lsp/codeaction.vim
+++ b/autoload/lsp/codeaction.vim
@@ -100,24 +100,24 @@ export def ApplyCodeAction(lspserver: dict<any>, actionlist: list<dict<any>>, qu
       wrap: 0,
       title: 'Code action',
       callback: (_, result) => {
-        # Invalid item selected or closed the popup
-        if result <= 0 || result > text->len()
-          return
-        endif
+	# Invalid item selected or closed the popup
+	if result <= 0 || result > text->len()
+	  return
+	endif
 
-        # Do the code action
-        HandleCodeAction(lspserver, actions[result - 1])
+	# Do the code action
+	HandleCodeAction(lspserver, actions[result - 1])
       },
       filter: (winid, key) => {
-        if key == 'h' || key == 'l'
-          winid->popup_close(-1)
-        elseif key->str2nr() > 0
-          # assume less than 10 entries are present
-          winid->popup_close(key->str2nr())
-        else
-          return popup_filter_menu(winid, key)
-        endif
-        return 1
+	if key == 'h' || key == 'l'
+	  winid->popup_close(-1)
+	elseif key->str2nr() > 0
+	  # assume less than 10 entries are present
+	  winid->popup_close(key->str2nr())
+	else
+	  return popup_filter_menu(winid, key)
+	endif
+	return 1
       },
     })
   else
diff --git a/autoload/lsp/completion.vim b/autoload/lsp/completion.vim
index dbf5b7e..11ba8fd 100644
--- a/autoload/lsp/completion.vim
+++ b/autoload/lsp/completion.vim
@@ -141,28 +141,28 @@ export def CompletionReply(lspserver: dict<any>, cItems: any)
   var start_col = start_idx + 1
 
   if opt.lspOptions.ultisnipsSupport
-      call UltiSnips#SnippetsInCurrentScope(1)
-      for key in matchfuzzy(g:current_ulti_dict_info->keys(), prefix)
-          var item = g:current_ulti_dict_info[key]
-          var parts = split(item.location, ':')
-          var txt = readfile(parts[0])[str2nr(parts[1]) : str2nr(parts[1]) + 20]
-          var restxt = item.description .. "\n\n"
-          for line in txt
-              if line == ""
-                  break
-              else
-                  restxt = restxt .. line .. "\n"
-              endif
-          endfor
-          items->add({
-              label: key,
-              data: {
-                  entryNames: [key],
-              },
-              kind: 15,
-              documentation: restxt,
-          })
+    call UltiSnips#SnippetsInCurrentScope(1)
+    for key in matchfuzzy(g:current_ulti_dict_info->keys(), prefix)
+      var item = g:current_ulti_dict_info[key]
+      var parts = split(item.location, ':')
+      var txt = readfile(parts[0])[str2nr(parts[1]) : str2nr(parts[1]) + 20]
+      var restxt = item.description .. "\n\n"
+      for line in txt
+	if line == ""
+	  break
+	else
+	  restxt = restxt .. line .. "\n"
+	endif
       endfor
+      items->add({
+	label: key,
+	data: {
+	  entryNames: [key],
+	},
+	kind: 15,
+	documentation: restxt,
+      })
+    endfor
   endif
 
   #writefile([$'chcol = {chcol}, starttext = [{starttext}], prefix = [{prefix}], start_idx = {start_idx}, end_idx = {end_idx}, start_col = {start_col}'], '/tmp/lspcomplete.log', 'a')
@@ -176,17 +176,17 @@ export def CompletionReply(lspserver: dict<any>, cItems: any)
     if item->has_key('textEdit') && opt.lspOptions.completionMatcher != 'fuzzy'
       var start_charcol: number
       if prefix != ''
-        start_charcol = charidx(starttext, start_idx) + 1
+	start_charcol = charidx(starttext, start_idx) + 1
       else
-        start_charcol = chcol
+	start_charcol = chcol
       endif
       var textEdit = item.textEdit
       var textEditStartCol = textEdit.range.start.character
       if textEditStartCol != start_charcol
-        var offset = start_charcol - textEditStartCol - 1
-        d.word = textEdit.newText[offset : ]
+	var offset = start_charcol - textEditStartCol - 1
+	d.word = textEdit.newText[offset : ]
       else
-        d.word = textEdit.newText
+	d.word = textEdit.newText
       endif
     elseif item->has_key('insertText')
       d.word = item.insertText
@@ -203,25 +203,25 @@ export def CompletionReply(lspserver: dict<any>, cItems: any)
 
       # plain text completion
       if prefix != ''
-        # If the completion item text doesn't start with the current (case
-        # ignored) keyword prefix, skip it.
-        if opt.lspOptions.completionMatcher == 'icase'
-          if d.word->tolower()->stridx(prefix) != 0
-            continue
-          endif
-        # If the completion item text doesn't fuzzy match with the current
-        # keyword prefix, skip it.
-        elseif opt.lspOptions.completionMatcher == 'fuzzy'
-          if matchfuzzy([d.word], prefix)->empty()
-            continue
-          endif
-        # If the completion item text doesn't start with the current keyword
-        # prefix, skip it.
-        else
-          if d.word->stridx(prefix) != 0
-            continue
-          endif
-        endif
+	# If the completion item text doesn't start with the current (case
+	# ignored) keyword prefix, skip it.
+	if opt.lspOptions.completionMatcher == 'icase'
+	  if d.word->tolower()->stridx(prefix) != 0
+	    continue
+	  endif
+	# If the completion item text doesn't fuzzy match with the current
+	# keyword prefix, skip it.
+	elseif opt.lspOptions.completionMatcher == 'fuzzy'
+	  if matchfuzzy([d.word], prefix)->empty()
+	    continue
+	  endif
+	# If the completion item text doesn't start with the current keyword
+	# prefix, skip it.
+	else
+	  if d.word->stridx(prefix) != 0
+	    continue
+	  endif
+	endif
       endif
     endif
 
@@ -242,18 +242,18 @@ export def CompletionReply(lspserver: dict<any>, cItems: any)
       d.info = 'Lazy doc'
     else
       if item->has_key('detail') && item.detail != ''
-        # Solve a issue where if a server send a detail field
-        # with a "\n", on the menu will be everything joined with
-        # a "^@" separating it. (example: clangd)
-        d.menu = item.detail->split("\n")[0]
+	# Solve a issue where if a server send a detail field
+	# with a "\n", on the menu will be everything joined with
+	# a "^@" separating it. (example: clangd)
+	d.menu = item.detail->split("\n")[0]
       endif
       if item->has_key('documentation')
-        if item.documentation->type() == v:t_string && item.documentation != ''
-          d.info = item.documentation
-        elseif item.documentation->type() == v:t_dict
-            && item.documentation.value->type() == v:t_string
-          d.info = item.documentation.value
-        endif
+	if item.documentation->type() == v:t_string && item.documentation != ''
+	  d.info = item.documentation
+	elseif item.documentation->type() == v:t_dict
+	    && item.documentation.value->type() == v:t_string
+	  d.info = item.documentation.value
+	endif
       endif
     endif
 
@@ -320,14 +320,14 @@ def ShowCompletionDocumentation(cItem: any)
     if cItem.documentation->type() == v:t_dict
       # MarkupContent
       if cItem.documentation.kind == 'plaintext'
-        infoText->extend(cItem.documentation.value->split("\n"))
-        infoKind = 'text'
+	infoText->extend(cItem.documentation.value->split("\n"))
+	infoKind = 'text'
       elseif cItem.documentation.kind == 'markdown'
-        infoText->extend(cItem.documentation.value->split("\n"))
-        infoKind = 'lspgfm'
+	infoText->extend(cItem.documentation.value->split("\n"))
+	infoKind = 'lspgfm'
       else
-        util.ErrMsg($'Error: Unsupported documentation type ({cItem.documentation.kind})')
-        return
+	util.ErrMsg($'Error: Unsupported documentation type ({cItem.documentation.kind})')
+	return
       endif
     elseif cItem.documentation->type() == v:t_string
       infoText->extend(cItem.documentation->split("\n"))
@@ -364,7 +364,7 @@ enddef
 # process the 'completionItem/resolve' reply from the LSP server
 # Result: CompletionItem
 export def CompletionResolveReply(lspserver: dict<any>, cItem: any)
-    ShowCompletionDocumentation(cItem)
+  ShowCompletionDocumentation(cItem)
 enddef
 
 # omni complete handler
@@ -474,9 +474,9 @@ def LspResolve()
   var item = v:event.completed_item
   if item->has_key('user_data') && !item.user_data->empty()
       if !item.user_data->has_key('documentation')
-        lspserver.resolveCompletion(item.user_data)
+	lspserver.resolveCompletion(item.user_data)
       else
-        ShowCompletionDocumentation(item.user_data)
+	ShowCompletionDocumentation(item.user_data)
       endif
   endif
 enddef
@@ -567,9 +567,9 @@ export def BufferInit(lspserver: dict<any>, bnr: number, ftype: string)
   endif
 
   acmds->add({bufnr: bnr,
-                 event: 'CompleteChanged',
-                 group: 'LSPBufferAutocmds',
-                 cmd: 'LspSetPopupFileType()'})
+	      event: 'CompleteChanged',
+	      group: 'LSPBufferAutocmds',
+	      cmd: 'LspSetPopupFileType()'})
 
   # Execute LSP server initiated text edits after completion
   acmds->add({bufnr: bnr,
diff --git a/test/runner.vim b/test/runner.vim
index e52f3ad..845787a 100644
--- a/test/runner.vim
+++ b/test/runner.vim
@@ -27,25 +27,33 @@ def LspRunTests()
     v:errmsg = ''
     try
       :%bw!
-      exe 'g:' .. f
+      exe $'g:{f}'
     catch
-      call add(v:errors, "Error: Test " .. f .. " failed with exception " .. v:exception .. " at " .. v:throwpoint)
+      call add(v:errors, $'Error: Test {f} failed with exception {v:exception} at {v:throwpoint}')
     endtry
     if v:errmsg != ''
-      call add(v:errors, "Error: Test " .. f .. " generated error " .. v:errmsg)
+      call add(v:errors, $'Error: Test {f} generated error {v:errmsg}')
     endif
     if !v:errors->empty()
       writefile(v:errors, 'results.txt', 'a')
-      writefile([f .. ': FAIL'], 'results.txt', 'a')
+      writefile([$'{f}: FAIL'], 'results.txt', 'a')
     else
-      writefile([f .. ': pass'], 'results.txt', 'a')
+      writefile([$'{f}: pass'], 'results.txt', 'a')
     endif
   endfor
 enddef
 
-exe 'source ' .. g:TestName
+try
 
-g:StartLangServer()
+  exe $'source {g:TestName}'
+  g:StartLangServer()
+
+catch
+
+  call add(v:errors, $'Error: Failed to start language server for {g:TestName} with exception {v:exception} at {v:throwpoint}')
+  qall!
+
+endtry
 
 LspRunTests()
 qall!
-- 
2.51.0