vimsupport_test.py 69 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011
  1. # coding: utf-8
  2. #
  3. # Copyright (C) 2015-2018 YouCompleteMe contributors
  4. #
  5. # This file is part of YouCompleteMe.
  6. #
  7. # YouCompleteMe is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # YouCompleteMe is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
  19. # Intentionally not importing unicode_literals!
  20. from __future__ import print_function
  21. from __future__ import division
  22. from __future__ import absolute_import
  23. # Not installing aliases from python-future; it's unreliable and slow.
  24. from builtins import * # noqa
  25. from ycm.tests import PathToTestFile
  26. from ycm.tests.test_utils import ( CurrentWorkingDirectory, ExtendedMock,
  27. MockVimBuffers, MockVimModule, Version,
  28. VimBuffer, VimError, WindowsAndMacOnly )
  29. MockVimModule()
  30. from ycm import vimsupport
  31. from nose.tools import eq_
  32. from hamcrest import ( assert_that, calling, contains, empty, equal_to,
  33. has_entry, raises )
  34. from mock import MagicMock, call, patch
  35. from ycmd.utils import ToBytes
  36. import os
  37. import json
  38. @patch( 'vim.eval', new_callable = ExtendedMock )
  39. def SetLocationListsForBuffer_Current_test( vim_eval ):
  40. diagnostics = [ {
  41. 'bufnr': 3,
  42. 'filename': 'some_filename',
  43. 'lnum': 5,
  44. 'col': 22,
  45. 'type': 'E',
  46. 'valid': 1
  47. } ]
  48. current_buffer = VimBuffer( '/test', number = 3 )
  49. with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
  50. vimsupport.SetLocationListsForBuffer( 3, diagnostics )
  51. vim_eval.assert_has_exact_calls( [
  52. call( 'setloclist( 1, {0} )'.format( json.dumps( diagnostics ) ) )
  53. ] )
  54. @patch( 'vim.eval', new_callable = ExtendedMock )
  55. def SetLocationListsForBuffer_NotCurrent_test( vim_eval ):
  56. diagnostics = [ {
  57. 'bufnr': 3,
  58. 'filename': 'some_filename',
  59. 'lnum': 5,
  60. 'col': 22,
  61. 'type': 'E',
  62. 'valid': 1
  63. } ]
  64. current_buffer = VimBuffer( '/test', number = 3 )
  65. other_buffer = VimBuffer( '/notcurrent', number = 1 )
  66. with MockVimBuffers( [ current_buffer, other_buffer ], [ current_buffer ] ):
  67. vimsupport.SetLocationListsForBuffer( 1, diagnostics )
  68. vim_eval.assert_not_called()
  69. @patch( 'vim.eval', new_callable = ExtendedMock, side_effect = [ -1, 1 ] )
  70. def SetLocationListsForBuffer_NotVisible_test( vim_eval ):
  71. diagnostics = [ {
  72. 'bufnr': 3,
  73. 'filename': 'some_filename',
  74. 'lnum': 5,
  75. 'col': 22,
  76. 'type': 'E',
  77. 'valid': 1
  78. } ]
  79. current_buffer = VimBuffer( '/test', number = 3 )
  80. other_buffer = VimBuffer( '/notcurrent', number = 1 )
  81. with MockVimBuffers( [ current_buffer, other_buffer ], [ current_buffer ] ):
  82. vimsupport.SetLocationListsForBuffer( 1, diagnostics )
  83. vim_eval.assert_not_called()
  84. @patch( 'vim.eval', new_callable = ExtendedMock, side_effect = [ -1, 1 ] )
  85. def SetLocationListsForBuffer_MultipleWindows_test( vim_eval ):
  86. diagnostics = [ {
  87. 'bufnr': 3,
  88. 'filename': 'some_filename',
  89. 'lnum': 5,
  90. 'col': 22,
  91. 'type': 'E',
  92. 'valid': 1
  93. } ]
  94. current_buffer = VimBuffer( '/test', number = 3 )
  95. other_buffer = VimBuffer( '/notcurrent', number = 1 )
  96. with MockVimBuffers( [ current_buffer, other_buffer ],
  97. [ current_buffer, other_buffer ] ):
  98. vimsupport.SetLocationListsForBuffer( 1, diagnostics )
  99. vim_eval.assert_has_exact_calls( [
  100. call( 'setloclist( 2, {0} )'.format( json.dumps( diagnostics ) ) )
  101. ] )
  102. @patch( 'vim.eval', new_callable = ExtendedMock )
  103. def SetLocationList_test( vim_eval ):
  104. diagnostics = [ {
  105. 'bufnr': 3,
  106. 'filename': 'some_filename',
  107. 'lnum': 5,
  108. 'col': 22,
  109. 'type': 'E',
  110. 'valid': 1
  111. } ]
  112. current_buffer = VimBuffer( '/test', number = 3 )
  113. with MockVimBuffers( [ current_buffer ], [ current_buffer ], ( 1, 1 ) ):
  114. vimsupport.SetLocationList( diagnostics )
  115. vim_eval.assert_has_calls( [
  116. call( 'setloclist( 0, {0} )'.format( json.dumps( diagnostics ) ) ),
  117. ] )
  118. @patch( 'vim.eval', new_callable = ExtendedMock )
  119. def SetLocationList_NotCurrent_test( vim_eval ):
  120. diagnostics = [ {
  121. 'bufnr': 3,
  122. 'filename': 'some_filename',
  123. 'lnum': 5,
  124. 'col': 22,
  125. 'type': 'E',
  126. 'valid': 1
  127. } ]
  128. current_buffer = VimBuffer( '/test', number = 3 )
  129. other_buffer = VimBuffer( '/notcurrent', number = 1 )
  130. with MockVimBuffers( [ current_buffer, other_buffer ],
  131. [ current_buffer, other_buffer ],
  132. ( 1, 1 ) ):
  133. vimsupport.SetLocationList( diagnostics )
  134. # This version does not check the current buffer and just sets the current win
  135. vim_eval.assert_has_exact_calls( [
  136. call( 'setloclist( 0, {0} )'.format( json.dumps( diagnostics ) ) ),
  137. ] )
  138. @patch( 'ycm.vimsupport.VariableExists', return_value = True )
  139. @patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' )
  140. @patch( 'vim.command', new_callable = ExtendedMock )
  141. def OpenLocationList_test( vim_command, fitting_height, variable_exists ):
  142. vimsupport.OpenLocationList( focus = False, autoclose = True )
  143. vim_command.assert_has_exact_calls( [
  144. call( 'lopen' ),
  145. call( 'augroup ycmlocation' ),
  146. call( 'autocmd! * <buffer>' ),
  147. call( 'autocmd WinLeave <buffer> '
  148. 'if bufnr( "%" ) == expand( "<abuf>" ) | q | endif '
  149. '| autocmd! ycmlocation' ),
  150. call( 'augroup END' ),
  151. call( 'doautocmd User YcmLocationOpened' ),
  152. call( 'silent! wincmd p' )
  153. ] )
  154. fitting_height.assert_called_once_with()
  155. variable_exists.assert_called_once_with( '#User#YcmLocationOpened' )
  156. @patch( 'vim.command' )
  157. def SetFittingHeightForCurrentWindow_LineWrapOn_test( vim_command, *args ):
  158. # Create a two lines buffer whose first line is longer than the window width.
  159. current_buffer = VimBuffer( 'buffer',
  160. contents = [ 'a' * 140, 'b' * 80 ] )
  161. with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim:
  162. vim.current.window.width = 120
  163. vim.current.window.options[ 'wrap' ] = True
  164. vimsupport.SetFittingHeightForCurrentWindow()
  165. vim_command.assert_called_once_with( '3wincmd _' )
  166. @patch( 'vim.command' )
  167. def SetFittingHeightForCurrentWindow_LineWrapOff_test( vim_command, *args ):
  168. # Create a two lines buffer whose first line is longer than the window width.
  169. current_buffer = VimBuffer( 'buffer',
  170. contents = [ 'a' * 140, 'b' * 80 ] )
  171. with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim:
  172. vim.current.window.width = 120
  173. vim.current.window.options[ 'wrap' ] = False
  174. vimsupport.SetFittingHeightForCurrentWindow()
  175. vim_command.assert_called_once_with( '2wincmd _' )
  176. def AssertBuffersAreEqualAsBytes( result_buffer, expected_buffer ):
  177. eq_( len( result_buffer ), len( expected_buffer ) )
  178. for result_line, expected_line in zip( result_buffer, expected_buffer ):
  179. eq_( ToBytes( result_line ), ToBytes( expected_line ) )
  180. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  181. def ReplaceChunk_SingleLine_Repl_1_test():
  182. # Replace with longer range
  183. result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] )
  184. start, end = _BuildLocations( 1, 11, 1, 17 )
  185. vimsupport.ReplaceChunk( start, end, 'pie', result_buffer )
  186. AssertBuffersAreEqualAsBytes( [ 'This is a pie' ], result_buffer )
  187. # and replace again
  188. start, end = _BuildLocations( 1, 10, 1, 11 )
  189. vimsupport.ReplaceChunk( start, end, ' piece of ', result_buffer )
  190. AssertBuffersAreEqualAsBytes( [ 'This is a piece of pie' ], result_buffer )
  191. # and once more, for luck
  192. start, end = _BuildLocations( 1, 1, 1, 5 )
  193. vimsupport.ReplaceChunk( start, end, 'How long', result_buffer )
  194. AssertBuffersAreEqualAsBytes( [ 'How long is a piece of pie' ],
  195. result_buffer )
  196. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  197. def ReplaceChunk_SingleLine_Repl_2_test():
  198. # Replace with shorter range
  199. result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] )
  200. start, end = _BuildLocations( 1, 11, 1, 17 )
  201. vimsupport.ReplaceChunk( start, end, 'test', result_buffer )
  202. AssertBuffersAreEqualAsBytes( [ 'This is a test' ], result_buffer )
  203. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  204. def ReplaceChunk_SingleLine_Repl_3_test():
  205. # Replace with equal range
  206. result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] )
  207. start, end = _BuildLocations( 1, 6, 1, 8 )
  208. vimsupport.ReplaceChunk( start, end, 'be', result_buffer )
  209. AssertBuffersAreEqualAsBytes( [ 'This be a string' ], result_buffer )
  210. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  211. def ReplaceChunk_SingleLine_Add_1_test():
  212. # Insert at start
  213. result_buffer = VimBuffer( 'buffer', contents = [ 'is a string' ] )
  214. start, end = _BuildLocations( 1, 1, 1, 1 )
  215. vimsupport.ReplaceChunk( start, end, 'This ', result_buffer )
  216. AssertBuffersAreEqualAsBytes( [ 'This is a string' ], result_buffer )
  217. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  218. def ReplaceChunk_SingleLine_Add_2_test():
  219. # Insert at end
  220. result_buffer = VimBuffer( 'buffer', contents = [ 'This is a ' ] )
  221. start, end = _BuildLocations( 1, 11, 1, 11 )
  222. vimsupport.ReplaceChunk( start, end, 'string', result_buffer )
  223. AssertBuffersAreEqualAsBytes( [ 'This is a string' ], result_buffer )
  224. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  225. def ReplaceChunk_SingleLine_Add_3_test():
  226. # Insert in the middle
  227. result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] )
  228. start, end = _BuildLocations( 1, 8, 1, 8 )
  229. vimsupport.ReplaceChunk( start, end, ' not', result_buffer )
  230. AssertBuffersAreEqualAsBytes( [ 'This is not a string' ], result_buffer )
  231. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  232. def ReplaceChunk_SingleLine_Del_1_test():
  233. # Delete from start
  234. result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] )
  235. start, end = _BuildLocations( 1, 1, 1, 6 )
  236. vimsupport.ReplaceChunk( start, end, '', result_buffer )
  237. AssertBuffersAreEqualAsBytes( [ 'is a string' ], result_buffer )
  238. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  239. def ReplaceChunk_SingleLine_Del_2_test():
  240. # Delete from end
  241. result_buffer = VimBuffer( 'buffer', contents = [ 'This is a string' ] )
  242. start, end = _BuildLocations( 1, 10, 1, 18 )
  243. vimsupport.ReplaceChunk( start, end, '', result_buffer )
  244. AssertBuffersAreEqualAsBytes( [ 'This is a' ], result_buffer )
  245. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  246. def ReplaceChunk_SingleLine_Del_3_test():
  247. # Delete from middle
  248. result_buffer = VimBuffer( 'buffer', contents = [ 'This is not a string' ] )
  249. start, end = _BuildLocations( 1, 9, 1, 13 )
  250. vimsupport.ReplaceChunk( start, end, '', result_buffer )
  251. AssertBuffersAreEqualAsBytes( [ 'This is a string' ], result_buffer )
  252. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  253. def ReplaceChunk_SingleLine_Unicode_ReplaceUnicodeChars_test():
  254. # Replace Unicode characters.
  255. result_buffer = VimBuffer(
  256. 'buffer', contents = [ 'This Uniçø∂‰ string is in the middle' ] )
  257. start, end = _BuildLocations( 1, 6, 1, 20 )
  258. vimsupport.ReplaceChunk( start, end, 'Unicode ', result_buffer )
  259. AssertBuffersAreEqualAsBytes( [ 'This Unicode string is in the middle' ],
  260. result_buffer )
  261. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  262. def ReplaceChunk_SingleLine_Unicode_ReplaceAfterUnicode_test():
  263. # Replace ASCII characters after Unicode characters in the line.
  264. result_buffer = VimBuffer(
  265. 'buffer', contents = [ 'This Uniçø∂‰ string is in the middle' ] )
  266. start, end = _BuildLocations( 1, 30, 1, 43 )
  267. vimsupport.ReplaceChunk( start, end, 'fåke', result_buffer )
  268. AssertBuffersAreEqualAsBytes( [ 'This Uniçø∂‰ string is fåke' ],
  269. result_buffer )
  270. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  271. def ReplaceChunk_SingleLine_Unicode_Grown_test():
  272. # Replace ASCII characters after Unicode characters in the line.
  273. result_buffer = VimBuffer( 'buffer', contents = [ 'a' ] )
  274. start, end = _BuildLocations( 1, 1, 1, 2 )
  275. vimsupport.ReplaceChunk( start, end, 'å', result_buffer )
  276. AssertBuffersAreEqualAsBytes( [ 'å' ], result_buffer )
  277. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  278. def ReplaceChunk_RemoveSingleLine_test():
  279. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  280. 'aBa',
  281. 'aCa' ] )
  282. start, end = _BuildLocations( 2, 1, 3, 1 )
  283. vimsupport.ReplaceChunk( start, end, '', result_buffer )
  284. # First line is not affected.
  285. AssertBuffersAreEqualAsBytes( [ 'aAa',
  286. 'aCa' ], result_buffer )
  287. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  288. def ReplaceChunk_SingleToMultipleLines_test():
  289. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  290. 'aBa',
  291. 'aCa' ] )
  292. start, end = _BuildLocations( 2, 3, 2, 4 )
  293. vimsupport.ReplaceChunk( start, end, 'cccc', result_buffer )
  294. AssertBuffersAreEqualAsBytes( [ 'aAa',
  295. 'aBcccc',
  296. 'aCa' ], result_buffer )
  297. # now make another change to the second line
  298. start, end = _BuildLocations( 2, 2, 2, 2 )
  299. vimsupport.ReplaceChunk( start, end, 'Eb\nbF', result_buffer )
  300. AssertBuffersAreEqualAsBytes( [ 'aAa',
  301. 'aEb',
  302. 'bFBcccc',
  303. 'aCa' ], result_buffer )
  304. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  305. def ReplaceChunk_SingleToMultipleLines2_test():
  306. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  307. 'aBa',
  308. 'aCa' ] )
  309. start, end = _BuildLocations( 2, 2, 2, 2 )
  310. vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nG', result_buffer )
  311. AssertBuffersAreEqualAsBytes( [ 'aAa',
  312. 'aEb',
  313. 'bFb',
  314. 'GBa',
  315. 'aCa' ], result_buffer )
  316. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  317. def ReplaceChunk_SingleToMultipleLines3_test():
  318. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  319. 'aBa',
  320. 'aCa' ] )
  321. start, end = _BuildLocations( 2, 2, 2, 2 )
  322. vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nbGb', result_buffer )
  323. AssertBuffersAreEqualAsBytes( [ 'aAa',
  324. 'aEb',
  325. 'bFb',
  326. 'bGbBa',
  327. 'aCa' ], result_buffer )
  328. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  329. def ReplaceChunk_SingleToMultipleLinesReplace_test():
  330. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  331. 'aBa',
  332. 'aCa' ] )
  333. start, end = _BuildLocations( 1, 2, 1, 4 )
  334. vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nbGb', result_buffer )
  335. AssertBuffersAreEqualAsBytes( [ 'aEb',
  336. 'bFb',
  337. 'bGb',
  338. 'aBa',
  339. 'aCa' ], result_buffer )
  340. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  341. def ReplaceChunk_SingleToMultipleLinesReplace_2_test():
  342. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  343. 'aBa',
  344. 'aCa' ] )
  345. start, end = _BuildLocations( 1, 4, 1, 4 )
  346. vimsupport.ReplaceChunk( start, end, 'cccc', result_buffer )
  347. AssertBuffersAreEqualAsBytes( [ 'aAacccc',
  348. 'aBa',
  349. 'aCa', ], result_buffer )
  350. # now do a subsequent change (insert in the middle of the first line)
  351. start, end = _BuildLocations( 1, 2, 1, 4 )
  352. vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nbGb', result_buffer )
  353. AssertBuffersAreEqualAsBytes( [ 'aEb',
  354. 'bFb',
  355. 'bGbcccc',
  356. 'aBa',
  357. 'aCa' ], result_buffer )
  358. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  359. def ReplaceChunk_MultipleLinesToSingleLine_test():
  360. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  361. 'aBa',
  362. 'aCaaaa' ] )
  363. start, end = _BuildLocations( 3, 4, 3, 5 )
  364. vimsupport.ReplaceChunk( start, end, 'dd\ndd', result_buffer )
  365. AssertBuffersAreEqualAsBytes( [ 'aAa',
  366. 'aBa',
  367. 'aCadd',
  368. 'ddaa' ], result_buffer )
  369. # make another modification applying offsets
  370. start, end = _BuildLocations( 3, 3, 3, 4 )
  371. vimsupport.ReplaceChunk( start, end, 'cccc', result_buffer )
  372. AssertBuffersAreEqualAsBytes( [ 'aAa',
  373. 'aBa',
  374. 'aCccccdd',
  375. 'ddaa' ], result_buffer )
  376. # and another, for luck
  377. start, end = _BuildLocations( 2, 2, 3, 2 )
  378. vimsupport.ReplaceChunk( start, end, 'E', result_buffer )
  379. AssertBuffersAreEqualAsBytes( [ 'aAa',
  380. 'aECccccdd',
  381. 'ddaa' ], result_buffer )
  382. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  383. def ReplaceChunk_MultipleLinesToSameMultipleLines_test():
  384. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  385. 'aBa',
  386. 'aCa',
  387. 'aDe' ] )
  388. start, end = _BuildLocations( 2, 2, 3, 2 )
  389. vimsupport.ReplaceChunk( start, end, 'Eb\nbF', result_buffer )
  390. AssertBuffersAreEqualAsBytes( [ 'aAa',
  391. 'aEb',
  392. 'bFCa',
  393. 'aDe' ], result_buffer )
  394. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  395. def ReplaceChunk_MultipleLinesToMoreMultipleLines_test():
  396. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  397. 'aBa',
  398. 'aCa',
  399. 'aDe' ] )
  400. start, end = _BuildLocations( 2, 2, 3, 2 )
  401. vimsupport.ReplaceChunk( start, end, 'Eb\nbFb\nbG', result_buffer )
  402. AssertBuffersAreEqualAsBytes( [ 'aAa',
  403. 'aEb',
  404. 'bFb',
  405. 'bGCa',
  406. 'aDe' ], result_buffer )
  407. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  408. def ReplaceChunk_MultipleLinesToLessMultipleLines_test():
  409. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  410. 'aBa',
  411. 'aCa',
  412. 'aDe' ] )
  413. start, end = _BuildLocations( 1, 2, 3, 2 )
  414. vimsupport.ReplaceChunk( start, end, 'Eb\nbF', result_buffer )
  415. AssertBuffersAreEqualAsBytes( [ 'aEb',
  416. 'bFCa',
  417. 'aDe' ], result_buffer )
  418. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  419. def ReplaceChunk_MultipleLinesToEvenLessMultipleLines_test():
  420. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  421. 'aBa',
  422. 'aCa',
  423. 'aDe' ] )
  424. start, end = _BuildLocations( 1, 2, 4, 2 )
  425. vimsupport.ReplaceChunk( start, end, 'Eb\nbF', result_buffer )
  426. AssertBuffersAreEqualAsBytes( [ 'aEb',
  427. 'bFDe' ], result_buffer )
  428. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  429. def ReplaceChunk_SpanBufferEdge_test():
  430. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  431. 'aBa',
  432. 'aCa' ] )
  433. start, end = _BuildLocations( 1, 1, 1, 3 )
  434. vimsupport.ReplaceChunk( start, end, 'bDb', result_buffer )
  435. AssertBuffersAreEqualAsBytes( [ 'bDba',
  436. 'aBa',
  437. 'aCa' ], result_buffer )
  438. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  439. def ReplaceChunk_DeleteTextInLine_test():
  440. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  441. 'aBa',
  442. 'aCa' ] )
  443. start, end = _BuildLocations( 2, 2, 2, 3 )
  444. vimsupport.ReplaceChunk( start, end, '', result_buffer )
  445. AssertBuffersAreEqualAsBytes( [ 'aAa',
  446. 'aa',
  447. 'aCa' ], result_buffer )
  448. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  449. def ReplaceChunk_AddTextInLine_test():
  450. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  451. 'aBa',
  452. 'aCa' ] )
  453. start, end = _BuildLocations( 2, 2, 2, 2 )
  454. vimsupport.ReplaceChunk( start, end, 'bDb', result_buffer )
  455. AssertBuffersAreEqualAsBytes( [ 'aAa',
  456. 'abDbBa',
  457. 'aCa' ], result_buffer )
  458. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  459. def ReplaceChunk_ReplaceTextInLine_test():
  460. result_buffer = VimBuffer( 'buffer', contents = [ 'aAa',
  461. 'aBa',
  462. 'aCa' ] )
  463. start, end = _BuildLocations( 2, 2, 2, 3 )
  464. vimsupport.ReplaceChunk( start, end, 'bDb', result_buffer )
  465. AssertBuffersAreEqualAsBytes( [ 'aAa',
  466. 'abDba',
  467. 'aCa' ], result_buffer )
  468. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  469. def ReplaceChunk_NewlineChunk_test():
  470. result_buffer = VimBuffer( 'buffer', contents = [ 'first line',
  471. 'second line' ] )
  472. start, end = _BuildLocations( 1, 11, 2, 1 )
  473. vimsupport.ReplaceChunk( start, end, '\n', result_buffer )
  474. AssertBuffersAreEqualAsBytes( [ 'first line',
  475. 'second line' ], result_buffer )
  476. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  477. def ReplaceChunk_BeyondEndOfFile_test():
  478. result_buffer = VimBuffer( 'buffer', contents = [ 'first line',
  479. 'second line' ] )
  480. start, end = _BuildLocations( 1, 11, 3, 1 )
  481. vimsupport.ReplaceChunk( start, end, '\n', result_buffer )
  482. AssertBuffersAreEqualAsBytes( [ 'first line' ], result_buffer )
  483. @patch( 'vim.current.window.cursor', ( 1, 3 ) )
  484. def ReplaceChunk_CursorPosition_test():
  485. result_buffer = VimBuffer( 'buffer', contents = [ 'bar' ] )
  486. start, end = _BuildLocations( 1, 1, 1, 1 )
  487. vimsupport.ReplaceChunk( start,
  488. end,
  489. 'xyz\nfoo',
  490. result_buffer )
  491. AssertBuffersAreEqualAsBytes( [ 'xyz', 'foobar' ], result_buffer )
  492. # Cursor line is 0-based.
  493. assert_that( vimsupport.CurrentLineAndColumn(), contains( 1, 6 ) )
  494. def _BuildLocations( start_line, start_column, end_line, end_column ):
  495. return {
  496. 'line_num' : start_line,
  497. 'column_num': start_column,
  498. }, {
  499. 'line_num' : end_line,
  500. 'column_num': end_column,
  501. }
  502. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  503. def ReplaceChunksInBuffer_SortedChunks_test():
  504. chunks = [
  505. _BuildChunk( 1, 4, 1, 4, '(' ),
  506. _BuildChunk( 1, 11, 1, 11, ')' )
  507. ]
  508. result_buffer = VimBuffer( 'buffer', contents = [ 'CT<10 >> 2> ct' ] )
  509. vimsupport.ReplaceChunksInBuffer( chunks, result_buffer )
  510. AssertBuffersAreEqualAsBytes( [ 'CT<(10 >> 2)> ct' ], result_buffer )
  511. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  512. def ReplaceChunksInBuffer_UnsortedChunks_test():
  513. chunks = [
  514. _BuildChunk( 1, 11, 1, 11, ')' ),
  515. _BuildChunk( 1, 4, 1, 4, '(' )
  516. ]
  517. result_buffer = VimBuffer( 'buffer', contents = [ 'CT<10 >> 2> ct' ] )
  518. vimsupport.ReplaceChunksInBuffer( chunks, result_buffer )
  519. AssertBuffersAreEqualAsBytes( [ 'CT<(10 >> 2)> ct' ], result_buffer )
  520. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  521. def ReplaceChunksInBuffer_LineOverlappingChunks_test():
  522. chunks = [
  523. _BuildChunk( 1, 11, 2, 1, '\n ' ),
  524. _BuildChunk( 2, 12, 3, 1, '\n ' ),
  525. _BuildChunk( 3, 11, 4, 1, '\n ' )
  526. ]
  527. result_buffer = VimBuffer( 'buffer', contents = [ 'first line',
  528. 'second line',
  529. 'third line',
  530. 'fourth line' ] )
  531. vimsupport.ReplaceChunksInBuffer( chunks, result_buffer )
  532. AssertBuffersAreEqualAsBytes( [ 'first line',
  533. ' second line',
  534. ' third line',
  535. ' fourth line' ], result_buffer )
  536. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  537. def ReplaceChunksInBuffer_OutdentChunks_test():
  538. chunks = [
  539. _BuildChunk( 1, 1, 1, 5, ' ' ),
  540. _BuildChunk( 1, 15, 2, 9, '\n ' ),
  541. _BuildChunk( 2, 20, 3, 3, '\n' )
  542. ]
  543. result_buffer = VimBuffer( 'buffer', contents = [ ' first line',
  544. ' second line',
  545. ' third line' ] )
  546. vimsupport.ReplaceChunksInBuffer( chunks, result_buffer )
  547. AssertBuffersAreEqualAsBytes( [ ' first line',
  548. ' second line',
  549. ' third line' ], result_buffer )
  550. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  551. def ReplaceChunksInBuffer_OneLineIndentingChunks_test():
  552. chunks = [
  553. _BuildChunk( 1, 8, 2, 1, '\n ' ),
  554. _BuildChunk( 2, 9, 2, 10, '\n ' ),
  555. _BuildChunk( 2, 19, 2, 20, '\n ' )
  556. ]
  557. result_buffer = VimBuffer( 'buffer', contents = [ 'class {',
  558. 'method { statement }',
  559. '}' ] )
  560. vimsupport.ReplaceChunksInBuffer( chunks, result_buffer )
  561. AssertBuffersAreEqualAsBytes( [ 'class {',
  562. ' method {',
  563. ' statement',
  564. ' }',
  565. '}' ], result_buffer )
  566. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  567. def ReplaceChunksInBuffer_SameLocation_test():
  568. chunks = [
  569. _BuildChunk( 1, 1, 1, 1, 'this ' ),
  570. _BuildChunk( 1, 1, 1, 1, 'is ' ),
  571. _BuildChunk( 1, 1, 1, 1, 'pure ' )
  572. ]
  573. result_buffer = VimBuffer( 'buffer', contents = [ 'folly' ] )
  574. vimsupport.ReplaceChunksInBuffer( chunks, result_buffer )
  575. AssertBuffersAreEqualAsBytes( [ 'this is pure folly' ], result_buffer )
  576. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  577. @patch( 'ycm.vimsupport.VariableExists', return_value = False )
  578. @patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' )
  579. @patch( 'ycm.vimsupport.GetBufferNumberForFilename',
  580. return_value = 1,
  581. new_callable = ExtendedMock )
  582. @patch( 'ycm.vimsupport.BufferIsVisible',
  583. return_value = True,
  584. new_callable = ExtendedMock )
  585. @patch( 'ycm.vimsupport.OpenFilename' )
  586. @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock )
  587. @patch( 'vim.eval', new_callable = ExtendedMock )
  588. @patch( 'vim.command', new_callable = ExtendedMock )
  589. def ReplaceChunks_SingleFile_Open_test( vim_command,
  590. vim_eval,
  591. post_vim_message,
  592. open_filename,
  593. buffer_is_visible,
  594. get_buffer_number_for_filename,
  595. set_fitting_height,
  596. variable_exists ):
  597. single_buffer_name = os.path.realpath( 'single_file' )
  598. chunks = [
  599. _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name )
  600. ]
  601. result_buffer = VimBuffer(
  602. single_buffer_name,
  603. contents = [
  604. 'line1',
  605. 'line2',
  606. 'line3'
  607. ]
  608. )
  609. with patch( 'vim.buffers', [ None, result_buffer, None ] ):
  610. vimsupport.ReplaceChunks( chunks )
  611. # Ensure that we applied the replacement correctly
  612. eq_( result_buffer.GetLines(), [
  613. 'replacementline2',
  614. 'line3',
  615. ] )
  616. # GetBufferNumberForFilename is called twice:
  617. # - once to the check if we would require opening the file (so that we can
  618. # raise a warning)
  619. # - once whilst applying the changes
  620. get_buffer_number_for_filename.assert_has_exact_calls( [
  621. call( single_buffer_name ),
  622. call( single_buffer_name ),
  623. ] )
  624. # BufferIsVisible is called twice for the same reasons as above
  625. buffer_is_visible.assert_has_exact_calls( [
  626. call( 1 ),
  627. call( 1 ),
  628. ] )
  629. # we don't attempt to open any files
  630. open_filename.assert_not_called()
  631. # But we do set the quickfix list
  632. vim_eval.assert_has_exact_calls( [
  633. call( 'setqflist( {0} )'.format( json.dumps( [ {
  634. 'bufnr': 1,
  635. 'filename': single_buffer_name,
  636. 'lnum': 1,
  637. 'col': 1,
  638. 'text': 'replacement',
  639. 'type': 'F'
  640. } ] ) ) ),
  641. ] )
  642. # And it is ReplaceChunks that prints the message showing the number of
  643. # changes
  644. post_vim_message.assert_has_exact_calls( [
  645. call( 'Applied 1 changes', warning = False ),
  646. ] )
  647. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  648. @patch( 'ycm.vimsupport.VariableExists', return_value = False )
  649. @patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' )
  650. @patch( 'ycm.vimsupport.GetBufferNumberForFilename',
  651. side_effect = [ -1, -1, 1 ],
  652. new_callable = ExtendedMock )
  653. @patch( 'ycm.vimsupport.BufferIsVisible',
  654. side_effect = [ False, False, True ],
  655. new_callable = ExtendedMock )
  656. @patch( 'ycm.vimsupport.OpenFilename',
  657. new_callable = ExtendedMock )
  658. @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock )
  659. @patch( 'ycm.vimsupport.Confirm',
  660. return_value = True,
  661. new_callable = ExtendedMock )
  662. @patch( 'vim.eval', return_value = 10, new_callable = ExtendedMock )
  663. @patch( 'vim.command', new_callable = ExtendedMock )
  664. def ReplaceChunks_SingleFile_NotOpen_test( vim_command,
  665. vim_eval,
  666. confirm,
  667. post_vim_message,
  668. open_filename,
  669. buffer_is_visible,
  670. get_buffer_number_for_filename,
  671. set_fitting_height,
  672. variable_exists ):
  673. single_buffer_name = os.path.realpath( 'single_file' )
  674. chunks = [
  675. _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name )
  676. ]
  677. result_buffer = VimBuffer(
  678. single_buffer_name,
  679. contents = [
  680. 'line1',
  681. 'line2',
  682. 'line3'
  683. ]
  684. )
  685. with patch( 'vim.buffers', [ None, result_buffer, None ] ):
  686. vimsupport.ReplaceChunks( chunks )
  687. # We checked if it was OK to open the file
  688. confirm.assert_has_exact_calls( [
  689. call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) )
  690. ] )
  691. # Ensure that we applied the replacement correctly
  692. eq_( result_buffer.GetLines(), [
  693. 'replacementline2',
  694. 'line3',
  695. ] )
  696. # GetBufferNumberForFilename is called 3 times. The return values are set in
  697. # the @patch call above:
  698. # - once to the check if we would require opening the file (so that we can
  699. # raise a warning) (-1 return)
  700. # - once whilst applying the changes (-1 return)
  701. # - finally after calling OpenFilename (1 return)
  702. get_buffer_number_for_filename.assert_has_exact_calls( [
  703. call( single_buffer_name ),
  704. call( single_buffer_name ),
  705. call( single_buffer_name ),
  706. ] )
  707. # BufferIsVisible is called 3 times for the same reasons as above, with the
  708. # return of each one
  709. buffer_is_visible.assert_has_exact_calls( [
  710. call( -1 ),
  711. call( -1 ),
  712. call( 1 ),
  713. ] )
  714. # We open 'single_file' as expected.
  715. open_filename.assert_called_with( single_buffer_name, {
  716. 'focus': True,
  717. 'fix': True,
  718. 'size': 10
  719. } )
  720. # And close it again, then show the quickfix window.
  721. vim_command.assert_has_exact_calls( [
  722. call( 'lclose' ),
  723. call( 'hide' ),
  724. ] )
  725. # And update the quickfix list
  726. vim_eval.assert_has_exact_calls( [
  727. call( '&previewheight' ),
  728. call( 'setqflist( {0} )'.format( json.dumps( [ {
  729. 'bufnr': 1,
  730. 'filename': single_buffer_name,
  731. 'lnum': 1,
  732. 'col': 1,
  733. 'text': 'replacement',
  734. 'type': 'F'
  735. } ] ) ) ),
  736. ] )
  737. # And it is ReplaceChunks that prints the message showing the number of
  738. # changes
  739. post_vim_message.assert_has_exact_calls( [
  740. call( 'Applied 1 changes', warning = False ),
  741. ] )
  742. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  743. @patch( 'ycm.vimsupport.VariableExists', return_value = False )
  744. @patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' )
  745. @patch( 'ycm.vimsupport.GetBufferNumberForFilename',
  746. side_effect = [ -1, 1 ],
  747. new_callable = ExtendedMock )
  748. @patch( 'ycm.vimsupport.BufferIsVisible',
  749. side_effect = [ False, True ],
  750. new_callable = ExtendedMock )
  751. @patch( 'ycm.vimsupport.OpenFilename',
  752. new_callable = ExtendedMock )
  753. @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock )
  754. @patch( 'ycm.vimsupport.Confirm',
  755. return_value = True,
  756. new_callable = ExtendedMock )
  757. @patch( 'vim.eval', return_value = 10, new_callable = ExtendedMock )
  758. @patch( 'vim.command', new_callable = ExtendedMock )
  759. def ReplaceChunks_SingleFile_NotOpen_Silent_test(
  760. vim_command,
  761. vim_eval,
  762. confirm,
  763. post_vim_message,
  764. open_filename,
  765. buffer_is_visible,
  766. get_buffer_number_for_filename,
  767. set_fitting_height,
  768. variable_exists ):
  769. # This test is the same as ReplaceChunks_SingleFile_NotOpen_test, but we pass
  770. # the silent flag, as used by post-complete actions, and shows the stuff we
  771. # _don't_ call in that case.
  772. single_buffer_name = os.path.realpath( 'single_file' )
  773. chunks = [
  774. _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name )
  775. ]
  776. result_buffer = VimBuffer(
  777. single_buffer_name,
  778. contents = [
  779. 'line1',
  780. 'line2',
  781. 'line3'
  782. ]
  783. )
  784. with patch( 'vim.buffers', [ None, result_buffer, None ] ):
  785. vimsupport.ReplaceChunks( chunks, silent=True )
  786. # We didn't check if it was OK to open the file (silent)
  787. confirm.assert_not_called()
  788. # Ensure that we applied the replacement correctly
  789. eq_( result_buffer.GetLines(), [
  790. 'replacementline2',
  791. 'line3',
  792. ] )
  793. # GetBufferNumberForFilename is called 2 times. The return values are set in
  794. # the @patch call above:
  795. # - once whilst applying the changes (-1 return)
  796. # - finally after calling OpenFilename (1 return)
  797. get_buffer_number_for_filename.assert_has_exact_calls( [
  798. call( single_buffer_name ),
  799. call( single_buffer_name ),
  800. ] )
  801. # BufferIsVisible is called 2 times for the same reasons as above, with the
  802. # return of each one
  803. buffer_is_visible.assert_has_exact_calls( [
  804. call( -1 ),
  805. call( 1 ),
  806. ] )
  807. # We open 'single_file' as expected.
  808. open_filename.assert_called_with( single_buffer_name, {
  809. 'focus': True,
  810. 'fix': True,
  811. 'size': 10
  812. } )
  813. # And close it again, but don't show the quickfix window
  814. vim_command.assert_has_exact_calls( [
  815. call( 'lclose' ),
  816. call( 'hide' ),
  817. ] )
  818. set_fitting_height.assert_not_called()
  819. # But we _don't_ update the QuickFix list
  820. vim_eval.assert_has_exact_calls( [
  821. call( '&previewheight' ),
  822. ] )
  823. # And we don't print a message either
  824. post_vim_message.assert_not_called()
  825. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  826. @patch( 'ycm.vimsupport.GetBufferNumberForFilename',
  827. side_effect = [ -1, -1, 1 ],
  828. new_callable = ExtendedMock )
  829. @patch( 'ycm.vimsupport.BufferIsVisible',
  830. side_effect = [ False, False, True ],
  831. new_callable = ExtendedMock )
  832. @patch( 'ycm.vimsupport.OpenFilename',
  833. new_callable = ExtendedMock )
  834. @patch( 'ycm.vimsupport.PostVimMessage',
  835. new_callable = ExtendedMock )
  836. @patch( 'ycm.vimsupport.Confirm',
  837. return_value = False,
  838. new_callable = ExtendedMock )
  839. @patch( 'vim.eval',
  840. return_value = 10,
  841. new_callable = ExtendedMock )
  842. @patch( 'vim.command', new_callable = ExtendedMock )
  843. def ReplaceChunks_User_Declines_To_Open_File_test(
  844. vim_command,
  845. vim_eval,
  846. confirm,
  847. post_vim_message,
  848. open_filename,
  849. buffer_is_visible,
  850. get_buffer_number_for_filename ):
  851. # Same as above, except the user selects Cancel when asked if they should
  852. # allow us to open lots of (ahem, 1) file.
  853. single_buffer_name = os.path.realpath( 'single_file' )
  854. chunks = [
  855. _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name )
  856. ]
  857. result_buffer = VimBuffer(
  858. single_buffer_name,
  859. contents = [
  860. 'line1',
  861. 'line2',
  862. 'line3'
  863. ]
  864. )
  865. with patch( 'vim.buffers', [ None, result_buffer, None ] ):
  866. vimsupport.ReplaceChunks( chunks )
  867. # We checked if it was OK to open the file
  868. confirm.assert_has_exact_calls( [
  869. call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) )
  870. ] )
  871. # Ensure that buffer is not changed
  872. eq_( result_buffer.GetLines(), [
  873. 'line1',
  874. 'line2',
  875. 'line3',
  876. ] )
  877. # GetBufferNumberForFilename is called once. The return values are set in
  878. # the @patch call above:
  879. # - once to the check if we would require opening the file (so that we can
  880. # raise a warning) (-1 return)
  881. get_buffer_number_for_filename.assert_has_exact_calls( [
  882. call( single_buffer_name ),
  883. ] )
  884. # BufferIsVisible is called once for the above file, which wasn't visible.
  885. buffer_is_visible.assert_has_exact_calls( [
  886. call( -1 ),
  887. ] )
  888. # We don't attempt to open any files or update any quickfix list or anything
  889. # like that
  890. open_filename.assert_not_called()
  891. vim_eval.assert_not_called()
  892. vim_command.assert_not_called()
  893. post_vim_message.assert_not_called()
  894. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  895. @patch( 'ycm.vimsupport.GetBufferNumberForFilename',
  896. side_effect = [ -1, -1, 1 ],
  897. new_callable = ExtendedMock )
  898. # Key difference is here: In the final check, BufferIsVisible returns False
  899. @patch( 'ycm.vimsupport.BufferIsVisible',
  900. side_effect = [ False, False, False ],
  901. new_callable = ExtendedMock )
  902. @patch( 'ycm.vimsupport.OpenFilename',
  903. new_callable = ExtendedMock )
  904. @patch( 'ycm.vimsupport.PostVimMessage',
  905. new_callable = ExtendedMock )
  906. @patch( 'ycm.vimsupport.Confirm',
  907. return_value = True,
  908. new_callable = ExtendedMock )
  909. @patch( 'vim.eval',
  910. return_value = 10,
  911. new_callable = ExtendedMock )
  912. @patch( 'vim.command',
  913. new_callable = ExtendedMock )
  914. def ReplaceChunks_User_Aborts_Opening_File_test(
  915. vim_command,
  916. vim_eval,
  917. confirm,
  918. post_vim_message,
  919. open_filename,
  920. buffer_is_visible,
  921. get_buffer_number_for_filename ):
  922. # Same as above, except the user selects Abort or Quick during the
  923. # "swap-file-found" dialog
  924. single_buffer_name = os.path.realpath( 'single_file' )
  925. chunks = [
  926. _BuildChunk( 1, 1, 2, 1, 'replacement', single_buffer_name )
  927. ]
  928. result_buffer = VimBuffer(
  929. single_buffer_name,
  930. contents = [
  931. 'line1',
  932. 'line2',
  933. 'line3'
  934. ]
  935. )
  936. with patch( 'vim.buffers', [ None, result_buffer, None ] ):
  937. assert_that( calling( vimsupport.ReplaceChunks ).with_args( chunks ),
  938. raises( RuntimeError,
  939. 'Unable to open file: .+single_file\n'
  940. 'FixIt/Refactor operation aborted prior to completion. '
  941. 'Your files have not been fully updated. '
  942. 'Please use undo commands to revert the applied changes.' ) )
  943. # We checked if it was OK to open the file
  944. confirm.assert_has_exact_calls( [
  945. call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) )
  946. ] )
  947. # Ensure that buffer is not changed
  948. eq_( result_buffer.GetLines(), [
  949. 'line1',
  950. 'line2',
  951. 'line3',
  952. ] )
  953. # We tried to open this file
  954. open_filename.assert_called_with( single_buffer_name, {
  955. 'focus': True,
  956. 'fix': True,
  957. 'size': 10
  958. } )
  959. vim_eval.assert_called_with( "&previewheight" )
  960. # But raised an exception before issuing the message at the end
  961. post_vim_message.assert_not_called()
  962. @patch( 'vim.current.window.cursor', ( 1, 1 ) )
  963. @patch( 'ycm.vimsupport.VariableExists', return_value = False )
  964. @patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' )
  965. @patch( 'ycm.vimsupport.GetBufferNumberForFilename', side_effect = [
  966. 22, # first_file (check)
  967. -1, # second_file (check)
  968. 22, # first_file (apply)
  969. -1, # second_file (apply)
  970. 19, # second_file (check after open)
  971. ],
  972. new_callable = ExtendedMock )
  973. @patch( 'ycm.vimsupport.BufferIsVisible', side_effect = [
  974. True, # first_file (check)
  975. False, # second_file (check)
  976. True, # first_file (apply)
  977. False, # second_file (apply)
  978. True, # side_effect (check after open)
  979. ],
  980. new_callable = ExtendedMock )
  981. @patch( 'ycm.vimsupport.OpenFilename',
  982. new_callable = ExtendedMock )
  983. @patch( 'ycm.vimsupport.PostVimMessage',
  984. new_callable = ExtendedMock )
  985. @patch( 'ycm.vimsupport.Confirm', return_value = True,
  986. new_callable = ExtendedMock )
  987. @patch( 'vim.eval', return_value = 10,
  988. new_callable = ExtendedMock )
  989. @patch( 'vim.command',
  990. new_callable = ExtendedMock )
  991. def ReplaceChunks_MultiFile_Open_test( vim_command,
  992. vim_eval,
  993. confirm,
  994. post_vim_message,
  995. open_filename,
  996. buffer_is_visible,
  997. get_buffer_number_for_filename,
  998. set_fitting_height,
  999. variable_exists ):
  1000. # Chunks are split across 2 files, one is already open, one isn't
  1001. first_buffer_name = os.path.realpath( '1_first_file' )
  1002. second_buffer_name = os.path.realpath( '2_second_file' )
  1003. chunks = [
  1004. _BuildChunk( 1, 1, 2, 1, 'first_file_replacement ', first_buffer_name ),
  1005. _BuildChunk( 2, 1, 2, 1, 'second_file_replacement ', second_buffer_name ),
  1006. ]
  1007. first_file = VimBuffer(
  1008. first_buffer_name,
  1009. number = 22,
  1010. contents = [
  1011. 'line1',
  1012. 'line2',
  1013. 'line3',
  1014. ]
  1015. )
  1016. second_file = VimBuffer(
  1017. second_buffer_name,
  1018. number = 19,
  1019. contents = [
  1020. 'another line1',
  1021. 'ACME line2',
  1022. ]
  1023. )
  1024. vim_buffers = [ None ] * 23
  1025. vim_buffers[ 22 ] = first_file
  1026. vim_buffers[ 19 ] = second_file
  1027. with patch( 'vim.buffers', vim_buffers ):
  1028. vimsupport.ReplaceChunks( chunks )
  1029. # We checked for the right file names
  1030. get_buffer_number_for_filename.assert_has_exact_calls( [
  1031. call( first_buffer_name ),
  1032. call( second_buffer_name ),
  1033. call( first_buffer_name ),
  1034. call( second_buffer_name ),
  1035. call( second_buffer_name ),
  1036. ] )
  1037. # We checked if it was OK to open the file
  1038. confirm.assert_has_exact_calls( [
  1039. call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) )
  1040. ] )
  1041. # Ensure that buffers are updated
  1042. eq_( second_file.GetLines(), [
  1043. 'another line1',
  1044. 'second_file_replacement ACME line2',
  1045. ] )
  1046. eq_( first_file.GetLines(), [
  1047. 'first_file_replacement line2',
  1048. 'line3',
  1049. ] )
  1050. # We open '2_second_file' as expected.
  1051. open_filename.assert_called_with( second_buffer_name, {
  1052. 'focus': True,
  1053. 'fix': True,
  1054. 'size': 10
  1055. } )
  1056. # And close it again, then show the quickfix window.
  1057. vim_command.assert_has_exact_calls( [
  1058. call( 'lclose' ),
  1059. call( 'hide' ),
  1060. ] )
  1061. # And update the quickfix list with each entry
  1062. vim_eval.assert_has_exact_calls( [
  1063. call( '&previewheight' ),
  1064. call( 'setqflist( {0} )'.format( json.dumps( [ {
  1065. 'bufnr': 22,
  1066. 'filename': first_buffer_name,
  1067. 'lnum': 1,
  1068. 'col': 1,
  1069. 'text': 'first_file_replacement ',
  1070. 'type': 'F'
  1071. }, {
  1072. 'bufnr': 19,
  1073. 'filename': second_buffer_name,
  1074. 'lnum': 2,
  1075. 'col': 1,
  1076. 'text': 'second_file_replacement ',
  1077. 'type': 'F'
  1078. } ] ) ) ),
  1079. ] )
  1080. # And it is ReplaceChunks that prints the message showing the number of
  1081. # changes
  1082. post_vim_message.assert_has_exact_calls( [
  1083. call( 'Applied 2 changes', warning = False ),
  1084. ] )
  1085. def _BuildChunk( start_line,
  1086. start_column,
  1087. end_line,
  1088. end_column,
  1089. replacement_text, filepath='test_file_name' ):
  1090. return {
  1091. 'range': {
  1092. 'start': {
  1093. 'filepath': filepath,
  1094. 'line_num': start_line,
  1095. 'column_num': start_column,
  1096. },
  1097. 'end': {
  1098. 'filepath': filepath,
  1099. 'line_num': end_line,
  1100. 'column_num': end_column,
  1101. },
  1102. },
  1103. 'replacement_text': replacement_text
  1104. }
  1105. def GetDiagnosticMatchPattern_ErrorInMiddleOfLine_test():
  1106. current_buffer = VimBuffer(
  1107. 'some_file',
  1108. contents = [ 'Highlight this error please' ]
  1109. )
  1110. with patch( 'vim.current.buffer', current_buffer ):
  1111. assert_that(
  1112. vimsupport.GetDiagnosticMatchPattern( 1, 16, 1, 21 ),
  1113. equal_to( '\\%1l\\%16c\\_.\\{-}\\%1l\\%21c' )
  1114. )
  1115. def AddDiagnosticSyntaxMatch_WarningAtEndOfLine_test():
  1116. current_buffer = VimBuffer(
  1117. 'some_file',
  1118. contents = [ 'Highlight this warning' ]
  1119. )
  1120. with patch( 'vim.current.buffer', current_buffer ):
  1121. assert_that(
  1122. vimsupport.GetDiagnosticMatchPattern( 1, 16, 1, 23 ),
  1123. equal_to( '\\%1l\\%16c\\_.\\{-}\\%1l\\%23c' )
  1124. )
  1125. def AddDiagnosticSyntaxMatch_UnicodeAtEndOfLine_test():
  1126. current_buffer = VimBuffer(
  1127. 'some_file',
  1128. contents = [ 'Highlight unicøde' ]
  1129. )
  1130. with patch( 'vim.current.buffer', current_buffer ):
  1131. assert_that(
  1132. vimsupport.GetDiagnosticMatchPattern( 1, 16, 1, 19 ),
  1133. equal_to( '\\%1l\\%16c\\_.\\{-}\\%1l\\%19c' )
  1134. )
  1135. def AddDiagnosticSyntaxMatch_NonPositivePosition_test():
  1136. current_buffer = VimBuffer(
  1137. 'some_file',
  1138. contents = [ 'Some contents' ]
  1139. )
  1140. with patch( 'vim.current.buffer', current_buffer ):
  1141. assert_that(
  1142. vimsupport.GetDiagnosticMatchPattern( 0, 0, 0, 0 ),
  1143. equal_to( '\\%1l\\%1c\\_.\\{-}\\%1l\\%1c' )
  1144. )
  1145. assert_that(
  1146. vimsupport.GetDiagnosticMatchPattern( -1, -2, -3, -4 ),
  1147. equal_to( '\\%1l\\%1c\\_.\\{-}\\%1l\\%1c' )
  1148. )
  1149. @patch( 'vim.command', new_callable=ExtendedMock )
  1150. @patch( 'vim.current', new_callable=ExtendedMock )
  1151. def WriteToPreviewWindow_test( vim_current, vim_command ):
  1152. vim_current.window.options.__getitem__ = MagicMock( return_value = True )
  1153. vimsupport.WriteToPreviewWindow( "test" )
  1154. vim_command.assert_has_exact_calls( [
  1155. call( 'silent! pclose!' ),
  1156. call( 'silent! pedit! _TEMP_FILE_' ),
  1157. call( 'silent! wincmd P' ),
  1158. call( 'silent! wincmd p' ) ] )
  1159. vim_current.buffer.__setitem__.assert_called_with(
  1160. slice( None, None, None ), [ 'test' ] )
  1161. vim_current.buffer.options.__setitem__.assert_has_exact_calls( [
  1162. call( 'modifiable', True ),
  1163. call( 'readonly', False ),
  1164. call( 'buftype', 'nofile' ),
  1165. call( 'bufhidden', 'wipe' ),
  1166. call( 'buflisted', False ),
  1167. call( 'swapfile', False ),
  1168. call( 'modifiable', False ),
  1169. call( 'modified', False ),
  1170. call( 'readonly', True ),
  1171. ], any_order = True )
  1172. @patch( 'vim.current' )
  1173. def WriteToPreviewWindow_MultiLine_test( vim_current ):
  1174. vim_current.window.options.__getitem__ = MagicMock( return_value = True )
  1175. vimsupport.WriteToPreviewWindow( "test\ntest2" )
  1176. vim_current.buffer.__setitem__.assert_called_with(
  1177. slice( None, None, None ), [ 'test', 'test2' ] )
  1178. @patch( 'vim.command', new_callable=ExtendedMock )
  1179. @patch( 'vim.current', new_callable=ExtendedMock )
  1180. def WriteToPreviewWindow_JumpFail_test( vim_current, vim_command ):
  1181. vim_current.window.options.__getitem__ = MagicMock( return_value = False )
  1182. vimsupport.WriteToPreviewWindow( "test" )
  1183. vim_command.assert_has_exact_calls( [
  1184. call( 'silent! pclose!' ),
  1185. call( 'silent! pedit! _TEMP_FILE_' ),
  1186. call( 'silent! wincmd P' ),
  1187. call( 'redraw' ),
  1188. call( "echo 'test'" ),
  1189. ] )
  1190. vim_current.buffer.__setitem__.assert_not_called()
  1191. vim_current.buffer.options.__setitem__.assert_not_called()
  1192. @patch( 'vim.command', new_callable=ExtendedMock )
  1193. @patch( 'vim.current', new_callable=ExtendedMock )
  1194. def WriteToPreviewWindow_JumpFail_MultiLine_test( vim_current, vim_command ):
  1195. vim_current.window.options.__getitem__ = MagicMock( return_value = False )
  1196. vimsupport.WriteToPreviewWindow( "test\ntest2" )
  1197. vim_command.assert_has_exact_calls( [
  1198. call( 'silent! pclose!' ),
  1199. call( 'silent! pedit! _TEMP_FILE_' ),
  1200. call( 'silent! wincmd P' ),
  1201. call( 'redraw' ),
  1202. call( "echo 'test'" ),
  1203. call( "echo 'test2'" ),
  1204. ] )
  1205. vim_current.buffer.__setitem__.assert_not_called()
  1206. vim_current.buffer.options.__setitem__.assert_not_called()
  1207. def BufferIsVisibleForFilename_test():
  1208. visible_buffer = VimBuffer( 'visible_filename', number = 1 )
  1209. hidden_buffer = VimBuffer( 'hidden_filename', number = 2 )
  1210. with MockVimBuffers( [ visible_buffer, hidden_buffer ], [ visible_buffer ] ):
  1211. eq_( vimsupport.BufferIsVisibleForFilename( 'visible_filename' ), True )
  1212. eq_( vimsupport.BufferIsVisibleForFilename( 'hidden_filename' ), False )
  1213. eq_( vimsupport.BufferIsVisibleForFilename( 'another_filename' ), False )
  1214. def CloseBuffersForFilename_test():
  1215. current_buffer = VimBuffer( 'some_filename', number = 2 )
  1216. other_buffer = VimBuffer( 'some_filename', number = 5 )
  1217. with MockVimBuffers( [ current_buffer, other_buffer ],
  1218. [ current_buffer ] ) as vim:
  1219. vimsupport.CloseBuffersForFilename( 'some_filename' )
  1220. assert_that( vim.buffers, empty() )
  1221. @patch( 'vim.command', new_callable = ExtendedMock )
  1222. @patch( 'vim.current', new_callable = ExtendedMock )
  1223. def OpenFilename_test( vim_current, vim_command ):
  1224. # Options used to open a logfile.
  1225. options = {
  1226. 'size': vimsupport.GetIntValue( '&previewheight' ),
  1227. 'fix': True,
  1228. 'focus': False,
  1229. 'watch': True,
  1230. 'position': 'end'
  1231. }
  1232. vimsupport.OpenFilename( __file__, options )
  1233. vim_command.assert_has_exact_calls( [
  1234. call( '12split {0}'.format( __file__ ) ),
  1235. call( "exec "
  1236. "'au BufEnter <buffer> :silent! checktime {0}'".format( __file__ ) ),
  1237. call( 'silent! normal! Gzz' ),
  1238. call( 'silent! wincmd p' )
  1239. ] )
  1240. vim_current.buffer.options.__setitem__.assert_has_exact_calls( [
  1241. call( 'autoread', True ),
  1242. ] )
  1243. vim_current.window.options.__setitem__.assert_has_exact_calls( [
  1244. call( 'winfixheight', True )
  1245. ] )
  1246. def GetUnsavedAndSpecifiedBufferData_EncodedUnicodeCharsInBuffers_test():
  1247. filepath = os.path.realpath( 'filename' )
  1248. contents = [ ToBytes( u'abc' ), ToBytes( u'fДa' ) ]
  1249. vim_buffer = VimBuffer( filepath, contents = contents )
  1250. with patch( 'vim.buffers', [ vim_buffer ] ):
  1251. assert_that( vimsupport.GetUnsavedAndSpecifiedBufferData( vim_buffer,
  1252. filepath ),
  1253. has_entry( filepath,
  1254. has_entry( u'contents', u'abc\nfДa\n' ) ) )
  1255. def GetBufferFilepath_NoBufferName_UnicodeWorkingDirectory_test():
  1256. vim_buffer = VimBuffer( '', number = 42 )
  1257. unicode_dir = PathToTestFile( u'uni¢𐍈d€' )
  1258. with CurrentWorkingDirectory( unicode_dir ):
  1259. assert_that( vimsupport.GetBufferFilepath( vim_buffer ),
  1260. equal_to( os.path.join( unicode_dir, '42' ) ) )
  1261. # NOTE: Vim returns byte offsets for columns, not actual character columns. This
  1262. # makes 'ДД' have 4 columns: column 0, column 2 and column 4.
  1263. @patch( 'vim.current.line', ToBytes( 'ДДaa' ) )
  1264. @patch( 'ycm.vimsupport.CurrentColumn', side_effect = [ 4 ] )
  1265. def TextBeforeCursor_EncodedUnicode_test( *args ):
  1266. eq_( vimsupport.TextBeforeCursor(), u'ДД' )
  1267. # NOTE: Vim returns byte offsets for columns, not actual character columns. This
  1268. # makes 'ДД' have 4 columns: column 0, column 2 and column 4.
  1269. @patch( 'vim.current.line', ToBytes( 'aaДД' ) )
  1270. @patch( 'ycm.vimsupport.CurrentColumn', side_effect = [ 2 ] )
  1271. def TextAfterCursor_EncodedUnicode_test( *args ):
  1272. eq_( vimsupport.TextAfterCursor(), u'ДД' )
  1273. @patch( 'vim.current.line', ToBytes( 'fДa' ) )
  1274. def CurrentLineContents_EncodedUnicode_test( *args ):
  1275. eq_( vimsupport.CurrentLineContents(), u'fДa' )
  1276. @patch( 'vim.eval', side_effect = lambda x: x )
  1277. def VimExpressionToPythonType_IntAsUnicode_test( *args ):
  1278. eq_( vimsupport.VimExpressionToPythonType( '123' ), 123 )
  1279. @patch( 'vim.eval', side_effect = lambda x: x )
  1280. def VimExpressionToPythonType_IntAsBytes_test( *args ):
  1281. eq_( vimsupport.VimExpressionToPythonType( ToBytes( '123' ) ), 123 )
  1282. @patch( 'vim.eval', side_effect = lambda x: x )
  1283. def VimExpressionToPythonType_StringAsUnicode_test( *args ):
  1284. eq_( vimsupport.VimExpressionToPythonType( 'foo' ), 'foo' )
  1285. @patch( 'vim.eval', side_effect = lambda x: x )
  1286. def VimExpressionToPythonType_StringAsBytes_test( *args ):
  1287. eq_( vimsupport.VimExpressionToPythonType( ToBytes( 'foo' ) ), 'foo' )
  1288. @patch( 'vim.eval', side_effect = lambda x: x )
  1289. def VimExpressionToPythonType_ListPassthrough_test( *args ):
  1290. eq_( vimsupport.VimExpressionToPythonType( [ 1, 2 ] ), [ 1, 2 ] )
  1291. @patch( 'vim.eval', side_effect = lambda x: x )
  1292. def VimExpressionToPythonType_ObjectPassthrough_test( *args ):
  1293. eq_( vimsupport.VimExpressionToPythonType( { 1: 2 } ), { 1: 2 } )
  1294. @patch( 'vim.eval', side_effect = lambda x: x )
  1295. def VimExpressionToPythonType_GeneratorPassthrough_test( *args ):
  1296. gen = ( x**2 for x in [ 1, 2, 3 ] )
  1297. eq_( vimsupport.VimExpressionToPythonType( gen ), gen )
  1298. @patch( 'vim.eval',
  1299. new_callable = ExtendedMock,
  1300. side_effect = [ None, 2, None ] )
  1301. def SelectFromList_LastItem_test( vim_eval ):
  1302. eq_( vimsupport.SelectFromList( 'test', [ 'a', 'b' ] ),
  1303. 1 )
  1304. vim_eval.assert_has_exact_calls( [
  1305. call( 'inputsave()' ),
  1306. call( 'inputlist( ["test", "1: a", "2: b"] )' ),
  1307. call( 'inputrestore()' )
  1308. ] )
  1309. @patch( 'vim.eval',
  1310. new_callable = ExtendedMock,
  1311. side_effect = [ None, 1, None ] )
  1312. def SelectFromList_FirstItem_test( vim_eval ):
  1313. eq_( vimsupport.SelectFromList( 'test', [ 'a', 'b' ] ),
  1314. 0 )
  1315. vim_eval.assert_has_exact_calls( [
  1316. call( 'inputsave()' ),
  1317. call( 'inputlist( ["test", "1: a", "2: b"] )' ),
  1318. call( 'inputrestore()' )
  1319. ] )
  1320. @patch( 'vim.eval', side_effect = [ None, 3, None ] )
  1321. def SelectFromList_OutOfRange_test( vim_eval ):
  1322. assert_that( calling( vimsupport.SelectFromList ).with_args( 'test',
  1323. [ 'a', 'b' ] ),
  1324. raises( RuntimeError, vimsupport.NO_SELECTION_MADE_MSG ) )
  1325. @patch( 'vim.eval', side_effect = [ None, 0, None ] )
  1326. def SelectFromList_SelectPrompt_test( vim_eval ):
  1327. assert_that( calling( vimsupport.SelectFromList ).with_args( 'test',
  1328. [ 'a', 'b' ] ),
  1329. raises( RuntimeError, vimsupport.NO_SELECTION_MADE_MSG ) )
  1330. @patch( 'vim.eval', side_effect = [ None, -199, None ] )
  1331. def SelectFromList_Negative_test( vim_eval ):
  1332. assert_that( calling( vimsupport.SelectFromList ).with_args( 'test',
  1333. [ 'a', 'b' ] ),
  1334. raises( RuntimeError, vimsupport.NO_SELECTION_MADE_MSG ) )
  1335. def Filetypes_IntegerFiletype_test():
  1336. current_buffer = VimBuffer( 'buffer', number = 1, filetype = '42' )
  1337. with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
  1338. assert_that( vimsupport.CurrentFiletypes(), contains( '42' ) )
  1339. assert_that( vimsupport.GetBufferFiletypes( 1 ), contains( '42' ) )
  1340. assert_that( vimsupport.FiletypesForBuffer( current_buffer ),
  1341. contains( '42' ) )
  1342. @patch( 'ycm.vimsupport.VariableExists', return_value = False )
  1343. @patch( 'ycm.vimsupport.SearchInCurrentBuffer', return_value = 0 )
  1344. @patch( 'vim.current' )
  1345. def InsertNamespace_insert_test( vim_current, *args ):
  1346. contents = [ '',
  1347. 'namespace Taqueria {',
  1348. '',
  1349. ' int taco = Math' ]
  1350. vim_current.buffer = VimBuffer( '', contents = contents )
  1351. vim_current.window.cursor = ( 1, 1 )
  1352. vimsupport.InsertNamespace( 'System' )
  1353. expected_buffer = [ 'using System;',
  1354. '',
  1355. 'namespace Taqueria {',
  1356. '',
  1357. ' int taco = Math' ]
  1358. AssertBuffersAreEqualAsBytes( expected_buffer, vim_current.buffer )
  1359. @patch( 'ycm.vimsupport.VariableExists', return_value = False )
  1360. @patch( 'ycm.vimsupport.SearchInCurrentBuffer', return_value = 2 )
  1361. @patch( 'vim.current' )
  1362. def InsertNamespace_append_test( vim_current, *args ):
  1363. contents = [ 'namespace Taqueria {',
  1364. ' using System;',
  1365. '',
  1366. ' class Tasty {',
  1367. ' int taco;',
  1368. ' List salad = new List' ]
  1369. vim_current.buffer = VimBuffer( '', contents = contents )
  1370. vim_current.window.cursor = ( 1, 1 )
  1371. vimsupport.InsertNamespace( 'System.Collections' )
  1372. expected_buffer = [ 'namespace Taqueria {',
  1373. ' using System;',
  1374. ' using System.Collections;',
  1375. '',
  1376. ' class Tasty {',
  1377. ' int taco;',
  1378. ' List salad = new List' ]
  1379. AssertBuffersAreEqualAsBytes( expected_buffer, vim_current.buffer )
  1380. @patch( 'vim.command', new_callable = ExtendedMock )
  1381. def JumpToLocation_SameFile_SameBuffer_NoSwapFile_test( vim_command ):
  1382. # No 'u' prefix for the current buffer name string to simulate Vim returning
  1383. # bytes on Python 2 but unicode on Python 3.
  1384. current_buffer = VimBuffer( 'uni¢𐍈d€' )
  1385. with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim:
  1386. vimsupport.JumpToLocation( os.path.realpath( u'uni¢𐍈d€' ),
  1387. 2,
  1388. 5,
  1389. 'aboveleft',
  1390. 'same-buffer' )
  1391. assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) )
  1392. vim_command.assert_has_exact_calls( [
  1393. call( 'normal! m\'' ),
  1394. call( 'normal! zz' )
  1395. ] )
  1396. @patch( 'vim.command', new_callable = ExtendedMock )
  1397. def JumpToLocation_DifferentFile_SameBuffer_Unmodified_test( vim_command ):
  1398. current_buffer = VimBuffer( 'uni¢𐍈d€' )
  1399. with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim:
  1400. target_name = os.path.realpath( u'different_uni¢𐍈d€' )
  1401. vimsupport.JumpToLocation( target_name, 2, 5, 'belowright', 'same-buffer' )
  1402. assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) )
  1403. vim_command.assert_has_exact_calls( [
  1404. call( 'normal! m\'' ),
  1405. call( u'keepjumps belowright edit {0}'.format( target_name ) ),
  1406. call( 'normal! zz' )
  1407. ] )
  1408. @patch( 'vim.command', new_callable = ExtendedMock )
  1409. def JumpToLocation_DifferentFile_SameBuffer_Modified_CannotHide_test(
  1410. vim_command ):
  1411. current_buffer = VimBuffer( 'uni¢𐍈d€', modified = True )
  1412. with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim:
  1413. target_name = os.path.realpath( u'different_uni¢𐍈d€' )
  1414. vimsupport.JumpToLocation( target_name, 2, 5, 'botright', 'same-buffer' )
  1415. assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) )
  1416. vim_command.assert_has_exact_calls( [
  1417. call( 'normal! m\'' ),
  1418. call( u'keepjumps botright split {0}'.format( target_name ) ),
  1419. call( 'normal! zz' )
  1420. ] )
  1421. @patch( 'vim.command', new_callable = ExtendedMock )
  1422. def JumpToLocation_DifferentFile_SameBuffer_Modified_CanHide_test(
  1423. vim_command ):
  1424. current_buffer = VimBuffer( 'uni¢𐍈d€', modified = True, bufhidden = "hide" )
  1425. with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim:
  1426. target_name = os.path.realpath( u'different_uni¢𐍈d€' )
  1427. vimsupport.JumpToLocation( target_name, 2, 5, 'leftabove', 'same-buffer' )
  1428. assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) )
  1429. vim_command.assert_has_exact_calls( [
  1430. call( 'normal! m\'' ),
  1431. call( u'keepjumps leftabove edit {0}'.format( target_name ) ),
  1432. call( 'normal! zz' )
  1433. ] )
  1434. @patch( 'vim.command',
  1435. side_effect = [ None, VimError( 'Unknown code' ), None ] )
  1436. def JumpToLocation_DifferentFile_SameBuffer_SwapFile_Unexpected_test(
  1437. vim_command ):
  1438. current_buffer = VimBuffer( 'uni¢𐍈d€' )
  1439. with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
  1440. assert_that(
  1441. calling( vimsupport.JumpToLocation ).with_args(
  1442. os.path.realpath( u'different_uni¢𐍈d€' ),
  1443. 2,
  1444. 5,
  1445. 'rightbelow',
  1446. 'same-buffer' ),
  1447. raises( VimError, 'Unknown code' )
  1448. )
  1449. @patch( 'vim.command',
  1450. new_callable = ExtendedMock,
  1451. side_effect = [ None, VimError( 'E325' ), None ] )
  1452. def JumpToLocation_DifferentFile_SameBuffer_SwapFile_Quit_test( vim_command ):
  1453. current_buffer = VimBuffer( 'uni¢𐍈d€' )
  1454. with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
  1455. target_name = os.path.realpath( u'different_uni¢𐍈d€' )
  1456. vimsupport.JumpToLocation( target_name, 2, 5, 'topleft', 'same-buffer' )
  1457. vim_command.assert_has_exact_calls( [
  1458. call( 'normal! m\'' ),
  1459. call( u'keepjumps topleft edit {0}'.format( target_name ) )
  1460. ] )
  1461. @patch( 'vim.command',
  1462. new_callable = ExtendedMock,
  1463. side_effect = [ None, KeyboardInterrupt, None ] )
  1464. def JumpToLocation_DifferentFile_SameBuffer_SwapFile_Abort_test( vim_command ):
  1465. current_buffer = VimBuffer( 'uni¢𐍈d€' )
  1466. with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
  1467. target_name = os.path.realpath( u'different_uni¢𐍈d€' )
  1468. vimsupport.JumpToLocation( target_name, 2, 5, 'vertical', 'same-buffer' )
  1469. vim_command.assert_has_exact_calls( [
  1470. call( 'normal! m\'' ),
  1471. call( u'keepjumps vertical edit {0}'.format( target_name ) )
  1472. ] )
  1473. @patch( 'vim.command', new_callable = ExtendedMock )
  1474. def JumpToLocation_DifferentFile_Split_CurrentTab_NotAlreadyOpened_test(
  1475. vim_command ):
  1476. current_buffer = VimBuffer( 'uni¢𐍈d€' )
  1477. current_window = MagicMock( buffer = current_buffer )
  1478. current_tab = MagicMock( windows = [ current_window ] )
  1479. with MockVimBuffers( [ current_buffer ], [ current_buffer ] ) as vim:
  1480. vim.current.tabpage = current_tab
  1481. target_name = os.path.realpath( u'different_uni¢𐍈d€' )
  1482. vimsupport.JumpToLocation( target_name,
  1483. 2,
  1484. 5,
  1485. 'aboveleft',
  1486. 'split-or-existing-window' )
  1487. vim_command.assert_has_exact_calls( [
  1488. call( 'normal! m\'' ),
  1489. call( u'keepjumps aboveleft split {0}'.format( target_name ) ),
  1490. call( 'normal! zz' )
  1491. ] )
  1492. @patch( 'vim.command', new_callable = ExtendedMock )
  1493. def JumpToLocation_DifferentFile_Split_CurrentTab_AlreadyOpened_test(
  1494. vim_command ):
  1495. current_buffer = VimBuffer( 'uni¢𐍈d€' )
  1496. different_buffer = VimBuffer( 'different_uni¢𐍈d€' )
  1497. current_window = MagicMock( buffer = current_buffer )
  1498. different_window = MagicMock( buffer = different_buffer )
  1499. current_tab = MagicMock( windows = [ current_window, different_window ] )
  1500. with MockVimBuffers( [ current_buffer, different_buffer ],
  1501. [ current_buffer ] ) as vim:
  1502. vim.current.tabpage = current_tab
  1503. vimsupport.JumpToLocation( os.path.realpath( u'different_uni¢𐍈d€' ),
  1504. 2,
  1505. 5,
  1506. 'belowright',
  1507. 'split-or-existing-window' )
  1508. assert_that( vim.current.tabpage, equal_to( current_tab ) )
  1509. assert_that( vim.current.window, equal_to( different_window ) )
  1510. assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) )
  1511. vim_command.assert_has_exact_calls( [
  1512. call( 'normal! m\'' ),
  1513. call( 'normal! zz' )
  1514. ] )
  1515. @WindowsAndMacOnly
  1516. @patch( 'vim.command', new_callable = ExtendedMock )
  1517. def JumpToLocation_DifferentFile_Split_CurrentTab_AlreadyOpened_Case_test(
  1518. vim_command ):
  1519. current_buffer = VimBuffer( 'current_buffer' )
  1520. different_buffer = VimBuffer( 'AnotHer_buFfeR' )
  1521. current_window = MagicMock( buffer = current_buffer )
  1522. different_window = MagicMock( buffer = different_buffer )
  1523. current_tab = MagicMock( windows = [ current_window, different_window ] )
  1524. with MockVimBuffers( [ current_buffer, different_buffer ],
  1525. [ current_buffer ] ) as vim:
  1526. vim.current.tabpage = current_tab
  1527. vimsupport.JumpToLocation( os.path.realpath( 'anOther_BuffEr' ),
  1528. 4,
  1529. 1,
  1530. 'belowright',
  1531. 'split-or-existing-window' )
  1532. assert_that( vim.current.tabpage, equal_to( current_tab ) )
  1533. assert_that( vim.current.window, equal_to( different_window ) )
  1534. assert_that( vim.current.window.cursor, equal_to( ( 4, 0 ) ) )
  1535. vim_command.assert_has_exact_calls( [
  1536. call( 'normal! m\'' ),
  1537. call( 'normal! zz' )
  1538. ] )
  1539. @patch( 'vim.command', new_callable = ExtendedMock )
  1540. def JumpToLocation_DifferentFile_Split_AllTabs_NotAlreadyOpened_test(
  1541. vim_command ):
  1542. current_buffer = VimBuffer( 'uni¢𐍈d€' )
  1543. with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
  1544. target_name = os.path.realpath( u'different_uni¢𐍈d€' )
  1545. vimsupport.JumpToLocation( target_name,
  1546. 2,
  1547. 5,
  1548. 'tab',
  1549. 'split-or-existing-window' )
  1550. vim_command.assert_has_exact_calls( [
  1551. call( 'normal! m\'' ),
  1552. call( u'keepjumps tab split {0}'.format( target_name ) ),
  1553. call( 'normal! zz' )
  1554. ] )
  1555. @patch( 'vim.command', new_callable = ExtendedMock )
  1556. def JumpToLocation_DifferentFile_Split_AllTabs_AlreadyOpened_test(
  1557. vim_command ):
  1558. current_buffer = VimBuffer( 'uni¢𐍈d€' )
  1559. different_buffer = VimBuffer( 'different_uni¢𐍈d€' )
  1560. current_window = MagicMock( buffer = current_buffer )
  1561. different_window = MagicMock( buffer = different_buffer )
  1562. current_tab = MagicMock( windows = [ current_window, different_window ] )
  1563. with patch( 'vim.tabpages', [ current_tab ] ):
  1564. with MockVimBuffers( [ current_buffer, different_buffer ],
  1565. [ current_buffer ] ) as vim:
  1566. vimsupport.JumpToLocation( os.path.realpath( u'different_uni¢𐍈d€' ),
  1567. 2,
  1568. 5,
  1569. 'tab',
  1570. 'split-or-existing-window' )
  1571. assert_that( vim.current.tabpage, equal_to( current_tab ) )
  1572. assert_that( vim.current.window, equal_to( different_window ) )
  1573. assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) )
  1574. vim_command.assert_has_exact_calls( [
  1575. call( 'normal! m\'' ),
  1576. call( 'normal! zz' )
  1577. ] )
  1578. @patch( 'vim.command', new_callable = ExtendedMock )
  1579. def JumpToLocation_DifferentFile_NewOrExistingTab_NotAlreadyOpened_test(
  1580. vim_command ):
  1581. current_buffer = VimBuffer( 'uni¢𐍈d€' )
  1582. with MockVimBuffers( [ current_buffer ], [ current_buffer ] ):
  1583. target_name = os.path.realpath( u'different_uni¢𐍈d€' )
  1584. vimsupport.JumpToLocation( target_name,
  1585. 2,
  1586. 5,
  1587. 'aboveleft vertical',
  1588. 'new-or-existing-tab' )
  1589. vim_command.assert_has_exact_calls( [
  1590. call( 'normal! m\'' ),
  1591. call( u'keepjumps aboveleft vertical tabedit {0}'.format( target_name ) ),
  1592. call( 'normal! zz' )
  1593. ] )
  1594. @patch( 'vim.command', new_callable = ExtendedMock )
  1595. def JumpToLocation_DifferentFile_NewOrExistingTab_AlreadyOpened_test(
  1596. vim_command ):
  1597. current_buffer = VimBuffer( 'uni¢𐍈d€' )
  1598. different_buffer = VimBuffer( 'different_uni¢𐍈d€' )
  1599. current_window = MagicMock( buffer = current_buffer )
  1600. different_window = MagicMock( buffer = different_buffer )
  1601. current_tab = MagicMock( windows = [ current_window, different_window ] )
  1602. with patch( 'vim.tabpages', [ current_tab ] ):
  1603. with MockVimBuffers( [ current_buffer, different_buffer ],
  1604. [ current_buffer ] ) as vim:
  1605. vimsupport.JumpToLocation( os.path.realpath( u'different_uni¢𐍈d€' ),
  1606. 2,
  1607. 5,
  1608. 'belowright tab',
  1609. 'new-or-existing-tab' )
  1610. assert_that( vim.current.tabpage, equal_to( current_tab ) )
  1611. assert_that( vim.current.window, equal_to( different_window ) )
  1612. assert_that( vim.current.window.cursor, equal_to( ( 2, 4 ) ) )
  1613. vim_command.assert_has_exact_calls( [
  1614. call( 'normal! m\'' ),
  1615. call( 'normal! zz' )
  1616. ] )
  1617. @patch( 'ycm.tests.test_utils.VIM_VERSION', Version( 7, 4, 1578 ) )
  1618. def VimVersionAtLeast_test():
  1619. assert_that( vimsupport.VimVersionAtLeast( '7.3.414' ) )
  1620. assert_that( vimsupport.VimVersionAtLeast( '7.4.1578' ) )
  1621. assert_that( not vimsupport.VimVersionAtLeast( '7.4.1579' ) )
  1622. assert_that( not vimsupport.VimVersionAtLeast( '7.4.1898' ) )
  1623. assert_that( not vimsupport.VimVersionAtLeast( '8.1.278' ) )