hover.test.vim 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. function! s:WaitForCommandRequestComplete()
  2. call WaitForAssert( { ->
  3. \ assert_true( py3eval(
  4. \ 'ycm_state.GetCommandRequest() is not None and '
  5. \ . 'ycm_state.GetCommandRequest().Done()' ) )
  6. \ } )
  7. call WaitForAssert( { ->
  8. \ assert_equal( -1,
  9. \ youcompleteme#Test_GetPollers().command.id )
  10. \ } )
  11. endfunction
  12. function! s:CheckNoCommandRequest()
  13. call WaitForAssert( { ->
  14. \ assert_true( py3eval(
  15. \ 'ycm_state.GetCommandRequest() is None or '
  16. \ . 'ycm_state.GetCommandRequest().Done()' ) )
  17. \ } )
  18. call WaitForAssert( { ->
  19. \ assert_equal( -1,
  20. \ youcompleteme#Test_GetPollers().command.id )
  21. \ } )
  22. endfunction
  23. function! s:CheckPopupVisible( row, col, text, syntax )
  24. " Takes a buffer position, converts it to a screen position and checks the
  25. " popup found at that location
  26. redraw
  27. let loc = screenpos( win_getid(), a:row, a:col )
  28. return s:CheckPopupVisibleScreenPos( loc, a:text, a:syntax )
  29. endfunction
  30. function! s:CheckPopupVisibleScreenPos( loc, text, syntax )
  31. " Takes a position dict like the one returned by screenpos() and verifies it
  32. " has 'text' (a list of lines) and 'syntax' the &syntax setting
  33. " popup found at that location
  34. redraw
  35. call s:WaitForCommandRequestComplete()
  36. call WaitForAssert( { ->
  37. \ assert_notequal( 0,
  38. \ popup_locate( a:loc.row, a:loc.col ),
  39. \ 'Locate popup at ('
  40. \ . a:loc.row
  41. \ . ','
  42. \ . a:loc.col
  43. \ . ')' )
  44. \ } )
  45. let popup = popup_locate( a:loc.row, a:loc.col )
  46. if a:text isnot v:none
  47. call assert_equal( a:text,
  48. \ getbufline( winbufnr( popup ), 1, '$' ) )
  49. endif
  50. call assert_equal( a:syntax, getbufvar( winbufnr( popup ), '&syntax' ) )
  51. endfunction
  52. function! s:CheckPopupNotVisible( row, col, with_request=v:true )
  53. " Takes a buffer position and ensures there is no popup visible at that
  54. " position. Like CheckPopupVisible, the position must be valid (i.e. there
  55. " must be buffer text at that position). Otherwise, you need to pass the
  56. " _screen_ position to CheckPopupNotVisibleScreenPos
  57. redraw
  58. let loc = screenpos( win_getid(), a:row, a:col )
  59. return s:CheckPopupNotVisibleScreenPos( loc, a:with_request )
  60. endfunction
  61. function! s:CheckPopupNotVisibleScreenPos( loc, with_request=v:true )
  62. " Takes a position dict like the one returned by screenpos() and verifies it
  63. " does not have a popup drawn on it.
  64. redraw
  65. if a:with_request
  66. call s:WaitForCommandRequestComplete()
  67. else
  68. call s:CheckNoCommandRequest()
  69. endif
  70. call WaitForAssert( { ->
  71. \ assert_equal( 0,
  72. \ popup_locate( a:loc.row, a:loc.col ) )
  73. \ } )
  74. endfunction
  75. let s:python_oneline = {
  76. \ 'GetDoc': [ 'Test_OneLine()', '', 'This is the one line output.' ],
  77. \ 'GetType': [ 'def Test_OneLine()' ],
  78. \ }
  79. let s:cpp_lifetime = {
  80. \ 'GetDoc': [ 'field lifetime',
  81. \ '',
  82. \ 'Type: char',
  83. \ 'nobody will live > 128 years',
  84. \ '',
  85. \ '// In PointInTime',
  86. \ 'char lifetime' ],
  87. \ 'GetType': [ 'char lifetime; // In PointInTime' ],
  88. \ }
  89. function! SetUp()
  90. let g:ycm_use_clangd = 1
  91. let g:ycm_keep_logfiles = 1
  92. let g:ycm_log_level = 'DEBUG'
  93. set signcolumn=no
  94. nmap <leader>D <Plug>(YCMHover)
  95. call youcompleteme#test#setup#SetUp()
  96. endfunction
  97. function! TearDown()
  98. let g:ycm_auto_hover='CursorHold'
  99. call assert_equal( -1, youcompleteme#Test_GetPollers().command.id )
  100. endfunction
  101. function! Test_Hover_Uses_GetDoc()
  102. call youcompleteme#test#setup#OpenFile( '/test/testdata/python/doc.py', {} )
  103. call assert_equal( 'python', &syntax )
  104. " no doc
  105. call setpos( '.', [ 0, 1, 1 ] )
  106. doautocmd CursorHold
  107. call assert_equal( { 'command': 'GetDoc', 'syntax': '' }, b:ycm_hover )
  108. call s:CheckPopupNotVisible( 2, 1 )
  109. call s:CheckPopupNotVisible( 2, 2 )
  110. " some doc - autocommand
  111. call setpos( '.', [ 0, 12, 3 ] )
  112. doautocmd CursorHold
  113. call s:CheckPopupVisible( 11, 4, s:python_oneline.GetDoc, '' )
  114. call popup_clear()
  115. " some doc - mapping
  116. call setpos( '.', [ 0, 12, 3 ] )
  117. normal \D
  118. call s:CheckPopupVisible( 11, 4, s:python_oneline.GetDoc, '' )
  119. call popup_clear()
  120. endfunction
  121. function! Test_Hover_Uses_GetHover()
  122. call youcompleteme#test#setup#OpenFile( '/test/testdata/python/doc.py', {} )
  123. py3 <<EOPYTHON
  124. from unittest import mock
  125. with mock.patch.object( ycm_state,
  126. 'GetDefinedSubcommands',
  127. return_value = [ 'GetHover' ] ):
  128. vim.command( 'doautocmd CursorHold' )
  129. EOPYTHON
  130. call assert_equal( { 'command': 'GetHover', 'syntax': 'markdown' },
  131. \ b:ycm_hover )
  132. " Only the generic LSP completer supports the GetHover response, so i guess we
  133. " test the error condition here...
  134. " Python desn't support GetHover
  135. call setpos( '.', [ 0, 12, 3 ] )
  136. normal \D
  137. call s:CheckPopupNotVisible( 11, 4 )
  138. call popup_clear()
  139. endfunction
  140. function! Test_Hover_Uses_None()
  141. call youcompleteme#test#setup#OpenFile( '/test/testdata/python/doc.py', {} )
  142. py3 <<EOPYTHON
  143. from unittest import mock
  144. with mock.patch.object( ycm_state, 'GetDefinedSubcommands', return_value = [] ):
  145. vim.command( 'doautocmd CursorHold' )
  146. EOPYTHON
  147. call assert_equal( {}, b:ycm_hover )
  148. call setpos( '.', [ 0, 12, 3 ] )
  149. normal \D
  150. call s:CheckPopupNotVisible( 11, 4, v:false )
  151. call popup_clear()
  152. endfunction
  153. function! Test_Hover_Uses_GetType()
  154. call youcompleteme#test#setup#OpenFile( '/test/testdata/python/doc.py', {} )
  155. py3 <<EOPYTHON
  156. from unittest import mock
  157. with mock.patch.object( ycm_state,
  158. 'GetDefinedSubcommands',
  159. return_value = [ 'GetType' ] ):
  160. vim.command( 'doautocmd CursorHold' )
  161. EOPYTHON
  162. call assert_equal( { 'command': 'GetType', 'syntax': 'python' }, b:ycm_hover )
  163. call s:CheckPopupNotVisible( 2, 1, v:none )
  164. call s:CheckPopupNotVisible( 2, 2, v:none )
  165. " some doc - autocommand
  166. call setpos( '.', [ 0, 12, 3 ] )
  167. doautocmd CursorHold
  168. call s:CheckPopupVisible( 11, 4, s:python_oneline.GetType, 'python' )
  169. call popup_clear()
  170. " some doc - mapping
  171. call setpos( '.', [ 0, 12, 3 ] )
  172. normal \D
  173. call s:CheckPopupVisible( 11, 4, s:python_oneline.GetType, 'python' )
  174. " hide it again
  175. normal \D
  176. call s:CheckPopupNotVisible( 11, 4 )
  177. " show it again
  178. normal \D
  179. call s:CheckPopupVisible( 11, 4, s:python_oneline.GetType, 'python' )
  180. call popup_clear()
  181. endfunction
  182. function! Test_Hover_NonNative()
  183. call youcompleteme#test#setup#OpenFile( '_not_a_file', { 'native_ft': 0 } )
  184. setfiletype NoASupportedFileType
  185. let messages_before = execute( 'messages' )
  186. doautocmd CursorHold
  187. call s:CheckNoCommandRequest()
  188. call assert_false( exists( 'b:ycm_hover' ) )
  189. call assert_equal( messages_before, execute( 'messages' ) )
  190. normal \D
  191. call s:CheckNoCommandRequest()
  192. call assert_false( exists( 'b:ycm_hover' ) )
  193. call assert_equal( messages_before, execute( 'messages' ) )
  194. call popup_clear()
  195. endfunction
  196. function SetUp_Test_Hover_Disabled_NonNative()
  197. let g:ycm_auto_hover = ''
  198. endfunction
  199. function! Test_Hover_Disabled_NonNative()
  200. call youcompleteme#test#setup#OpenFile( '_not_a_file', { 'native_ft': 0 } )
  201. setfiletype NoASupportedFileType
  202. let messages_before = execute( 'messages' )
  203. silent! doautocmd CursorHold
  204. call s:CheckNoCommandRequest()
  205. call assert_false( exists( 'b:ycm_hover' ) )
  206. call assert_equal( messages_before, execute( 'messages' ) )
  207. call popup_clear()
  208. endfunction
  209. function! SetUp_Test_AutoHover_Disabled()
  210. let g:ycm_auto_hover = ''
  211. endfunction
  212. function! Test_AutoHover_Disabled()
  213. call youcompleteme#test#setup#OpenFile( '/test/testdata/python/doc.py', {} )
  214. let messages_before = execute( 'messages' )
  215. call assert_false( exists( 'b:ycm_hover' ) )
  216. call setpos( '.', [ 0, 12, 3 ] )
  217. silent! doautocmd CursorHold
  218. call s:CheckPopupNotVisible( 11, 4, v:false )
  219. call assert_equal( messages_before, execute( 'messages' ) )
  220. " Manual hover is still supported
  221. normal \D
  222. call assert_true( exists( 'b:ycm_hover' ) )
  223. call s:CheckPopupVisible( 11, 4, s:python_oneline.GetDoc, '' )
  224. call assert_equal( messages_before, execute( 'messages' ) )
  225. " Manual close hover is still supported
  226. normal \D
  227. call s:CheckPopupNotVisible( 11, 4, v:false )
  228. call assert_equal( messages_before, execute( 'messages' ) )
  229. call popup_clear()
  230. endfunction
  231. function! Test_Hover_MoveCursor()
  232. call youcompleteme#test#setup#OpenFile( '/test/testdata/python/doc.py', {} )
  233. " needed so that the feedkeys calls actually trigger vim to notice the cursor
  234. " moving. We also need to enter/exit insert mode as Vim only checks for these
  235. " cursor moved events in very specific times. In particular, _not_ while
  236. " running a script (like we are here), but it _does_ on enter/exit insert
  237. " mode.
  238. call test_override( 'char_avail', 1 )
  239. call setpos( '.', [ 0, 12, 3 ] )
  240. doautocmd CursorHold
  241. call s:CheckPopupVisible( 11, 3, s:python_oneline.GetDoc, '' )
  242. call feedkeys( "li\<Esc>", 'xt' )
  243. call s:CheckPopupVisible( 11, 3, s:python_oneline.GetDoc, '' )
  244. " letters within word
  245. call feedkeys( "4li\<Esc>", 'xt' )
  246. call s:CheckPopupVisible( 11, 3, s:python_oneline.GetDoc, '' )
  247. " word
  248. call feedkeys( "wi\<Esc>", 'xt' )
  249. call s:CheckPopupNotVisible( 11, 3 )
  250. call feedkeys( "b\\D", 'xt' )
  251. call s:CheckPopupVisible( 11, 3, s:python_oneline.GetDoc, '' )
  252. call test_override( 'ALL', 0 )
  253. call popup_clear()
  254. endfunction
  255. function! Test_Hover_Dismiss()
  256. call youcompleteme#test#setup#OpenFile( '/test/testdata/python/doc.py', {} )
  257. " needed so that the feedkeys calls actually trigger vim to notice the cursor
  258. " moving. We also need to enter/exit insert mode as Vim only checks for these
  259. " cursor moved events in very specific times. In particular, _not_ while
  260. " running a script (like we are here), but it _does_ on enter/exit insert
  261. " mode.
  262. call test_override( 'char_avail', 1 )
  263. call setpos( '.', [ 0, 12, 3 ] )
  264. doautocmd CursorHold
  265. call s:CheckPopupVisible( 11, 3, s:python_oneline.GetDoc, '' )
  266. " Dismiss
  267. normal \D
  268. call s:CheckPopupNotVisible( 11, 3, v:false )
  269. " Make sure it doesn't come back
  270. silent! doautocmd CursorHold
  271. call s:CheckPopupNotVisible( 11, 3, v:false )
  272. " Move the cursor (again this is tricky). I couldn't find any tests in vim's
  273. " own code that trigger CursorMoved, so we just cheat. (for the record, just
  274. " moving the cursor in the middle of this script does not trigger CursorMoved)
  275. doautocmd CursorMoved
  276. doautocmd CursorHold
  277. call s:CheckPopupVisible( 11, 3, s:python_oneline.GetDoc, '' )
  278. call popup_clear()
  279. endfunction
  280. function! SetUp_Test_Hover_Custom_Syntax()
  281. augroup MyYCMCustom
  282. autocmd!
  283. autocmd FileType cpp let b:ycm_hover = {
  284. \ 'command': 'GetDoc',
  285. \ 'syntax': 'cpp',
  286. \ }
  287. augroup END
  288. endfunction
  289. function! Test_Hover_Custom_Syntax()
  290. call youcompleteme#test#setup#OpenFile( '/test/testdata/cpp/completion.cc',
  291. \ {} )
  292. call assert_equal( 'cpp', &filetype )
  293. call assert_equal( { 'command': 'GetDoc', 'syntax': 'cpp' }, b:ycm_hover )
  294. call setpos( '.', [ 0, 6, 8 ] )
  295. doautocmd CursorHold
  296. call assert_equal( { 'command': 'GetDoc', 'syntax': 'cpp' }, b:ycm_hover )
  297. call s:CheckPopupVisibleScreenPos( { 'row': 7, 'col': 9 },
  298. \ s:cpp_lifetime.GetDoc,
  299. \ 'cpp' )
  300. normal \D
  301. call s:CheckPopupNotVisibleScreenPos( { 'row': 7, 'col': 9 }, v:false )
  302. call popup_clear()
  303. endfunction
  304. function! TearDown_Test_Hover_Custom_Syntax()
  305. silent! au! MyYCMCustom
  306. endfunction
  307. function! SetUp_Test_Hover_Custom_Command()
  308. augroup MyYCMCustom
  309. autocmd!
  310. autocmd FileType cpp let b:ycm_hover = {
  311. \ 'command': 'GetType',
  312. \ 'syntax': 'cpp',
  313. \ }
  314. augroup END
  315. endfunction
  316. function! Test_Hover_Custom_Command()
  317. call youcompleteme#test#setup#OpenFile( '/test/testdata/cpp/completion.cc',
  318. \ {} )
  319. call assert_equal( 'cpp', &filetype )
  320. call assert_equal( { 'command': 'GetType', 'syntax': 'cpp' }, b:ycm_hover )
  321. call setpos( '.', [ 0, 6, 8 ] )
  322. doautocmd CursorHold
  323. call assert_equal( { 'command': 'GetType', 'syntax': 'cpp' }, b:ycm_hover )
  324. call s:CheckPopupVisible( 5, 9, s:cpp_lifetime.GetType, 'cpp' )
  325. call popup_clear()
  326. endfunction
  327. function! TearDown_Test_Hover_Custom_Command()
  328. silent! au! MyYCMCustom
  329. endfunction
  330. function! Test_Long_Single_Line()
  331. call youcompleteme#test#setup#OpenFile( '/test/testdata/python/doc.py', {} )
  332. call cursor( [ 37, 3 ] )
  333. normal \D
  334. " The popup should cover at least the whole of the line above, and not the
  335. " current line
  336. call s:CheckPopupVisible( 36, 1, v:none, '' )
  337. call s:CheckPopupVisible( 36, &columns, v:none, '' )
  338. call s:CheckPopupNotVisible( 37, 1, v:false )
  339. call s:CheckPopupNotVisible( 37, &columns, v:false )
  340. " Also wrap is ON so it should cover at least 2 lines + 2 for the header/empty
  341. " line
  342. call s:CheckPopupVisible( 35, 1, v:none, '' )
  343. call s:CheckPopupVisible( 35, &columns, v:none, '' )
  344. call s:CheckPopupVisible( 33, 1, v:none, '' )
  345. call s:CheckPopupVisible( 33, &columns, v:none, '' )
  346. call popup_clear()
  347. endfunction
  348. function! Test_Long_Wrapped()
  349. call youcompleteme#test#setup#OpenFile( '/test/testdata/python/doc.py', {} )
  350. call cursor( [ 38, 22 ] )
  351. normal \D
  352. " The popup should cover at least the whole of the line above, and not the
  353. " current line. In this case, it's because the popup was shifted.
  354. call s:CheckPopupVisible( 37, 1, v:none, '' )
  355. call s:CheckPopupVisible( 37, &columns, v:none, '' )
  356. call s:CheckPopupNotVisible( 38, 1, v:false )
  357. call s:CheckPopupNotVisible( 38, &columns, v:false )
  358. " Also, wrap is off, so it should be _exactly_ 9 lines + 2 for the signature
  359. " and the empty line
  360. call s:CheckPopupVisible( 27, 1, v:none, '' )
  361. call s:CheckPopupVisible( 27, &columns, v:none, '' )
  362. call s:CheckPopupNotVisible( 26, 1, v:false )
  363. call s:CheckPopupNotVisible( 26, &columns, v:false )
  364. call popup_clear()
  365. endfunction