]> Sergey Matveev's repositories - vim-lsp.git/blob - test/clangd_tests.vim
3c88ff0a9bf306a13d192a9b9d6af71a5eafbf48
[vim-lsp.git] / test / clangd_tests.vim
1 vim9script
2 # Unit tests for Vim Language Server Protocol (LSP) clangd client
3
4 source common.vim
5
6 var lspOpts = {autoComplete: false, highlightDiagInline: true}
7 g:LspOptionsSet(lspOpts)
8
9 g:LSPTest_modifyDiags = false
10
11 var lspServers = [{
12       filetype: ['c', 'cpp'],
13       path: (exepath('clangd-15') ?? exepath('clangd')),
14       args: ['--background-index', '--clang-tidy'],
15       initializationOptions: { clangdFileStatus: true },
16       customNotificationHandlers: {
17         'textDocument/clangd.fileStatus': (lspserver: dict<any>, reply: dict<any>) => {
18           g:LSPTest_customNotificationHandlerReplied = true
19         }
20       },
21       processDiagHandler: (diags: list<dict<any>>) => {
22         if g:LSPTest_modifyDiags != true
23           return diags
24         endif
25
26         return diags->map((ix, diag) => {
27           diag.message = $'this is overridden'
28           return diag
29         })
30       }
31   }]
32 call LspAddServer(lspServers)
33
34 var clangdVerDetail = systemlist($'{shellescape(lspServers[0].path)} --version')
35 var clangdVerMajor = clangdVerDetail->matchstr('.*version \d\+\..*')->substitute('.* \(\d\+\)\..*', '\1', 'g')->str2nr()
36 echomsg clangdVerDetail
37
38
39 # Test for formatting a file using LspFormat
40 def g:Test_LspFormat()
41   :silent! edit XLspFormat.c
42   sleep 200m
43   setline(1, ['  int i;', '  int j;'])
44   :redraw!
45   :LspFormat
46   assert_equal(['int i;', 'int j;'], getline(1, '$'))
47
48   deletebufline('', 1, '$')
49   setline(1, ['int f1(int i)', '{', 'int j = 10; return j;', '}'])
50   :redraw!
51   :LspFormat
52   assert_equal(['int f1(int i) {', '  int j = 10;', '  return j;', '}'],
53                                                         getline(1, '$'))
54
55   deletebufline('', 1, '$')
56   setline(1, ['', 'int     i;'])
57   :redraw!
58   :LspFormat
59   assert_equal(['', 'int i;'], getline(1, '$'))
60
61   deletebufline('', 1, '$')
62   setline(1, [' int i;'])
63   :redraw!
64   :LspFormat
65   assert_equal(['int i;'], getline(1, '$'))
66
67   deletebufline('', 1, '$')
68   setline(1, ['  int  i; '])
69   :redraw!
70   :LspFormat
71   assert_equal(['int i;'], getline(1, '$'))
72
73   deletebufline('', 1, '$')
74   setline(1, ['int  i;', '', '', ''])
75   :redraw!
76   :LspFormat
77   assert_equal(['int i;'], getline(1, '$'))
78
79   deletebufline('', 1, '$')
80   setline(1, ['int f1(){int x;int y;x=1;y=2;return x+y;}'])
81   :redraw!
82   :LspFormat
83   var expected: list<string> =<< trim END
84     int f1() {
85       int x;
86       int y;
87       x = 1;
88       y = 2;
89       return x + y;
90     }
91   END
92   assert_equal(expected, getline(1, '$'))
93
94   deletebufline('', 1, '$')
95   setline(1, ['', '', '', ''])
96   :redraw!
97   :LspFormat
98   assert_equal([''], getline(1, '$'))
99
100   deletebufline('', 1, '$')
101   var lines: list<string> =<< trim END
102     int f1() {
103       int i, j;
104         for (i = 1; i < 10; i++) { j++; }
105         for (j = 1; j < 10; j++) { i++; }
106     }
107   END
108   setline(1, lines)
109   :redraw!
110   :4LspFormat
111   expected =<< trim END
112     int f1() {
113       int i, j;
114         for (i = 1; i < 10; i++) { j++; }
115         for (j = 1; j < 10; j++) {
116           i++;
117         }
118     }
119   END
120   assert_equal(expected, getline(1, '$'))
121   bw!
122
123   # empty file
124   assert_equal('', execute('LspFormat'))
125
126   # file without an LSP server
127   edit a.raku
128   assert_equal('Error: Language server for "raku" file type supporting "documentFormatting" feature is not found',
129                execute('LspFormat')->split("\n")[0])
130
131   :%bw!
132 enddef
133
134 # Test for formatting a file using 'formatexpr'
135 def g:Test_LspFormatExpr()
136   :silent! edit XLspFormat.c
137   sleep 200m
138   setlocal formatexpr=lsp#lsp#FormatExpr()
139   setline(1, ['  int i;', '  int j;'])
140   :redraw!
141   normal! ggVGgq
142   assert_equal(['int i;', 'int j;'], getline(1, '$'))
143
144   # empty line/file
145   deletebufline('', 1, '$')
146   setline(1, [''])
147   redraw!
148   normal! ggVGgq
149   assert_equal([''], getline(1, '$'))
150
151   setlocal formatexpr&
152   :%bw!
153 enddef
154
155 # Test for :LspShowReferences - showing all the references to a symbol in a
156 # file using LSP
157 def g:Test_LspShowReferences()
158   :silent! edit XshowRefs.c
159   sleep 200m
160   var lines: list<string> =<< trim END
161     int count;
162     void redFunc()
163     {
164         int count, i;
165         count = 10;
166         i = count;
167     }
168     void blueFunc()
169     {
170         int count, j;
171         count = 20;
172         j = count;
173     }
174   END
175   setline(1, lines)
176   :redraw!
177   cursor(5, 2)
178   var bnr: number = bufnr()
179   :LspShowReferences
180   sleep 100m
181   assert_equal('quickfix', getwinvar(winnr('$'), '&buftype'))
182   var loclist: list<dict<any>> = getloclist(0)
183   assert_equal(bnr, loclist[0].bufnr)
184   assert_equal(3, loclist->len())
185   assert_equal([4, 6], [loclist[0].lnum, loclist[0].col])
186   assert_equal([5, 2], [loclist[1].lnum, loclist[1].col])
187   assert_equal([6, 6], [loclist[2].lnum, loclist[2].col])
188   :lclose
189   cursor(1, 5)
190   :LspShowReferences
191   assert_equal(1, getloclist(0)->len())
192   loclist = getloclist(0)
193   assert_equal([1, 5], [loclist[0].lnum, loclist[0].col])
194   :lclose
195
196   # Test for opening in qf list
197   g:LspOptionsSet({ useQuickfixForLocations: true })
198   cursor(5, 2)
199   :LspShowReferences
200   sleep 100m
201   assert_equal('quickfix', getwinvar(winnr('$'), '&buftype'))
202   :cclose
203   var qfl: list<dict<any>> = getqflist()
204   assert_equal(3, qfl->len())
205   assert_equal(bufnr(), qfl[0].bufnr)
206   assert_equal([4, 6], [qfl[0].lnum, qfl[0].col])
207   assert_equal([5, 2], [qfl[1].lnum, qfl[1].col])
208   assert_equal([6, 6], [qfl[2].lnum, qfl[2].col])
209   cursor(1, 5)
210   :LspShowReferences
211   assert_equal(1, getqflist()->len())
212   qfl = getqflist()
213   assert_equal([1, 5], [qfl[0].lnum, qfl[0].col])
214   :cclose
215   g:LspOptionsSet({ useQuickfixForLocations: false })
216
217   # Test for maintaining buffer focus
218   g:LspOptionsSet({ keepFocusInReferences: false })
219   :LspShowReferences
220   assert_equal('', getwinvar(0, '&buftype'))
221   :lclose
222   g:LspOptionsSet({ keepFocusInReferences: true })
223
224   # Test for LspPeekReferences
225
226   # Opening the preview window with an unsaved buffer displays the "E37: No
227   # write since last change" error message.  To disable this message, mark the
228   # buffer as not modified.
229   setlocal nomodified
230   cursor(10, 6)
231   :LspPeekReferences
232   sleep 50m
233   var ids = popup_list()
234   assert_equal(2, ids->len())
235   var filePopupAttrs = ids[0]->popup_getoptions()
236   var refPopupAttrs = ids[1]->popup_getoptions()
237   assert_match('XshowRefs', filePopupAttrs.title)
238   assert_equal('Symbol References', refPopupAttrs.title)
239   assert_equal(10, line('.', ids[0]))
240   assert_equal(3, line('$', ids[1]))
241   feedkeys("jj\<CR>", 'xt')
242   assert_equal(12, line('.'))
243   assert_equal([], popup_list())
244   popup_clear()
245
246   bw!
247
248   # empty file
249   assert_equal('', execute('LspShowReferences'))
250
251   # file without an LSP server
252   edit a.raku
253   assert_equal('Error: Language server for "raku" file type supporting "references" feature is not found',
254                execute('LspShowReferences')->split("\n")[0])
255
256   :%bw!
257 enddef
258
259 # Test for LSP diagnostics
260 def g:Test_LspDiag()
261   :silent! edit XLspDiag.c
262   sleep 200m
263   var lines: list<string> =<< trim END
264     void blueFunc()
265     {
266         int count, j:
267         count = 20;
268         j <= count;
269         j = 10;
270         MyFunc();
271     }
272   END
273   setline(1, lines)
274   g:WaitForServerFileLoad(1)
275   var bnr: number = bufnr()
276   :redraw!
277   :LspDiagShow
278   var qfl: list<dict<any>> = getloclist(0)
279   assert_equal('quickfix', getwinvar(winnr('$'), '&buftype'))
280   assert_equal(bnr, qfl[0].bufnr)
281   assert_equal(3, qfl->len())
282   assert_equal([3, 14, 'E'], [qfl[0].lnum, qfl[0].col, qfl[0].type])
283   assert_equal([5, 2, 'W'], [qfl[1].lnum, qfl[1].col, qfl[1].type])
284   assert_equal([7, 2, 'W'], [qfl[2].lnum, qfl[2].col, qfl[2].type])
285   close
286   g:LspOptionsSet({showDiagInPopup: false})
287   normal gg
288   var output = execute('LspDiagCurrent')->split("\n")
289   assert_equal('Warn: No diagnostic messages found for current line', output[0])
290   :LspDiagFirst
291   assert_equal([3, 14], [line('.'), col('.')])
292   output = execute('LspDiagCurrent')->split("\n")
293   assert_equal("Expected ';' at end of declaration (fix available)", output[0])
294   :normal! 0
295   :LspDiagHere
296   assert_equal([3, 14], [line('.'), col('.')])
297   :LspDiagNext
298   assert_equal([5, 2], [line('.'), col('.')])
299   :LspDiagNext
300   assert_equal([7, 2], [line('.'), col('.')])
301   output = execute('LspDiagNext')->split("\n")
302   assert_equal('Warn: No more diagnostics found', output[0])
303   :LspDiagPrev
304   :LspDiagPrev
305   :LspDiagPrev
306   output = execute('LspDiagPrev')->split("\n")
307   assert_equal('Warn: No more diagnostics found', output[0])
308
309   # Test for maintaining buffer focus
310   g:LspOptionsSet({ keepFocusInDiags: false })
311   :LspDiagShow
312   assert_equal('', getwinvar(0, '&buftype'))
313   :lclose
314   g:LspOptionsSet({ keepFocusInDiags: true })
315
316   # :[count]LspDiagNext
317   cursor(3, 1)
318   :2LspDiagNext
319   assert_equal([5, 2], [line('.'), col('.')])
320   :2LspDiagNext
321   assert_equal([7, 2], [line('.'), col('.')])
322   output = execute(':2LspDiagNext')->split("\n")
323   assert_equal('Warn: No more diagnostics found', output[0])
324
325   # :[count]LspDiagPrev
326   cursor(7, 2)
327   :4LspDiagPrev
328   assert_equal([3, 14], [line('.'), col('.')])
329   output = execute(':4LspDiagPrev')->split("\n")
330   assert_equal('Warn: No more diagnostics found', output[0])
331
332   :%d
333   setline(1, ['void blueFunc()', '{', '}'])
334   g:WaitForDiags(0)
335   output = execute('LspDiagShow')->split("\n")
336   assert_match('Warn: No diagnostic messages found for', output[0])
337   g:LspOptionsSet({showDiagInPopup: true})
338
339   popup_clear()
340   :%bw!
341 enddef
342
343 # Test for LSP diagnostics handler
344 def g:Test_LspProcessDiagHandler()
345   g:LSPTest_modifyDiags = true
346   g:LspOptionsSet({showDiagInPopup: false})
347
348   :silent! edit XLspProcessDiag.c
349   sleep 200m
350   var lines: list<string> =<< trim END
351     void blueFunc()
352     {
353         int count, j:
354     }
355   END
356   setline(1, lines)
357   g:WaitForServerFileLoad(1)
358   :redraw!
359   normal gg
360
361   :LspDiagFirst
362   assert_equal([3, 14], [line('.'), col('.')])
363
364   var output = execute('LspDiagCurrent')->split("\n")
365   assert_equal("this is overridden", output[0])
366
367   g:LspOptionsSet({showDiagInPopup: true})
368   g:LSPTest_modifyDiags = false
369   :%bw!
370 enddef
371
372 # Diag location list should be automatically updated when the list of diags
373 # changes.
374 def g:Test_DiagLocListAutoUpdate()
375   :silent! edit XdiagLocListAutoUpdate.c
376   :sleep 200m
377   setloclist(0, [], 'f')
378   var lines: list<string> =<< trim END
379     int i:
380     int j;
381   END
382   setline(1, lines)
383   var bnr = bufnr()
384   g:WaitForServerFileLoad(1)
385   :redraw!
386   var d = lsp#diag#GetDiagsForBuf()[0]
387   assert_equal({start: {line: 0, character: 5}, end: {line: 0, character: 6}},
388                d.range)
389
390   :LspDiagShow
391   assert_equal(1, line('$'))
392   wincmd w
393   setline(2, 'int j:')
394   redraw!
395   g:WaitForDiags(2)
396   var l = lsp#diag#GetDiagsForBuf()
397   assert_equal({start: {line: 0, character: 5}, end: {line: 0, character: 6}},
398                l[0].range)
399   assert_equal({start: {line: 1, character: 5}, end: {line: 1, character: 6}},
400                l[1].range)
401   wincmd w
402   assert_equal(2, line('$'))
403   wincmd w
404   deletebufline('', 1, '$')
405   redraw!
406   g:WaitForDiags(0)
407   assert_equal([], lsp#diag#GetDiagsForBuf())
408   wincmd w
409   assert_equal([''], getline(1, '$'))
410   :lclose
411
412   setloclist(0, [], 'f')
413   :%bw!
414 enddef
415
416 # Test that the client have been able to configure the server to speak utf-32
417 def g:Test_UnicodeColumnCalc()
418   :silent! edit XUnicodeColumn.c
419   sleep 200m
420   var lines: list<string> =<< trim END
421     int count;
422     int fn(int a)
423     {
424       int 😊😊😊😊;
425       😊😊😊😊 = a;
426
427       int b;
428       b = a;
429       return    count + 1;
430     }
431   END
432   setline(1, lines)
433   :redraw!
434
435   cursor(5, 1) # 😊😊😊😊 = a;
436   search('a')
437   assert_equal([],
438               execute('LspGotoDefinition')->split("\n"))
439   assert_equal([2, 12], [line('.'), col('.')])
440
441   cursor(8, 1) # b = a;
442   search('a')
443   assert_equal([],
444               execute('LspGotoDefinition')->split("\n"))
445   assert_equal([2, 12], [line('.'), col('.')])
446
447   :%bw!
448 enddef
449
450 # Test for multiple LSP diagnostics on the same line
451 def g:Test_LspDiag_Multi()
452   :silent! edit XLspDiagMulti.c
453   sleep 200m
454
455   var bnr: number = bufnr()
456
457   var lines =<< trim END
458     int i = "a";
459     int j = i;
460     int y = 0;
461   END
462   setline(1, lines)
463   :redraw!
464   # TODO: Waiting count doesn't include Warning, Info, and Hint diags
465   if clangdVerMajor > 14
466         g:WaitForServerFileLoad(3)
467   else
468         g:WaitForServerFileLoad(2)
469   endif
470   :LspDiagShow
471   var qfl: list<dict<any>> = getloclist(0)
472   assert_equal('quickfix', getwinvar(winnr('$'), '&buftype'))
473   assert_equal(bnr, qfl[0].bufnr)
474   assert_equal(3, qfl->len())
475   if clangdVerMajor > 14
476         assert_equal([1, 5, 'E'], [qfl[0].lnum, qfl[0].col, qfl[0].type])
477   else
478         assert_equal([1, 5, 'W'], [qfl[0].lnum, qfl[0].col, qfl[0].type])
479   endif
480   assert_equal([1, 9, 'E'], [qfl[1].lnum, qfl[1].col, qfl[1].type])
481   assert_equal([2, 9, 'E'], [qfl[2].lnum, qfl[2].col, qfl[2].type])
482   close
483
484   :sleep 100m
485   cursor(2, 1)
486   assert_equal('', execute('LspDiagPrev'))
487   assert_equal([1, 9], [line('.'), col('.')])
488
489   assert_equal('', execute('LspDiagPrev'))
490   assert_equal([1, 5], [line('.'), col('.')])
491
492   var output = execute('LspDiagPrev')->split("\n")
493   assert_equal('Warn: No more diagnostics found', output[0])
494
495   cursor(2, 1)
496   assert_equal('', execute('LspDiagFirst'))
497   assert_equal([1, 5], [line('.'), col('.')])
498   assert_equal('', execute('LspDiagNext'))
499   assert_equal([1, 9], [line('.'), col('.')])
500   cursor(1, 1)
501   assert_equal('', execute('LspDiagLast'))
502   assert_equal([2, 9], [line('.'), col('.')])
503   popup_clear()
504
505   # Test for :LspDiagHere on a line with multiple diagnostics
506   cursor(1, 1)
507   :LspDiagHere
508   assert_equal([1, 5], [line('.'), col('.')])
509   var ids = popup_list()
510   assert_equal(1, ids->len())
511   assert_match('Incompatible pointer to integer', getbufline(ids[0]->winbufnr(), 1, '$')[0])
512   popup_clear()
513   cursor(1, 6)
514   :LspDiagHere
515   assert_equal([1, 9], [line('.'), col('.')])
516   ids = popup_list()
517   assert_equal(1, ids->len())
518   assert_match('Initializer element is not', getbufline(ids[0]->winbufnr(), 1, '$')[0])
519   popup_clear()
520
521   # Line without diagnostics
522   cursor(3, 1)
523   output = execute('LspDiagHere')->split("\n")
524   assert_equal('Warn: No more diagnostics found on this line', output[0])
525
526   g:LspOptionsSet({showDiagInPopup: false})
527   for i in range(1, 5)
528     cursor(1, i)
529     output = execute('LspDiagCurrent')->split('\n')
530     assert_match('Incompatible pointer to integer', output[0])
531   endfor
532   for i in range(6, 12)
533     cursor(1, i)
534     output = execute('LspDiagCurrent')->split('\n')
535     assert_match('Initializer element is not ', output[0])
536   endfor
537   g:LspOptionsSet({showDiagInPopup: true})
538
539   # Check for exact diag ":LspDiagCurrent!"
540   g:LspOptionsSet({showDiagInPopup: false})
541   for i in range(1, 4)
542     cursor(1, i)
543     output = execute('LspDiagCurrent!')->split('\n')
544     assert_equal('Warn: No diagnostic messages found for current position', output[0])
545   endfor
546
547   cursor(1, 5)
548   output = execute('LspDiagCurrent!')->split('\n')
549   assert_match('Incompatible pointer to integer', output[0])
550
551   for i in range(6, 8)
552     cursor(1, i)
553     output = execute('LspDiagCurrent!')->split('\n')
554     assert_equal('Warn: No diagnostic messages found for current position', output[0])
555   endfor
556
557   for i in range(9, 11)
558     cursor(1, i)
559     output = execute('LspDiagCurrent!')->split('\n')
560     assert_match('Initializer element is not ', output[0])
561   endfor
562   for i in range(12, 12)
563     cursor(1, i)
564     output = execute('LspDiagCurrent!')->split('\n')
565     assert_equal('Warn: No diagnostic messages found for current position', output[0])
566   endfor
567
568   g:LspOptionsSet({showDiagInPopup: true})
569
570   # :[count]LspDiagNext
571   g:LspOptionsSet({showDiagInPopup: false})
572   cursor(1, 1)
573   :2LspDiagNext
574   assert_equal([1, 9], [line('.'), col('.')])
575   :2LspDiagNext
576   assert_equal([2, 9], [line('.'), col('.')])
577   output = execute(':2LspDiagNext')->split("\n")
578   assert_equal('Warn: No more diagnostics found', output[0])
579
580   cursor(1, 1)
581   :99LspDiagNext
582   assert_equal([2, 9], [line('.'), col('.')])
583   g:LspOptionsSet({showDiagInPopup: true})
584
585   # :[count]LspDiagPrev
586   g:LspOptionsSet({showDiagInPopup: false})
587   cursor(1, 1)
588   :2LspDiagPrev
589   assert_equal('Warn: No more diagnostics found', output[0])
590   cursor(3, 3)
591   :2LspDiagPrev
592   assert_equal([1, 9], [line('.'), col('.')])
593   :2LspDiagPrev
594   assert_equal([1, 5], [line('.'), col('.')])
595   output = execute(':2LspDiagPrev')->split("\n")
596   assert_equal('Warn: No more diagnostics found', output[0])
597
598   cursor(3, 3)
599   :99LspDiagPrev
600   assert_equal([1, 5], [line('.'), col('.')])
601   g:LspOptionsSet({showDiagInPopup: true})
602
603   :%bw!
604 enddef
605
606 # Test for highlight diag inline
607 def g:Test_LspHighlightDiagInline()
608   :silent! edit XLspHighlightDiag.c
609   sleep 200m
610   setline(1, [
611     'int main()',
612     '{',
613       '    struct obj obj',
614       '',
615       '    return 1;',
616     '}',
617   ])
618
619   # TODO: Waiting count doesn't include Warning, Info, and Hint diags
620   g:WaitForDiags(2)
621
622   var props = prop_list(1)
623   assert_equal(0, props->len())
624   props = prop_list(2)
625   assert_equal(0, props->len())
626   props = prop_list(3)
627   assert_equal(2, props->len())
628   assert_equal([
629     {'id': 0, 'col': 12, 'type_bufnr': 0, 'end': 1, 'type': 'LspDiagInlineInfo', 'length': 3, 'start': 1},
630     {'id': 0, 'col': 16, 'type_bufnr': 0, 'end': 1, 'type': 'LspDiagInlineError', 'length': 3, 'start': 1}
631   ], props)
632   props = prop_list(4)
633   assert_equal(0, props->len())
634   props = prop_list(5)
635   assert_equal(1, props->len())
636   assert_equal([{'id': 0, 'col': 5, 'type_bufnr': 0, 'end': 1, 'type': 'LspDiagInlineError', 'length': 6, 'start': 1}], props)
637   props = prop_list(6)
638   assert_equal(0, props->len())
639
640   :%bw!
641 enddef
642
643 # Test for :LspCodeAction
644 def g:Test_LspCodeAction()
645   silent! edit XLspCodeAction.c
646   sleep 200m
647   var lines: list<string> =<< trim END
648     void testFunc()
649     {
650         int count;
651         count == 20;
652     }
653   END
654   setline(1, lines)
655   g:WaitForServerFileLoad(0)
656   cursor(4, 1)
657   redraw!
658   :LspCodeAction 1
659   assert_equal("\tcount = 20;", getline(4))
660
661   setline(4, "\tcount = 20:")
662   cursor(4, 1)
663   sleep 500m
664   :LspCodeAction 0
665   assert_equal("\tcount = 20:", getline(4))
666
667   cursor(4, 1)
668   :LspCodeAction 2
669   assert_equal("\tcount = 20:", getline(4))
670
671   cursor(4, 1)
672   :LspCodeAction 1
673   assert_equal("\tcount = 20;", getline(4))
674   bw!
675
676   # pattern and string prefix
677   silent! edit XLspCodeActionPattern.c
678   sleep 200m
679   var lines2: list<string> =<< trim END
680     void testFunc()
681     {
682         int count;
683         if (count = 1) {
684         }
685     }
686   END
687   setline(1, lines2)
688   g:WaitForServerFileLoad(0)
689   cursor(4, 1)
690   redraw!
691   :LspCodeAction use
692   assert_equal("\tif (count == 1) {", getline(4))
693
694   setline(4, "\tif (count = 1) {")
695   cursor(4, 1)
696   sleep 500m
697   :LspCodeAction /paren
698   assert_equal("\tif ((count = 1)) {", getline(4))
699
700   setline(4, "\tif (count = 1) {")
701   cursor(4, 1)
702   sleep 500m
703   :LspCodeAction NON_EXISTING_PREFIX
704   assert_equal("\tif (count = 1) {", getline(4))
705
706   cursor(4, 1)
707   :LspCodeAction /NON_EXISTING_REGEX
708   assert_equal("\tif (count = 1) {", getline(4))
709   bw!
710
711   # empty file
712   assert_equal('', execute('LspCodeAction'))
713
714   # file without an LSP server
715   edit a.raku
716   assert_equal('Error: Language server for "raku" file type supporting "codeAction" feature is not found',
717                execute('LspCodeAction')->split("\n")[0])
718
719   :%bw!
720 enddef
721
722 # Test for :LspRename
723 def g:Test_LspRename()
724   silent! edit XLspRename.c
725   sleep 200m
726   var lines: list<string> =<< trim END
727     void F1(int count)
728     {
729         count = 20;
730
731         ++count;
732     }
733
734     void F2(int count)
735     {
736         count = 5;
737     }
738   END
739   setline(1, lines)
740   g:WaitForServerFileLoad(0)
741   cursor(1, 1)
742   search('count')
743   redraw!
744   feedkeys(":LspRename\<CR>er\<CR>", "xt")
745   redraw!
746   var expected: list<string> =<< trim END
747     void F1(int counter)
748     {
749         counter = 20;
750
751         ++counter;
752     }
753
754     void F2(int count)
755     {
756         count = 5;
757     }
758   END
759   assert_equal(expected, getline(1, '$'))
760
761   cursor(1, 1)
762   search('counter')
763   LspRename countvar
764   var expected2: list<string> =<< trim END
765     void F1(int countvar)
766     {
767         countvar = 20;
768
769         ++countvar;
770     }
771
772     void F2(int count)
773     {
774         count = 5;
775     }
776   END
777   assert_equal(expected2, getline(1, '$'))
778   sleep 100m
779   bw!
780
781   # empty file
782   assert_equal('', execute('LspRename'))
783
784   # file without an LSP server
785   edit a.raku
786   assert_equal('Error: Language server for "raku" file type supporting "rename" feature is not found',
787                execute('LspRename')->split("\n")[0])
788
789   :%bw!
790 enddef
791
792 # Test for :LspSelectionExpand and :LspSelectionShrink
793 def g:Test_LspSelection()
794   silent! edit XLspSelection.c
795   sleep 200m
796   var lines: list<string> =<< trim END
797     void fnSel(int count)
798     {
799         int i;
800         for (i = 0; i < 10; i++) {
801            count++;
802         }
803         count = 20;
804     }
805   END
806   setline(1, lines)
807   g:WaitForServerFileLoad(0)
808   # start a block-wise visual mode, LspSelectionExpand should change this to
809   # a characterwise visual mode.
810   exe "normal! 1G\<C-V>G\"_y"
811   cursor(2, 1)
812   redraw!
813   :LspSelectionExpand
814   redraw!
815   normal! y
816   assert_equal('v', visualmode())
817   assert_equal([2, 8], [line("'<"), line("'>")])
818   # start a linewise visual mode, LspSelectionExpand should change this to
819   # a characterwise visual mode.
820   exe "normal! 3GViB\"_y"
821   cursor(4, 29)
822   redraw!
823   :LspSelectionExpand
824   redraw!
825   normal! y
826   assert_equal('v', visualmode())
827   assert_equal([4, 5, 6, 5], [line("'<"), col("'<"), line("'>"), col("'>")])
828
829   # Expand the visual selection
830   xnoremap <silent> le <Cmd>LspSelectionExpand<CR>
831   xnoremap <silent> ls <Cmd>LspSelectionShrink<CR>
832   cursor(5, 8)
833   normal vley
834   assert_equal([5, 8, 5, 12], [line("'<"), col("'<"), line("'>"), col("'>")])
835   cursor(5, 8)
836   normal vleley
837   assert_equal([5, 8, 5, 14], [line("'<"), col("'<"), line("'>"), col("'>")])
838   cursor(5, 8)
839   normal vleleley
840   assert_equal([4, 30, 6, 5], [line("'<"), col("'<"), line("'>"), col("'>")])
841   cursor(5, 8)
842   normal vleleleley
843   assert_equal([4, 5, 6, 5], [line("'<"), col("'<"), line("'>"), col("'>")])
844   cursor(5, 8)
845   normal vleleleleley
846   assert_equal([2, 1, 8, 1], [line("'<"), col("'<"), line("'>"), col("'>")])
847   cursor(5, 8)
848   normal vleleleleleley
849   assert_equal([1, 1, 8, 1], [line("'<"), col("'<"), line("'>"), col("'>")])
850   cursor(5, 8)
851   normal vleleleleleleley
852   assert_equal([1, 1, 8, 1], [line("'<"), col("'<"), line("'>"), col("'>")])
853
854   # Shrink the visual selection
855   cursor(5, 8)
856   normal vlsy
857   assert_equal([5, 8, 5, 12], [line("'<"), col("'<"), line("'>"), col("'>")])
858   cursor(5, 8)
859   normal vlelsy
860   assert_equal([5, 8, 5, 12], [line("'<"), col("'<"), line("'>"), col("'>")])
861   cursor(5, 8)
862   normal vlelelsy
863   assert_equal([5, 8, 5, 12], [line("'<"), col("'<"), line("'>"), col("'>")])
864   cursor(5, 8)
865   normal vlelelelsy
866   assert_equal([5, 8, 5, 14], [line("'<"), col("'<"), line("'>"), col("'>")])
867   cursor(5, 8)
868   normal vlelelelelsy
869   assert_equal([4, 30, 6, 5], [line("'<"), col("'<"), line("'>"), col("'>")])
870   cursor(5, 8)
871   normal vlelelelelelsy
872   assert_equal([4, 5, 6, 5], [line("'<"), col("'<"), line("'>"), col("'>")])
873   cursor(5, 8)
874   normal vlelelelelelelsy
875   assert_equal([2, 1, 8, 1], [line("'<"), col("'<"), line("'>"), col("'>")])
876
877   xunmap le
878   xunmap ls
879   bw!
880
881   # empty file
882   assert_equal('', execute('LspSelectionExpand'))
883
884   # file without an LSP server
885   edit a.raku
886   assert_equal('Error: Language server for "raku" file type supporting "selectionRange" feature is not found',
887                execute('LspSelectionExpand')->split("\n")[0])
888
889   :%bw!
890 enddef
891
892 # Test for :LspGotoDefinition, :LspGotoDeclaration and :LspGotoImpl
893 def g:Test_LspGotoSymbol()
894   settagstack(0, {items: []})
895   silent! edit XLspGotoSymbol.cpp
896   sleep 600m
897   var lines: list<string> =<< trim END
898     class base {
899         public:
900             virtual void print();
901     };
902
903     void base::print()
904     {
905     }
906
907     class derived : public base {
908         public:
909             void print() {}
910     };
911
912     void f1(void)
913     {
914         base *bp;
915         derived d;
916         bp = &d;
917
918         bp->print();
919     }
920   END
921   setline(1, lines)
922   g:WaitForServerFileLoad(0)
923
924   cursor(21, 6)
925   :LspGotoDeclaration
926   assert_equal([3, 19], [line('.'), col('.')])
927   exe "normal! \<C-t>"
928   assert_equal([21, 6], [line('.'), col('.')])
929   assert_equal(1, winnr('$'))
930
931   :LspGotoDefinition
932   assert_equal([6, 12], [line('.'), col('.')])
933   exe "normal! \<C-t>"
934   assert_equal([21, 6], [line('.'), col('.')])
935   assert_equal(1, winnr('$'))
936
937   # Command modifiers
938   :topleft LspGotoDefinition
939   assert_equal([6, 12], [line('.'), col('.')])
940   assert_equal([1, 2], [winnr(), winnr('$')])
941   close
942   exe "normal! \<C-t>"
943   assert_equal([21, 6], [line('.'), col('.')])
944
945   :tab LspGotoDefinition
946   assert_equal([6, 12], [line('.'), col('.')])
947   assert_equal([2, 2, 1], [tabpagenr(), tabpagenr('$'), winnr('$')])
948   tabclose
949   exe "normal! \<C-t>"
950   assert_equal([21, 6], [line('.'), col('.')])
951
952   # :LspGotoTypeDef
953   cursor(21, 2)
954   :LspGotoTypeDef
955   assert_equal([1, 7], [line('.'), col('.')])
956   exe "normal! \<C-t>"
957   assert_equal([21, 2], [line('.'), col('.')])
958
959   # :LspGotoImpl
960   cursor(21, 6)
961   :LspGotoImpl
962   assert_equal([12, 11], [line('.'), col('.')])
963   exe "normal! \<C-t>"
964   assert_equal([21, 6], [line('.'), col('.')])
965
966   # FIXME: The following tests are failing in Github CI. Comment out for now.
967   if 0
968   # Error cases
969   :messages clear
970   cursor(11, 5)
971   :LspGotoDeclaration
972   var m = execute('messages')->split("\n")
973   assert_equal('symbol declaration is not found', m[1])
974   :messages clear
975   :LspGotoDefinition
976   m = execute('messages')->split("\n")
977   assert_equal('symbol definition is not found', m[1])
978   :messages clear
979   :LspGotoImpl
980   m = execute('messages')->split("\n")
981   assert_equal('symbol implementation is not found', m[1])
982   :messages clear
983   endif
984
985   # Test for LspPeekDeclaration
986   cursor(21, 6)
987   var bnum = bufnr()
988   :LspPeekDeclaration
989   var plist = popup_list()
990   assert_true(1, plist->len())
991   assert_equal(bnum, plist[0]->winbufnr())
992   assert_equal(3, line('.', plist[0]))
993   popup_clear()
994   # tag stack should not be changed
995   assert_fails("normal! \<C-t>", 'E555:')
996
997   # Test for LspPeekDefinition
998   :LspPeekDefinition
999   plist = popup_list()
1000   assert_true(1, plist->len())
1001   assert_equal(bnum, plist[0]->winbufnr())
1002   assert_equal(6, line('.', plist[0]))
1003   popup_clear()
1004   # tag stack should not be changed
1005   assert_fails("normal! \<C-t>", 'E555:')
1006
1007   # FIXME: :LspPeekTypeDef and :LspPeekImpl are supported only with clang-14.
1008   # This clangd version is not available in Github CI.
1009
1010   :%bw!
1011
1012   # empty file
1013   assert_equal('', execute('LspGotoDefinition'))
1014   assert_equal('', execute('LspGotoDeclaration'))
1015   assert_equal('', execute('LspGotoImpl'))
1016
1017   # file without an LSP server
1018   edit a.raku
1019   assert_equal('Error: Language server for "raku" file type supporting "definition" feature is not found',
1020                execute('LspGotoDefinition')->split("\n")[0])
1021   assert_equal('Error: Language server for "raku" file type supporting "declaration" feature is not found',
1022                execute('LspGotoDeclaration')->split("\n")[0])
1023   assert_equal('Error: Language server for "raku" file type supporting "implementation" feature is not found',
1024                execute('LspGotoImpl')->split("\n")[0])
1025
1026   :%bw!
1027 enddef
1028
1029 # Test for :LspHighlight
1030 def g:Test_LspHighlight()
1031   silent! edit XLspHighlight.c
1032   sleep 200m
1033   var lines: list<string> =<< trim END
1034     void f1(int arg)
1035     {
1036       int i = arg;
1037       arg = 2;
1038       if (arg == 2) {
1039         arg = 3;
1040       }
1041     }
1042   END
1043   setline(1, lines)
1044   g:WaitForServerFileLoad(0)
1045   cursor(1, 13)
1046   :LspHighlight
1047   var expected: dict<any>
1048   expected = {id: 0, col: 13, end: 1, type: 'LspTextRef', length: 3, start: 1}
1049   expected.type_bufnr = 0
1050   assert_equal([expected], prop_list(1))
1051   expected = {id: 0, col: 11, end: 1, type: 'LspReadRef', length: 3, start: 1}
1052   expected.type_bufnr = 0
1053   assert_equal([expected], prop_list(3))
1054   expected = {id: 0, col: 3, end: 1, type: 'LspWriteRef', length: 3, start: 1}
1055   expected.type_bufnr = 0
1056   assert_equal([expected], prop_list(4))
1057   :LspHighlightClear
1058   assert_equal([], prop_list(1))
1059   assert_equal([], prop_list(3))
1060   assert_equal([], prop_list(4))
1061
1062   cursor(5, 3) # if (arg == 2) {
1063   var output = execute('LspHighlight')->split("\n")
1064   assert_equal('Warn: No highlight for the current position', output[0])
1065   :%bw!
1066 enddef
1067
1068 # Test for :LspHover
1069 def g:Test_LspHover()
1070   silent! edit XLspHover.c
1071   sleep 200m
1072   var lines: list<string> =<< trim END
1073     int f1(int a)
1074     {
1075       return 0;
1076     }
1077
1078     void f2(void)
1079     {
1080       f1(5);
1081       char *z = "z";
1082       f1(z);
1083     }
1084   END
1085   setline(1, lines)
1086   if clangdVerMajor > 14
1087         g:WaitForServerFileLoad(1)
1088   else
1089         g:WaitForServerFileLoad(0)
1090   endif
1091   cursor(8, 4)
1092   var output = execute(':LspHover')->split("\n")
1093   assert_equal([], output)
1094   var p: list<number> = popup_list()
1095   assert_equal(1, p->len())
1096   assert_equal(['### function `f1`  ', '', '---', '→ `int`  ', 'Parameters:  ', '- `int a`', '', '---', '```cpp', 'int f1(int a)', '```'], getbufline(winbufnr(p[0]), 1, '$'))
1097   popup_close(p[0])
1098   cursor(7, 1)
1099   output = execute(':LspHover')->split("\n")
1100   assert_equal('Warn: No documentation found for current keyword', output[0])
1101   output = execute(':silent LspHover')->split("\n")
1102   assert_equal([], output)
1103   assert_equal([], popup_list())
1104
1105   # Show current diagnostic as to open another popup.
1106   # Then we can test that LspHover closes all existing popups
1107   cursor(10, 6)
1108   :LspDiagCurrent
1109   assert_equal(1, popup_list()->len())
1110   :LspHover
1111   assert_equal(1, popup_list()->len())
1112   popup_clear()
1113
1114   :%bw!
1115 enddef
1116
1117 # Test for :LspShowSignature
1118 def g:Test_LspShowSignature()
1119   silent! edit XLspShowSignature.c
1120   sleep 200m
1121   var lines: list<string> =<< trim END
1122     int MyFunc(int a, int b)
1123     {
1124       return 0;
1125     }
1126
1127     void f2(void)
1128     {
1129       MyFunc( 
1130     }
1131   END
1132   setline(1, lines)
1133   g:WaitForServerFileLoad(2)
1134   cursor(8, 10)
1135   :LspShowSignature
1136   var p: list<number> = popup_list()
1137   var bnr: number = winbufnr(p[0])
1138   assert_equal(1, p->len())
1139   assert_equal(['MyFunc(int a, int b) -> int'], getbufline(bnr, 1, '$'))
1140   var expected: dict<any>
1141   expected = {id: 0, col: 8, end: 1, type: 'signature', length: 5, start: 1}
1142   expected.type_bufnr = bnr
1143   assert_equal([expected], prop_list(1, {bufnr: bnr}))
1144   popup_close(p[0])
1145
1146   setline(line('.'), '  MyFunc(10, ')
1147   cursor(8, 13)
1148   :LspShowSignature
1149   p = popup_list()
1150   bnr = winbufnr(p[0])
1151   assert_equal(1, p->len())
1152   assert_equal(['MyFunc(int a, int b) -> int'], getbufline(bnr, 1, '$'))
1153   expected = {id: 0, col: 15, end: 1, type: 'signature', length: 5, start: 1}
1154   expected.type_bufnr = bnr
1155   assert_equal([expected], prop_list(1, {bufnr: bnr}))
1156   popup_close(p[0])
1157   :%bw!
1158 enddef
1159
1160 # Test for :LspSymbolSearch
1161 def g:Test_LspSymbolSearch()
1162   silent! edit XLspSymbolSearch.c
1163   sleep 200m
1164   var lines: list<string> =<< trim END
1165     void lsptest_funcA()
1166     {
1167     }
1168
1169     void lsptest_funcB()
1170     {
1171     }
1172
1173     void lsptest_funcC()
1174     {
1175     }
1176   END
1177   setline(1, lines)
1178   g:WaitForServerFileLoad(0)
1179
1180   cursor(1, 1)
1181   feedkeys(":LspSymbolSearch lsptest_funcB\<CR>\<CR>", "xt")
1182   assert_equal([5, 6], [line('.'), col('.')])
1183
1184   cursor(1, 1)
1185   feedkeys(":LspSymbolSearch lsptest_func\<CR>\<Down>\<Down>\<CR>", "xt")
1186   assert_equal([9, 6], [line('.'), col('.')])
1187
1188   cursor(1, 1)
1189   feedkeys(":LspSymbolSearch lsptest_funcA\<CR>\<BS>B\<CR>", "xt")
1190   assert_equal([5, 6], [line('.'), col('.')])
1191
1192   var output = execute(':LspSymbolSearch lsptest_nonexist')->split("\n")
1193   assert_equal('Warn: Symbol "lsptest_nonexist" is not found', output[0])
1194
1195   :%bw!
1196 enddef
1197
1198 # Test for :LspIncomingCalls
1199 def g:Test_LspIncomingCalls()
1200   silent! edit XLspIncomingCalls.c
1201   sleep 200m
1202   var lines: list<string> =<< trim END
1203     void xFuncIncoming(void)
1204     {
1205     }
1206
1207     void aFuncIncoming(void)
1208     {
1209       xFuncIncoming();
1210     }
1211
1212     void bFuncIncoming(void)
1213     {
1214       xFuncIncoming();
1215     }
1216   END
1217   setline(1, lines)
1218   g:WaitForServerFileLoad(0)
1219   cursor(1, 6)
1220   :LspIncomingCalls
1221   assert_equal([1, 2], [winnr(), winnr('$')])
1222   var l = getline(1, '$')
1223   assert_equal('# Incoming calls to "xFuncIncoming"', l[0])
1224   assert_match('- xFuncIncoming (XLspIncomingCalls.c \[.*\])', l[1])
1225   assert_match('  + aFuncIncoming (XLspIncomingCalls.c \[.*\])', l[2])
1226   assert_match('  + bFuncIncoming (XLspIncomingCalls.c \[.*\])', l[3])
1227   :%bw!
1228 enddef
1229
1230 # Test for :LspOutline
1231 def g:Test_LspOutline()
1232   silent! edit XLspOutline.c
1233   sleep 200m
1234   var lines: list<string> =<< trim END
1235     void aFuncOutline(void)
1236     {
1237     }
1238
1239     void bFuncOutline(void)
1240     {
1241     }
1242   END
1243   setline(1, lines)
1244   g:WaitForServerFileLoad(0)
1245   var winid = win_getid()
1246   :LspOutline
1247   assert_equal(2, winnr('$'))
1248   var bnum = winbufnr(winid + 1)
1249   assert_equal('LSP-Outline', bufname(bnum))
1250   assert_equal(['Function', '  aFuncOutline', '  bFuncOutline'], getbufline(bnum, 4, '$'))
1251
1252   # Validate position vert topleft
1253   assert_equal(['row', [['leaf', winid + 1], ['leaf', winid]]], winlayout())
1254
1255   # Validate default width is 20
1256   assert_equal(20, winwidth(winid + 1))
1257
1258   execute $':{bnum}bw'
1259
1260   # Validate position vert botright
1261   g:LspOptionsSet({ outlineOnRight: true })
1262   :LspOutline
1263   assert_equal(2, winnr('$'))
1264   bnum = winbufnr(winid + 2)
1265   assert_equal('LSP-Outline', bufname(bnum))
1266   assert_equal(['Function', '  aFuncOutline', '  bFuncOutline'], getbufline(bnum, 4, '$'))
1267   assert_equal(['row', [['leaf', winid], ['leaf', winid + 2]]], winlayout())
1268   g:LspOptionsSet({ outlineOnRight: false })
1269   execute $':{bnum}bw'
1270
1271   # Validate <mods> position botright (below)
1272   :botright LspOutline
1273   assert_equal(2, winnr('$'))
1274   bnum = winbufnr(winid + 3)
1275   assert_equal('LSP-Outline', bufname(bnum))
1276   assert_equal(['Function', '  aFuncOutline', '  bFuncOutline'], getbufline(bnum, 4, '$'))
1277   assert_equal(['col', [['leaf', winid], ['leaf', winid + 3]]], winlayout())
1278   execute $':{bnum}bw'
1279
1280   # Validate that outlineWinSize works for LspOutline
1281   g:LspOptionsSet({ outlineWinSize: 40 })
1282   :LspOutline
1283   assert_equal(2, winnr('$'))
1284   bnum = winbufnr(winid + 4)
1285   assert_equal('LSP-Outline', bufname(bnum))
1286   assert_equal(['Function', '  aFuncOutline', '  bFuncOutline'], getbufline(bnum, 4, '$'))
1287   assert_equal(40, winwidth(winid + 4))
1288   execute $':{bnum}bw'
1289   g:LspOptionsSet({ outlineWinSize: 20 })
1290
1291   # Validate that <count> works for LspOutline
1292   :37LspOutline
1293   assert_equal(2, winnr('$'))
1294   bnum = winbufnr(winid + 5)
1295   assert_equal('LSP-Outline', bufname(bnum))
1296   assert_equal(['Function', '  aFuncOutline', '  bFuncOutline'], getbufline(bnum, 4, '$'))
1297   assert_equal(37, winwidth(winid + 5))
1298   execute $':{bnum}bw'
1299
1300   :%bw!
1301 enddef
1302
1303 # Test for setting the 'tagfunc'
1304 def g:Test_LspTagFunc()
1305   var lines: list<string> =<< trim END
1306     void aFuncTag(void)
1307     {
1308       xFuncTag();
1309     }
1310
1311     void bFuncTag(void)
1312     {
1313       xFuncTag();
1314     }
1315
1316     void xFuncTag(void)
1317     {
1318     }
1319   END
1320   writefile(lines, 'Xtagfunc.c')
1321   :silent! edit Xtagfunc.c
1322   g:WaitForServerFileLoad(1)
1323   :setlocal tagfunc=lsp#lsp#TagFunc
1324   cursor(3, 4)
1325   :exe "normal \<C-]>"
1326   assert_equal([11, 6], [line('.'), col('.')])
1327   cursor(1, 1)
1328   assert_fails('exe "normal \<C-]>"', 'E433: No tags file')
1329
1330   :set tagfunc&
1331   :%bw!
1332   delete('Xtagfunc.c')
1333 enddef
1334
1335 # Test for the LspDiagsUpdated autocmd
1336 def g:Test_LspDiagsUpdated_Autocmd()
1337   g:LspAutoCmd = 0
1338   autocmd_add([{event: 'User', pattern: 'LspDiagsUpdated', cmd: 'g:LspAutoCmd = g:LspAutoCmd + 1'}])
1339   silent! edit XLspDiagsAutocmd.c
1340   sleep 200m
1341   var lines: list<string> =<< trim END
1342     void aFuncDiag(void)
1343     {
1344         return;
1345     }
1346   END
1347   setline(1, lines)
1348   g:WaitForServerFileLoad(0)
1349   setline(3, '    return:')
1350   redraw!
1351   g:WaitForDiags(1)
1352   setline(3, '    return;')
1353   redraw!
1354   g:WaitForDiags(0)
1355   :%bw!
1356   autocmd_delete([{event: 'User', pattern: 'LspDiagsUpdated'}])
1357   assert_equal(5, g:LspAutoCmd)
1358 enddef
1359
1360 # Test custom notification handlers
1361 def g:Test_LspCustomNotificationHandlers()
1362
1363   g:LSPTest_customNotificationHandlerReplied = false
1364
1365   silent! edit XcustomNotification.c
1366   sleep 200m
1367   var lines: list<string> =<< trim END
1368     int a = 1;
1369     int main(void) {
1370       return a;
1371     }
1372   END
1373   setline(1, lines)
1374   g:WaitForAssert(() => assert_equal(true, g:LSPTest_customNotificationHandlerReplied))
1375   :%bw!
1376 enddef
1377
1378 def g:Test_ScanFindIdent()
1379   :silent! edit XscanFindIdent.c
1380   sleep 200m
1381   var lines: list<string> =<< trim END
1382     int countFI;
1383     int fnFI(int a)
1384     {
1385       int hello;
1386       hello =    a;
1387       return    countFI + 1;
1388     }
1389   END
1390   setline(1, lines)
1391   g:WaitForServerFileLoad(0)
1392   :redraw!
1393
1394   # LspGotoDefinition et al
1395   cursor(5, 10)
1396   assert_equal([], execute('LspGotoDefinition')->split("\n"))
1397   assert_equal([2, 14], [line('.'), col('.')])
1398
1399   cursor(6, 10)
1400   assert_equal([], execute('LspGotoDefinition')->split("\n"))
1401   assert_equal([1, 5], [line('.'), col('.')])
1402
1403   # LspShowReferences
1404   cursor(6, 10)
1405   assert_equal([], execute('LspShowReferences')->split("\n"))
1406   :lclose
1407
1408   # LspRename
1409   cursor(6, 10)
1410   assert_equal([], execute('LspRename counterFI')->split("\n"))
1411   sleep 100m
1412   assert_equal('int counterFI;', getline(1))
1413   assert_equal('  return    counterFI + 1;', getline(6))
1414
1415   :%bw!
1416 enddef
1417
1418 # Test for doing omni completion from the first column
1419 def g:Test_OmniComplete_FirstColumn()
1420   :silent! edit XOmniCompleteFirstColumn.c
1421   sleep 200m
1422   var lines: list<string> =<< trim END
1423     typedef struct Foo_ {
1424     } Foo_t;
1425
1426     #define FOO 1
1427   END
1428   setline(1, lines)
1429   g:WaitForServerFileLoad(0)
1430   redraw!
1431
1432   feedkeys("G0i\<C-X>\<C-O>", 'xt')
1433   assert_equal('Foo_t#define FOO 1', getline('.'))
1434   :%bw!
1435 enddef
1436
1437 # Test for doing omni completion from the first column
1438 def g:Test_OmniComplete_Multibyte()
1439   :silent! edit XOmniCompleteMultibyte.c
1440   sleep 200m
1441   var lines: list<string> =<< trim END
1442     #include <string.h>
1443     void Fn(void)
1444     {
1445       int thisVar = 1;
1446       int len = strlen("©©©©©") + thisVar;
1447     }
1448   END
1449   setline(1, lines)
1450   g:WaitForServerFileLoad(0)
1451   redraw!
1452
1453   cursor(5, 36)
1454   feedkeys("cwthis\<C-X>\<C-O>", 'xt')
1455   assert_equal('  int len = strlen("©©©©©") + thisVar;', getline('.'))
1456   :%bw!
1457 enddef
1458
1459 # Test for doing omni completion from the first column
1460 def g:Test_OmniComplete_Struct()
1461   :silent! edit XOmniCompleteStruct.c
1462   sleep 200m
1463   var lines: list<string> =<< trim END
1464     struct test_ {
1465         int foo;
1466         int bar;
1467         int baz;
1468     };
1469     void Fn(void)
1470     {
1471         struct test_ myTest;
1472         struct test_ *pTest;
1473         myTest.bar = 10;
1474         pTest->bar = 20;
1475     }
1476   END
1477   setline(1, lines)
1478   g:WaitForServerFileLoad(0)
1479   redraw!
1480
1481   cursor(10, 12)
1482   feedkeys("cwb\<C-X>\<C-O>\<C-N>\<C-Y>", 'xt')
1483   assert_equal('    myTest.baz = 10;', getline('.'))
1484   cursor(11, 12)
1485   feedkeys("cw\<C-X>\<C-O>\<C-N>\<C-N>\<C-Y>", 'xt')
1486   assert_equal('    pTest->foo = 20;', getline('.'))
1487   :%bw!
1488 enddef
1489
1490 # Test for the :LspServer command.
1491 def g:Test_LspServer()
1492   new a.raku
1493   assert_equal(['Warn: No Lsp servers found for "a.raku"'],
1494                execute('LspServer debug on')->split("\n"))
1495   assert_equal(['Warn: No Lsp servers found for "a.raku"'],
1496                execute('LspServer restart')->split("\n"))
1497   assert_equal(['Warn: No Lsp servers found for "a.raku"'],
1498                execute('LspServer show status')->split("\n"))
1499   assert_equal(['Warn: No Lsp servers found for "a.raku"'],
1500                execute('LspServer trace verbose')->split("\n"))
1501   assert_equal(['Error: LspServer - Unsupported argument "xyz"'],
1502                execute('LspServer xyz')->split("\n"))
1503   assert_equal(['Error: Argument required'],
1504                execute('LspServer debug')->split("\n"))
1505   assert_equal(['Error: Unsupported argument "xyz"'],
1506                execute('LspServer debug xyz')->split("\n"))
1507   assert_equal(['Error: Unsupported argument "on xyz"'],
1508                execute('LspServer debug on xyz')->split("\n"))
1509   assert_equal(['Error: Argument required'],
1510                execute('LspServer show')->split("\n"))
1511   assert_equal(['Error: Unsupported argument "xyz"'],
1512                execute('LspServer show xyz')->split("\n"))
1513   assert_equal(['Error: Unsupported argument "status xyz"'],
1514                execute('LspServer show status xyz')->split("\n"))
1515   assert_equal(['Error: Argument required'],
1516                execute('LspServer trace')->split("\n"))
1517   assert_equal(['Error: Unsupported argument "xyz"'],
1518                execute('LspServer trace xyz')->split("\n"))
1519   assert_equal(['Error: Unsupported argument "verbose xyz"'],
1520                execute('LspServer trace verbose xyz')->split("\n"))
1521   :%bw!
1522 enddef
1523
1524 # TODO:
1525 # 1. Add a test for autocompletion with a single match while ignoring case.
1526 #    After the full matched name is typed, the completion popup should still
1527 #    be displayed. e.g.
1528 #
1529 #      int MyVar = 1;
1530 #      int abc = myvar<C-N><C-Y>
1531 # 2. Add a test for jumping to a non-existing symbol definition, declaration.
1532
1533 # Start the C language server.  Returns true on success and false on failure.
1534 def g:StartLangServer(): bool
1535   return g:StartLangServerWithFile('Xtest.c')
1536 enddef
1537
1538 # vim: shiftwidth=2 softtabstop=2 noexpandtab