vimsupport_test.py 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219
  1. # Copyright (C) 2015 YouCompleteMe contributors
  2. #
  3. # This file is part of YouCompleteMe.
  4. #
  5. # YouCompleteMe is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # YouCompleteMe is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with YouCompleteMe. If not, see <http://www.gnu.org/licenses/>.
  17. from __future__ import unicode_literals
  18. from __future__ import print_function
  19. from __future__ import division
  20. from __future__ import absolute_import
  21. from future import standard_library
  22. standard_library.install_aliases()
  23. from builtins import * # noqa
  24. from ycm.test_utils import ExtendedMock, MockVimModule, MockVimCommand
  25. MockVimModule()
  26. from ycm import vimsupport
  27. from nose.tools import eq_
  28. from hamcrest import assert_that, calling, raises, none
  29. from mock import MagicMock, call, patch
  30. import os
  31. import json
  32. def ReplaceChunk_SingleLine_Repl_1_test():
  33. # Replace with longer range
  34. # 12345678901234567
  35. result_buffer = [ "This is a string" ]
  36. start, end = _BuildLocations( 1, 1, 1, 5 )
  37. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  38. end,
  39. 'How long',
  40. 0,
  41. 0,
  42. result_buffer )
  43. eq_( [ "How long is a string" ], result_buffer )
  44. eq_( line_offset, 0 )
  45. eq_( char_offset, 4 )
  46. # and replace again, using delta
  47. start, end = _BuildLocations( 1, 10, 1, 11 )
  48. ( new_line_offset, new_char_offset ) = vimsupport.ReplaceChunk(
  49. start,
  50. end,
  51. ' piece of ',
  52. line_offset,
  53. char_offset,
  54. result_buffer )
  55. line_offset += new_line_offset
  56. char_offset += new_char_offset
  57. eq_( [ 'How long is a piece of string' ], result_buffer )
  58. eq_( new_line_offset, 0 )
  59. eq_( new_char_offset, 9 )
  60. eq_( line_offset, 0 )
  61. eq_( char_offset, 13 )
  62. # and once more, for luck
  63. start, end = _BuildLocations( 1, 11, 1, 17 )
  64. ( new_line_offset, new_char_offset ) = vimsupport.ReplaceChunk(
  65. start,
  66. end,
  67. 'pie',
  68. line_offset,
  69. char_offset,
  70. result_buffer )
  71. line_offset += new_line_offset
  72. char_offset += new_char_offset
  73. eq_( ['How long is a piece of pie' ], result_buffer )
  74. eq_( new_line_offset, 0 )
  75. eq_( new_char_offset, -3 )
  76. eq_( line_offset, 0 )
  77. eq_( char_offset, 10 )
  78. def ReplaceChunk_SingleLine_Repl_2_test():
  79. # Replace with shorter range
  80. # 12345678901234567
  81. result_buffer = [ "This is a string" ]
  82. start, end = _BuildLocations( 1, 11, 1, 17 )
  83. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  84. end,
  85. 'test',
  86. 0,
  87. 0,
  88. result_buffer )
  89. eq_( [ "This is a test" ], result_buffer )
  90. eq_( line_offset, 0 )
  91. eq_( char_offset, -2 )
  92. def ReplaceChunk_SingleLine_Repl_3_test():
  93. # Replace with equal range
  94. # 12345678901234567
  95. result_buffer = [ "This is a string" ]
  96. start, end = _BuildLocations( 1, 6, 1, 8 )
  97. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  98. end,
  99. 'be',
  100. 0,
  101. 0,
  102. result_buffer )
  103. eq_( [ "This be a string" ], result_buffer )
  104. eq_( line_offset, 0 )
  105. eq_( char_offset, 0 )
  106. def ReplaceChunk_SingleLine_Add_1_test():
  107. # Insert at start
  108. result_buffer = [ "is a string" ]
  109. start, end = _BuildLocations( 1, 1, 1, 1 )
  110. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  111. end,
  112. 'This ',
  113. 0,
  114. 0,
  115. result_buffer )
  116. eq_( [ "This is a string" ], result_buffer )
  117. eq_( line_offset, 0 )
  118. eq_( char_offset, 5 )
  119. def ReplaceChunk_SingleLine_Add_2_test():
  120. # Insert at end
  121. result_buffer = [ "This is a " ]
  122. start, end = _BuildLocations( 1, 11, 1, 11 )
  123. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  124. end,
  125. 'string',
  126. 0,
  127. 0,
  128. result_buffer )
  129. eq_( [ "This is a string" ], result_buffer )
  130. eq_( line_offset, 0 )
  131. eq_( char_offset, 6 )
  132. def ReplaceChunk_SingleLine_Add_3_test():
  133. # Insert in the middle
  134. result_buffer = [ "This is a string" ]
  135. start, end = _BuildLocations( 1, 8, 1, 8 )
  136. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  137. end,
  138. ' not',
  139. 0,
  140. 0,
  141. result_buffer )
  142. eq_( [ "This is not a string" ], result_buffer )
  143. eq_( line_offset, 0 )
  144. eq_( char_offset, 4 )
  145. def ReplaceChunk_SingleLine_Del_1_test():
  146. # Delete from start
  147. result_buffer = [ "This is a string" ]
  148. start, end = _BuildLocations( 1, 1, 1, 6 )
  149. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  150. end,
  151. '',
  152. 0,
  153. 0,
  154. result_buffer )
  155. eq_( [ "is a string" ], result_buffer )
  156. eq_( line_offset, 0 )
  157. eq_( char_offset, -5 )
  158. def ReplaceChunk_SingleLine_Del_2_test():
  159. # Delete from end
  160. result_buffer = [ "This is a string" ]
  161. start, end = _BuildLocations( 1, 10, 1, 18 )
  162. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  163. end,
  164. '',
  165. 0,
  166. 0,
  167. result_buffer )
  168. eq_( [ "This is a" ], result_buffer )
  169. eq_( line_offset, 0 )
  170. eq_( char_offset, -8 )
  171. def ReplaceChunk_SingleLine_Del_3_test():
  172. # Delete from middle
  173. result_buffer = [ "This is not a string" ]
  174. start, end = _BuildLocations( 1, 9, 1, 13 )
  175. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  176. end,
  177. '',
  178. 0,
  179. 0,
  180. result_buffer )
  181. eq_( [ "This is a string" ], result_buffer )
  182. eq_( line_offset, 0 )
  183. eq_( char_offset, -4 )
  184. def ReplaceChunk_RemoveSingleLine_test():
  185. result_buffer = [ "aAa", "aBa", "aCa" ]
  186. start, end = _BuildLocations( 2, 1, 3, 1 )
  187. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, '',
  188. 0, 0, result_buffer )
  189. expected_buffer = [ "aAa", "aCa" ]
  190. eq_( expected_buffer, result_buffer )
  191. eq_( line_offset, -1 )
  192. eq_( char_offset, 0 )
  193. def ReplaceChunk_SingleToMultipleLines_test():
  194. result_buffer = [ "aAa",
  195. "aBa",
  196. "aCa" ]
  197. start, end = _BuildLocations( 2, 2, 2, 2 )
  198. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'Eb\nbF',
  199. 0, 0, result_buffer )
  200. expected_buffer = [ "aAa",
  201. "aEb",
  202. "bFBa",
  203. "aCa" ]
  204. eq_( expected_buffer, result_buffer )
  205. eq_( line_offset, 1 )
  206. eq_( char_offset, 1 )
  207. # now make another change to the "2nd" line
  208. start, end = _BuildLocations( 2, 3, 2, 4 )
  209. ( new_line_offset, new_char_offset ) = vimsupport.ReplaceChunk(
  210. start,
  211. end,
  212. 'cccc',
  213. line_offset,
  214. char_offset,
  215. result_buffer )
  216. line_offset += new_line_offset
  217. char_offset += new_char_offset
  218. eq_( [ "aAa", "aEb", "bFBcccc", "aCa" ], result_buffer )
  219. eq_( line_offset, 1 )
  220. eq_( char_offset, 4 )
  221. def ReplaceChunk_SingleToMultipleLines2_test():
  222. result_buffer = [ "aAa", "aBa", "aCa" ]
  223. start, end = _BuildLocations( 2, 2, 2, 2 )
  224. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  225. end,
  226. 'Eb\nbFb\nG',
  227. 0,
  228. 0,
  229. result_buffer )
  230. expected_buffer = [ "aAa", "aEb", "bFb", "GBa", "aCa" ]
  231. eq_( expected_buffer, result_buffer )
  232. eq_( line_offset, 2 )
  233. eq_( char_offset, 0 )
  234. def ReplaceChunk_SingleToMultipleLines3_test():
  235. result_buffer = [ "aAa", "aBa", "aCa" ]
  236. start, end = _BuildLocations( 2, 2, 2, 2 )
  237. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  238. end,
  239. 'Eb\nbFb\nbGb',
  240. 0,
  241. 0,
  242. result_buffer )
  243. expected_buffer = [ "aAa", "aEb", "bFb", "bGbBa", "aCa" ]
  244. eq_( expected_buffer, result_buffer )
  245. eq_( line_offset, 2 )
  246. eq_( char_offset, 2 )
  247. def ReplaceChunk_SingleToMultipleLinesReplace_test():
  248. result_buffer = [ "aAa", "aBa", "aCa" ]
  249. start, end = _BuildLocations( 1, 2, 1, 4 )
  250. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  251. end,
  252. 'Eb\nbFb\nbGb',
  253. 0,
  254. 0,
  255. result_buffer )
  256. expected_buffer = [ "aEb", "bFb", "bGb", "aBa", "aCa" ]
  257. eq_( expected_buffer, result_buffer )
  258. eq_( line_offset, 2 )
  259. eq_( char_offset, 0 )
  260. def ReplaceChunk_SingleToMultipleLinesReplace_2_test():
  261. result_buffer = [ "aAa",
  262. "aBa",
  263. "aCa" ]
  264. start, end = _BuildLocations( 1, 2, 1, 4 )
  265. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  266. end,
  267. 'Eb\nbFb\nbGb',
  268. 0,
  269. 0,
  270. result_buffer )
  271. expected_buffer = [ "aEb",
  272. "bFb",
  273. "bGb",
  274. "aBa",
  275. "aCa" ]
  276. eq_( expected_buffer, result_buffer )
  277. eq_( line_offset, 2 )
  278. eq_( char_offset, 0 )
  279. # now do a subsequent change (insert at end of line "1")
  280. start, end = _BuildLocations( 1, 4, 1, 4 )
  281. ( new_line_offset, new_char_offset ) = vimsupport.ReplaceChunk(
  282. start,
  283. end,
  284. 'cccc',
  285. line_offset,
  286. char_offset,
  287. result_buffer )
  288. line_offset += new_line_offset
  289. char_offset += new_char_offset
  290. eq_( [ "aEb",
  291. "bFb",
  292. "bGbcccc",
  293. "aBa",
  294. "aCa" ], result_buffer )
  295. eq_( line_offset, 2 )
  296. eq_( char_offset, 4 )
  297. def ReplaceChunk_MultipleLinesToSingleLine_test():
  298. result_buffer = [ "aAa", "aBa", "aCaaaa" ]
  299. start, end = _BuildLocations( 2, 2, 3, 2 )
  300. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'E',
  301. 0, 0, result_buffer )
  302. expected_buffer = [ "aAa", "aECaaaa" ]
  303. eq_( expected_buffer, result_buffer )
  304. eq_( line_offset, -1 )
  305. eq_( char_offset, 1 )
  306. # make another modification applying offsets
  307. start, end = _BuildLocations( 3, 3, 3, 4 )
  308. ( new_line_offset, new_char_offset ) = vimsupport.ReplaceChunk(
  309. start,
  310. end,
  311. 'cccc',
  312. line_offset,
  313. char_offset,
  314. result_buffer )
  315. line_offset += new_line_offset
  316. char_offset += new_char_offset
  317. eq_( [ "aAa", "aECccccaaa" ], result_buffer )
  318. eq_( line_offset, -1 )
  319. eq_( char_offset, 4 )
  320. # and another, for luck
  321. start, end = _BuildLocations( 3, 4, 3, 5 )
  322. ( new_line_offset, new_char_offset ) = vimsupport.ReplaceChunk(
  323. start,
  324. end,
  325. 'dd\ndd',
  326. line_offset,
  327. char_offset,
  328. result_buffer )
  329. line_offset += new_line_offset
  330. char_offset += new_char_offset
  331. eq_( [ "aAa", "aECccccdd", "ddaa" ], result_buffer )
  332. eq_( line_offset, 0 )
  333. eq_( char_offset, -2 )
  334. def ReplaceChunk_MultipleLinesToSameMultipleLines_test():
  335. result_buffer = [ "aAa", "aBa", "aCa", "aDe" ]
  336. start, end = _BuildLocations( 2, 2, 3, 2 )
  337. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'Eb\nbF',
  338. 0, 0, result_buffer )
  339. expected_buffer = [ "aAa", "aEb", "bFCa", "aDe" ]
  340. eq_( expected_buffer, result_buffer )
  341. eq_( line_offset, 0 )
  342. eq_( char_offset, 1 )
  343. def ReplaceChunk_MultipleLinesToMoreMultipleLines_test():
  344. result_buffer = [ "aAa", "aBa", "aCa", "aDe" ]
  345. start, end = _BuildLocations( 2, 2, 3, 2 )
  346. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  347. end,
  348. 'Eb\nbFb\nbG',
  349. 0,
  350. 0,
  351. result_buffer )
  352. expected_buffer = [ "aAa", "aEb", "bFb", "bGCa", "aDe" ]
  353. eq_( expected_buffer, result_buffer )
  354. eq_( line_offset, 1 )
  355. eq_( char_offset, 1 )
  356. def ReplaceChunk_MultipleLinesToLessMultipleLines_test():
  357. result_buffer = [ "aAa", "aBa", "aCa", "aDe" ]
  358. start, end = _BuildLocations( 1, 2, 3, 2 )
  359. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'Eb\nbF',
  360. 0, 0, result_buffer )
  361. expected_buffer = [ "aEb", "bFCa", "aDe" ]
  362. eq_( expected_buffer, result_buffer )
  363. eq_( line_offset, -1 )
  364. eq_( char_offset, 1 )
  365. def ReplaceChunk_MultipleLinesToEvenLessMultipleLines_test():
  366. result_buffer = [ "aAa", "aBa", "aCa", "aDe" ]
  367. start, end = _BuildLocations( 1, 2, 4, 2 )
  368. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'Eb\nbF',
  369. 0, 0, result_buffer )
  370. expected_buffer = [ "aEb", "bFDe" ]
  371. eq_( expected_buffer, result_buffer )
  372. eq_( line_offset, -2 )
  373. eq_( char_offset, 1 )
  374. def ReplaceChunk_SpanBufferEdge_test():
  375. result_buffer = [ "aAa", "aBa", "aCa" ]
  376. start, end = _BuildLocations( 1, 1, 1, 3 )
  377. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'bDb',
  378. 0, 0, result_buffer )
  379. expected_buffer = [ "bDba", "aBa", "aCa" ]
  380. eq_( expected_buffer, result_buffer )
  381. eq_( line_offset, 0 )
  382. eq_( char_offset, 1 )
  383. def ReplaceChunk_DeleteTextInLine_test():
  384. result_buffer = [ "aAa", "aBa", "aCa" ]
  385. start, end = _BuildLocations( 2, 2, 2, 3 )
  386. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, '',
  387. 0, 0, result_buffer )
  388. expected_buffer = [ "aAa", "aa", "aCa" ]
  389. eq_( expected_buffer, result_buffer )
  390. eq_( line_offset, 0 )
  391. eq_( char_offset, -1 )
  392. def ReplaceChunk_AddTextInLine_test():
  393. result_buffer = [ "aAa", "aBa", "aCa" ]
  394. start, end = _BuildLocations( 2, 2, 2, 2 )
  395. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'bDb',
  396. 0, 0, result_buffer )
  397. expected_buffer = [ "aAa", "abDbBa", "aCa" ]
  398. eq_( expected_buffer, result_buffer )
  399. eq_( line_offset, 0 )
  400. eq_( char_offset, 3 )
  401. def ReplaceChunk_ReplaceTextInLine_test():
  402. result_buffer = [ "aAa", "aBa", "aCa" ]
  403. start, end = _BuildLocations( 2, 2, 2, 3 )
  404. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'bDb',
  405. 0, 0, result_buffer )
  406. expected_buffer = [ "aAa", "abDba", "aCa" ]
  407. eq_( expected_buffer, result_buffer )
  408. eq_( line_offset, 0 )
  409. eq_( char_offset, 2 )
  410. def ReplaceChunk_SingleLineOffsetWorks_test():
  411. result_buffer = [ "aAa", "aBa", "aCa" ]
  412. start, end = _BuildLocations( 1, 1, 1, 2 )
  413. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'bDb',
  414. 1, 1, result_buffer )
  415. expected_buffer = [ "aAa", "abDba", "aCa" ]
  416. eq_( expected_buffer, result_buffer )
  417. eq_( line_offset, 0 )
  418. eq_( char_offset, 2 )
  419. def ReplaceChunk_SingleLineToMultipleLinesOffsetWorks_test():
  420. result_buffer = [ "aAa", "aBa", "aCa" ]
  421. start, end = _BuildLocations( 1, 1, 1, 2 )
  422. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'Db\nE',
  423. 1, 1, result_buffer )
  424. expected_buffer = [ "aAa", "aDb", "Ea", "aCa" ]
  425. eq_( expected_buffer, result_buffer )
  426. eq_( line_offset, 1 )
  427. eq_( char_offset, -1 )
  428. def ReplaceChunk_MultipleLinesToSingleLineOffsetWorks_test():
  429. result_buffer = [ "aAa", "aBa", "aCa" ]
  430. start, end = _BuildLocations( 1, 1, 2, 2 )
  431. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'bDb',
  432. 1, 1, result_buffer )
  433. expected_buffer = [ "aAa", "abDbCa" ]
  434. eq_( expected_buffer, result_buffer )
  435. eq_( line_offset, -1 )
  436. eq_( char_offset, 3 )
  437. def ReplaceChunk_MultipleLineOffsetWorks_test():
  438. result_buffer = [ "aAa", "aBa", "aCa" ]
  439. start, end = _BuildLocations( 3, 1, 4, 3 )
  440. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  441. end,
  442. 'bDb\nbEb\nbFb',
  443. -1,
  444. 1,
  445. result_buffer )
  446. expected_buffer = [ "aAa", "abDb", "bEb", "bFba" ]
  447. eq_( expected_buffer, result_buffer )
  448. eq_( line_offset, 1 )
  449. eq_( char_offset, 1 )
  450. def _BuildLocations( start_line, start_column, end_line, end_column ):
  451. return {
  452. 'line_num' : start_line,
  453. 'column_num': start_column,
  454. }, {
  455. 'line_num' : end_line,
  456. 'column_num': end_column,
  457. }
  458. def ReplaceChunksInBuffer_SortedChunks_test():
  459. chunks = [
  460. _BuildChunk( 1, 4, 1, 4, '('),
  461. _BuildChunk( 1, 11, 1, 11, ')' )
  462. ]
  463. result_buffer = [ "CT<10 >> 2> ct" ]
  464. vimsupport.ReplaceChunksInBuffer( chunks, result_buffer, None )
  465. expected_buffer = [ "CT<(10 >> 2)> ct" ]
  466. eq_( expected_buffer, result_buffer )
  467. def ReplaceChunksInBuffer_UnsortedChunks_test():
  468. chunks = [
  469. _BuildChunk( 1, 11, 1, 11, ')'),
  470. _BuildChunk( 1, 4, 1, 4, '(' )
  471. ]
  472. result_buffer = [ "CT<10 >> 2> ct" ]
  473. vimsupport.ReplaceChunksInBuffer( chunks, result_buffer, None )
  474. expected_buffer = [ "CT<(10 >> 2)> ct" ]
  475. eq_( expected_buffer, result_buffer )
  476. class MockBuffer( object ):
  477. """An object that looks like a vim.buffer object, enough for ReplaceChunk to
  478. generate a location list"""
  479. def __init__( self, lines, name, number ):
  480. self.lines = lines
  481. self.name = name
  482. self.number = number
  483. def __getitem__( self, index ):
  484. return self.lines[ index ]
  485. def __len__( self ):
  486. return len( self.lines )
  487. def __setitem__( self, key, value ):
  488. return self.lines.__setitem__( key, value )
  489. @patch( 'ycm.vimsupport.GetBufferNumberForFilename',
  490. return_value=1,
  491. new_callable=ExtendedMock )
  492. @patch( 'ycm.vimsupport.BufferIsVisible',
  493. return_value=True,
  494. new_callable=ExtendedMock )
  495. @patch( 'ycm.vimsupport.OpenFilename' )
  496. @patch( 'ycm.vimsupport.EchoTextVimWidth', new_callable=ExtendedMock )
  497. @patch( 'vim.eval', new_callable=ExtendedMock )
  498. @patch( 'vim.command', new_callable=ExtendedMock )
  499. def ReplaceChunks_SingleFile_Open_test( vim_command,
  500. vim_eval,
  501. echo_text_vim_width,
  502. open_filename,
  503. buffer_is_visible,
  504. get_buffer_number_for_filename ):
  505. chunks = [
  506. _BuildChunk( 1, 1, 2, 1, 'replacement', 'single_file' )
  507. ]
  508. result_buffer = MockBuffer( [
  509. 'line1',
  510. 'line2',
  511. 'line3',
  512. ], 'single_file', 1 )
  513. with patch( 'vim.buffers', [ None, result_buffer, None ] ):
  514. vimsupport.ReplaceChunks( chunks )
  515. # Ensure that we applied the replacement correctly
  516. eq_( result_buffer.lines, [
  517. 'replacementline2',
  518. 'line3',
  519. ] )
  520. # GetBufferNumberForFilename is called twice:
  521. # - once to the check if we would require opening the file (so that we can
  522. # raise a warning)
  523. # - once whilst applying the changes
  524. get_buffer_number_for_filename.assert_has_exact_calls( [
  525. call( 'single_file', False ),
  526. call( 'single_file', False ),
  527. ] )
  528. # BufferIsVisible is called twice for the same reasons as above
  529. buffer_is_visible.assert_has_exact_calls( [
  530. call( 1 ),
  531. call( 1 ),
  532. ] )
  533. # we don't attempt to open any files
  534. open_filename.assert_not_called()
  535. # But we do set the quickfix list
  536. vim_eval.assert_has_exact_calls( [
  537. call( 'setqflist( {0} )'.format( json.dumps( [ {
  538. 'bufnr': 1,
  539. 'filename': 'single_file',
  540. 'lnum': 1,
  541. 'col': 1,
  542. 'text': 'replacement',
  543. 'type': 'F'
  544. } ] ) ) ),
  545. ] )
  546. vim_command.assert_has_calls( [
  547. call( 'copen 1' )
  548. ] )
  549. # And it is ReplaceChunks that prints the message showing the number of
  550. # changes
  551. echo_text_vim_width.assert_has_exact_calls( [
  552. call( 'Applied 1 changes' ),
  553. ] )
  554. @patch( 'ycm.vimsupport.GetBufferNumberForFilename',
  555. side_effect=[ -1, -1, 1 ],
  556. new_callable=ExtendedMock )
  557. @patch( 'ycm.vimsupport.BufferIsVisible',
  558. side_effect=[ False, False, True ],
  559. new_callable=ExtendedMock )
  560. @patch( 'ycm.vimsupport.OpenFilename',
  561. new_callable=ExtendedMock )
  562. @patch( 'ycm.vimsupport.EchoTextVimWidth', new_callable=ExtendedMock )
  563. @patch( 'ycm.vimsupport.Confirm',
  564. return_value=True,
  565. new_callable=ExtendedMock )
  566. @patch( 'vim.eval', return_value=10, new_callable=ExtendedMock )
  567. @patch( 'vim.command', new_callable=ExtendedMock )
  568. def ReplaceChunks_SingleFile_NotOpen_test( vim_command,
  569. vim_eval,
  570. confirm,
  571. echo_text_vim_width,
  572. open_filename,
  573. buffer_is_visible,
  574. get_buffer_number_for_filename ):
  575. chunks = [
  576. _BuildChunk( 1, 1, 2, 1, 'replacement', 'single_file' )
  577. ]
  578. result_buffer = MockBuffer( [
  579. 'line1',
  580. 'line2',
  581. 'line3',
  582. ], 'single_file', 1 )
  583. with patch( 'vim.buffers', [ None, result_buffer, None ] ):
  584. vimsupport.ReplaceChunks( chunks )
  585. # We checked if it was OK to open the file
  586. confirm.assert_has_exact_calls( [
  587. call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) )
  588. ] )
  589. # Ensure that we applied the replacement correctly
  590. eq_( result_buffer.lines, [
  591. 'replacementline2',
  592. 'line3',
  593. ] )
  594. # GetBufferNumberForFilename is called 3 times. The return values are set in
  595. # the @patch call above:
  596. # - once to the check if we would require opening the file (so that we can
  597. # raise a warning) (-1 return)
  598. # - once whilst applying the changes (-1 return)
  599. # - finally after calling OpenFilename (1 return)
  600. get_buffer_number_for_filename.assert_has_exact_calls( [
  601. call( 'single_file', False ),
  602. call( 'single_file', False ),
  603. call( 'single_file', False ),
  604. ] )
  605. # BufferIsVisible is called 3 times for the same reasons as above, with the
  606. # return of each one
  607. buffer_is_visible.assert_has_exact_calls( [
  608. call( -1 ),
  609. call( -1 ),
  610. call( 1 ),
  611. ] )
  612. # We open 'single_file' as expected.
  613. open_filename.assert_called_with( 'single_file', {
  614. 'focus': True,
  615. 'fix': True,
  616. 'size': 10
  617. } )
  618. # And close it again, then show the preview window (note, we don't check exact
  619. # calls because there are other calls which are checked elsewhere)
  620. vim_command.assert_has_calls( [
  621. call( 'lclose' ),
  622. call( 'hide' ),
  623. call( 'copen 1' ),
  624. ] )
  625. # And update the quickfix list
  626. vim_eval.assert_has_exact_calls( [
  627. call( '&previewheight' ),
  628. call( 'setqflist( {0} )'.format( json.dumps( [ {
  629. 'bufnr': 1,
  630. 'filename': 'single_file',
  631. 'lnum': 1,
  632. 'col': 1,
  633. 'text': 'replacement',
  634. 'type': 'F'
  635. } ] ) ) ),
  636. ] )
  637. # And it is ReplaceChunks that prints the message showing the number of
  638. # changes
  639. echo_text_vim_width.assert_has_exact_calls( [
  640. call( 'Applied 1 changes' ),
  641. ] )
  642. @patch( 'ycm.vimsupport.GetBufferNumberForFilename',
  643. side_effect=[ -1, -1, 1 ],
  644. new_callable=ExtendedMock )
  645. @patch( 'ycm.vimsupport.BufferIsVisible',
  646. side_effect=[ False, False, True ],
  647. new_callable=ExtendedMock )
  648. @patch( 'ycm.vimsupport.OpenFilename',
  649. new_callable=ExtendedMock )
  650. @patch( 'ycm.vimsupport.EchoTextVimWidth',
  651. new_callable=ExtendedMock )
  652. @patch( 'ycm.vimsupport.Confirm',
  653. return_value=False,
  654. new_callable=ExtendedMock )
  655. @patch( 'vim.eval',
  656. return_value=10,
  657. new_callable=ExtendedMock )
  658. @patch( 'vim.command', new_callable=ExtendedMock )
  659. def ReplaceChunks_User_Declines_To_Open_File_test(
  660. vim_command,
  661. vim_eval,
  662. confirm,
  663. echo_text_vim_width,
  664. open_filename,
  665. buffer_is_visible,
  666. get_buffer_number_for_filename ):
  667. # Same as above, except the user selects Cancel when asked if they should
  668. # allow us to open lots of (ahem, 1) file.
  669. chunks = [
  670. _BuildChunk( 1, 1, 2, 1, 'replacement', 'single_file' )
  671. ]
  672. result_buffer = MockBuffer( [
  673. 'line1',
  674. 'line2',
  675. 'line3',
  676. ], 'single_file', 1 )
  677. with patch( 'vim.buffers', [ None, result_buffer, None ] ):
  678. vimsupport.ReplaceChunks( chunks )
  679. # We checked if it was OK to open the file
  680. confirm.assert_has_exact_calls( [
  681. call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) )
  682. ] )
  683. # Ensure that buffer is not changed
  684. eq_( result_buffer.lines, [
  685. 'line1',
  686. 'line2',
  687. 'line3',
  688. ] )
  689. # GetBufferNumberForFilename is called once. The return values are set in
  690. # the @patch call above:
  691. # - once to the check if we would require opening the file (so that we can
  692. # raise a warning) (-1 return)
  693. get_buffer_number_for_filename.assert_has_exact_calls( [
  694. call( 'single_file', False ),
  695. ] )
  696. # BufferIsVisible is called once for the above file, which wasn't visible.
  697. buffer_is_visible.assert_has_exact_calls( [
  698. call( -1 ),
  699. ] )
  700. # We don't attempt to open any files or update any quickfix list or anything
  701. # like that
  702. open_filename.assert_not_called()
  703. vim_eval.assert_not_called()
  704. vim_command.assert_not_called()
  705. echo_text_vim_width.assert_not_called()
  706. @patch( 'ycm.vimsupport.GetBufferNumberForFilename',
  707. side_effect=[ -1, -1, 1 ],
  708. new_callable=ExtendedMock )
  709. # Key difference is here: In the final check, BufferIsVisible returns False
  710. @patch( 'ycm.vimsupport.BufferIsVisible',
  711. side_effect=[ False, False, False ],
  712. new_callable=ExtendedMock )
  713. @patch( 'ycm.vimsupport.OpenFilename',
  714. new_callable=ExtendedMock )
  715. @patch( 'ycm.vimsupport.EchoTextVimWidth',
  716. new_callable=ExtendedMock )
  717. @patch( 'ycm.vimsupport.Confirm',
  718. return_value=True,
  719. new_callable=ExtendedMock )
  720. @patch( 'vim.eval',
  721. return_value=10,
  722. new_callable=ExtendedMock )
  723. @patch( 'vim.command',
  724. new_callable=ExtendedMock )
  725. def ReplaceChunks_User_Aborts_Opening_File_test(
  726. vim_command,
  727. vim_eval,
  728. confirm,
  729. echo_text_vim_width,
  730. open_filename,
  731. buffer_is_visible,
  732. get_buffer_number_for_filename ):
  733. # Same as above, except the user selects Abort or Quick during the
  734. # "swap-file-found" dialog
  735. chunks = [
  736. _BuildChunk( 1, 1, 2, 1, 'replacement', 'single_file' )
  737. ]
  738. result_buffer = MockBuffer( [
  739. 'line1',
  740. 'line2',
  741. 'line3',
  742. ], 'single_file', 1 )
  743. with patch( 'vim.buffers', [ None, result_buffer, None ] ):
  744. assert_that( calling( vimsupport.ReplaceChunks ).with_args( chunks ),
  745. raises( RuntimeError,
  746. 'Unable to open file: single_file\nFixIt/Refactor operation '
  747. 'aborted prior to completion. Your files have not been '
  748. 'fully updated. Please use undo commands to revert the '
  749. 'applied changes.' ) )
  750. # We checked if it was OK to open the file
  751. confirm.assert_has_exact_calls( [
  752. call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) )
  753. ] )
  754. # Ensure that buffer is not changed
  755. eq_( result_buffer.lines, [
  756. 'line1',
  757. 'line2',
  758. 'line3',
  759. ] )
  760. # We tried to open this file
  761. open_filename.assert_called_with( "single_file", {
  762. 'focus': True,
  763. 'fix': True,
  764. 'size': 10
  765. } )
  766. vim_eval.assert_called_with( "&previewheight" )
  767. # But raised an exception before issuing the message at the end
  768. echo_text_vim_width.assert_not_called()
  769. @patch( 'ycm.vimsupport.GetBufferNumberForFilename', side_effect=[
  770. 22, # first_file (check)
  771. -1, # another_file (check)
  772. 22, # first_file (apply)
  773. -1, # another_file (apply)
  774. 19, # another_file (check after open)
  775. ],
  776. new_callable=ExtendedMock )
  777. @patch( 'ycm.vimsupport.BufferIsVisible', side_effect=[
  778. True, # first_file (check)
  779. False, # second_file (check)
  780. True, # first_file (apply)
  781. False, # second_file (apply)
  782. True, # side_effect (check after open)
  783. ],
  784. new_callable=ExtendedMock)
  785. @patch( 'ycm.vimsupport.OpenFilename',
  786. new_callable=ExtendedMock)
  787. @patch( 'ycm.vimsupport.EchoTextVimWidth',
  788. new_callable=ExtendedMock)
  789. @patch( 'ycm.vimsupport.Confirm', return_value=True,
  790. new_callable=ExtendedMock)
  791. @patch( 'vim.eval', return_value=10,
  792. new_callable=ExtendedMock)
  793. @patch( 'vim.command',
  794. new_callable=ExtendedMock)
  795. def ReplaceChunks_MultiFile_Open_test( vim_command,
  796. vim_eval,
  797. confirm,
  798. echo_text_vim_width,
  799. open_filename,
  800. buffer_is_visible,
  801. get_buffer_number_for_filename ):
  802. # Chunks are split across 2 files, one is already open, one isn't
  803. chunks = [
  804. _BuildChunk( 1, 1, 2, 1, 'first_file_replacement ', '1_first_file' ),
  805. _BuildChunk( 2, 1, 2, 1, 'second_file_replacement ', '2_another_file' ),
  806. ]
  807. first_file = MockBuffer( [
  808. 'line1',
  809. 'line2',
  810. 'line3',
  811. ], '1_first_file', 22 )
  812. another_file = MockBuffer( [
  813. 'another line1',
  814. 'ACME line2',
  815. ], '2_another_file', 19 )
  816. vim_buffers = [ None ] * 23
  817. vim_buffers[ 22 ] = first_file
  818. vim_buffers[ 19 ] = another_file
  819. with patch( 'vim.buffers', vim_buffers ):
  820. vimsupport.ReplaceChunks( chunks )
  821. # We checked for the right file names
  822. get_buffer_number_for_filename.assert_has_exact_calls( [
  823. call( '1_first_file', False ),
  824. call( '2_another_file', False ),
  825. call( '1_first_file', False ),
  826. call( '2_another_file', False ),
  827. call( '2_another_file', False ),
  828. ] )
  829. # We checked if it was OK to open the file
  830. confirm.assert_has_exact_calls( [
  831. call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) )
  832. ] )
  833. # Ensure that buffers are updated
  834. eq_( another_file.lines, [
  835. 'another line1',
  836. 'second_file_replacement ACME line2',
  837. ] )
  838. eq_( first_file.lines, [
  839. 'first_file_replacement line2',
  840. 'line3',
  841. ] )
  842. # We open '2_another_file' as expected.
  843. open_filename.assert_called_with( '2_another_file', {
  844. 'focus': True,
  845. 'fix': True,
  846. 'size': 10
  847. } )
  848. # And close it again, then show the preview window (note, we don't check exact
  849. # calls because there are other calls which are checked elsewhere)
  850. vim_command.assert_has_calls( [
  851. call( 'lclose' ),
  852. call( 'hide' ),
  853. call( 'copen 2' ),
  854. ] )
  855. # And update the quickfix list with each entry
  856. vim_eval.assert_has_exact_calls( [
  857. call( '&previewheight' ),
  858. call( 'setqflist( {0} )'.format( json.dumps( [ {
  859. 'bufnr': 22,
  860. 'filename': '1_first_file',
  861. 'lnum': 1,
  862. 'col': 1,
  863. 'text': 'first_file_replacement ',
  864. 'type': 'F'
  865. }, {
  866. 'bufnr': 19,
  867. 'filename': '2_another_file',
  868. 'lnum': 2,
  869. 'col': 1,
  870. 'text': 'second_file_replacement ',
  871. 'type': 'F'
  872. } ] ) ) ),
  873. ] )
  874. # And it is ReplaceChunks that prints the message showing the number of
  875. # changes
  876. echo_text_vim_width.assert_has_exact_calls( [
  877. call( 'Applied 2 changes' ),
  878. ] )
  879. def _BuildChunk( start_line,
  880. start_column,
  881. end_line,
  882. end_column,
  883. replacement_text, filepath='test_file_name' ):
  884. return {
  885. 'range': {
  886. 'start': {
  887. 'filepath': filepath,
  888. 'line_num': start_line,
  889. 'column_num': start_column,
  890. },
  891. 'end': {
  892. 'filepath': filepath,
  893. 'line_num': end_line,
  894. 'column_num': end_column,
  895. },
  896. },
  897. 'replacement_text': replacement_text
  898. }
  899. @patch( 'vim.command', new_callable=ExtendedMock )
  900. @patch( 'vim.current', new_callable=ExtendedMock)
  901. def WriteToPreviewWindow_test( vim_current, vim_command ):
  902. vim_current.window.options.__getitem__ = MagicMock( return_value = True )
  903. vimsupport.WriteToPreviewWindow( "test" )
  904. vim_command.assert_has_exact_calls( [
  905. call( 'silent! pclose!' ),
  906. call( 'silent! pedit! _TEMP_FILE_' ),
  907. call( 'silent! wincmd P' ),
  908. call( 'silent! wincmd p' ) ] )
  909. vim_current.buffer.__setitem__.assert_called_with(
  910. slice( None, None, None ), [ 'test' ] )
  911. vim_current.buffer.options.__setitem__.assert_has_exact_calls( [
  912. call( 'modifiable', True ),
  913. call( 'readonly', False ),
  914. call( 'buftype', 'nofile' ),
  915. call( 'swapfile', False ),
  916. call( 'modifiable', False ),
  917. call( 'modified', False ),
  918. call( 'readonly', True ),
  919. ], any_order = True )
  920. @patch( 'vim.current' )
  921. def WriteToPreviewWindow_MultiLine_test( vim_current ):
  922. vim_current.window.options.__getitem__ = MagicMock( return_value = True )
  923. vimsupport.WriteToPreviewWindow( "test\ntest2" )
  924. vim_current.buffer.__setitem__.assert_called_with(
  925. slice( None, None, None ), [ 'test', 'test2' ] )
  926. @patch( 'vim.command', new_callable=ExtendedMock )
  927. @patch( 'vim.current', new_callable=ExtendedMock )
  928. def WriteToPreviewWindow_JumpFail_test( vim_current, vim_command ):
  929. vim_current.window.options.__getitem__ = MagicMock( return_value = False )
  930. vimsupport.WriteToPreviewWindow( "test" )
  931. vim_command.assert_has_exact_calls( [
  932. call( 'silent! pclose!' ),
  933. call( 'silent! pedit! _TEMP_FILE_' ),
  934. call( 'silent! wincmd P' ),
  935. call( "echom 'test'" ),
  936. ] )
  937. vim_current.buffer.__setitem__.assert_not_called()
  938. vim_current.buffer.options.__setitem__.assert_not_called()
  939. @patch( 'vim.command', new_callable=ExtendedMock )
  940. @patch( 'vim.current', new_callable=ExtendedMock )
  941. def WriteToPreviewWindow_JumpFail_MultiLine_test( vim_current, vim_command ):
  942. vim_current.window.options.__getitem__ = MagicMock( return_value = False )
  943. vimsupport.WriteToPreviewWindow( "test\ntest2" )
  944. vim_command.assert_has_exact_calls( [
  945. call( 'silent! pclose!' ),
  946. call( 'silent! pedit! _TEMP_FILE_' ),
  947. call( 'silent! wincmd P' ),
  948. call( "echom 'test'" ),
  949. call( "echom 'test2'" ),
  950. ] )
  951. vim_current.buffer.__setitem__.assert_not_called()
  952. vim_current.buffer.options.__setitem__.assert_not_called()
  953. def CheckFilename_test():
  954. assert_that(
  955. calling( vimsupport.CheckFilename ).with_args( None ),
  956. raises( RuntimeError, "'None' is not a valid filename" )
  957. )
  958. assert_that(
  959. calling( vimsupport.CheckFilename ).with_args( 'nonexistent_file' ),
  960. raises( RuntimeError,
  961. "filename 'nonexistent_file' cannot be opened. "
  962. "\[Errno 2\] No such file or directory: 'nonexistent_file'" )
  963. )
  964. assert_that( vimsupport.CheckFilename( __file__ ), none() )
  965. def BufferIsVisibleForFilename_test():
  966. buffers = [
  967. {
  968. 'number': 1,
  969. 'filename': os.path.realpath( 'visible_filename' ),
  970. 'window': 1
  971. },
  972. {
  973. 'number': 2,
  974. 'filename': os.path.realpath( 'hidden_filename' ),
  975. }
  976. ]
  977. with patch( 'vim.buffers', buffers ):
  978. eq_( vimsupport.BufferIsVisibleForFilename( 'visible_filename' ), True )
  979. eq_( vimsupport.BufferIsVisibleForFilename( 'hidden_filename' ), False )
  980. eq_( vimsupport.BufferIsVisibleForFilename( 'another_filename' ), False )
  981. @patch( 'ycm.vimsupport.GetBufferNumberForFilename',
  982. side_effect = [ 2, 5, -1 ] )
  983. @patch( 'vim.command',
  984. side_effect = MockVimCommand,
  985. new_callable = ExtendedMock )
  986. def CloseBuffersForFilename_test( vim_command, *args ):
  987. vimsupport.CloseBuffersForFilename( 'some_filename' )
  988. vim_command.assert_has_exact_calls( [
  989. call( 'silent! bwipeout! 2' ),
  990. call( 'silent! bwipeout! 5' )
  991. ], any_order = True )
  992. @patch( 'vim.command', new_callable = ExtendedMock )
  993. @patch( 'vim.current', new_callable = ExtendedMock )
  994. def OpenFilename_test( vim_current, vim_command ):
  995. # Options used to open a logfile
  996. options = {
  997. 'size': vimsupport.GetIntValue( '&previewheight' ),
  998. 'fix': True,
  999. 'watch': True,
  1000. 'position': 'end'
  1001. }
  1002. vimsupport.OpenFilename( __file__, options )
  1003. vim_command.assert_has_exact_calls( [
  1004. call( '12split {0}'.format( __file__ ) ),
  1005. call( "exec "
  1006. "'au BufEnter <buffer> :silent! checktime {0}'".format( __file__ ) ),
  1007. call( 'silent! normal G zz' ),
  1008. call( 'silent! wincmd p' )
  1009. ] )
  1010. vim_current.buffer.options.__setitem__.assert_has_exact_calls( [
  1011. call( 'autoread', True ),
  1012. ] )
  1013. vim_current.window.options.__setitem__.assert_has_exact_calls( [
  1014. call( 'winfixheight', True )
  1015. ] )