vimsupport_test.py 53 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533
  1. # coding: utf-8
  2. #
  3. # Copyright (C) 2015-2016 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. from future import standard_library
  24. standard_library.install_aliases()
  25. from builtins import * # noqa
  26. from ycm.tests import PathToTestFile
  27. from ycm.tests.test_utils import ( CurrentWorkingDirectory, ExtendedMock,
  28. MockVimCommand, MockVimModule, VimBuffer )
  29. MockVimModule()
  30. from ycm import vimsupport
  31. from nose.tools import eq_
  32. from hamcrest import assert_that, calling, equal_to, has_entry, none, raises
  33. from mock import MagicMock, call, patch
  34. from ycmd.utils import ToBytes
  35. import os
  36. import json
  37. def AssertBuffersAreEqualAsBytes( result_buffer, expected_buffer ):
  38. eq_( len( result_buffer ), len( expected_buffer ) )
  39. for result_line, expected_line in zip( result_buffer, expected_buffer ):
  40. eq_( ToBytes( result_line ), ToBytes( expected_line ) )
  41. def ReplaceChunk_SingleLine_Repl_1_test():
  42. # Replace with longer range
  43. result_buffer = [ 'This is a string' ]
  44. start, end = _BuildLocations( 1, 1, 1, 5 )
  45. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  46. end,
  47. 'How long',
  48. 0,
  49. 0,
  50. result_buffer )
  51. AssertBuffersAreEqualAsBytes( [ 'How long is a string' ], result_buffer )
  52. eq_( line_offset, 0 )
  53. eq_( char_offset, 4 )
  54. # and replace again, using delta
  55. start, end = _BuildLocations( 1, 10, 1, 11 )
  56. ( new_line_offset, new_char_offset ) = vimsupport.ReplaceChunk(
  57. start,
  58. end,
  59. ' piece of ',
  60. line_offset,
  61. char_offset,
  62. result_buffer )
  63. line_offset += new_line_offset
  64. char_offset += new_char_offset
  65. AssertBuffersAreEqualAsBytes( [ 'How long is a piece of string' ],
  66. result_buffer )
  67. eq_( new_line_offset, 0 )
  68. eq_( new_char_offset, 9 )
  69. eq_( line_offset, 0 )
  70. eq_( char_offset, 13 )
  71. # and once more, for luck
  72. start, end = _BuildLocations( 1, 11, 1, 17 )
  73. ( new_line_offset, new_char_offset ) = vimsupport.ReplaceChunk(
  74. start,
  75. end,
  76. 'pie',
  77. line_offset,
  78. char_offset,
  79. result_buffer )
  80. line_offset += new_line_offset
  81. char_offset += new_char_offset
  82. AssertBuffersAreEqualAsBytes( [ 'How long is a piece of pie' ],
  83. result_buffer )
  84. eq_( new_line_offset, 0 )
  85. eq_( new_char_offset, -3 )
  86. eq_( line_offset, 0 )
  87. eq_( char_offset, 10 )
  88. def ReplaceChunk_SingleLine_Repl_2_test():
  89. # Replace with shorter range
  90. result_buffer = [ 'This is a string' ]
  91. start, end = _BuildLocations( 1, 11, 1, 17 )
  92. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  93. end,
  94. 'test',
  95. 0,
  96. 0,
  97. result_buffer )
  98. AssertBuffersAreEqualAsBytes( [ 'This is a test' ], result_buffer )
  99. eq_( line_offset, 0 )
  100. eq_( char_offset, -2 )
  101. def ReplaceChunk_SingleLine_Repl_3_test():
  102. # Replace with equal range
  103. result_buffer = [ 'This is a string' ]
  104. start, end = _BuildLocations( 1, 6, 1, 8 )
  105. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  106. end,
  107. 'be',
  108. 0,
  109. 0,
  110. result_buffer )
  111. AssertBuffersAreEqualAsBytes( [ 'This be a string' ], result_buffer )
  112. eq_( line_offset, 0 )
  113. eq_( char_offset, 0 )
  114. def ReplaceChunk_SingleLine_Add_1_test():
  115. # Insert at start
  116. result_buffer = [ 'is a string' ]
  117. start, end = _BuildLocations( 1, 1, 1, 1 )
  118. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  119. end,
  120. 'This ',
  121. 0,
  122. 0,
  123. result_buffer )
  124. AssertBuffersAreEqualAsBytes( [ 'This is a string' ], result_buffer )
  125. eq_( line_offset, 0 )
  126. eq_( char_offset, 5 )
  127. def ReplaceChunk_SingleLine_Add_2_test():
  128. # Insert at end
  129. result_buffer = [ 'This is a ' ]
  130. start, end = _BuildLocations( 1, 11, 1, 11 )
  131. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  132. end,
  133. 'string',
  134. 0,
  135. 0,
  136. result_buffer )
  137. AssertBuffersAreEqualAsBytes( [ 'This is a string' ], result_buffer )
  138. eq_( line_offset, 0 )
  139. eq_( char_offset, 6 )
  140. def ReplaceChunk_SingleLine_Add_3_test():
  141. # Insert in the middle
  142. result_buffer = [ 'This is a string' ]
  143. start, end = _BuildLocations( 1, 8, 1, 8 )
  144. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  145. end,
  146. ' not',
  147. 0,
  148. 0,
  149. result_buffer )
  150. AssertBuffersAreEqualAsBytes( [ 'This is not a string' ], result_buffer )
  151. eq_( line_offset, 0 )
  152. eq_( char_offset, 4 )
  153. def ReplaceChunk_SingleLine_Del_1_test():
  154. # Delete from start
  155. result_buffer = [ 'This is a string' ]
  156. start, end = _BuildLocations( 1, 1, 1, 6 )
  157. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  158. end,
  159. '',
  160. 0,
  161. 0,
  162. result_buffer )
  163. AssertBuffersAreEqualAsBytes( [ 'is a string' ], result_buffer )
  164. eq_( line_offset, 0 )
  165. eq_( char_offset, -5 )
  166. def ReplaceChunk_SingleLine_Del_2_test():
  167. # Delete from end
  168. result_buffer = [ 'This is a string' ]
  169. start, end = _BuildLocations( 1, 10, 1, 18 )
  170. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  171. end,
  172. '',
  173. 0,
  174. 0,
  175. result_buffer )
  176. AssertBuffersAreEqualAsBytes( [ 'This is a' ], result_buffer )
  177. eq_( line_offset, 0 )
  178. eq_( char_offset, -8 )
  179. def ReplaceChunk_SingleLine_Del_3_test():
  180. # Delete from middle
  181. result_buffer = [ 'This is not a string' ]
  182. start, end = _BuildLocations( 1, 9, 1, 13 )
  183. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  184. end,
  185. '',
  186. 0,
  187. 0,
  188. result_buffer )
  189. AssertBuffersAreEqualAsBytes( [ 'This is a string' ], result_buffer )
  190. eq_( line_offset, 0 )
  191. eq_( char_offset, -4 )
  192. def ReplaceChunk_SingleLine_Unicode_ReplaceUnicodeChars_test():
  193. # Replace Unicode characters.
  194. result_buffer = [ 'This Uniçø∂‰ string is in the middle' ]
  195. start, end = _BuildLocations( 1, 6, 1, 20 )
  196. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  197. end,
  198. 'Unicode ',
  199. 0,
  200. 0,
  201. result_buffer )
  202. AssertBuffersAreEqualAsBytes( [ 'This Unicode string is in the middle' ],
  203. result_buffer )
  204. eq_( line_offset, 0 )
  205. eq_( char_offset, -6 )
  206. def ReplaceChunk_SingleLine_Unicode_ReplaceAfterUnicode_test():
  207. # Replace ASCII characters after Unicode characters in the line.
  208. result_buffer = [ 'This Uniçø∂‰ string is in the middle' ]
  209. start, end = _BuildLocations( 1, 30, 1, 43 )
  210. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  211. end,
  212. 'fåke',
  213. 0,
  214. 0,
  215. result_buffer )
  216. AssertBuffersAreEqualAsBytes( [ 'This Uniçø∂‰ string is fåke' ],
  217. result_buffer )
  218. eq_( line_offset, 0 )
  219. eq_( char_offset, -8 )
  220. def ReplaceChunk_SingleLine_Unicode_Grown_test():
  221. # Replace ASCII characters after Unicode characters in the line.
  222. result_buffer = [ 'a' ]
  223. start, end = _BuildLocations( 1, 1, 1, 2 )
  224. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  225. end,
  226. 'å',
  227. 0,
  228. 0,
  229. result_buffer )
  230. AssertBuffersAreEqualAsBytes( [ 'å' ], result_buffer )
  231. eq_( line_offset, 0 )
  232. eq_( char_offset, 1 ) # Note: byte difference (a = 1 byte, å = 2 bytes)
  233. def ReplaceChunk_RemoveSingleLine_test():
  234. result_buffer = [ 'aAa',
  235. 'aBa',
  236. 'aCa' ]
  237. start, end = _BuildLocations( 2, 1, 3, 1 )
  238. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, '',
  239. 0, 0, result_buffer )
  240. # First line is not affected.
  241. expected_buffer = [ 'aAa',
  242. 'aCa' ]
  243. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  244. eq_( line_offset, -1 )
  245. eq_( char_offset, 0 )
  246. def ReplaceChunk_SingleToMultipleLines_test():
  247. result_buffer = [ 'aAa',
  248. 'aBa',
  249. 'aCa' ]
  250. start, end = _BuildLocations( 2, 2, 2, 2 )
  251. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'Eb\nbF',
  252. 0, 0, result_buffer )
  253. expected_buffer = [ 'aAa',
  254. 'aEb',
  255. 'bFBa',
  256. 'aCa' ]
  257. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  258. eq_( line_offset, 1 )
  259. eq_( char_offset, 1 )
  260. # now make another change to the "2nd" line
  261. start, end = _BuildLocations( 2, 3, 2, 4 )
  262. ( new_line_offset, new_char_offset ) = vimsupport.ReplaceChunk(
  263. start,
  264. end,
  265. 'cccc',
  266. line_offset,
  267. char_offset,
  268. result_buffer )
  269. line_offset += new_line_offset
  270. char_offset += new_char_offset
  271. AssertBuffersAreEqualAsBytes( [ 'aAa',
  272. 'aEb',
  273. 'bFBcccc',
  274. 'aCa' ], result_buffer )
  275. eq_( line_offset, 1 )
  276. eq_( char_offset, 4 )
  277. def ReplaceChunk_SingleToMultipleLines2_test():
  278. result_buffer = [ 'aAa', 'aBa', 'aCa' ]
  279. start, end = _BuildLocations( 2, 2, 2, 2 )
  280. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  281. end,
  282. 'Eb\nbFb\nG',
  283. 0,
  284. 0,
  285. result_buffer )
  286. expected_buffer = [ 'aAa',
  287. 'aEb',
  288. 'bFb',
  289. 'GBa',
  290. 'aCa' ]
  291. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  292. eq_( line_offset, 2 )
  293. eq_( char_offset, 0 )
  294. def ReplaceChunk_SingleToMultipleLines3_test():
  295. result_buffer = [ 'aAa', 'aBa', 'aCa' ]
  296. start, end = _BuildLocations( 2, 2, 2, 2 )
  297. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  298. end,
  299. 'Eb\nbFb\nbGb',
  300. 0,
  301. 0,
  302. result_buffer )
  303. expected_buffer = [ 'aAa',
  304. 'aEb',
  305. 'bFb',
  306. 'bGbBa',
  307. 'aCa' ]
  308. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  309. eq_( line_offset, 2 )
  310. eq_( char_offset, 2 )
  311. def ReplaceChunk_SingleToMultipleLinesReplace_test():
  312. result_buffer = [ 'aAa', 'aBa', 'aCa' ]
  313. start, end = _BuildLocations( 1, 2, 1, 4 )
  314. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  315. end,
  316. 'Eb\nbFb\nbGb',
  317. 0,
  318. 0,
  319. result_buffer )
  320. expected_buffer = [ 'aEb',
  321. 'bFb',
  322. 'bGb',
  323. 'aBa',
  324. 'aCa' ]
  325. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  326. eq_( line_offset, 2 )
  327. eq_( char_offset, 0 )
  328. def ReplaceChunk_SingleToMultipleLinesReplace_2_test():
  329. result_buffer = [ 'aAa',
  330. 'aBa',
  331. 'aCa' ]
  332. start, end = _BuildLocations( 1, 2, 1, 4 )
  333. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  334. end,
  335. 'Eb\nbFb\nbGb',
  336. 0,
  337. 0,
  338. result_buffer )
  339. expected_buffer = [ 'aEb',
  340. 'bFb',
  341. 'bGb',
  342. 'aBa',
  343. 'aCa' ]
  344. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  345. eq_( line_offset, 2 )
  346. eq_( char_offset, 0 )
  347. # now do a subsequent change (insert at end of line "1")
  348. start, end = _BuildLocations( 1, 4, 1, 4 )
  349. ( new_line_offset, new_char_offset ) = vimsupport.ReplaceChunk(
  350. start,
  351. end,
  352. 'cccc',
  353. line_offset,
  354. char_offset,
  355. result_buffer )
  356. line_offset += new_line_offset
  357. char_offset += new_char_offset
  358. AssertBuffersAreEqualAsBytes( [ 'aEb',
  359. 'bFb',
  360. 'bGbcccc',
  361. 'aBa',
  362. 'aCa' ], result_buffer )
  363. eq_( line_offset, 2 )
  364. eq_( char_offset, 4 )
  365. def ReplaceChunk_MultipleLinesToSingleLine_test():
  366. result_buffer = [ 'aAa', 'aBa', 'aCaaaa' ]
  367. start, end = _BuildLocations( 2, 2, 3, 2 )
  368. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'E',
  369. 0, 0, result_buffer )
  370. expected_buffer = [ 'aAa', 'aECaaaa' ]
  371. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  372. eq_( line_offset, -1 )
  373. eq_( char_offset, 1 )
  374. # make another modification applying offsets
  375. start, end = _BuildLocations( 3, 3, 3, 4 )
  376. ( new_line_offset, new_char_offset ) = vimsupport.ReplaceChunk(
  377. start,
  378. end,
  379. 'cccc',
  380. line_offset,
  381. char_offset,
  382. result_buffer )
  383. line_offset += new_line_offset
  384. char_offset += new_char_offset
  385. AssertBuffersAreEqualAsBytes( [ 'aAa',
  386. 'aECccccaaa' ], result_buffer )
  387. eq_( line_offset, -1 )
  388. eq_( char_offset, 4 )
  389. # and another, for luck
  390. start, end = _BuildLocations( 3, 4, 3, 5 )
  391. ( new_line_offset, new_char_offset ) = vimsupport.ReplaceChunk(
  392. start,
  393. end,
  394. 'dd\ndd',
  395. line_offset,
  396. char_offset,
  397. result_buffer )
  398. line_offset += new_line_offset
  399. char_offset += new_char_offset
  400. AssertBuffersAreEqualAsBytes( [ 'aAa',
  401. 'aECccccdd',
  402. 'ddaa' ], result_buffer )
  403. eq_( line_offset, 0 )
  404. eq_( char_offset, -2 )
  405. def ReplaceChunk_MultipleLinesToSameMultipleLines_test():
  406. result_buffer = [ 'aAa',
  407. 'aBa',
  408. 'aCa',
  409. 'aDe' ]
  410. start, end = _BuildLocations( 2, 2, 3, 2 )
  411. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'Eb\nbF',
  412. 0, 0, result_buffer )
  413. expected_buffer = [ 'aAa',
  414. 'aEb',
  415. 'bFCa',
  416. 'aDe' ]
  417. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  418. eq_( line_offset, 0 )
  419. eq_( char_offset, 1 )
  420. def ReplaceChunk_MultipleLinesToMoreMultipleLines_test():
  421. result_buffer = [ 'aAa',
  422. 'aBa',
  423. 'aCa',
  424. 'aDe' ]
  425. start, end = _BuildLocations( 2, 2, 3, 2 )
  426. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  427. end,
  428. 'Eb\nbFb\nbG',
  429. 0,
  430. 0,
  431. result_buffer )
  432. expected_buffer = [ 'aAa',
  433. 'aEb',
  434. 'bFb',
  435. 'bGCa',
  436. 'aDe' ]
  437. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  438. eq_( line_offset, 1 )
  439. eq_( char_offset, 1 )
  440. def ReplaceChunk_MultipleLinesToLessMultipleLines_test():
  441. result_buffer = [ 'aAa',
  442. 'aBa',
  443. 'aCa',
  444. 'aDe' ]
  445. start, end = _BuildLocations( 1, 2, 3, 2 )
  446. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'Eb\nbF',
  447. 0, 0, result_buffer )
  448. expected_buffer = [ 'aEb', 'bFCa', 'aDe' ]
  449. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  450. eq_( line_offset, -1 )
  451. eq_( char_offset, 1 )
  452. def ReplaceChunk_MultipleLinesToEvenLessMultipleLines_test():
  453. result_buffer = [ 'aAa',
  454. 'aBa',
  455. 'aCa',
  456. 'aDe' ]
  457. start, end = _BuildLocations( 1, 2, 4, 2 )
  458. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'Eb\nbF',
  459. 0, 0, result_buffer )
  460. expected_buffer = [ 'aEb', 'bFDe' ]
  461. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  462. eq_( line_offset, -2 )
  463. eq_( char_offset, 1 )
  464. def ReplaceChunk_SpanBufferEdge_test():
  465. result_buffer = [ 'aAa',
  466. 'aBa',
  467. 'aCa' ]
  468. start, end = _BuildLocations( 1, 1, 1, 3 )
  469. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'bDb',
  470. 0, 0, result_buffer )
  471. expected_buffer = [ 'bDba',
  472. 'aBa',
  473. 'aCa' ]
  474. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  475. eq_( line_offset, 0 )
  476. eq_( char_offset, 1 )
  477. def ReplaceChunk_DeleteTextInLine_test():
  478. result_buffer = [ 'aAa',
  479. 'aBa',
  480. 'aCa' ]
  481. start, end = _BuildLocations( 2, 2, 2, 3 )
  482. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, '',
  483. 0, 0, result_buffer )
  484. expected_buffer = [ 'aAa',
  485. 'aa',
  486. 'aCa' ]
  487. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  488. eq_( line_offset, 0 )
  489. eq_( char_offset, -1 )
  490. def ReplaceChunk_AddTextInLine_test():
  491. result_buffer = [ 'aAa',
  492. 'aBa',
  493. 'aCa' ]
  494. start, end = _BuildLocations( 2, 2, 2, 2 )
  495. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'bDb',
  496. 0, 0, result_buffer )
  497. expected_buffer = [ 'aAa',
  498. 'abDbBa',
  499. 'aCa' ]
  500. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  501. eq_( line_offset, 0 )
  502. eq_( char_offset, 3 )
  503. def ReplaceChunk_ReplaceTextInLine_test():
  504. result_buffer = [ 'aAa',
  505. 'aBa',
  506. 'aCa' ]
  507. start, end = _BuildLocations( 2, 2, 2, 3 )
  508. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'bDb',
  509. 0, 0, result_buffer )
  510. expected_buffer = [ 'aAa',
  511. 'abDba',
  512. 'aCa' ]
  513. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  514. eq_( line_offset, 0 )
  515. eq_( char_offset, 2 )
  516. def ReplaceChunk_SingleLineOffsetWorks_test():
  517. result_buffer = [ 'aAa',
  518. 'aBa',
  519. 'aCa' ]
  520. start, end = _BuildLocations( 1, 1, 1, 2 )
  521. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'bDb',
  522. 1, 1, result_buffer )
  523. expected_buffer = [ 'aAa',
  524. 'abDba',
  525. 'aCa' ]
  526. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  527. eq_( line_offset, 0 )
  528. eq_( char_offset, 2 )
  529. def ReplaceChunk_SingleLineToMultipleLinesOffsetWorks_test():
  530. result_buffer = [ 'aAa',
  531. 'aBa',
  532. 'aCa' ]
  533. start, end = _BuildLocations( 1, 1, 1, 2 )
  534. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'Db\nE',
  535. 1, 1, result_buffer )
  536. expected_buffer = [ 'aAa',
  537. 'aDb',
  538. 'Ea',
  539. 'aCa' ]
  540. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  541. eq_( line_offset, 1 )
  542. eq_( char_offset, -1 )
  543. def ReplaceChunk_MultipleLinesToSingleLineOffsetWorks_test():
  544. result_buffer = [ 'aAa',
  545. 'aBa',
  546. 'aCa' ]
  547. start, end = _BuildLocations( 1, 1, 2, 2 )
  548. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start, end, 'bDb',
  549. 1, 1, result_buffer )
  550. expected_buffer = [ 'aAa',
  551. 'abDbCa' ]
  552. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  553. eq_( line_offset, -1 )
  554. eq_( char_offset, 3 )
  555. def ReplaceChunk_MultipleLineOffsetWorks_test():
  556. result_buffer = [ 'aAa',
  557. 'aBa',
  558. 'aCa' ]
  559. start, end = _BuildLocations( 3, 1, 4, 3 )
  560. ( line_offset, char_offset ) = vimsupport.ReplaceChunk( start,
  561. end,
  562. 'bDb\nbEb\nbFb',
  563. -1,
  564. 1,
  565. result_buffer )
  566. expected_buffer = [ 'aAa',
  567. 'abDb',
  568. 'bEb',
  569. 'bFba' ]
  570. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  571. eq_( line_offset, 1 )
  572. eq_( char_offset, 1 )
  573. def _BuildLocations( start_line, start_column, end_line, end_column ):
  574. return {
  575. 'line_num' : start_line,
  576. 'column_num': start_column,
  577. }, {
  578. 'line_num' : end_line,
  579. 'column_num': end_column,
  580. }
  581. def ReplaceChunksInBuffer_SortedChunks_test():
  582. chunks = [
  583. _BuildChunk( 1, 4, 1, 4, '(' ),
  584. _BuildChunk( 1, 11, 1, 11, ')' )
  585. ]
  586. result_buffer = [ 'CT<10 >> 2> ct' ]
  587. vimsupport.ReplaceChunksInBuffer( chunks, result_buffer, None )
  588. expected_buffer = [ 'CT<(10 >> 2)> ct' ]
  589. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  590. def ReplaceChunksInBuffer_UnsortedChunks_test():
  591. chunks = [
  592. _BuildChunk( 1, 11, 1, 11, ')' ),
  593. _BuildChunk( 1, 4, 1, 4, '(' )
  594. ]
  595. result_buffer = [ 'CT<10 >> 2> ct' ]
  596. vimsupport.ReplaceChunksInBuffer( chunks, result_buffer, None )
  597. expected_buffer = [ 'CT<(10 >> 2)> ct' ]
  598. AssertBuffersAreEqualAsBytes( expected_buffer, result_buffer )
  599. @patch( 'ycm.vimsupport.VariableExists', return_value = False )
  600. @patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' )
  601. @patch( 'ycm.vimsupport.GetBufferNumberForFilename',
  602. return_value = 1,
  603. new_callable = ExtendedMock )
  604. @patch( 'ycm.vimsupport.BufferIsVisible',
  605. return_value = True,
  606. new_callable = ExtendedMock )
  607. @patch( 'ycm.vimsupport.OpenFilename' )
  608. @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock )
  609. @patch( 'vim.eval', new_callable = ExtendedMock )
  610. @patch( 'vim.command', new_callable = ExtendedMock )
  611. def ReplaceChunks_SingleFile_Open_test( vim_command,
  612. vim_eval,
  613. post_vim_message,
  614. open_filename,
  615. buffer_is_visible,
  616. get_buffer_number_for_filename,
  617. set_fitting_height,
  618. variable_exists ):
  619. chunks = [
  620. _BuildChunk( 1, 1, 2, 1, 'replacement', 'single_file' )
  621. ]
  622. result_buffer = VimBuffer(
  623. 'single_file',
  624. contents = [
  625. 'line1',
  626. 'line2',
  627. 'line3'
  628. ]
  629. )
  630. with patch( 'vim.buffers', [ None, result_buffer, None ] ):
  631. vimsupport.ReplaceChunks( chunks )
  632. # Ensure that we applied the replacement correctly
  633. eq_( result_buffer.GetLines(), [
  634. 'replacementline2',
  635. 'line3',
  636. ] )
  637. # GetBufferNumberForFilename is called twice:
  638. # - once to the check if we would require opening the file (so that we can
  639. # raise a warning)
  640. # - once whilst applying the changes
  641. get_buffer_number_for_filename.assert_has_exact_calls( [
  642. call( 'single_file', False ),
  643. call( 'single_file', False ),
  644. ] )
  645. # BufferIsVisible is called twice for the same reasons as above
  646. buffer_is_visible.assert_has_exact_calls( [
  647. call( 1 ),
  648. call( 1 ),
  649. ] )
  650. # we don't attempt to open any files
  651. open_filename.assert_not_called()
  652. # But we do set the quickfix list
  653. vim_eval.assert_has_exact_calls( [
  654. call( 'setqflist( {0} )'.format( json.dumps( [ {
  655. 'bufnr': 1,
  656. 'filename': 'single_file',
  657. 'lnum': 1,
  658. 'col': 1,
  659. 'text': 'replacement',
  660. 'type': 'F'
  661. } ] ) ) ),
  662. ] )
  663. vim_command.assert_has_exact_calls( [
  664. call( 'botright copen' ),
  665. call( 'silent! wincmd p' )
  666. ] )
  667. set_fitting_height.assert_called_once_with()
  668. # And it is ReplaceChunks that prints the message showing the number of
  669. # changes
  670. post_vim_message.assert_has_exact_calls( [
  671. call( 'Applied 1 changes', warning = False ),
  672. ] )
  673. @patch( 'ycm.vimsupport.VariableExists', return_value = False )
  674. @patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' )
  675. @patch( 'ycm.vimsupport.GetBufferNumberForFilename',
  676. side_effect = [ -1, -1, 1 ],
  677. new_callable = ExtendedMock )
  678. @patch( 'ycm.vimsupport.BufferIsVisible',
  679. side_effect = [ False, False, True ],
  680. new_callable = ExtendedMock )
  681. @patch( 'ycm.vimsupport.OpenFilename',
  682. new_callable = ExtendedMock )
  683. @patch( 'ycm.vimsupport.PostVimMessage', new_callable = ExtendedMock )
  684. @patch( 'ycm.vimsupport.Confirm',
  685. return_value = True,
  686. new_callable = ExtendedMock )
  687. @patch( 'vim.eval', return_value = 10, new_callable = ExtendedMock )
  688. @patch( 'vim.command', new_callable = ExtendedMock )
  689. def ReplaceChunks_SingleFile_NotOpen_test( vim_command,
  690. vim_eval,
  691. confirm,
  692. post_vim_message,
  693. open_filename,
  694. buffer_is_visible,
  695. get_buffer_number_for_filename,
  696. set_fitting_height,
  697. variable_exists ):
  698. chunks = [
  699. _BuildChunk( 1, 1, 2, 1, 'replacement', 'single_file' )
  700. ]
  701. result_buffer = VimBuffer(
  702. 'single_file',
  703. contents = [
  704. 'line1',
  705. 'line2',
  706. 'line3'
  707. ]
  708. )
  709. with patch( 'vim.buffers', [ None, result_buffer, None ] ):
  710. vimsupport.ReplaceChunks( chunks )
  711. # We checked if it was OK to open the file
  712. confirm.assert_has_exact_calls( [
  713. call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) )
  714. ] )
  715. # Ensure that we applied the replacement correctly
  716. eq_( result_buffer.GetLines(), [
  717. 'replacementline2',
  718. 'line3',
  719. ] )
  720. # GetBufferNumberForFilename is called 3 times. The return values are set in
  721. # the @patch call above:
  722. # - once to the check if we would require opening the file (so that we can
  723. # raise a warning) (-1 return)
  724. # - once whilst applying the changes (-1 return)
  725. # - finally after calling OpenFilename (1 return)
  726. get_buffer_number_for_filename.assert_has_exact_calls( [
  727. call( 'single_file', False ),
  728. call( 'single_file', False ),
  729. call( 'single_file', False ),
  730. ] )
  731. # BufferIsVisible is called 3 times for the same reasons as above, with the
  732. # return of each one
  733. buffer_is_visible.assert_has_exact_calls( [
  734. call( -1 ),
  735. call( -1 ),
  736. call( 1 ),
  737. ] )
  738. # We open 'single_file' as expected.
  739. open_filename.assert_called_with( 'single_file', {
  740. 'focus': True,
  741. 'fix': True,
  742. 'size': 10
  743. } )
  744. # And close it again, then show the quickfix window.
  745. vim_command.assert_has_exact_calls( [
  746. call( 'lclose' ),
  747. call( 'hide' ),
  748. call( 'botright copen' ),
  749. call( 'silent! wincmd p' )
  750. ] )
  751. set_fitting_height.assert_called_once_with()
  752. # And update the quickfix list
  753. vim_eval.assert_has_exact_calls( [
  754. call( '&previewheight' ),
  755. call( 'setqflist( {0} )'.format( json.dumps( [ {
  756. 'bufnr': 1,
  757. 'filename': 'single_file',
  758. 'lnum': 1,
  759. 'col': 1,
  760. 'text': 'replacement',
  761. 'type': 'F'
  762. } ] ) ) ),
  763. ] )
  764. # And it is ReplaceChunks that prints the message showing the number of
  765. # changes
  766. post_vim_message.assert_has_exact_calls( [
  767. call( 'Applied 1 changes', warning = False ),
  768. ] )
  769. @patch( 'ycm.vimsupport.GetBufferNumberForFilename',
  770. side_effect = [ -1, -1, 1 ],
  771. new_callable = ExtendedMock )
  772. @patch( 'ycm.vimsupport.BufferIsVisible',
  773. side_effect = [ False, False, True ],
  774. new_callable = ExtendedMock )
  775. @patch( 'ycm.vimsupport.OpenFilename',
  776. new_callable = ExtendedMock )
  777. @patch( 'ycm.vimsupport.PostVimMessage',
  778. new_callable = ExtendedMock )
  779. @patch( 'ycm.vimsupport.Confirm',
  780. return_value = False,
  781. new_callable = ExtendedMock )
  782. @patch( 'vim.eval',
  783. return_value = 10,
  784. new_callable = ExtendedMock )
  785. @patch( 'vim.command', new_callable = ExtendedMock )
  786. def ReplaceChunks_User_Declines_To_Open_File_test(
  787. vim_command,
  788. vim_eval,
  789. confirm,
  790. post_vim_message,
  791. open_filename,
  792. buffer_is_visible,
  793. get_buffer_number_for_filename ):
  794. # Same as above, except the user selects Cancel when asked if they should
  795. # allow us to open lots of (ahem, 1) file.
  796. chunks = [
  797. _BuildChunk( 1, 1, 2, 1, 'replacement', 'single_file' )
  798. ]
  799. result_buffer = VimBuffer(
  800. 'single_file',
  801. contents = [
  802. 'line1',
  803. 'line2',
  804. 'line3'
  805. ]
  806. )
  807. with patch( 'vim.buffers', [ None, result_buffer, None ] ):
  808. vimsupport.ReplaceChunks( chunks )
  809. # We checked if it was OK to open the file
  810. confirm.assert_has_exact_calls( [
  811. call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) )
  812. ] )
  813. # Ensure that buffer is not changed
  814. eq_( result_buffer.GetLines(), [
  815. 'line1',
  816. 'line2',
  817. 'line3',
  818. ] )
  819. # GetBufferNumberForFilename is called once. The return values are set in
  820. # the @patch call above:
  821. # - once to the check if we would require opening the file (so that we can
  822. # raise a warning) (-1 return)
  823. get_buffer_number_for_filename.assert_has_exact_calls( [
  824. call( 'single_file', False ),
  825. ] )
  826. # BufferIsVisible is called once for the above file, which wasn't visible.
  827. buffer_is_visible.assert_has_exact_calls( [
  828. call( -1 ),
  829. ] )
  830. # We don't attempt to open any files or update any quickfix list or anything
  831. # like that
  832. open_filename.assert_not_called()
  833. vim_eval.assert_not_called()
  834. vim_command.assert_not_called()
  835. post_vim_message.assert_not_called()
  836. @patch( 'ycm.vimsupport.GetBufferNumberForFilename',
  837. side_effect = [ -1, -1, 1 ],
  838. new_callable = ExtendedMock )
  839. # Key difference is here: In the final check, BufferIsVisible returns False
  840. @patch( 'ycm.vimsupport.BufferIsVisible',
  841. side_effect = [ False, False, False ],
  842. new_callable = ExtendedMock )
  843. @patch( 'ycm.vimsupport.OpenFilename',
  844. new_callable = ExtendedMock )
  845. @patch( 'ycm.vimsupport.PostVimMessage',
  846. new_callable = ExtendedMock )
  847. @patch( 'ycm.vimsupport.Confirm',
  848. return_value = True,
  849. new_callable = ExtendedMock )
  850. @patch( 'vim.eval',
  851. return_value = 10,
  852. new_callable = ExtendedMock )
  853. @patch( 'vim.command',
  854. new_callable = ExtendedMock )
  855. def ReplaceChunks_User_Aborts_Opening_File_test(
  856. vim_command,
  857. vim_eval,
  858. confirm,
  859. post_vim_message,
  860. open_filename,
  861. buffer_is_visible,
  862. get_buffer_number_for_filename ):
  863. # Same as above, except the user selects Abort or Quick during the
  864. # "swap-file-found" dialog
  865. chunks = [
  866. _BuildChunk( 1, 1, 2, 1, 'replacement', 'single_file' )
  867. ]
  868. result_buffer = VimBuffer(
  869. 'single_file',
  870. contents = [
  871. 'line1',
  872. 'line2',
  873. 'line3'
  874. ]
  875. )
  876. with patch( 'vim.buffers', [ None, result_buffer, None ] ):
  877. assert_that( calling( vimsupport.ReplaceChunks ).with_args( chunks ),
  878. raises( RuntimeError,
  879. 'Unable to open file: single_file\nFixIt/Refactor operation '
  880. 'aborted prior to completion. Your files have not been '
  881. 'fully updated. Please use undo commands to revert the '
  882. 'applied changes.' ) )
  883. # We checked if it was OK to open the file
  884. confirm.assert_has_exact_calls( [
  885. call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) )
  886. ] )
  887. # Ensure that buffer is not changed
  888. eq_( result_buffer.GetLines(), [
  889. 'line1',
  890. 'line2',
  891. 'line3',
  892. ] )
  893. # We tried to open this file
  894. open_filename.assert_called_with( "single_file", {
  895. 'focus': True,
  896. 'fix': True,
  897. 'size': 10
  898. } )
  899. vim_eval.assert_called_with( "&previewheight" )
  900. # But raised an exception before issuing the message at the end
  901. post_vim_message.assert_not_called()
  902. @patch( 'ycm.vimsupport.VariableExists', return_value = False )
  903. @patch( 'ycm.vimsupport.SetFittingHeightForCurrentWindow' )
  904. @patch( 'ycm.vimsupport.GetBufferNumberForFilename', side_effect = [
  905. 22, # first_file (check)
  906. -1, # another_file (check)
  907. 22, # first_file (apply)
  908. -1, # another_file (apply)
  909. 19, # another_file (check after open)
  910. ],
  911. new_callable = ExtendedMock )
  912. @patch( 'ycm.vimsupport.BufferIsVisible', side_effect = [
  913. True, # first_file (check)
  914. False, # second_file (check)
  915. True, # first_file (apply)
  916. False, # second_file (apply)
  917. True, # side_effect (check after open)
  918. ],
  919. new_callable = ExtendedMock)
  920. @patch( 'ycm.vimsupport.OpenFilename',
  921. new_callable = ExtendedMock)
  922. @patch( 'ycm.vimsupport.PostVimMessage',
  923. new_callable = ExtendedMock)
  924. @patch( 'ycm.vimsupport.Confirm', return_value = True,
  925. new_callable = ExtendedMock)
  926. @patch( 'vim.eval', return_value = 10,
  927. new_callable = ExtendedMock)
  928. @patch( 'vim.command',
  929. new_callable = ExtendedMock)
  930. def ReplaceChunks_MultiFile_Open_test( vim_command,
  931. vim_eval,
  932. confirm,
  933. post_vim_message,
  934. open_filename,
  935. buffer_is_visible,
  936. get_buffer_number_for_filename,
  937. set_fitting_height,
  938. variable_exists ):
  939. # Chunks are split across 2 files, one is already open, one isn't
  940. chunks = [
  941. _BuildChunk( 1, 1, 2, 1, 'first_file_replacement ', '1_first_file' ),
  942. _BuildChunk( 2, 1, 2, 1, 'second_file_replacement ', '2_another_file' ),
  943. ]
  944. first_file = VimBuffer(
  945. '1_first_file',
  946. number = 22,
  947. contents = [
  948. 'line1',
  949. 'line2',
  950. 'line3',
  951. ]
  952. )
  953. another_file = VimBuffer(
  954. '2_another_file',
  955. number = 19,
  956. contents = [
  957. 'another line1',
  958. 'ACME line2',
  959. ]
  960. )
  961. vim_buffers = [ None ] * 23
  962. vim_buffers[ 22 ] = first_file
  963. vim_buffers[ 19 ] = another_file
  964. with patch( 'vim.buffers', vim_buffers ):
  965. vimsupport.ReplaceChunks( chunks )
  966. # We checked for the right file names
  967. get_buffer_number_for_filename.assert_has_exact_calls( [
  968. call( '1_first_file', False ),
  969. call( '2_another_file', False ),
  970. call( '1_first_file', False ),
  971. call( '2_another_file', False ),
  972. call( '2_another_file', False ),
  973. ] )
  974. # We checked if it was OK to open the file
  975. confirm.assert_has_exact_calls( [
  976. call( vimsupport.FIXIT_OPENING_BUFFERS_MESSAGE_FORMAT.format( 1 ) )
  977. ] )
  978. # Ensure that buffers are updated
  979. eq_( another_file.GetLines(), [
  980. 'another line1',
  981. 'second_file_replacement ACME line2',
  982. ] )
  983. eq_( first_file.GetLines(), [
  984. 'first_file_replacement line2',
  985. 'line3',
  986. ] )
  987. # We open '2_another_file' as expected.
  988. open_filename.assert_called_with( '2_another_file', {
  989. 'focus': True,
  990. 'fix': True,
  991. 'size': 10
  992. } )
  993. # And close it again, then show the quickfix window.
  994. vim_command.assert_has_exact_calls( [
  995. call( 'lclose' ),
  996. call( 'hide' ),
  997. call( 'botright copen' ),
  998. call( 'silent! wincmd p' )
  999. ] )
  1000. set_fitting_height.assert_called_once_with()
  1001. # And update the quickfix list with each entry
  1002. vim_eval.assert_has_exact_calls( [
  1003. call( '&previewheight' ),
  1004. call( 'setqflist( {0} )'.format( json.dumps( [ {
  1005. 'bufnr': 22,
  1006. 'filename': '1_first_file',
  1007. 'lnum': 1,
  1008. 'col': 1,
  1009. 'text': 'first_file_replacement ',
  1010. 'type': 'F'
  1011. }, {
  1012. 'bufnr': 19,
  1013. 'filename': '2_another_file',
  1014. 'lnum': 2,
  1015. 'col': 1,
  1016. 'text': 'second_file_replacement ',
  1017. 'type': 'F'
  1018. } ] ) ) ),
  1019. ] )
  1020. # And it is ReplaceChunks that prints the message showing the number of
  1021. # changes
  1022. post_vim_message.assert_has_exact_calls( [
  1023. call( 'Applied 2 changes', warning = False ),
  1024. ] )
  1025. def _BuildChunk( start_line,
  1026. start_column,
  1027. end_line,
  1028. end_column,
  1029. replacement_text, filepath='test_file_name' ):
  1030. return {
  1031. 'range': {
  1032. 'start': {
  1033. 'filepath': filepath,
  1034. 'line_num': start_line,
  1035. 'column_num': start_column,
  1036. },
  1037. 'end': {
  1038. 'filepath': filepath,
  1039. 'line_num': end_line,
  1040. 'column_num': end_column,
  1041. },
  1042. },
  1043. 'replacement_text': replacement_text
  1044. }
  1045. @patch( 'vim.eval', new_callable = ExtendedMock )
  1046. def AddDiagnosticSyntaxMatch_ErrorInMiddleOfLine_test( vim_eval ):
  1047. current_buffer = VimBuffer(
  1048. 'some_file',
  1049. contents = [ 'Highlight this error please' ]
  1050. )
  1051. with patch( 'vim.current.buffer', current_buffer ):
  1052. vimsupport.AddDiagnosticSyntaxMatch( 1, 16, 1, 21 )
  1053. vim_eval.assert_called_once_with(
  1054. r"matchadd('YcmErrorSection', '\%1l\%16c\_.\{-}\%1l\%21c')" )
  1055. @patch( 'vim.eval', new_callable = ExtendedMock )
  1056. def AddDiagnosticSyntaxMatch_WarningAtEndOfLine_test( vim_eval ):
  1057. current_buffer = VimBuffer(
  1058. 'some_file',
  1059. contents = [ 'Highlight this warning' ]
  1060. )
  1061. with patch( 'vim.current.buffer', current_buffer ):
  1062. vimsupport.AddDiagnosticSyntaxMatch( 1, 16, 1, 23, is_error = False )
  1063. vim_eval.assert_called_once_with(
  1064. r"matchadd('YcmWarningSection', '\%1l\%16c\_.\{-}\%1l\%23c')" )
  1065. @patch( 'vim.command', new_callable=ExtendedMock )
  1066. @patch( 'vim.current', new_callable=ExtendedMock)
  1067. def WriteToPreviewWindow_test( vim_current, vim_command ):
  1068. vim_current.window.options.__getitem__ = MagicMock( return_value = True )
  1069. vimsupport.WriteToPreviewWindow( "test" )
  1070. vim_command.assert_has_exact_calls( [
  1071. call( 'silent! pclose!' ),
  1072. call( 'silent! pedit! _TEMP_FILE_' ),
  1073. call( 'silent! wincmd P' ),
  1074. call( 'silent! wincmd p' ) ] )
  1075. vim_current.buffer.__setitem__.assert_called_with(
  1076. slice( None, None, None ), [ 'test' ] )
  1077. vim_current.buffer.options.__setitem__.assert_has_exact_calls( [
  1078. call( 'modifiable', True ),
  1079. call( 'readonly', False ),
  1080. call( 'buftype', 'nofile' ),
  1081. call( 'swapfile', False ),
  1082. call( 'modifiable', False ),
  1083. call( 'modified', False ),
  1084. call( 'readonly', True ),
  1085. ], any_order = True )
  1086. @patch( 'vim.current' )
  1087. def WriteToPreviewWindow_MultiLine_test( vim_current ):
  1088. vim_current.window.options.__getitem__ = MagicMock( return_value = True )
  1089. vimsupport.WriteToPreviewWindow( "test\ntest2" )
  1090. vim_current.buffer.__setitem__.assert_called_with(
  1091. slice( None, None, None ), [ 'test', 'test2' ] )
  1092. @patch( 'vim.command', new_callable=ExtendedMock )
  1093. @patch( 'vim.current', new_callable=ExtendedMock )
  1094. def WriteToPreviewWindow_JumpFail_test( vim_current, vim_command ):
  1095. vim_current.window.options.__getitem__ = MagicMock( return_value = False )
  1096. vimsupport.WriteToPreviewWindow( "test" )
  1097. vim_command.assert_has_exact_calls( [
  1098. call( 'silent! pclose!' ),
  1099. call( 'silent! pedit! _TEMP_FILE_' ),
  1100. call( 'silent! wincmd P' ),
  1101. call( 'redraw' ),
  1102. call( "echo 'test'" ),
  1103. ] )
  1104. vim_current.buffer.__setitem__.assert_not_called()
  1105. vim_current.buffer.options.__setitem__.assert_not_called()
  1106. @patch( 'vim.command', new_callable=ExtendedMock )
  1107. @patch( 'vim.current', new_callable=ExtendedMock )
  1108. def WriteToPreviewWindow_JumpFail_MultiLine_test( vim_current, vim_command ):
  1109. vim_current.window.options.__getitem__ = MagicMock( return_value = False )
  1110. vimsupport.WriteToPreviewWindow( "test\ntest2" )
  1111. vim_command.assert_has_exact_calls( [
  1112. call( 'silent! pclose!' ),
  1113. call( 'silent! pedit! _TEMP_FILE_' ),
  1114. call( 'silent! wincmd P' ),
  1115. call( 'redraw' ),
  1116. call( "echo 'test'" ),
  1117. call( "echo 'test2'" ),
  1118. ] )
  1119. vim_current.buffer.__setitem__.assert_not_called()
  1120. vim_current.buffer.options.__setitem__.assert_not_called()
  1121. def CheckFilename_test():
  1122. assert_that(
  1123. calling( vimsupport.CheckFilename ).with_args( None ),
  1124. raises( RuntimeError, "'None' is not a valid filename" )
  1125. )
  1126. assert_that(
  1127. calling( vimsupport.CheckFilename ).with_args( 'nonexistent_file' ),
  1128. raises( RuntimeError,
  1129. "filename 'nonexistent_file' cannot be opened. "
  1130. "No such file or directory." )
  1131. )
  1132. assert_that( vimsupport.CheckFilename( __file__ ), none() )
  1133. def BufferIsVisibleForFilename_test():
  1134. vim_buffers = [
  1135. VimBuffer(
  1136. os.path.realpath( 'visible_filename' ),
  1137. number = 1,
  1138. window = 1
  1139. ),
  1140. VimBuffer(
  1141. os.path.realpath( 'hidden_filename' ),
  1142. number = 2,
  1143. window = None
  1144. )
  1145. ]
  1146. with patch( 'vim.buffers', vim_buffers ):
  1147. eq_( vimsupport.BufferIsVisibleForFilename( 'visible_filename' ), True )
  1148. eq_( vimsupport.BufferIsVisibleForFilename( 'hidden_filename' ), False )
  1149. eq_( vimsupport.BufferIsVisibleForFilename( 'another_filename' ), False )
  1150. @patch( 'vim.command',
  1151. side_effect = MockVimCommand,
  1152. new_callable = ExtendedMock )
  1153. def CloseBuffersForFilename_test( vim_command, *args ):
  1154. vim_buffers = [
  1155. VimBuffer(
  1156. os.path.realpath( 'some_filename' ),
  1157. number = 2
  1158. ),
  1159. VimBuffer(
  1160. os.path.realpath( 'some_filename' ),
  1161. number = 5
  1162. )
  1163. ]
  1164. with patch( 'vim.buffers', vim_buffers ):
  1165. vimsupport.CloseBuffersForFilename( 'some_filename' )
  1166. vim_command.assert_has_exact_calls( [
  1167. call( 'silent! bwipeout! 2' ),
  1168. call( 'silent! bwipeout! 5' )
  1169. ], any_order = True )
  1170. @patch( 'vim.command', new_callable = ExtendedMock )
  1171. @patch( 'vim.current', new_callable = ExtendedMock )
  1172. def OpenFilename_test( vim_current, vim_command ):
  1173. # Options used to open a logfile
  1174. options = {
  1175. 'size': vimsupport.GetIntValue( '&previewheight' ),
  1176. 'fix': True,
  1177. 'watch': True,
  1178. 'position': 'end'
  1179. }
  1180. vimsupport.OpenFilename( __file__, options )
  1181. vim_command.assert_has_exact_calls( [
  1182. call( '12split {0}'.format( __file__ ) ),
  1183. call( "exec "
  1184. "'au BufEnter <buffer> :silent! checktime {0}'".format( __file__ ) ),
  1185. call( 'silent! normal G zz' ),
  1186. call( 'silent! wincmd p' )
  1187. ] )
  1188. vim_current.buffer.options.__setitem__.assert_has_exact_calls( [
  1189. call( 'autoread', True ),
  1190. ] )
  1191. vim_current.window.options.__setitem__.assert_has_exact_calls( [
  1192. call( 'winfixheight', True )
  1193. ] )
  1194. def GetUnsavedAndSpecifiedBufferData_EncodedUnicodeCharsInBuffers_test():
  1195. filepath = os.path.realpath( 'filename' )
  1196. contents = [ ToBytes( u'abc' ), ToBytes( u'fДa' ) ]
  1197. vim_buffer = VimBuffer( filepath, contents = contents )
  1198. with patch( 'vim.buffers', [ vim_buffer ] ):
  1199. assert_that( vimsupport.GetUnsavedAndSpecifiedBufferData( filepath ),
  1200. has_entry( filepath,
  1201. has_entry( u'contents', u'abc\nfДa\n' ) ) )
  1202. def GetBufferFilepath_NoBufferName_UnicodeWorkingDirectory_test():
  1203. vim_buffer = VimBuffer( '', number = 42 )
  1204. unicode_dir = PathToTestFile( u'uni¢𐍈d€' )
  1205. with CurrentWorkingDirectory( unicode_dir ):
  1206. assert_that( vimsupport.GetBufferFilepath( vim_buffer ),
  1207. equal_to( os.path.join( unicode_dir, '42' ) ) )
  1208. # NOTE: Vim returns byte offsets for columns, not actual character columns. This
  1209. # makes 'ДД' have 4 columns: column 0, column 2 and column 4.
  1210. @patch( 'vim.current.line', ToBytes( 'ДДaa' ) )
  1211. @patch( 'ycm.vimsupport.CurrentColumn', side_effect = [ 4 ] )
  1212. def TextBeforeCursor_EncodedUnicode_test( *args ):
  1213. eq_( vimsupport.TextBeforeCursor(), u'ДД' )
  1214. # NOTE: Vim returns byte offsets for columns, not actual character columns. This
  1215. # makes 'ДД' have 4 columns: column 0, column 2 and column 4.
  1216. @patch( 'vim.current.line', ToBytes( 'aaДД' ) )
  1217. @patch( 'ycm.vimsupport.CurrentColumn', side_effect = [ 2 ] )
  1218. def TextAfterCursor_EncodedUnicode_test( *args ):
  1219. eq_( vimsupport.TextAfterCursor(), u'ДД' )
  1220. @patch( 'vim.current.line', ToBytes( 'fДa' ) )
  1221. def CurrentLineContents_EncodedUnicode_test( *args ):
  1222. eq_( vimsupport.CurrentLineContents(), u'fДa' )
  1223. @patch( 'vim.eval', side_effect = lambda x: x )
  1224. def VimExpressionToPythonType_IntAsUnicode_test( *args ):
  1225. eq_( vimsupport.VimExpressionToPythonType( '123' ), 123 )
  1226. @patch( 'vim.eval', side_effect = lambda x: x )
  1227. def VimExpressionToPythonType_IntAsBytes_test( *args ):
  1228. eq_( vimsupport.VimExpressionToPythonType( ToBytes( '123' ) ), 123 )
  1229. @patch( 'vim.eval', side_effect = lambda x: x )
  1230. def VimExpressionToPythonType_StringAsUnicode_test( *args ):
  1231. eq_( vimsupport.VimExpressionToPythonType( 'foo' ), 'foo' )
  1232. @patch( 'vim.eval', side_effect = lambda x: x )
  1233. def VimExpressionToPythonType_StringAsBytes_test( *args ):
  1234. eq_( vimsupport.VimExpressionToPythonType( ToBytes( 'foo' ) ), 'foo' )
  1235. @patch( 'vim.eval', side_effect = lambda x: x )
  1236. def VimExpressionToPythonType_ListPassthrough_test( *args ):
  1237. eq_( vimsupport.VimExpressionToPythonType( [ 1, 2 ] ), [ 1, 2 ] )
  1238. @patch( 'vim.eval', side_effect = lambda x: x )
  1239. def VimExpressionToPythonType_ObjectPassthrough_test( *args ):
  1240. eq_( vimsupport.VimExpressionToPythonType( { 1: 2 } ), { 1: 2 } )
  1241. @patch( 'vim.eval', side_effect = lambda x: x )
  1242. def VimExpressionToPythonType_GeneratorPassthrough_test( *args ):
  1243. gen = ( x**2 for x in [ 1, 2, 3 ] )
  1244. eq_( vimsupport.VimExpressionToPythonType( gen ), gen )
  1245. @patch( 'vim.eval',
  1246. new_callable = ExtendedMock,
  1247. side_effect = [ None, 2, None ] )
  1248. def SelectFromList_LastItem_test( vim_eval ):
  1249. eq_( vimsupport.SelectFromList( 'test', [ 'a', 'b' ] ),
  1250. 1 )
  1251. vim_eval.assert_has_exact_calls( [
  1252. call( 'inputsave()' ),
  1253. call( 'inputlist( ["test", "1: a", "2: b"] )' ),
  1254. call( 'inputrestore()' )
  1255. ] )
  1256. @patch( 'vim.eval',
  1257. new_callable = ExtendedMock,
  1258. side_effect = [ None, 1, None ] )
  1259. def SelectFromList_FirstItem_test( vim_eval ):
  1260. eq_( vimsupport.SelectFromList( 'test', [ 'a', 'b' ] ),
  1261. 0 )
  1262. vim_eval.assert_has_exact_calls( [
  1263. call( 'inputsave()' ),
  1264. call( 'inputlist( ["test", "1: a", "2: b"] )' ),
  1265. call( 'inputrestore()' )
  1266. ] )
  1267. @patch( 'vim.eval', side_effect = [ None, 3, None ] )
  1268. def SelectFromList_OutOfRange_test( vim_eval ):
  1269. assert_that( calling( vimsupport.SelectFromList).with_args( 'test',
  1270. [ 'a', 'b' ] ),
  1271. raises( RuntimeError, vimsupport.NO_SELECTION_MADE_MSG ) )
  1272. @patch( 'vim.eval', side_effect = [ None, 0, None ] )
  1273. def SelectFromList_SelectPrompt_test( vim_eval ):
  1274. assert_that( calling( vimsupport.SelectFromList ).with_args( 'test',
  1275. [ 'a', 'b' ] ),
  1276. raises( RuntimeError, vimsupport.NO_SELECTION_MADE_MSG ) )
  1277. @patch( 'vim.eval', side_effect = [ None, -199, None ] )
  1278. def SelectFromList_Negative_test( vim_eval ):
  1279. assert_that( calling( vimsupport.SelectFromList ).with_args( 'test',
  1280. [ 'a', 'b' ] ),
  1281. raises( RuntimeError, vimsupport.NO_SELECTION_MADE_MSG ) )