hover.test.vim 15 KB

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