extmark_spec.lua 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815
  1. local helpers = require('test.functional.helpers')(after_each)
  2. local Screen = require('test.functional.ui.screen')
  3. local request = helpers.request
  4. local eq = helpers.eq
  5. local ok = helpers.ok
  6. local curbufmeths = helpers.curbufmeths
  7. local bufmeths = helpers.bufmeths
  8. local pcall_err = helpers.pcall_err
  9. local insert = helpers.insert
  10. local feed = helpers.feed
  11. local clear = helpers.clear
  12. local command = helpers.command
  13. local meths = helpers.meths
  14. local assert_alive = helpers.assert_alive
  15. local function expect(contents)
  16. return eq(contents, helpers.curbuf_contents())
  17. end
  18. local function set_extmark(ns_id, id, line, col, opts)
  19. if opts == nil then
  20. opts = {}
  21. end
  22. if id ~= nil and id ~= 0 then
  23. opts.id = id
  24. end
  25. return curbufmeths.set_extmark(ns_id, line, col, opts)
  26. end
  27. local function get_extmarks(ns_id, start, end_, opts)
  28. if opts == nil then
  29. opts = {}
  30. end
  31. return curbufmeths.get_extmarks(ns_id, start, end_, opts)
  32. end
  33. local function get_extmark_by_id(ns_id, id, opts)
  34. if opts == nil then
  35. opts = {}
  36. end
  37. return curbufmeths.get_extmark_by_id(ns_id, id, opts)
  38. end
  39. local function check_undo_redo(ns, mark, sr, sc, er, ec) --s = start, e = end
  40. local rv = get_extmark_by_id(ns, mark)
  41. eq({er, ec}, rv)
  42. feed("u")
  43. rv = get_extmark_by_id(ns, mark)
  44. eq({sr, sc}, rv)
  45. feed("<c-r>")
  46. rv = get_extmark_by_id(ns, mark)
  47. eq({er, ec}, rv)
  48. end
  49. local function batch_set(ns_id, positions)
  50. local ids = {}
  51. for _, pos in ipairs(positions) do
  52. table.insert(ids, set_extmark(ns_id, 0, pos[1], pos[2]))
  53. end
  54. return ids
  55. end
  56. local function batch_check(ns_id, ids, positions)
  57. local actual, expected = {}, {}
  58. for i,id in ipairs(ids) do
  59. expected[id] = positions[i]
  60. end
  61. for _, mark in pairs(get_extmarks(ns_id, 0, -1, {})) do
  62. actual[mark[1]] = {mark[2], mark[3]}
  63. end
  64. eq(expected, actual)
  65. end
  66. local function batch_check_undo_redo(ns_id, ids, before, after)
  67. batch_check(ns_id, ids, after)
  68. feed("u")
  69. batch_check(ns_id, ids, before)
  70. feed("<c-r>")
  71. batch_check(ns_id, ids, after)
  72. end
  73. describe('API/extmarks', function()
  74. local screen
  75. local marks, positions, init_text, row, col
  76. local ns, ns2
  77. before_each(function()
  78. -- Initialize some namespaces and insert 12345 into a buffer
  79. marks = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
  80. positions = {{0, 0,}, {0, 2}, {0, 3}}
  81. init_text = "12345"
  82. row = 0
  83. col = 2
  84. clear()
  85. insert(init_text)
  86. ns = request('nvim_create_namespace', "my-fancy-plugin")
  87. ns2 = request('nvim_create_namespace', "my-fancy-plugin2")
  88. end)
  89. it("can end extranges past final newline using end_col = 0", function()
  90. set_extmark(ns, marks[1], 0, 0, {
  91. end_col = 0,
  92. end_row = 1
  93. })
  94. eq("end_col value outside range",
  95. pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = 1 }))
  96. end)
  97. it("can end extranges past final newline when strict mode is false", function()
  98. set_extmark(ns, marks[1], 0, 0, {
  99. end_col = 1,
  100. end_row = 1,
  101. strict = false,
  102. })
  103. end)
  104. it("can end extranges past final column when strict mode is false", function()
  105. set_extmark(ns, marks[1], 0, 0, {
  106. end_col = 6,
  107. end_row = 0,
  108. strict = false,
  109. })
  110. end)
  111. it('adds, updates and deletes marks', function()
  112. local rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
  113. eq(marks[1], rv)
  114. rv = get_extmark_by_id(ns, marks[1])
  115. eq({positions[1][1], positions[1][2]}, rv)
  116. -- Test adding a second mark on same row works
  117. rv = set_extmark(ns, marks[2], positions[2][1], positions[2][2])
  118. eq(marks[2], rv)
  119. -- Test an update, (same pos)
  120. rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
  121. eq(marks[1], rv)
  122. rv = get_extmark_by_id(ns, marks[2])
  123. eq({positions[2][1], positions[2][2]}, rv)
  124. -- Test an update, (new pos)
  125. row = positions[1][1]
  126. col = positions[1][2] + 1
  127. rv = set_extmark(ns, marks[1], row, col)
  128. eq(marks[1], rv)
  129. rv = get_extmark_by_id(ns, marks[1])
  130. eq({row, col}, rv)
  131. -- remove the test marks
  132. eq(true, curbufmeths.del_extmark(ns, marks[1]))
  133. eq(false, curbufmeths.del_extmark(ns, marks[1]))
  134. eq(true, curbufmeths.del_extmark(ns, marks[2]))
  135. eq(false, curbufmeths.del_extmark(ns, marks[3]))
  136. eq(false, curbufmeths.del_extmark(ns, 1000))
  137. end)
  138. it('can clear a specific namespace range', function()
  139. set_extmark(ns, 1, 0, 1)
  140. set_extmark(ns2, 1, 0, 1)
  141. -- force a new undo buffer
  142. feed('o<esc>')
  143. curbufmeths.clear_namespace(ns2, 0, -1)
  144. eq({{1, 0, 1}}, get_extmarks(ns, {0, 0}, {-1, -1}))
  145. eq({}, get_extmarks(ns2, {0, 0}, {-1, -1}))
  146. feed('u')
  147. eq({{1, 0, 1}}, get_extmarks(ns, {0, 0}, {-1, -1}))
  148. eq({}, get_extmarks(ns2, {0, 0}, {-1, -1}))
  149. feed('<c-r>')
  150. eq({{1, 0, 1}}, get_extmarks(ns, {0, 0}, {-1, -1}))
  151. eq({}, get_extmarks(ns2, {0, 0}, {-1, -1}))
  152. end)
  153. it('can clear a namespace range using 0,-1', function()
  154. set_extmark(ns, 1, 0, 1)
  155. set_extmark(ns2, 1, 0, 1)
  156. -- force a new undo buffer
  157. feed('o<esc>')
  158. curbufmeths.clear_namespace(-1, 0, -1)
  159. eq({}, get_extmarks(ns, {0, 0}, {-1, -1}))
  160. eq({}, get_extmarks(ns2, {0, 0}, {-1, -1}))
  161. feed('u')
  162. eq({}, get_extmarks(ns, {0, 0}, {-1, -1}))
  163. eq({}, get_extmarks(ns2, {0, 0}, {-1, -1}))
  164. feed('<c-r>')
  165. eq({}, get_extmarks(ns, {0, 0}, {-1, -1}))
  166. eq({}, get_extmarks(ns2, {0, 0}, {-1, -1}))
  167. end)
  168. it('querying for information and ranges', function()
  169. --marks = {1, 2, 3}
  170. --positions = {{0, 0,}, {0, 2}, {0, 3}}
  171. -- add some more marks
  172. for i, m in ipairs(marks) do
  173. if positions[i] ~= nil then
  174. local rv = set_extmark(ns, m, positions[i][1], positions[i][2])
  175. eq(m, rv)
  176. end
  177. end
  178. -- {0, 0} and {-1, -1} work as extreme values
  179. eq({{1, 0, 0}}, get_extmarks(ns, {0, 0}, {0, 0}))
  180. eq({}, get_extmarks(ns, {-1, -1}, {-1, -1}))
  181. local rv = get_extmarks(ns, {0, 0}, {-1, -1})
  182. for i, m in ipairs(marks) do
  183. if positions[i] ~= nil then
  184. eq({m, positions[i][1], positions[i][2]}, rv[i])
  185. end
  186. end
  187. -- 0 and -1 works as short hand extreme values
  188. eq({{1, 0, 0}}, get_extmarks(ns, 0, 0))
  189. eq({}, get_extmarks(ns, -1, -1))
  190. rv = get_extmarks(ns, 0, -1)
  191. for i, m in ipairs(marks) do
  192. if positions[i] ~= nil then
  193. eq({m, positions[i][1], positions[i][2]}, rv[i])
  194. end
  195. end
  196. -- next with mark id
  197. rv = get_extmarks(ns, marks[1], {-1, -1}, {limit=1})
  198. eq({{marks[1], positions[1][1], positions[1][2]}}, rv)
  199. rv = get_extmarks(ns, marks[2], {-1, -1}, {limit=1})
  200. eq({{marks[2], positions[2][1], positions[2][2]}}, rv)
  201. -- next with positional when mark exists at position
  202. rv = get_extmarks(ns, positions[1], {-1, -1}, {limit=1})
  203. eq({{marks[1], positions[1][1], positions[1][2]}}, rv)
  204. -- next with positional index (no mark at position)
  205. rv = get_extmarks(ns, {positions[1][1], positions[1][2] +1}, {-1, -1}, {limit=1})
  206. eq({{marks[2], positions[2][1], positions[2][2]}}, rv)
  207. -- next with Extremity index
  208. rv = get_extmarks(ns, {0,0}, {-1, -1}, {limit=1})
  209. eq({{marks[1], positions[1][1], positions[1][2]}}, rv)
  210. -- nextrange with mark id
  211. rv = get_extmarks(ns, marks[1], marks[3])
  212. eq({marks[1], positions[1][1], positions[1][2]}, rv[1])
  213. eq({marks[2], positions[2][1], positions[2][2]}, rv[2])
  214. -- nextrange with `limit`
  215. rv = get_extmarks(ns, marks[1], marks[3], {limit=2})
  216. eq(2, #rv)
  217. -- nextrange with positional when mark exists at position
  218. rv = get_extmarks(ns, positions[1], positions[3])
  219. eq({marks[1], positions[1][1], positions[1][2]}, rv[1])
  220. eq({marks[2], positions[2][1], positions[2][2]}, rv[2])
  221. rv = get_extmarks(ns, positions[2], positions[3])
  222. eq(2, #rv)
  223. -- nextrange with positional index (no mark at position)
  224. local lower = {positions[1][1], positions[2][2] -1}
  225. local upper = {positions[2][1], positions[3][2] - 1}
  226. rv = get_extmarks(ns, lower, upper)
  227. eq({{marks[2], positions[2][1], positions[2][2]}}, rv)
  228. lower = {positions[3][1], positions[3][2] + 1}
  229. upper = {positions[3][1], positions[3][2] + 2}
  230. rv = get_extmarks(ns, lower, upper)
  231. eq({}, rv)
  232. -- nextrange with extremity index
  233. lower = {positions[2][1], positions[2][2]+1}
  234. upper = {-1, -1}
  235. rv = get_extmarks(ns, lower, upper)
  236. eq({{marks[3], positions[3][1], positions[3][2]}}, rv)
  237. -- prev with mark id
  238. rv = get_extmarks(ns, marks[3], {0, 0}, {limit=1})
  239. eq({{marks[3], positions[3][1], positions[3][2]}}, rv)
  240. rv = get_extmarks(ns, marks[2], {0, 0}, {limit=1})
  241. eq({{marks[2], positions[2][1], positions[2][2]}}, rv)
  242. -- prev with positional when mark exists at position
  243. rv = get_extmarks(ns, positions[3], {0, 0}, {limit=1})
  244. eq({{marks[3], positions[3][1], positions[3][2]}}, rv)
  245. -- prev with positional index (no mark at position)
  246. rv = get_extmarks(ns, {positions[1][1], positions[1][2] +1}, {0, 0}, {limit=1})
  247. eq({{marks[1], positions[1][1], positions[1][2]}}, rv)
  248. -- prev with Extremity index
  249. rv = get_extmarks(ns, {-1,-1}, {0,0}, {limit=1})
  250. eq({{marks[3], positions[3][1], positions[3][2]}}, rv)
  251. -- prevrange with mark id
  252. rv = get_extmarks(ns, marks[3], marks[1])
  253. eq({marks[3], positions[3][1], positions[3][2]}, rv[1])
  254. eq({marks[2], positions[2][1], positions[2][2]}, rv[2])
  255. eq({marks[1], positions[1][1], positions[1][2]}, rv[3])
  256. -- prevrange with limit
  257. rv = get_extmarks(ns, marks[3], marks[1], {limit=2})
  258. eq(2, #rv)
  259. -- prevrange with positional when mark exists at position
  260. rv = get_extmarks(ns, positions[3], positions[1])
  261. eq({{marks[3], positions[3][1], positions[3][2]},
  262. {marks[2], positions[2][1], positions[2][2]},
  263. {marks[1], positions[1][1], positions[1][2]}}, rv)
  264. rv = get_extmarks(ns, positions[2], positions[1])
  265. eq(2, #rv)
  266. -- prevrange with positional index (no mark at position)
  267. lower = {positions[2][1], positions[2][2] + 1}
  268. upper = {positions[3][1], positions[3][2] + 1}
  269. rv = get_extmarks(ns, upper, lower)
  270. eq({{marks[3], positions[3][1], positions[3][2]}}, rv)
  271. lower = {positions[3][1], positions[3][2] + 1}
  272. upper = {positions[3][1], positions[3][2] + 2}
  273. rv = get_extmarks(ns, upper, lower)
  274. eq({}, rv)
  275. -- prevrange with extremity index
  276. lower = {0,0}
  277. upper = {positions[2][1], positions[2][2] - 1}
  278. rv = get_extmarks(ns, upper, lower)
  279. eq({{marks[1], positions[1][1], positions[1][2]}}, rv)
  280. end)
  281. it('querying for information with limit', function()
  282. -- add some more marks
  283. for i, m in ipairs(marks) do
  284. if positions[i] ~= nil then
  285. local rv = set_extmark(ns, m, positions[i][1], positions[i][2])
  286. eq(m, rv)
  287. end
  288. end
  289. local rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=1})
  290. eq(1, #rv)
  291. rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=2})
  292. eq(2, #rv)
  293. rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=3})
  294. eq(3, #rv)
  295. -- now in reverse
  296. rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=1})
  297. eq(1, #rv)
  298. rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=2})
  299. eq(2, #rv)
  300. rv = get_extmarks(ns, {0, 0}, {-1, -1}, {limit=3})
  301. eq(3, #rv)
  302. end)
  303. it('get_marks works when mark col > upper col', function()
  304. feed('A<cr>12345<esc>')
  305. feed('A<cr>12345<esc>')
  306. set_extmark(ns, 10, 0, 2) -- this shouldn't be found
  307. set_extmark(ns, 11, 2, 1) -- this shouldn't be found
  308. set_extmark(ns, marks[1], 0, 4) -- check col > our upper bound
  309. set_extmark(ns, marks[2], 1, 1) -- check col < lower bound
  310. set_extmark(ns, marks[3], 2, 0) -- check is inclusive
  311. eq({{marks[1], 0, 4},
  312. {marks[2], 1, 1},
  313. {marks[3], 2, 0}},
  314. get_extmarks(ns, {0, 3}, {2, 0}))
  315. end)
  316. it('get_marks works in reverse when mark col < lower col', function()
  317. feed('A<cr>12345<esc>')
  318. feed('A<cr>12345<esc>')
  319. set_extmark(ns, 10, 0, 1) -- this shouldn't be found
  320. set_extmark(ns, 11, 2, 4) -- this shouldn't be found
  321. set_extmark(ns, marks[1], 2, 1) -- check col < our lower bound
  322. set_extmark(ns, marks[2], 1, 4) -- check col > upper bound
  323. set_extmark(ns, marks[3], 0, 2) -- check is inclusive
  324. local rv = get_extmarks(ns, {2, 3}, {0, 2})
  325. eq({{marks[1], 2, 1},
  326. {marks[2], 1, 4},
  327. {marks[3], 0, 2}},
  328. rv)
  329. end)
  330. it('get_marks limit=0 returns nothing', function()
  331. set_extmark(ns, marks[1], positions[1][1], positions[1][2])
  332. local rv = get_extmarks(ns, {-1, -1}, {-1, -1}, {limit=0})
  333. eq({}, rv)
  334. end)
  335. it('marks move with line insertations', function()
  336. set_extmark(ns, marks[1], 0, 0)
  337. feed("yyP")
  338. check_undo_redo(ns, marks[1], 0, 0, 1, 0)
  339. end)
  340. it('marks move with multiline insertations', function()
  341. feed("a<cr>22<cr>33<esc>")
  342. set_extmark(ns, marks[1], 1, 1)
  343. feed('ggVGyP')
  344. check_undo_redo(ns, marks[1], 1, 1, 4, 1)
  345. end)
  346. it('marks move with line join', function()
  347. -- do_join in ops.c
  348. feed("a<cr>222<esc>")
  349. set_extmark(ns, marks[1], 1, 0)
  350. feed('ggJ')
  351. check_undo_redo(ns, marks[1], 1, 0, 0, 6)
  352. end)
  353. it('join works when no marks are present', function()
  354. screen = Screen.new(15, 10)
  355. screen:attach()
  356. feed("a<cr>1<esc>")
  357. feed('kJ')
  358. -- This shouldn't seg fault
  359. screen:expect([[
  360. 12345^ 1 |
  361. ~ |
  362. ~ |
  363. ~ |
  364. ~ |
  365. ~ |
  366. ~ |
  367. ~ |
  368. ~ |
  369. |
  370. ]])
  371. end)
  372. it('marks move with multiline join', function()
  373. -- do_join in ops.c
  374. feed("a<cr>222<cr>333<cr>444<esc>")
  375. set_extmark(ns, marks[1], 3, 0)
  376. feed('2GVGJ')
  377. check_undo_redo(ns, marks[1], 3, 0, 1, 8)
  378. end)
  379. it('marks move with line deletes', function()
  380. feed("a<cr>222<cr>333<cr>444<esc>")
  381. set_extmark(ns, marks[1], 2, 1)
  382. feed('ggjdd')
  383. check_undo_redo(ns, marks[1], 2, 1, 1, 1)
  384. end)
  385. it('marks move with multiline deletes', function()
  386. feed("a<cr>222<cr>333<cr>444<esc>")
  387. set_extmark(ns, marks[1], 3, 0)
  388. feed('gg2dd')
  389. check_undo_redo(ns, marks[1], 3, 0, 1, 0)
  390. -- regression test, undoing multiline delete when mark is on row 1
  391. feed('ugg3dd')
  392. check_undo_redo(ns, marks[1], 3, 0, 0, 0)
  393. end)
  394. it('marks move with open line', function()
  395. -- open_line in change.c
  396. -- testing marks below are also moved
  397. feed("yyP")
  398. set_extmark(ns, marks[1], 0, 4)
  399. set_extmark(ns, marks[2], 1, 4)
  400. feed('1G<s-o><esc>')
  401. check_undo_redo(ns, marks[1], 0, 4, 1, 4)
  402. check_undo_redo(ns, marks[2], 1, 4, 2, 4)
  403. feed('2Go<esc>')
  404. check_undo_redo(ns, marks[1], 1, 4, 1, 4)
  405. check_undo_redo(ns, marks[2], 2, 4, 3, 4)
  406. end)
  407. it('marks move with char inserts', function()
  408. -- insertchar in edit.c (the ins_str branch)
  409. screen = Screen.new(15, 10)
  410. screen:attach()
  411. set_extmark(ns, marks[1], 0, 3)
  412. feed('0')
  413. insert('abc')
  414. screen:expect([[
  415. ab^c12345 |
  416. ~ |
  417. ~ |
  418. ~ |
  419. ~ |
  420. ~ |
  421. ~ |
  422. ~ |
  423. ~ |
  424. |
  425. ]])
  426. local rv = get_extmark_by_id(ns, marks[1])
  427. eq({0, 6}, rv)
  428. check_undo_redo(ns, marks[1], 0, 3, 0, 6)
  429. end)
  430. -- gravity right as definted in tk library
  431. it('marks have gravity right', function()
  432. -- insertchar in edit.c (the ins_str branch)
  433. set_extmark(ns, marks[1], 0, 2)
  434. feed('03l')
  435. insert("X")
  436. check_undo_redo(ns, marks[1], 0, 2, 0, 2)
  437. -- check multibyte chars
  438. feed('03l<esc>')
  439. insert("~~")
  440. check_undo_redo(ns, marks[1], 0, 2, 0, 2)
  441. end)
  442. it('we can insert multibyte chars', function()
  443. -- insertchar in edit.c
  444. feed('a<cr>12345<esc>')
  445. set_extmark(ns, marks[1], 1, 2)
  446. -- Insert a fullwidth (two col) tilde, NICE
  447. feed('0i~<esc>')
  448. check_undo_redo(ns, marks[1], 1, 2, 1, 5)
  449. end)
  450. it('marks move with blockwise inserts', function()
  451. -- op_insert in ops.c
  452. feed('a<cr>12345<esc>')
  453. set_extmark(ns, marks[1], 1, 2)
  454. feed('0<c-v>lkI9<esc>')
  455. check_undo_redo(ns, marks[1], 1, 2, 1, 3)
  456. end)
  457. it('marks move with line splits (using enter)', function()
  458. -- open_line in change.c
  459. -- testing marks below are also moved
  460. feed("yyP")
  461. set_extmark(ns, marks[1], 0, 4)
  462. set_extmark(ns, marks[2], 1, 4)
  463. feed('1Gla<cr><esc>')
  464. check_undo_redo(ns, marks[1], 0, 4, 1, 2)
  465. check_undo_redo(ns, marks[2], 1, 4, 2, 4)
  466. end)
  467. it('marks at last line move on insert new line', function()
  468. -- open_line in change.c
  469. set_extmark(ns, marks[1], 0, 4)
  470. feed('0i<cr><esc>')
  471. check_undo_redo(ns, marks[1], 0, 4, 1, 4)
  472. end)
  473. it('yet again marks move with line splits', function()
  474. -- the first test above wasn't catching all errors..
  475. feed("A67890<esc>")
  476. set_extmark(ns, marks[1], 0, 4)
  477. feed("04li<cr><esc>")
  478. check_undo_redo(ns, marks[1], 0, 4, 1, 0)
  479. end)
  480. it('and one last time line splits...', function()
  481. set_extmark(ns, marks[1], 0, 1)
  482. set_extmark(ns, marks[2], 0, 2)
  483. feed("02li<cr><esc>")
  484. check_undo_redo(ns, marks[1], 0, 1, 0, 1)
  485. check_undo_redo(ns, marks[2], 0, 2, 1, 0)
  486. end)
  487. it('multiple marks move with mark splits', function()
  488. set_extmark(ns, marks[1], 0, 1)
  489. set_extmark(ns, marks[2], 0, 3)
  490. feed("0li<cr><esc>")
  491. check_undo_redo(ns, marks[1], 0, 1, 1, 0)
  492. check_undo_redo(ns, marks[2], 0, 3, 1, 2)
  493. end)
  494. it('deleting right before a mark works', function()
  495. -- op_delete in ops.c
  496. set_extmark(ns, marks[1], 0, 2)
  497. feed('0lx')
  498. check_undo_redo(ns, marks[1], 0, 2, 0, 1)
  499. end)
  500. it('deleting right after a mark works', function()
  501. -- op_delete in ops.c
  502. set_extmark(ns, marks[1], 0, 2)
  503. feed('02lx')
  504. check_undo_redo(ns, marks[1], 0, 2, 0, 2)
  505. end)
  506. it('marks move with char deletes', function()
  507. -- op_delete in ops.c
  508. set_extmark(ns, marks[1], 0, 2)
  509. feed('02dl')
  510. check_undo_redo(ns, marks[1], 0, 2, 0, 0)
  511. -- from the other side (nothing should happen)
  512. feed('$x')
  513. check_undo_redo(ns, marks[1], 0, 0, 0, 0)
  514. end)
  515. it('marks move with char deletes over a range', function()
  516. -- op_delete in ops.c
  517. set_extmark(ns, marks[1], 0, 2)
  518. set_extmark(ns, marks[2], 0, 3)
  519. feed('0l3dl<esc>')
  520. check_undo_redo(ns, marks[1], 0, 2, 0, 1)
  521. check_undo_redo(ns, marks[2], 0, 3, 0, 1)
  522. -- delete 1, nothing should happen to our marks
  523. feed('u')
  524. feed('$x')
  525. check_undo_redo(ns, marks[2], 0, 3, 0, 3)
  526. end)
  527. it('deleting marks at end of line works', function()
  528. set_extmark(ns, marks[1], 0, 4)
  529. feed('$x')
  530. check_undo_redo(ns, marks[1], 0, 4, 0, 4)
  531. -- check the copy happened correctly on delete at eol
  532. feed('$x')
  533. check_undo_redo(ns, marks[1], 0, 4, 0, 3)
  534. feed('u')
  535. check_undo_redo(ns, marks[1], 0, 4, 0, 4)
  536. end)
  537. it('marks move with blockwise deletes', function()
  538. -- op_delete in ops.c
  539. feed('a<cr>12345<esc>')
  540. set_extmark(ns, marks[1], 1, 4)
  541. feed('h<c-v>hhkd')
  542. check_undo_redo(ns, marks[1], 1, 4, 1, 1)
  543. end)
  544. it('marks move with blockwise deletes over a range', function()
  545. -- op_delete in ops.c
  546. feed('a<cr>12345<esc>')
  547. set_extmark(ns, marks[1], 0, 1)
  548. set_extmark(ns, marks[2], 0, 3)
  549. set_extmark(ns, marks[3], 1, 2)
  550. feed('0<c-v>k3lx')
  551. check_undo_redo(ns, marks[1], 0, 1, 0, 0)
  552. check_undo_redo(ns, marks[2], 0, 3, 0, 0)
  553. check_undo_redo(ns, marks[3], 1, 2, 1, 0)
  554. -- delete 1, nothing should happen to our marks
  555. feed('u')
  556. feed('$<c-v>jx')
  557. check_undo_redo(ns, marks[2], 0, 3, 0, 3)
  558. check_undo_redo(ns, marks[3], 1, 2, 1, 2)
  559. end)
  560. it('works with char deletes over multilines', function()
  561. feed('a<cr>12345<cr>test-me<esc>')
  562. set_extmark(ns, marks[1], 2, 5)
  563. feed('gg')
  564. feed('dv?-m?<cr>')
  565. check_undo_redo(ns, marks[1], 2, 5, 0, 0)
  566. end)
  567. it('marks outside of deleted range move with visual char deletes', function()
  568. -- op_delete in ops.c
  569. set_extmark(ns, marks[1], 0, 3)
  570. feed('0vx<esc>')
  571. check_undo_redo(ns, marks[1], 0, 3, 0, 2)
  572. feed("u")
  573. feed('0vlx<esc>')
  574. check_undo_redo(ns, marks[1], 0, 3, 0, 1)
  575. feed("u")
  576. feed('0v2lx<esc>')
  577. check_undo_redo(ns, marks[1], 0, 3, 0, 0)
  578. -- from the other side (nothing should happen)
  579. feed('$vx')
  580. check_undo_redo(ns, marks[1], 0, 0, 0, 0)
  581. end)
  582. it('marks outside of deleted range move with char deletes', function()
  583. -- op_delete in ops.c
  584. set_extmark(ns, marks[1], 0, 3)
  585. feed('0x<esc>')
  586. check_undo_redo(ns, marks[1], 0, 3, 0, 2)
  587. feed("u")
  588. feed('02x<esc>')
  589. check_undo_redo(ns, marks[1], 0, 3, 0, 1)
  590. feed("u")
  591. feed('0v3lx<esc>')
  592. check_undo_redo(ns, marks[1], 0, 3, 0, 0)
  593. -- from the other side (nothing should happen)
  594. feed("u")
  595. feed('$vx')
  596. check_undo_redo(ns, marks[1], 0, 3, 0, 3)
  597. end)
  598. it('marks move with P(backward) paste', function()
  599. -- do_put in ops.c
  600. feed('0iabc<esc>')
  601. set_extmark(ns, marks[1], 0, 7)
  602. feed('0veyP')
  603. check_undo_redo(ns, marks[1], 0, 7, 0, 15)
  604. end)
  605. it('marks move with p(forward) paste', function()
  606. -- do_put in ops.c
  607. feed('0iabc<esc>')
  608. set_extmark(ns, marks[1], 0, 7)
  609. feed('0veyp')
  610. check_undo_redo(ns, marks[1], 0, 7, 0, 15)
  611. end)
  612. it('marks move with blockwise P(backward) paste', function()
  613. -- do_put in ops.c
  614. feed('a<cr>12345<esc>')
  615. set_extmark(ns, marks[1], 1, 4)
  616. feed('<c-v>hhkyP<esc>')
  617. check_undo_redo(ns, marks[1], 1, 4, 1, 7)
  618. end)
  619. it('marks move with blockwise p(forward) paste', function()
  620. -- do_put in ops.c
  621. feed('a<cr>12345<esc>')
  622. set_extmark(ns, marks[1], 1, 4)
  623. feed('<c-v>hhkyp<esc>')
  624. check_undo_redo(ns, marks[1], 1, 4, 1, 7)
  625. end)
  626. describe('multiline regions', function()
  627. before_each(function()
  628. feed('dd')
  629. -- Achtung: code has been spiced with some unicode,
  630. -- to make life more interesting.
  631. -- luacheck whines about TABs inside strings for whatever reason.
  632. -- luacheck: push ignore 621
  633. insert([[
  634. static int nlua_rpcrequest(lua_State *lstate)
  635. {
  636. Ïf (!nlua_is_deferred_safe(lstate)) {
  637. // strictly not allowed
  638. Яetörn luaL_error(lstate, e_luv_api_disabled, "rpcrequest");
  639. }
  640. return nlua_rpc(lstate, true);
  641. }]])
  642. -- luacheck: pop
  643. end)
  644. it('delete', function()
  645. local pos1 = {
  646. {2, 4}, {2, 12}, {2, 13}, {2, 14}, {2, 25},
  647. {4, 8}, {4, 10}, {4, 20},
  648. {5, 3}, {6, 10}
  649. }
  650. local ids = batch_set(ns, pos1)
  651. batch_check(ns, ids, pos1)
  652. feed('3Gfiv2+ftd')
  653. batch_check_undo_redo(ns, ids, pos1, {
  654. {2, 4}, {2, 12}, {2, 13}, {2, 13}, {2, 13},
  655. {2, 13}, {2, 15}, {2, 25},
  656. {3, 3}, {4, 10}
  657. })
  658. end)
  659. -- TODO(bfredl): add more tests!
  660. end)
  661. it('replace works', function()
  662. set_extmark(ns, marks[1], 0, 2)
  663. feed('0r2')
  664. check_undo_redo(ns, marks[1], 0, 2, 0, 2)
  665. end)
  666. it('blockwise replace works', function()
  667. feed('a<cr>12345<esc>')
  668. set_extmark(ns, marks[1], 0, 2)
  669. feed('0<c-v>llkr1<esc>')
  670. check_undo_redo(ns, marks[1], 0, 2, 0, 3)
  671. end)
  672. it('shift line', function()
  673. -- shift_line in ops.c
  674. feed(':set shiftwidth=4<cr><esc>')
  675. set_extmark(ns, marks[1], 0, 2)
  676. feed('0>>')
  677. check_undo_redo(ns, marks[1], 0, 2, 0, 6)
  678. expect(' 12345')
  679. feed('>>')
  680. -- this is counter-intuitive. But what happens
  681. -- is that 4 spaces gets extended to one tab (== 8 spaces)
  682. check_undo_redo(ns, marks[1], 0, 6, 0, 3)
  683. expect('\t12345')
  684. feed('<LT><LT>') -- have to escape, same as <<
  685. check_undo_redo(ns, marks[1], 0, 3, 0, 6)
  686. end)
  687. it('blockwise shift', function()
  688. -- shift_block in ops.c
  689. feed(':set shiftwidth=4<cr><esc>')
  690. feed('a<cr>12345<esc>')
  691. set_extmark(ns, marks[1], 1, 2)
  692. feed('0<c-v>k>')
  693. check_undo_redo(ns, marks[1], 1, 2, 1, 6)
  694. feed('<c-v>j>')
  695. expect('\t12345\n\t12345')
  696. check_undo_redo(ns, marks[1], 1, 6, 1, 3)
  697. feed('<c-v>j<LT>')
  698. check_undo_redo(ns, marks[1], 1, 3, 1, 6)
  699. end)
  700. it('tab works with expandtab', function()
  701. -- ins_tab in edit.c
  702. feed(':set expandtab<cr><esc>')
  703. feed(':set shiftwidth=2<cr><esc>')
  704. set_extmark(ns, marks[1], 0, 2)
  705. feed('0i<tab><tab><esc>')
  706. check_undo_redo(ns, marks[1], 0, 2, 0, 6)
  707. end)
  708. it('tabs work', function()
  709. -- ins_tab in edit.c
  710. feed(':set noexpandtab<cr><esc>')
  711. feed(':set shiftwidth=2<cr><esc>')
  712. feed(':set softtabstop=2<cr><esc>')
  713. feed(':set tabstop=8<cr><esc>')
  714. set_extmark(ns, marks[1], 0, 2)
  715. feed('0i<tab><esc>')
  716. check_undo_redo(ns, marks[1], 0, 2, 0, 4)
  717. feed('0iX<tab><esc>')
  718. check_undo_redo(ns, marks[1], 0, 4, 0, 6)
  719. end)
  720. it('marks move when using :move', function()
  721. set_extmark(ns, marks[1], 0, 0)
  722. feed('A<cr>2<esc>:1move 2<cr><esc>')
  723. check_undo_redo(ns, marks[1], 0, 0, 1, 0)
  724. -- test codepath when moving lines up
  725. feed(':2move 0<cr><esc>')
  726. check_undo_redo(ns, marks[1], 1, 0, 0, 0)
  727. end)
  728. it('marks move when using :move part 2', function()
  729. -- make sure we didn't get lucky with the math...
  730. feed('A<cr>2<cr>3<cr>4<cr>5<cr>6<esc>')
  731. set_extmark(ns, marks[1], 1, 0)
  732. feed(':2,3move 5<cr><esc>')
  733. check_undo_redo(ns, marks[1], 1, 0, 3, 0)
  734. -- test codepath when moving lines up
  735. feed(':4,5move 1<cr><esc>')
  736. check_undo_redo(ns, marks[1], 3, 0, 1, 0)
  737. end)
  738. it('undo and redo of set and unset marks', function()
  739. -- Force a new undo head
  740. feed('o<esc>')
  741. set_extmark(ns, marks[1], 0, 1)
  742. feed('o<esc>')
  743. set_extmark(ns, marks[2], 0, -1)
  744. set_extmark(ns, marks[3], 0, -1)
  745. feed("u")
  746. local rv = get_extmarks(ns, {0, 0}, {-1, -1})
  747. eq(3, #rv)
  748. feed("<c-r>")
  749. rv = get_extmarks(ns, {0, 0}, {-1, -1})
  750. eq(3, #rv)
  751. -- Test updates
  752. feed('o<esc>')
  753. set_extmark(ns, marks[1], positions[1][1], positions[1][2])
  754. rv = get_extmarks(ns, marks[1], marks[1], {limit=1})
  755. eq(1, #rv)
  756. feed("u")
  757. feed("<c-r>")
  758. -- old value is NOT kept in history
  759. check_undo_redo(ns, marks[1], positions[1][1], positions[1][2], positions[1][1], positions[1][2])
  760. -- Test unset
  761. feed('o<esc>')
  762. curbufmeths.del_extmark(ns, marks[3])
  763. feed("u")
  764. rv = get_extmarks(ns, {0, 0}, {-1, -1})
  765. -- undo does NOT restore deleted marks
  766. eq(2, #rv)
  767. feed("<c-r>")
  768. rv = get_extmarks(ns, {0, 0}, {-1, -1})
  769. eq(2, #rv)
  770. end)
  771. it('undo and redo of marks deleted during edits', function()
  772. -- test extmark_adjust
  773. feed('A<cr>12345<esc>')
  774. set_extmark(ns, marks[1], 1, 2)
  775. feed('dd')
  776. check_undo_redo(ns, marks[1], 1, 2, 1, 0)
  777. end)
  778. it('namespaces work properly', function()
  779. local rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2])
  780. eq(1, rv)
  781. rv = set_extmark(ns2, marks[1], positions[1][1], positions[1][2])
  782. eq(1, rv)
  783. rv = get_extmarks(ns, {0, 0}, {-1, -1})
  784. eq(1, #rv)
  785. rv = get_extmarks(ns2, {0, 0}, {-1, -1})
  786. eq(1, #rv)
  787. -- Set more marks for testing the ranges
  788. set_extmark(ns, marks[2], positions[2][1], positions[2][2])
  789. set_extmark(ns, marks[3], positions[3][1], positions[3][2])
  790. set_extmark(ns2, marks[2], positions[2][1], positions[2][2])
  791. set_extmark(ns2, marks[3], positions[3][1], positions[3][2])
  792. -- get_next (limit set)
  793. rv = get_extmarks(ns, {0, 0}, positions[2], {limit=1})
  794. eq(1, #rv)
  795. rv = get_extmarks(ns2, {0, 0}, positions[2], {limit=1})
  796. eq(1, #rv)
  797. -- get_prev (limit set)
  798. rv = get_extmarks(ns, positions[1], {0, 0}, {limit=1})
  799. eq(1, #rv)
  800. rv = get_extmarks(ns2, positions[1], {0, 0}, {limit=1})
  801. eq(1, #rv)
  802. -- get_next (no limit)
  803. rv = get_extmarks(ns, positions[1], positions[2])
  804. eq(2, #rv)
  805. rv = get_extmarks(ns2, positions[1], positions[2])
  806. eq(2, #rv)
  807. -- get_prev (no limit)
  808. rv = get_extmarks(ns, positions[2], positions[1])
  809. eq(2, #rv)
  810. rv = get_extmarks(ns2, positions[2], positions[1])
  811. eq(2, #rv)
  812. curbufmeths.del_extmark(ns, marks[1])
  813. rv = get_extmarks(ns, {0, 0}, {-1, -1})
  814. eq(2, #rv)
  815. curbufmeths.del_extmark(ns2, marks[1])
  816. rv = get_extmarks(ns2, {0, 0}, {-1, -1})
  817. eq(2, #rv)
  818. end)
  819. it('mark set can create unique identifiers', function()
  820. -- create mark with id 1
  821. eq(1, set_extmark(ns, 1, positions[1][1], positions[1][2]))
  822. -- ask for unique id, it should be the next one, i e 2
  823. eq(2, set_extmark(ns, 0, positions[1][1], positions[1][2]))
  824. eq(3, set_extmark(ns, 3, positions[2][1], positions[2][2]))
  825. eq(4, set_extmark(ns, 0, positions[1][1], positions[1][2]))
  826. -- mixing manual and allocated id:s are not recommended, but it should
  827. -- do something reasonable
  828. eq(6, set_extmark(ns, 6, positions[2][1], positions[2][2]))
  829. eq(7, set_extmark(ns, 0, positions[1][1], positions[1][2]))
  830. eq(8, set_extmark(ns, 0, positions[1][1], positions[1][2]))
  831. end)
  832. it('auto indenting with enter works', function()
  833. -- op_reindent in ops.c
  834. feed(':set cindent<cr><esc>')
  835. feed(':set autoindent<cr><esc>')
  836. feed(':set shiftwidth=2<cr><esc>')
  837. feed("0iint <esc>A {1M1<esc>b<esc>")
  838. -- Set the mark on the M, should move..
  839. set_extmark(ns, marks[1], 0, 12)
  840. -- Set the mark before the cursor, should stay there
  841. set_extmark(ns, marks[2], 0, 10)
  842. feed("i<cr><esc>")
  843. local rv = get_extmark_by_id(ns, marks[1])
  844. eq({1, 3}, rv)
  845. rv = get_extmark_by_id(ns, marks[2])
  846. eq({0, 10}, rv)
  847. check_undo_redo(ns, marks[1], 0, 12, 1, 3)
  848. end)
  849. it('auto indenting entire line works', function()
  850. feed(':set cindent<cr><esc>')
  851. feed(':set autoindent<cr><esc>')
  852. feed(':set shiftwidth=2<cr><esc>')
  853. -- <c-f> will force an indent of 2
  854. feed("0iint <esc>A {<cr><esc>0i1M1<esc>")
  855. set_extmark(ns, marks[1], 1, 1)
  856. feed("0i<c-f><esc>")
  857. local rv = get_extmark_by_id(ns, marks[1])
  858. eq({1, 3}, rv)
  859. check_undo_redo(ns, marks[1], 1, 1, 1, 3)
  860. -- now check when cursor at eol
  861. feed("uA<c-f><esc>")
  862. rv = get_extmark_by_id(ns, marks[1])
  863. eq({1, 3}, rv)
  864. end)
  865. it('removing auto indenting with <C-D> works', function()
  866. feed(':set cindent<cr><esc>')
  867. feed(':set autoindent<cr><esc>')
  868. feed(':set shiftwidth=2<cr><esc>')
  869. feed("0i<tab><esc>")
  870. set_extmark(ns, marks[1], 0, 3)
  871. feed("bi<c-d><esc>")
  872. local rv = get_extmark_by_id(ns, marks[1])
  873. eq({0, 1}, rv)
  874. check_undo_redo(ns, marks[1], 0, 3, 0, 1)
  875. -- check when cursor at eol
  876. feed("uA<c-d><esc>")
  877. rv = get_extmark_by_id(ns, marks[1])
  878. eq({0, 1}, rv)
  879. end)
  880. it('indenting multiple lines with = works', function()
  881. feed(':set cindent<cr><esc>')
  882. feed(':set autoindent<cr><esc>')
  883. feed(':set shiftwidth=2<cr><esc>')
  884. feed("0iint <esc>A {<cr><bs>1M1<cr><bs>2M2<esc>")
  885. set_extmark(ns, marks[1], 1, 1)
  886. set_extmark(ns, marks[2], 2, 1)
  887. feed('=gg')
  888. check_undo_redo(ns, marks[1], 1, 1, 1, 3)
  889. check_undo_redo(ns, marks[2], 2, 1, 2, 5)
  890. end)
  891. it('substitutes by deleting inside the replace matches', function()
  892. -- do_sub in ex_cmds.c
  893. set_extmark(ns, marks[1], 0, 2)
  894. set_extmark(ns, marks[2], 0, 3)
  895. feed(':s/34/xx<cr>')
  896. check_undo_redo(ns, marks[1], 0, 2, 0, 4)
  897. check_undo_redo(ns, marks[2], 0, 3, 0, 4)
  898. end)
  899. it('substitutes when insert text > deleted', function()
  900. -- do_sub in ex_cmds.c
  901. set_extmark(ns, marks[1], 0, 2)
  902. set_extmark(ns, marks[2], 0, 3)
  903. feed(':s/34/xxx<cr>')
  904. check_undo_redo(ns, marks[1], 0, 2, 0, 5)
  905. check_undo_redo(ns, marks[2], 0, 3, 0, 5)
  906. end)
  907. it('substitutes when marks around eol', function()
  908. -- do_sub in ex_cmds.c
  909. set_extmark(ns, marks[1], 0, 4)
  910. set_extmark(ns, marks[2], 0, 5)
  911. feed(':s/5/xxx<cr>')
  912. check_undo_redo(ns, marks[1], 0, 4, 0, 7)
  913. check_undo_redo(ns, marks[2], 0, 5, 0, 7)
  914. end)
  915. it('substitutes over range insert text > deleted', function()
  916. -- do_sub in ex_cmds.c
  917. feed('A<cr>x34xx<esc>')
  918. feed('A<cr>xxx34<esc>')
  919. set_extmark(ns, marks[1], 0, 2)
  920. set_extmark(ns, marks[2], 1, 1)
  921. set_extmark(ns, marks[3], 2, 4)
  922. feed(':1,3s/34/xxx<cr><esc>')
  923. check_undo_redo(ns, marks[1], 0, 2, 0, 5)
  924. check_undo_redo(ns, marks[2], 1, 1, 1, 4)
  925. check_undo_redo(ns, marks[3], 2, 4, 2, 6)
  926. end)
  927. it('substitutes multiple matches in a line', function()
  928. -- do_sub in ex_cmds.c
  929. feed('ddi3x3x3<esc>')
  930. set_extmark(ns, marks[1], 0, 0)
  931. set_extmark(ns, marks[2], 0, 2)
  932. set_extmark(ns, marks[3], 0, 4)
  933. feed(':s/3/yy/g<cr><esc>')
  934. check_undo_redo(ns, marks[1], 0, 0, 0, 2)
  935. check_undo_redo(ns, marks[2], 0, 2, 0, 5)
  936. check_undo_redo(ns, marks[3], 0, 4, 0, 8)
  937. end)
  938. it('substitutes over multiple lines with newline in pattern', function()
  939. feed('A<cr>67890<cr>xx<esc>')
  940. set_extmark(ns, marks[1], 0, 3)
  941. set_extmark(ns, marks[2], 0, 4)
  942. set_extmark(ns, marks[3], 1, 0)
  943. set_extmark(ns, marks[4], 1, 5)
  944. set_extmark(ns, marks[5], 2, 0)
  945. feed([[:1,2s:5\n:5 <cr>]])
  946. check_undo_redo(ns, marks[1], 0, 3, 0, 3)
  947. check_undo_redo(ns, marks[2], 0, 4, 0, 6)
  948. check_undo_redo(ns, marks[3], 1, 0, 0, 6)
  949. check_undo_redo(ns, marks[4], 1, 5, 0, 11)
  950. check_undo_redo(ns, marks[5], 2, 0, 1, 0)
  951. end)
  952. it('inserting', function()
  953. feed('A<cr>67890<cr>xx<esc>')
  954. set_extmark(ns, marks[1], 0, 3)
  955. set_extmark(ns, marks[2], 0, 4)
  956. set_extmark(ns, marks[3], 1, 0)
  957. set_extmark(ns, marks[4], 1, 5)
  958. set_extmark(ns, marks[5], 2, 0)
  959. set_extmark(ns, marks[6], 1, 2)
  960. feed([[:1,2s:5\n67:X<cr>]])
  961. check_undo_redo(ns, marks[1], 0, 3, 0, 3)
  962. check_undo_redo(ns, marks[2], 0, 4, 0, 5)
  963. check_undo_redo(ns, marks[3], 1, 0, 0, 5)
  964. check_undo_redo(ns, marks[4], 1, 5, 0, 8)
  965. check_undo_redo(ns, marks[5], 2, 0, 1, 0)
  966. check_undo_redo(ns, marks[6], 1, 2, 0, 5)
  967. end)
  968. it('substitutes with multiple newlines in pattern', function()
  969. feed('A<cr>67890<cr>xx<esc>')
  970. set_extmark(ns, marks[1], 0, 4)
  971. set_extmark(ns, marks[2], 0, 5)
  972. set_extmark(ns, marks[3], 1, 0)
  973. set_extmark(ns, marks[4], 1, 5)
  974. set_extmark(ns, marks[5], 2, 0)
  975. feed([[:1,2s:\n.*\n:X<cr>]])
  976. check_undo_redo(ns, marks[1], 0, 4, 0, 4)
  977. check_undo_redo(ns, marks[2], 0, 5, 0, 6)
  978. check_undo_redo(ns, marks[3], 1, 0, 0, 6)
  979. check_undo_redo(ns, marks[4], 1, 5, 0, 6)
  980. check_undo_redo(ns, marks[5], 2, 0, 0, 6)
  981. end)
  982. it('substitutes over multiple lines with replace in substitution', function()
  983. feed('A<cr>67890<cr>xx<esc>')
  984. set_extmark(ns, marks[1], 0, 1)
  985. set_extmark(ns, marks[2], 0, 2)
  986. set_extmark(ns, marks[3], 0, 4)
  987. set_extmark(ns, marks[4], 1, 0)
  988. set_extmark(ns, marks[5], 2, 0)
  989. feed([[:1,2s:3:\r<cr>]])
  990. check_undo_redo(ns, marks[1], 0, 1, 0, 1)
  991. check_undo_redo(ns, marks[2], 0, 2, 1, 0)
  992. check_undo_redo(ns, marks[3], 0, 4, 1, 1)
  993. check_undo_redo(ns, marks[4], 1, 0, 2, 0)
  994. check_undo_redo(ns, marks[5], 2, 0, 3, 0)
  995. feed('u')
  996. feed([[:1,2s:3:\rxx<cr>]])
  997. eq({1, 3}, get_extmark_by_id(ns, marks[3]))
  998. end)
  999. it('substitutes over multiple lines with replace in substitution', function()
  1000. feed('A<cr>x3<cr>xx<esc>')
  1001. set_extmark(ns, marks[1], 1, 0)
  1002. set_extmark(ns, marks[2], 1, 1)
  1003. set_extmark(ns, marks[3], 1, 2)
  1004. feed([[:2,2s:3:\r<cr>]])
  1005. check_undo_redo(ns, marks[1], 1, 0, 1, 0)
  1006. check_undo_redo(ns, marks[2], 1, 1, 2, 0)
  1007. check_undo_redo(ns, marks[3], 1, 2, 2, 0)
  1008. end)
  1009. it('substitutes over multiple lines with replace in substitution', function()
  1010. feed('A<cr>x3<cr>xx<esc>')
  1011. set_extmark(ns, marks[1], 0, 1)
  1012. set_extmark(ns, marks[2], 0, 2)
  1013. set_extmark(ns, marks[3], 0, 4)
  1014. set_extmark(ns, marks[4], 1, 1)
  1015. set_extmark(ns, marks[5], 2, 0)
  1016. feed([[:1,2s:3:\r<cr>]])
  1017. check_undo_redo(ns, marks[1], 0, 1, 0, 1)
  1018. check_undo_redo(ns, marks[2], 0, 2, 1, 0)
  1019. check_undo_redo(ns, marks[3], 0, 4, 1, 1)
  1020. check_undo_redo(ns, marks[4], 1, 1, 3, 0)
  1021. check_undo_redo(ns, marks[5], 2, 0, 4, 0)
  1022. feed('u')
  1023. feed([[:1,2s:3:\rxx<cr>]])
  1024. check_undo_redo(ns, marks[3], 0, 4, 1, 3)
  1025. end)
  1026. it('substitutes with newline in match and sub, delta is 0', function()
  1027. feed('A<cr>67890<cr>xx<esc>')
  1028. set_extmark(ns, marks[1], 0, 3)
  1029. set_extmark(ns, marks[2], 0, 4)
  1030. set_extmark(ns, marks[3], 0, 5)
  1031. set_extmark(ns, marks[4], 1, 0)
  1032. set_extmark(ns, marks[5], 1, 5)
  1033. set_extmark(ns, marks[6], 2, 0)
  1034. feed([[:1,1s:5\n:\r<cr>]])
  1035. check_undo_redo(ns, marks[1], 0, 3, 0, 3)
  1036. check_undo_redo(ns, marks[2], 0, 4, 1, 0)
  1037. check_undo_redo(ns, marks[3], 0, 5, 1, 0)
  1038. check_undo_redo(ns, marks[4], 1, 0, 1, 0)
  1039. check_undo_redo(ns, marks[5], 1, 5, 1, 5)
  1040. check_undo_redo(ns, marks[6], 2, 0, 2, 0)
  1041. end)
  1042. it('substitutes with newline in match and sub, delta > 0', function()
  1043. feed('A<cr>67890<cr>xx<esc>')
  1044. set_extmark(ns, marks[1], 0, 3)
  1045. set_extmark(ns, marks[2], 0, 4)
  1046. set_extmark(ns, marks[3], 0, 5)
  1047. set_extmark(ns, marks[4], 1, 0)
  1048. set_extmark(ns, marks[5], 1, 5)
  1049. set_extmark(ns, marks[6], 2, 0)
  1050. feed([[:1,1s:5\n:\r\r<cr>]])
  1051. check_undo_redo(ns, marks[1], 0, 3, 0, 3)
  1052. check_undo_redo(ns, marks[2], 0, 4, 2, 0)
  1053. check_undo_redo(ns, marks[3], 0, 5, 2, 0)
  1054. check_undo_redo(ns, marks[4], 1, 0, 2, 0)
  1055. check_undo_redo(ns, marks[5], 1, 5, 2, 5)
  1056. check_undo_redo(ns, marks[6], 2, 0, 3, 0)
  1057. end)
  1058. it('substitutes with newline in match and sub, delta < 0', function()
  1059. feed('A<cr>67890<cr>xx<cr>xx<esc>')
  1060. set_extmark(ns, marks[1], 0, 3)
  1061. set_extmark(ns, marks[2], 0, 4)
  1062. set_extmark(ns, marks[3], 0, 5)
  1063. set_extmark(ns, marks[4], 1, 0)
  1064. set_extmark(ns, marks[5], 1, 5)
  1065. set_extmark(ns, marks[6], 2, 1)
  1066. set_extmark(ns, marks[7], 3, 0)
  1067. feed([[:1,2s:5\n.*\n:\r<cr>]])
  1068. check_undo_redo(ns, marks[1], 0, 3, 0, 3)
  1069. check_undo_redo(ns, marks[2], 0, 4, 1, 0)
  1070. check_undo_redo(ns, marks[3], 0, 5, 1, 0)
  1071. check_undo_redo(ns, marks[4], 1, 0, 1, 0)
  1072. check_undo_redo(ns, marks[5], 1, 5, 1, 0)
  1073. check_undo_redo(ns, marks[6], 2, 1, 1, 1)
  1074. check_undo_redo(ns, marks[7], 3, 0, 2, 0)
  1075. end)
  1076. it('substitutes with backrefs, newline inserted into sub', function()
  1077. feed('A<cr>67890<cr>xx<cr>xx<esc>')
  1078. set_extmark(ns, marks[1], 0, 3)
  1079. set_extmark(ns, marks[2], 0, 4)
  1080. set_extmark(ns, marks[3], 0, 5)
  1081. set_extmark(ns, marks[4], 1, 0)
  1082. set_extmark(ns, marks[5], 1, 5)
  1083. set_extmark(ns, marks[6], 2, 0)
  1084. feed([[:1,1s:5\(\n\):\0\1<cr>]])
  1085. check_undo_redo(ns, marks[1], 0, 3, 0, 3)
  1086. check_undo_redo(ns, marks[2], 0, 4, 2, 0)
  1087. check_undo_redo(ns, marks[3], 0, 5, 2, 0)
  1088. check_undo_redo(ns, marks[4], 1, 0, 2, 0)
  1089. check_undo_redo(ns, marks[5], 1, 5, 2, 5)
  1090. check_undo_redo(ns, marks[6], 2, 0, 3, 0)
  1091. end)
  1092. it('substitutes a ^', function()
  1093. set_extmark(ns, marks[1], 0, 0)
  1094. set_extmark(ns, marks[2], 0, 1)
  1095. feed([[:s:^:x<cr>]])
  1096. check_undo_redo(ns, marks[1], 0, 0, 0, 1)
  1097. check_undo_redo(ns, marks[2], 0, 1, 0, 2)
  1098. end)
  1099. it('using <c-a> without increase in order of magnitude', function()
  1100. -- do_addsub in ops.c
  1101. feed('ddiabc998xxx<esc>Tc')
  1102. set_extmark(ns, marks[1], 0, 2)
  1103. set_extmark(ns, marks[2], 0, 3)
  1104. set_extmark(ns, marks[3], 0, 5)
  1105. set_extmark(ns, marks[4], 0, 6)
  1106. set_extmark(ns, marks[5], 0, 7)
  1107. feed('<c-a>')
  1108. check_undo_redo(ns, marks[1], 0, 2, 0, 2)
  1109. check_undo_redo(ns, marks[2], 0, 3, 0, 6)
  1110. check_undo_redo(ns, marks[3], 0, 5, 0, 6)
  1111. check_undo_redo(ns, marks[4], 0, 6, 0, 6)
  1112. check_undo_redo(ns, marks[5], 0, 7, 0, 7)
  1113. end)
  1114. it('using <c-a> when increase in order of magnitude', function()
  1115. -- do_addsub in ops.c
  1116. feed('ddiabc999xxx<esc>Tc')
  1117. set_extmark(ns, marks[1], 0, 2)
  1118. set_extmark(ns, marks[2], 0, 3)
  1119. set_extmark(ns, marks[3], 0, 5)
  1120. set_extmark(ns, marks[4], 0, 6)
  1121. set_extmark(ns, marks[5], 0, 7)
  1122. feed('<c-a>')
  1123. check_undo_redo(ns, marks[1], 0, 2, 0, 2)
  1124. check_undo_redo(ns, marks[2], 0, 3, 0, 7)
  1125. check_undo_redo(ns, marks[3], 0, 5, 0, 7)
  1126. check_undo_redo(ns, marks[4], 0, 6, 0, 7)
  1127. check_undo_redo(ns, marks[5], 0, 7, 0, 8)
  1128. end)
  1129. it('using <c-a> when negative and without decrease in order of magnitude', function()
  1130. feed('ddiabc-999xxx<esc>T-')
  1131. set_extmark(ns, marks[1], 0, 2)
  1132. set_extmark(ns, marks[2], 0, 3)
  1133. set_extmark(ns, marks[3], 0, 6)
  1134. set_extmark(ns, marks[4], 0, 7)
  1135. set_extmark(ns, marks[5], 0, 8)
  1136. feed('<c-a>')
  1137. check_undo_redo(ns, marks[1], 0, 2, 0, 2)
  1138. check_undo_redo(ns, marks[2], 0, 3, 0, 7)
  1139. check_undo_redo(ns, marks[3], 0, 6, 0, 7)
  1140. check_undo_redo(ns, marks[4], 0, 7, 0, 7)
  1141. check_undo_redo(ns, marks[5], 0, 8, 0, 8)
  1142. end)
  1143. it('using <c-a> when negative and decrease in order of magnitude', function()
  1144. feed('ddiabc-1000xxx<esc>T-')
  1145. set_extmark(ns, marks[1], 0, 2)
  1146. set_extmark(ns, marks[2], 0, 3)
  1147. set_extmark(ns, marks[3], 0, 7)
  1148. set_extmark(ns, marks[4], 0, 8)
  1149. set_extmark(ns, marks[5], 0, 9)
  1150. feed('<c-a>')
  1151. check_undo_redo(ns, marks[1], 0, 2, 0, 2)
  1152. check_undo_redo(ns, marks[2], 0, 3, 0, 7)
  1153. check_undo_redo(ns, marks[3], 0, 7, 0, 7)
  1154. check_undo_redo(ns, marks[4], 0, 8, 0, 7)
  1155. check_undo_redo(ns, marks[5], 0, 9, 0, 8)
  1156. end)
  1157. it('using <c-x> without decrease in order of magnitude', function()
  1158. -- do_addsub in ops.c
  1159. feed('ddiabc999xxx<esc>Tc')
  1160. set_extmark(ns, marks[1], 0, 2)
  1161. set_extmark(ns, marks[2], 0, 3)
  1162. set_extmark(ns, marks[3], 0, 5)
  1163. set_extmark(ns, marks[4], 0, 6)
  1164. set_extmark(ns, marks[5], 0, 7)
  1165. feed('<c-x>')
  1166. check_undo_redo(ns, marks[1], 0, 2, 0, 2)
  1167. check_undo_redo(ns, marks[2], 0, 3, 0, 6)
  1168. check_undo_redo(ns, marks[3], 0, 5, 0, 6)
  1169. check_undo_redo(ns, marks[4], 0, 6, 0, 6)
  1170. check_undo_redo(ns, marks[5], 0, 7, 0, 7)
  1171. end)
  1172. it('using <c-x> when decrease in order of magnitude', function()
  1173. -- do_addsub in ops.c
  1174. feed('ddiabc1000xxx<esc>Tc')
  1175. set_extmark(ns, marks[1], 0, 2)
  1176. set_extmark(ns, marks[2], 0, 3)
  1177. set_extmark(ns, marks[3], 0, 6)
  1178. set_extmark(ns, marks[4], 0, 7)
  1179. set_extmark(ns, marks[5], 0, 8)
  1180. feed('<c-x>')
  1181. check_undo_redo(ns, marks[1], 0, 2, 0, 2)
  1182. check_undo_redo(ns, marks[2], 0, 3, 0, 6)
  1183. check_undo_redo(ns, marks[3], 0, 6, 0, 6)
  1184. check_undo_redo(ns, marks[4], 0, 7, 0, 6)
  1185. check_undo_redo(ns, marks[5], 0, 8, 0, 7)
  1186. end)
  1187. it('using <c-x> when negative and without increase in order of magnitude', function()
  1188. feed('ddiabc-998xxx<esc>T-')
  1189. set_extmark(ns, marks[1], 0, 2)
  1190. set_extmark(ns, marks[2], 0, 3)
  1191. set_extmark(ns, marks[3], 0, 6)
  1192. set_extmark(ns, marks[4], 0, 7)
  1193. set_extmark(ns, marks[5], 0, 8)
  1194. feed('<c-x>')
  1195. check_undo_redo(ns, marks[1], 0, 2, 0, 2)
  1196. check_undo_redo(ns, marks[2], 0, 3, 0, 7)
  1197. check_undo_redo(ns, marks[3], 0, 6, 0, 7)
  1198. check_undo_redo(ns, marks[4], 0, 7, 0, 7)
  1199. check_undo_redo(ns, marks[5], 0, 8, 0, 8)
  1200. end)
  1201. it('using <c-x> when negative and increase in order of magnitude', function()
  1202. feed('ddiabc-999xxx<esc>T-')
  1203. set_extmark(ns, marks[1], 0, 2)
  1204. set_extmark(ns, marks[2], 0, 3)
  1205. set_extmark(ns, marks[3], 0, 6)
  1206. set_extmark(ns, marks[4], 0, 7)
  1207. set_extmark(ns, marks[5], 0, 8)
  1208. feed('<c-x>')
  1209. check_undo_redo(ns, marks[1], 0, 2, 0, 2)
  1210. check_undo_redo(ns, marks[2], 0, 3, 0, 8)
  1211. check_undo_redo(ns, marks[3], 0, 6, 0, 8)
  1212. check_undo_redo(ns, marks[4], 0, 7, 0, 8)
  1213. check_undo_redo(ns, marks[5], 0, 8, 0, 9)
  1214. end)
  1215. it('throws consistent error codes', function()
  1216. local ns_invalid = ns2 + 1
  1217. eq("Invalid ns_id", pcall_err(set_extmark, ns_invalid, marks[1], positions[1][1], positions[1][2]))
  1218. eq("Invalid ns_id", pcall_err(curbufmeths.del_extmark, ns_invalid, marks[1]))
  1219. eq("Invalid ns_id", pcall_err(get_extmarks, ns_invalid, positions[1], positions[2]))
  1220. eq("Invalid ns_id", pcall_err(get_extmark_by_id, ns_invalid, marks[1]))
  1221. end)
  1222. it('when col = line-length, set the mark on eol', function()
  1223. set_extmark(ns, marks[1], 0, -1)
  1224. local rv = get_extmark_by_id(ns, marks[1])
  1225. eq({0, init_text:len()}, rv)
  1226. -- Test another
  1227. set_extmark(ns, marks[1], 0, -1)
  1228. rv = get_extmark_by_id(ns, marks[1])
  1229. eq({0, init_text:len()}, rv)
  1230. end)
  1231. it('when col = line-length, set the mark on eol', function()
  1232. local invalid_col = init_text:len() + 1
  1233. eq("col value outside range", pcall_err(set_extmark, ns, marks[1], 0, invalid_col))
  1234. end)
  1235. it('fails when line > line_count', function()
  1236. local invalid_col = init_text:len() + 1
  1237. local invalid_lnum = 3
  1238. eq('line value outside range', pcall_err(set_extmark, ns, marks[1], invalid_lnum, invalid_col))
  1239. eq({}, get_extmark_by_id(ns, marks[1]))
  1240. end)
  1241. it('bug from check_col in extmark_set', function()
  1242. -- This bug was caused by extmark_set always using check_col. check_col
  1243. -- always uses the current buffer. This wasn't working during undo so we
  1244. -- now use check_col and check_lnum only when they are required.
  1245. feed('A<cr>67890<cr>xx<esc>')
  1246. feed('A<cr>12345<cr>67890<cr>xx<esc>')
  1247. set_extmark(ns, marks[1], 3, 4)
  1248. feed([[:1,5s:5\n:5 <cr>]])
  1249. check_undo_redo(ns, marks[1], 3, 4, 2, 6)
  1250. end)
  1251. it('in read-only buffer', function()
  1252. command("view! runtime/doc/help.txt")
  1253. eq(true, curbufmeths.get_option('ro'))
  1254. local id = set_extmark(ns, 0, 0, 2)
  1255. eq({{id, 0, 2}}, get_extmarks(ns,0, -1))
  1256. end)
  1257. it('can set a mark to other buffer', function()
  1258. local buf = request('nvim_create_buf', 0, 1)
  1259. request('nvim_buf_set_lines', buf, 0, -1, 1, {"", ""})
  1260. local id = bufmeths.set_extmark(buf, ns, 1, 0, {})
  1261. eq({{id, 1, 0}}, bufmeths.get_extmarks(buf, ns, 0, -1, {}))
  1262. end)
  1263. it('does not crash with append/delete/undo sequence', function()
  1264. meths.exec([[
  1265. let ns = nvim_create_namespace('myplugin')
  1266. call nvim_buf_set_extmark(0, ns, 0, 0, {})
  1267. call append(0, '')
  1268. %delete
  1269. undo]],false)
  1270. assert_alive()
  1271. end)
  1272. it('works with left and right gravity', function()
  1273. -- right gravity should move with inserted text, while
  1274. -- left gravity should stay in place.
  1275. curbufmeths.set_extmark(ns, 0, 5, {right_gravity = false})
  1276. curbufmeths.set_extmark(ns, 0, 5, {right_gravity = true})
  1277. feed([[Aasdfasdf]])
  1278. eq({ {1, 0, 5}, {2, 0, 13} },
  1279. curbufmeths.get_extmarks(ns, 0, -1, {}))
  1280. -- but both move when text is inserted before
  1281. feed([[<esc>Iasdf<esc>]])
  1282. -- eq({}, curbufmeths.get_lines(0, -1, true))
  1283. eq({ {1, 0, 9}, {2, 0, 17} },
  1284. curbufmeths.get_extmarks(ns, 0, -1, {}))
  1285. -- clear text
  1286. curbufmeths.set_text(0, 0, 0, 17, {})
  1287. -- handles set_text correctly as well
  1288. eq({ {1, 0, 0}, {2, 0, 0} },
  1289. meths.buf_get_extmarks(0, ns, 0, -1, {}))
  1290. curbufmeths.set_text(0, 0, 0, 0, {'asdfasdf'})
  1291. eq({ {1, 0, 0}, {2, 0, 8} },
  1292. curbufmeths.get_extmarks(ns, 0, -1, {}))
  1293. feed('u')
  1294. -- handles pasting
  1295. meths.exec([[let @a='asdfasdf']], false)
  1296. feed([["ap]])
  1297. eq({ {1, 0, 0}, {2, 0, 8} },
  1298. meths.buf_get_extmarks(0, ns, 0, -1, {}))
  1299. end)
  1300. it('can accept "end_row" or "end_line" #16548', function()
  1301. set_extmark(ns, marks[1], 0, 0, {
  1302. end_col = 0,
  1303. end_line = 1
  1304. })
  1305. eq({ {1, 0, 0, {
  1306. end_col = 0,
  1307. end_row = 1,
  1308. right_gravity = true,
  1309. end_right_gravity = false,
  1310. }} }, get_extmarks(ns, 0, -1, {details=true}))
  1311. end)
  1312. it('can get details', function()
  1313. set_extmark(ns, marks[1], 0, 0, {
  1314. end_col = 0,
  1315. end_row = 1,
  1316. right_gravity = false,
  1317. end_right_gravity = true,
  1318. priority = 0,
  1319. hl_eol = true,
  1320. hl_mode = "blend",
  1321. hl_group = "String",
  1322. virt_text = { { "text", "Statement" } },
  1323. virt_text_pos = "right_align",
  1324. virt_text_hide = true,
  1325. virt_lines = { { { "lines", "Statement" } }},
  1326. virt_lines_above = true,
  1327. virt_lines_leftcol = true,
  1328. })
  1329. set_extmark(ns, marks[2], 0, 0, {
  1330. priority = 0,
  1331. virt_text = { { "text", "Statement" } },
  1332. virt_text_win_col = 1,
  1333. })
  1334. eq({0, 0, {
  1335. end_col = 0,
  1336. end_row = 1,
  1337. right_gravity = false,
  1338. end_right_gravity = true,
  1339. priority = 0,
  1340. hl_eol = true,
  1341. hl_mode = "blend",
  1342. hl_group = "String",
  1343. virt_text = { { "text", "Statement" } },
  1344. virt_text_pos = "right_align",
  1345. virt_text_hide = true,
  1346. virt_lines = { { { "lines", "Statement" } }},
  1347. virt_lines_above = true,
  1348. virt_lines_leftcol = true,
  1349. } }, get_extmark_by_id(ns, marks[1], { details = true }))
  1350. eq({0, 0, {
  1351. right_gravity = true,
  1352. priority = 0,
  1353. virt_text = { { "text", "Statement" } },
  1354. virt_text_hide = false,
  1355. virt_text_pos = "win_col",
  1356. virt_text_win_col = 1,
  1357. } }, get_extmark_by_id(ns, marks[2], { details = true }))
  1358. end)
  1359. end)
  1360. describe('Extmarks buffer api with many marks', function()
  1361. local ns1
  1362. local ns2
  1363. local ns_marks = {}
  1364. before_each(function()
  1365. clear()
  1366. ns1 = request('nvim_create_namespace', "ns1")
  1367. ns2 = request('nvim_create_namespace', "ns2")
  1368. ns_marks = {[ns1]={}, [ns2]={}}
  1369. local lines = {}
  1370. for i = 1,30 do
  1371. lines[#lines+1] = string.rep("x ",i)
  1372. end
  1373. curbufmeths.set_lines(0, -1, true, lines)
  1374. local ns = ns1
  1375. local q = 0
  1376. for i = 0,29 do
  1377. for j = 0,i do
  1378. local id = set_extmark(ns,0, i,j)
  1379. eq(nil, ns_marks[ns][id])
  1380. ok(id > 0)
  1381. ns_marks[ns][id] = {i,j}
  1382. ns = ns1+ns2-ns
  1383. q = q + 1
  1384. end
  1385. end
  1386. eq(233, #ns_marks[ns1])
  1387. eq(232, #ns_marks[ns2])
  1388. end)
  1389. local function get_marks(ns)
  1390. local mark_list = get_extmarks(ns, 0, -1)
  1391. local marks = {}
  1392. for _, mark in ipairs(mark_list) do
  1393. local id, row, col = unpack(mark)
  1394. eq(nil, marks[id], "duplicate mark")
  1395. marks[id] = {row,col}
  1396. end
  1397. return marks
  1398. end
  1399. it("can get marks", function()
  1400. eq(ns_marks[ns1], get_marks(ns1))
  1401. eq(ns_marks[ns2], get_marks(ns2))
  1402. end)
  1403. it("can clear all marks in ns", function()
  1404. curbufmeths.clear_namespace(ns1, 0, -1)
  1405. eq({}, get_marks(ns1))
  1406. eq(ns_marks[ns2], get_marks(ns2))
  1407. curbufmeths.clear_namespace(ns2, 0, -1)
  1408. eq({}, get_marks(ns1))
  1409. eq({}, get_marks(ns2))
  1410. end)
  1411. it("can clear line range", function()
  1412. curbufmeths.clear_namespace(ns1, 10, 20)
  1413. for id, mark in pairs(ns_marks[ns1]) do
  1414. if 10 <= mark[1] and mark[1] < 20 then
  1415. ns_marks[ns1][id] = nil
  1416. end
  1417. end
  1418. eq(ns_marks[ns1], get_marks(ns1))
  1419. eq(ns_marks[ns2], get_marks(ns2))
  1420. end)
  1421. it("can delete line", function()
  1422. feed('10Gdd')
  1423. for _, marks in pairs(ns_marks) do
  1424. for id, mark in pairs(marks) do
  1425. if mark[1] == 9 then
  1426. marks[id] = {9,0}
  1427. elseif mark[1] >= 10 then
  1428. mark[1] = mark[1] - 1
  1429. end
  1430. end
  1431. end
  1432. eq(ns_marks[ns1], get_marks(ns1))
  1433. eq(ns_marks[ns2], get_marks(ns2))
  1434. end)
  1435. it("can delete lines", function()
  1436. feed('10G10dd')
  1437. for _, marks in pairs(ns_marks) do
  1438. for id, mark in pairs(marks) do
  1439. if 9 <= mark[1] and mark[1] < 19 then
  1440. marks[id] = {9,0}
  1441. elseif mark[1] >= 19 then
  1442. mark[1] = mark[1] - 10
  1443. end
  1444. end
  1445. end
  1446. eq(ns_marks[ns1], get_marks(ns1))
  1447. eq(ns_marks[ns2], get_marks(ns2))
  1448. end)
  1449. it("can wipe buffer", function()
  1450. command('bwipe!')
  1451. eq({}, get_marks(ns1))
  1452. eq({}, get_marks(ns2))
  1453. end)
  1454. end)
  1455. describe('API/win_extmark', function()
  1456. local screen
  1457. local marks, line1, line2
  1458. local ns
  1459. before_each(function()
  1460. -- Initialize some namespaces and insert text into a buffer
  1461. marks = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}
  1462. line1 = "non ui-watched line"
  1463. line2 = "ui-watched line"
  1464. clear()
  1465. insert(line1)
  1466. feed("o<esc>")
  1467. insert(line2)
  1468. ns = request('nvim_create_namespace', "extmark-ui")
  1469. end)
  1470. it('sends and only sends ui-watched marks to ui', function()
  1471. screen = Screen.new(20, 4)
  1472. screen:attach()
  1473. -- should send this
  1474. set_extmark(ns, marks[1], 1, 0, { ui_watched = true })
  1475. -- should not send this
  1476. set_extmark(ns, marks[2], 0, 0, { ui_watched = false })
  1477. screen:expect({
  1478. grid = [[
  1479. non ui-watched line |
  1480. ui-watched lin^e |
  1481. ~ |
  1482. |
  1483. ]],
  1484. extmarks = {
  1485. [2] = {
  1486. -- positioned at the end of the 2nd line
  1487. { {id = 1000}, 1, 1, 1, 16 },
  1488. }
  1489. },
  1490. })
  1491. end)
  1492. it('sends multiple ui-watched marks to ui', function()
  1493. screen = Screen.new(20, 4)
  1494. screen:attach()
  1495. -- should send all of these
  1496. set_extmark(ns, marks[1], 1, 0, { ui_watched = true, virt_text_pos = "overlay" })
  1497. set_extmark(ns, marks[2], 1, 2, { ui_watched = true, virt_text_pos = "overlay" })
  1498. set_extmark(ns, marks[3], 1, 4, { ui_watched = true, virt_text_pos = "overlay" })
  1499. set_extmark(ns, marks[4], 1, 6, { ui_watched = true, virt_text_pos = "overlay" })
  1500. set_extmark(ns, marks[5], 1, 8, { ui_watched = true })
  1501. screen:expect({
  1502. grid = [[
  1503. non ui-watched line |
  1504. ui-watched lin^e |
  1505. ~ |
  1506. |
  1507. ]],
  1508. extmarks = {
  1509. [2] = {
  1510. -- earlier notifications
  1511. { {id = 1000}, 1, 1, 1, 0 },
  1512. { {id = 1000}, 1, 1, 1, 0 }, { {id = 1000}, 1, 2, 1, 2 },
  1513. { {id = 1000}, 1, 1, 1, 0 }, { {id = 1000}, 1, 2, 1, 2 }, { {id = 1000}, 1, 3, 1, 4 },
  1514. { {id = 1000}, 1, 1, 1, 0 }, { {id = 1000}, 1, 2, 1, 2 }, { {id = 1000}, 1, 3, 1, 4 }, { {id = 1000}, 1, 4, 1, 6 },
  1515. -- final
  1516. -- overlay
  1517. { {id = 1000}, 1, 1, 1, 0 },
  1518. { {id = 1000}, 1, 2, 1, 2 },
  1519. { {id = 1000}, 1, 3, 1, 4 },
  1520. { {id = 1000}, 1, 4, 1, 6 },
  1521. -- eol
  1522. { {id = 1000}, 1, 5, 1, 16 },
  1523. }
  1524. },
  1525. })
  1526. end)
  1527. it('updates ui-watched marks', function()
  1528. screen = Screen.new(20, 4)
  1529. screen:attach()
  1530. -- should send this
  1531. set_extmark(ns, marks[1], 1, 0, { ui_watched = true })
  1532. -- should not send this
  1533. set_extmark(ns, marks[2], 0, 0, { ui_watched = false })
  1534. -- make some changes
  1535. insert(" update")
  1536. screen:expect({
  1537. grid = [[
  1538. non ui-watched line |
  1539. ui-watched linupdat^e|
  1540. e |
  1541. |
  1542. ]],
  1543. extmarks = {
  1544. [2] = {
  1545. -- positioned at the end of the 2nd line
  1546. { {id = 1000}, 1, 1, 1, 16 },
  1547. -- updated and wrapped to 3rd line
  1548. { {id = 1000}, 1, 1, 2, 2 },
  1549. }
  1550. }
  1551. })
  1552. feed("<c-e>")
  1553. screen:expect({
  1554. grid = [[
  1555. ui-watched linupdat^e|
  1556. e |
  1557. ~ |
  1558. |
  1559. ]],
  1560. extmarks = {
  1561. [2] = {
  1562. -- positioned at the end of the 2nd line
  1563. { {id = 1000}, 1, 1, 1, 16 },
  1564. -- updated and wrapped to 3rd line
  1565. { {id = 1000}, 1, 1, 2, 2 },
  1566. -- scrolled up one line, should be handled by grid scroll
  1567. }
  1568. }
  1569. })
  1570. end)
  1571. it('sends ui-watched to splits', function()
  1572. screen = Screen.new(20, 8)
  1573. screen:attach({ext_multigrid=true})
  1574. -- should send this
  1575. set_extmark(ns, marks[1], 1, 0, { ui_watched = true })
  1576. -- should not send this
  1577. set_extmark(ns, marks[2], 0, 0, { ui_watched = false })
  1578. command('split')
  1579. screen:expect({
  1580. grid = [[
  1581. ## grid 1
  1582. [4:--------------------]|
  1583. [4:--------------------]|
  1584. [4:--------------------]|
  1585. [No Name] [+] |
  1586. [2:--------------------]|
  1587. [2:--------------------]|
  1588. [No Name] [+] |
  1589. [3:--------------------]|
  1590. ## grid 2
  1591. non ui-watched line |
  1592. ui-watched line |
  1593. ## grid 3
  1594. |
  1595. ## grid 4
  1596. non ui-watched line |
  1597. ui-watched lin^e |
  1598. ~ |
  1599. ]],
  1600. extmarks = {
  1601. [2] = {
  1602. -- positioned at the end of the 2nd line
  1603. { {id = 1000}, 1, 1, 1, 16 },
  1604. -- updated after split
  1605. { {id = 1000}, 1, 1, 1, 16 },
  1606. },
  1607. [4] = {
  1608. -- only after split
  1609. { {id = 1001}, 1, 1, 1, 16 },
  1610. }
  1611. }
  1612. })
  1613. -- make some changes
  1614. insert(" update")
  1615. screen:expect({
  1616. grid = [[
  1617. ## grid 1
  1618. [4:--------------------]|
  1619. [4:--------------------]|
  1620. [4:--------------------]|
  1621. [No Name] [+] |
  1622. [2:--------------------]|
  1623. [2:--------------------]|
  1624. [No Name] [+] |
  1625. [3:--------------------]|
  1626. ## grid 2
  1627. non ui-watched line |
  1628. ui-watched linupd@@@|
  1629. ## grid 3
  1630. |
  1631. ## grid 4
  1632. non ui-watched line |
  1633. ui-watched linupdat^e|
  1634. e |
  1635. ]],
  1636. extmarks = {
  1637. [2] = {
  1638. -- positioned at the end of the 2nd line
  1639. { {id = 1000}, 1, 1, 1, 16 },
  1640. -- updated after split
  1641. { {id = 1000}, 1, 1, 1, 16 },
  1642. },
  1643. [4] = {
  1644. { {id = 1001}, 1, 1, 1, 16 },
  1645. -- updated
  1646. { {id = 1001}, 1, 1, 2, 2 },
  1647. }
  1648. }
  1649. })
  1650. end)
  1651. end)