signature_help.test.vim 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. let s:timer_interval = 2000
  2. function! s:_ClearSigHelp()
  3. pythonx _sh_state = sh.UpdateSignatureHelp( _sh_state, {} )
  4. call assert_true( pyxeval( '_sh_state.popup_win_id is None' ),
  5. \ 'win id none with emtpy' )
  6. unlet! s:popup_win_id
  7. endfunction
  8. function! s:_CheckSignatureHelpAvailable( filetype )
  9. return pyxeval(
  10. \ 'ycm_state.SignatureHelpAvailableRequestComplete('
  11. \ . ' vim.eval( "a:filetype" ), False )' )
  12. endfunction
  13. function s:_GetSigHelpWinID()
  14. call WaitForAssert( {->
  15. \ assert_true(
  16. \ pyxeval(
  17. \ 'ycm_state.SignatureHelpRequestReady()'
  18. \ ),
  19. \ 'sig help request reqdy'
  20. \ )
  21. \ } )
  22. call WaitForAssert( {->
  23. \ assert_true(
  24. \ pyxeval(
  25. \ 'ycm_state._signature_help_state.popup_win_id is not None'
  26. \ ),
  27. \ 'popup_win_id'
  28. \ )
  29. \ } )
  30. let s:popup_win_id = pyxeval( 'ycm_state._signature_help_state.popup_win_id' )
  31. return s:popup_win_id
  32. endfunction
  33. function! s:_CheckPopupPosition( winid, pos )
  34. redraw
  35. let actual_pos = popup_getpos( a:winid )
  36. let ret = 0
  37. if a:pos->empty()
  38. return assert_true( actual_pos->empty(), 'popup pos empty' )
  39. endif
  40. for c in keys( a:pos )
  41. if !has_key( actual_pos, c )
  42. let ret += 1
  43. call assert_report( 'popup with ID '
  44. \ . string( a:winid )
  45. \ . ' has no '
  46. \ . c
  47. \ . ' in: '
  48. \ . string( actual_pos ) )
  49. else
  50. let ret += assert_equal( a:pos[ c ],
  51. \ actual_pos[ c ],
  52. \ c . ' in: ' . string( actual_pos ) )
  53. endif
  54. endfor
  55. return ret
  56. endfunction
  57. function! s:_CheckSigHelpAtPos( sh, cursor, pos )
  58. call setpos( '.', [ 0 ] + a:cursor )
  59. redraw
  60. pythonx _sh_state = sh.UpdateSignatureHelp( _sh_state,
  61. \ vim.eval( 'a:sh' ) )
  62. redraw
  63. let winid = pyxeval( '_sh_state.popup_win_id' )
  64. call s:_CheckPopupPosition( winid, a:pos )
  65. endfunction
  66. function! SetUp()
  67. let g:ycm_use_clangd = 1
  68. let g:ycm_confirm_extra_conf = 0
  69. let g:ycm_auto_trigger = 1
  70. let g:ycm_keep_logfiles = 1
  71. let g:ycm_log_level = 'DEBUG'
  72. call youcompleteme#test#setup#SetUp()
  73. pythonx from ycm import signature_help as sh
  74. pythonx _sh_state = sh.SignatureHelpState()
  75. endfunction
  76. function! TearDown()
  77. call s:_ClearSigHelp()
  78. call youcompleteme#test#setup#CleanUp()
  79. endfunction
  80. " This is how we might do screen dump tests
  81. " function! Test_Compl()
  82. " let setup =<< trim END
  83. " edit ../third_party/ycmd/ycmd/tests/clangd/testdata/general_fallback/make_drink.cc
  84. " call setpos( '.', [ 0, 7, 27 ] )
  85. " END
  86. " call writefile( setup, 'Xtest_Compl' )
  87. " let vim = RunVimInTerminal( '-Nu vimrc -S Xtest_Compl', {} )
  88. "
  89. " function! Test() closure
  90. " " Wait for Vim to be ready
  91. " call term_sendkeys( vim, "cl:" )
  92. " call term_wait( vim )
  93. " call VerifyScreenDump( vim, "signature_help_Test_Compl_01", {} )
  94. " endfunction
  95. "
  96. " call WaitForAssert( {-> Test()} )
  97. "
  98. " " clean up
  99. " call StopVimInTerminal(vim)
  100. " call delete('XtestPopup')
  101. " %bwipeout!
  102. " endfunction
  103. function! Test_Enough_Screen_Space()
  104. call assert_true( &lines >= 25,
  105. \ &lines . " is not enough rows. need 25." )
  106. call assert_true( &columns >= 80,
  107. \ &columns . " is not enough columns. need 80." )
  108. endfunction
  109. function! Test_Signatures_After_Trigger()
  110. call youcompleteme#test#setup#OpenFile(
  111. \ '/test/testdata/vim/mixed_filetype.vim',
  112. \ { 'native_ft': 0 } )
  113. call WaitFor( {-> s:_CheckSignatureHelpAvailable( 'vim' ) } )
  114. call WaitFor( {-> s:_CheckSignatureHelpAvailable( 'python' ) } )
  115. call setpos( '.', [ 0, 3, 17 ] )
  116. " Required to trigger TextChangedI
  117. " https://github.com/vim/vim/issues/4665#event-2480928194
  118. call test_override( 'char_avail', 1 )
  119. " Must do the checks in a timer callback because we need to stay in insert
  120. " mode until done. Use a func because it's big enough (a lambda is a little
  121. " neater in many contexts).
  122. function! Check( id ) closure
  123. call WaitForAssert( {->
  124. \ assert_true(
  125. \ pyxeval(
  126. \ 'ycm_state.SignatureHelpRequestReady()'
  127. \ ),
  128. \ 'sig help request ready'
  129. \ )
  130. \ } )
  131. call WaitForAssert( {->
  132. \ assert_true(
  133. \ pyxeval(
  134. \ "bool( ycm_state.GetSignatureHelpResponse()[ 'signatures' ] )"
  135. \ ),
  136. \ 'sig help request has signatures'
  137. \ )
  138. \ } )
  139. call WaitForAssert( {->
  140. \ assert_true(
  141. \ pyxeval(
  142. \ 'ycm_state._signature_help_state.popup_win_id is not None'
  143. \ ),
  144. \ 'popup_win_id'
  145. \ )
  146. \ } )
  147. let popup_win_id = pyxeval( 'ycm_state._signature_help_state.popup_win_id' )
  148. let pos = win_screenpos( popup_win_id )
  149. call assert_false( pos == [ 0, 0 ] )
  150. " Exit insert mode to ensure the test continues
  151. call test_override( 'ALL', 0 )
  152. call feedkeys( "\<ESC>" )
  153. endfunction
  154. call assert_false( pyxeval( 'ycm_state.SignatureHelpRequestReady()' ) )
  155. call timer_start( s:timer_interval, funcref( 'Check' ) )
  156. call feedkeys( 'cl(', 'ntx!' )
  157. call assert_false( pumvisible(), 'pumvisible()' )
  158. call WaitForAssert( {->
  159. \ assert_true(
  160. \ pyxeval(
  161. \ 'ycm_state._signature_help_state.popup_win_id is None'
  162. \ ),
  163. \ 'popup_win_id'
  164. \ )
  165. \ } )
  166. call test_override( 'ALL', 0 )
  167. %bwipeout!
  168. delfunc! Check
  169. endfunction
  170. function! Test_Signatures_With_PUM_NoSigns()
  171. call youcompleteme#test#setup#OpenFile(
  172. \ '/third_party/ycmd/ycmd/tests/clangd/testdata/general_fallback'
  173. \ . '/make_drink.cc', {} )
  174. call WaitFor( {-> s:_CheckSignatureHelpAvailable( 'cpp' ) } )
  175. " Make sure that error signs don't shift the window
  176. setlocal signcolumn=no
  177. call setpos( '.', [ 0, 7, 13 ] )
  178. " Required to trigger TextChangedI
  179. " https://github.com/vim/vim/issues/4665#event-2480928194
  180. call test_override( 'char_avail', 1 )
  181. function Check2( id ) closure
  182. call WaitForAssert( {-> assert_true( pumvisible() ) } )
  183. call WaitForAssert( {-> assert_notequal( [], complete_info().items ) } )
  184. call assert_equal( 7, pum_getpos().row )
  185. redraw
  186. " NOTE: anchor is 0-based
  187. call assert_equal( 6,
  188. \ pyxeval( 'ycm_state._signature_help_state.anchor[0]' ) )
  189. call assert_equal( 13,
  190. \ pyxeval( 'ycm_state._signature_help_state.anchor[1]' ) )
  191. " Popup is shifted due to 80 column screen
  192. call s:_CheckPopupPosition( s:_GetSigHelpWinID(),
  193. \ { 'line': 5, 'col': 5 } )
  194. call test_override( 'ALL', 0 )
  195. call feedkeys( "\<ESC>", 't' )
  196. endfunction
  197. " Must do the checks in a timer callback because we need to stay in insert
  198. " mode until done.
  199. function! Check( id ) closure
  200. call WaitForAssert( {->
  201. \ assert_true(
  202. \ pyxeval(
  203. \ 'ycm_state._signature_help_state.popup_win_id is not None'
  204. \ ),
  205. \ 'popup_win_id'
  206. \ )
  207. \ } )
  208. " Popup is shifted left due to 80 char screen
  209. call s:_CheckPopupPosition( s:_GetSigHelpWinID(),
  210. \ { 'line': 5, 'col': 5 } )
  211. call timer_start( s:timer_interval, funcref( 'Check2' ) )
  212. call feedkeys( ' TypeOfD', 't' )
  213. endfunction
  214. call assert_false( pyxeval( 'ycm_state.SignatureHelpRequestReady()' ) )
  215. call timer_start( s:timer_interval, funcref( 'Check' ) )
  216. call feedkeys( 'C(', 'ntx!' )
  217. call WaitForAssert( {->
  218. \ assert_true(
  219. \ pyxeval(
  220. \ 'ycm_state._signature_help_state.popup_win_id is None'
  221. \ ),
  222. \ 'popup_win_id'
  223. \ )
  224. \ } )
  225. call test_override( 'ALL', 0 )
  226. %bwipeout!
  227. delfunc! Check
  228. delfunc! Check2
  229. endfunction
  230. function! Test_Signatures_With_PUM_Signs()
  231. call youcompleteme#test#setup#OpenFile(
  232. \ '/third_party/ycmd/ycmd/tests/clangd/testdata/general_fallback'
  233. \ . '/make_drink.cc', {} )
  234. call WaitFor( {-> s:_CheckSignatureHelpAvailable( 'cpp' ) } )
  235. " Make sure that sign causes the popup to shift
  236. setlocal signcolumn=auto
  237. call setpos( '.', [ 0, 7, 13 ] )
  238. " Required to trigger TextChangedI
  239. " https://github.com/vim/vim/issues/4665#event-2480928194
  240. call test_override( 'char_avail', 1 )
  241. function Check2( id ) closure
  242. call WaitForAssert( {-> assert_true( pumvisible() ) } )
  243. call WaitForAssert( {-> assert_notequal( [], complete_info().items ) } )
  244. call assert_equal( 7, pum_getpos().row )
  245. redraw
  246. " NOTE: anchor is 0-based
  247. call assert_equal( 6,
  248. \ pyxeval( 'ycm_state._signature_help_state.anchor[0]' ) )
  249. call assert_equal( 13,
  250. \ pyxeval( 'ycm_state._signature_help_state.anchor[1]' ) )
  251. " Sign column is shown, popup shifts to the right 2 screen columns
  252. " Then shifts back due to 80 character screen width
  253. " FIXME: This test was supposed to show the shifting right. Write another
  254. " one which uses a much smaller popup to do that.
  255. call s:_CheckPopupPosition( s:_GetSigHelpWinID(),
  256. \ { 'line': 5, 'col': 5 } )
  257. call test_override( 'ALL', 0 )
  258. call feedkeys( "\<ESC>", 't' )
  259. endfunction
  260. " Must do the checks in a timer callback because we need to stay in insert
  261. " mode until done.
  262. function! Check( id ) closure
  263. call WaitForAssert( {->
  264. \ assert_true(
  265. \ pyxeval(
  266. \ 'ycm_state._signature_help_state.popup_win_id is not None'
  267. \ ),
  268. \ 'popup_win_id'
  269. \ )
  270. \ } )
  271. " Popup is shifted left due to 80 char screen
  272. call s:_CheckPopupPosition( s:_GetSigHelpWinID(),
  273. \ { 'line': 5, 'col': 5 } )
  274. call timer_start( s:timer_interval, funcref( 'Check2' ) )
  275. call feedkeys( ' TypeOfD', 't' )
  276. endfunction
  277. call assert_false( pyxeval( 'ycm_state.SignatureHelpRequestReady()' ) )
  278. call timer_start( s:timer_interval, funcref( 'Check' ) )
  279. call feedkeys( 'C(', 'ntx!' )
  280. call WaitForAssert( {->
  281. \ assert_true(
  282. \ pyxeval(
  283. \ 'ycm_state._signature_help_state.popup_win_id is None'
  284. \ ),
  285. \ 'popup_win_id'
  286. \ )
  287. \ } )
  288. call test_override( 'ALL', 0 )
  289. %bwipeout!
  290. delfunc! Check
  291. delfunc! Check2
  292. endfunction
  293. function! Test_Placement_Simple()
  294. call assert_true( &lines >= 25, "Enough rows" )
  295. call assert_true( &columns >= 25, "Enough columns" )
  296. let X = join( map( range( 0, &columns - 1 ), {->'X'} ), '' )
  297. for i in range( 0, &lines )
  298. call append( line('$'), X )
  299. endfor
  300. " Delete the blank line that is always added to a buffer
  301. 0delete
  302. call s:_ClearSigHelp()
  303. let v_sh = {
  304. \ 'activeSignature': 0,
  305. \ 'activeParameter': 0,
  306. \ 'signatures': [
  307. \ { 'label': 'test function', 'parameters': [] }
  308. \ ]
  309. \ }
  310. " When displayed in the middle with plenty of space
  311. call s:_CheckSigHelpAtPos( v_sh, [ 10, 3 ], {
  312. \ 'line': 9,
  313. \ 'col': 1
  314. \ } )
  315. " Confirm that anchoring works (i.e. it doesn't move!)
  316. call s:_CheckSigHelpAtPos( v_sh, [ 20, 10 ], {
  317. \ 'line': 9,
  318. \ 'col': 1
  319. \ } )
  320. call s:_ClearSigHelp()
  321. " Window slides from left of screen
  322. call s:_CheckSigHelpAtPos( v_sh, [ 10, 2 ], {
  323. \ 'line': 9,
  324. \ 'col': 1,
  325. \ } )
  326. call s:_ClearSigHelp()
  327. " Window slides from left of screen
  328. call s:_CheckSigHelpAtPos( v_sh, [ 10, 1 ], {
  329. \ 'line': 9,
  330. \ 'col': 1,
  331. \ } )
  332. call s:_ClearSigHelp()
  333. " Cursor at top-left of window
  334. call s:_CheckSigHelpAtPos( v_sh, [ 1, 1 ], {
  335. \ 'line': 2,
  336. \ 'col': 1,
  337. \ } )
  338. call s:_ClearSigHelp()
  339. " Cursor at top-right of window
  340. call s:_CheckSigHelpAtPos( v_sh, [ 1, &columns ], {
  341. \ 'line': 2,
  342. \ 'col': &columns - len( "test function" ) - 1,
  343. \ } )
  344. call s:_ClearSigHelp()
  345. " Bottom-left of window
  346. call s:_CheckSigHelpAtPos( v_sh, [ &lines + 1, 1 ], {
  347. \ 'line': &lines - 2,
  348. \ 'col': 1,
  349. \ } )
  350. call s:_ClearSigHelp()
  351. " Bottom-right of window
  352. call s:_CheckSigHelpAtPos( v_sh, [ &lines + 1, &columns ], {
  353. \ 'line': &lines - 2,
  354. \ 'col': &columns - len( "test function" ) - 1,
  355. \ } )
  356. call s:_ClearSigHelp()
  357. call popup_clear()
  358. %bwipeout!
  359. endfunction
  360. function! Test_Placement_MultiLine()
  361. call assert_true( &lines >= 25, "Enough rows" )
  362. call assert_true( &columns >= 25, "Enough columns" )
  363. let X = join( map( range( 0, &columns - 1 ), {->'X'} ), '' )
  364. for i in range( 0, &lines )
  365. call append( line('$'), X )
  366. endfor
  367. " Delete the blank line that is always added to a buffer
  368. 0delete
  369. call s:_ClearSigHelp()
  370. let v_sh = {
  371. \ 'activeSignature': 0,
  372. \ 'activeParameter': 0,
  373. \ 'signatures': [
  374. \ { 'label': 'test function', 'parameters': [] },
  375. \ { 'label': 'toast function', 'parameters': [
  376. \ { 'label': [ 0, 5 ] }
  377. \ ] },
  378. \ ]
  379. \ }
  380. " When displayed in the middle with plenty of space
  381. call s:_CheckSigHelpAtPos( v_sh, [ 10, 3 ], {
  382. \ 'line': 8,
  383. \ 'col': 1
  384. \ } )
  385. " Confirm that anchoring works (i.e. it doesn't move!)
  386. call s:_CheckSigHelpAtPos( v_sh, [ 20, 10 ], {
  387. \ 'line': 8,
  388. \ 'col': 1
  389. \ } )
  390. call s:_ClearSigHelp()
  391. " Window slides from left of screen
  392. call s:_CheckSigHelpAtPos( v_sh, [ 10, 2 ], {
  393. \ 'line': 8,
  394. \ 'col': 1,
  395. \ } )
  396. call s:_ClearSigHelp()
  397. " Window slides from left of screen
  398. call s:_CheckSigHelpAtPos( v_sh, [ 10, 1 ], {
  399. \ 'line': 8,
  400. \ 'col': 1,
  401. \ } )
  402. call s:_ClearSigHelp()
  403. " Cursor at top-left of window
  404. call s:_CheckSigHelpAtPos( v_sh, [ 1, 1 ], {
  405. \ 'line': 2,
  406. \ 'col': 1,
  407. \ } )
  408. call s:_ClearSigHelp()
  409. " Cursor at top-right of window
  410. call s:_CheckSigHelpAtPos( v_sh, [ 1, &columns ], {
  411. \ 'line': 2,
  412. \ 'col': &columns - len( "toast function" ) - 1,
  413. \ } )
  414. call s:_ClearSigHelp()
  415. " Bottom-left of window
  416. call s:_CheckSigHelpAtPos( v_sh, [ &lines + 1, 1 ], {
  417. \ 'line': &lines - 3,
  418. \ 'col': 1,
  419. \ } )
  420. call s:_ClearSigHelp()
  421. " Bottom-right of window
  422. call s:_CheckSigHelpAtPos( v_sh, [ &lines + 1, &columns ], {
  423. \ 'line': &lines - 3,
  424. \ 'col': &columns - len( "toast function" ) - 1,
  425. \ } )
  426. call s:_ClearSigHelp()
  427. call popup_clear()
  428. %bwipeout!
  429. endfunction
  430. function! Test_Signatures_TopLine()
  431. call youcompleteme#test#setup#OpenFile( 'test/testdata/python/test.py', {} )
  432. call WaitFor( {-> s:_CheckSignatureHelpAvailable( 'python' ) } )
  433. call setpos( '.', [ 0, 1, 24 ] )
  434. call test_override( 'char_avail', 1 )
  435. function! Check( id ) closure
  436. call s:_CheckPopupPosition( s:_GetSigHelpWinID(), { 'line': 2, 'col': 23 } )
  437. call test_override( 'ALL', 0 )
  438. call feedkeys( "\<ESC>" )
  439. endfunction
  440. call timer_start( s:timer_interval, funcref( 'Check' ) )
  441. call feedkeys( 'cl(', 'ntx!' )
  442. call test_override( 'ALL', 0 )
  443. %bwipeout!
  444. delfun! Check
  445. endfunction
  446. function! Test_Signatures_TopLineWithPUM()
  447. call youcompleteme#test#setup#OpenFile( 'test/testdata/python/test.py', {} )
  448. call WaitFor( {-> s:_CheckSignatureHelpAvailable( 'python' ) } )
  449. call setpos( '.', [ 0, 1, 24 ] )
  450. call test_override( 'char_avail', 1 )
  451. function! CheckSigHelpAndTriggerCompletion( id ) closure
  452. " Popup placed below the cursor
  453. call s:_CheckPopupPosition( s:_GetSigHelpWinID(), { 'line': 2, 'col': 23 } )
  454. " Push more characters into the typeahead buffer to trigger insert mode
  455. " completion.
  456. "
  457. " Nte for some reason the first semantic response can take quite some time,
  458. " and if our timer fires before then, the test just fails. so we take 2
  459. " seconds here.
  460. call timer_start( s:timer_interval,
  461. \ funcref( 'CheckCompletionVisibleAndSigHelpHidden' ) )
  462. call feedkeys( " os.", 't' )
  463. endfunction
  464. function! CheckCompletionVisibleAndSigHelpHidden( id ) closure
  465. " Completion popup now visible, overlapping where the sig help popup was
  466. redraw
  467. call WaitForAssert( {-> assert_true( pumvisible() ) } )
  468. call assert_equal( 1, get( pum_getpos(), 'row', -1 ) )
  469. call assert_equal( 28, get( pum_getpos(), 'col', -1 ) )
  470. " so we hide the sig help popup.
  471. call WaitForAssert( {->
  472. \ assert_true(
  473. \ pyxeval(
  474. \ 'ycm_state._signature_help_state.popup_win_id is None'
  475. \ ),
  476. \ 'popup_win_id'
  477. \ )
  478. \ } )
  479. call s:_CheckPopupPosition( s:popup_win_id, {} )
  480. " We're done in insert mode now.
  481. call test_override( 'ALL', 0 )
  482. call feedkeys( "\<ESC>", 't' )
  483. endfunction
  484. " Edit the line and trigger signature help
  485. call timer_start( s:timer_interval,
  486. \ funcref( 'CheckSigHelpAndTriggerCompletion' ) )
  487. call feedkeys( 'C(', 'ntx!' )
  488. call test_override( 'ALL', 0 )
  489. %bwipeout!
  490. delfunc! CheckSigHelpAndTriggerCompletion
  491. delfunc! CheckCompletionVisibleAndSigHelpHidden
  492. endfunction