marktree_spec.lua 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. local helpers = require("test.unit.helpers")(after_each)
  2. local itp = helpers.gen_itp(it)
  3. local ffi = helpers.ffi
  4. local eq = helpers.eq
  5. local ok = helpers.ok
  6. local lib = helpers.cimport("./src/nvim/marktree.h")
  7. local function tablelength(t)
  8. local count = 0
  9. for _ in pairs(t) do count = count + 1 end
  10. return count
  11. end
  12. local function pos_leq(a, b)
  13. return a[1] < b[1] or (a[1] == b[1] and a[2] <= b[2])
  14. end
  15. -- Checks that shadow and tree is consistent, and optionally
  16. -- return the order
  17. local function shadoworder(tree, shadow, iter, giveorder)
  18. ok(iter ~= nil)
  19. local status = lib.marktree_itr_first(tree, iter)
  20. local count = 0
  21. local pos2id, id2pos = {}, {}
  22. local last
  23. if not status and next(shadow) == nil then
  24. return pos2id, id2pos
  25. end
  26. repeat
  27. local mark = lib.marktree_itr_current(iter)
  28. local id = tonumber(mark.id)
  29. local spos = shadow[id]
  30. if (mark.row ~= spos[1] or mark.col ~= spos[2]) then
  31. error("invalid pos for "..id..":("..mark.row..", "..mark.col..") instead of ("..spos[1]..", "..spos[2]..")")
  32. end
  33. if mark.right_gravity ~= spos[3] then
  34. error("invalid gravity for "..id..":("..mark.row..", "..mark.col..")")
  35. end
  36. if count > 0 then
  37. if not pos_leq(last, spos) then
  38. error("DISORDER")
  39. end
  40. end
  41. count = count + 1
  42. last = spos
  43. if giveorder then
  44. pos2id[count] = id
  45. id2pos[id] = count
  46. end
  47. until not lib.marktree_itr_next(tree, iter)
  48. local shadowlen = tablelength(shadow)
  49. if shadowlen ~= count then
  50. error("missed some keys? (shadow "..shadowlen..", tree "..count..")")
  51. end
  52. return id2pos, pos2id
  53. end
  54. local function shadowsplice(shadow, start, old_extent, new_extent)
  55. local old_end = {start[1] + old_extent[1],
  56. (old_extent[1] == 0 and start[2] or 0) + old_extent[2]}
  57. local new_end = {start[1] + new_extent[1],
  58. (new_extent[1] == 0 and start[2] or 0) + new_extent[2]}
  59. local delta = {new_end[1] - old_end[1], new_end[2] - old_end[2]}
  60. for _, pos in pairs(shadow) do
  61. if pos_leq(start, pos) then
  62. if pos_leq(pos, old_end) then
  63. -- delete region
  64. if pos[3] then -- right gravity
  65. pos[1], pos[2] = new_end[1], new_end[2]
  66. else
  67. pos[1], pos[2] = start[1], start[2]
  68. end
  69. else
  70. if pos[1] == old_end[1] then
  71. pos[2] = pos[2] + delta[2]
  72. end
  73. pos[1] = pos[1] + delta[1]
  74. end
  75. end
  76. end
  77. end
  78. local function dosplice(tree, shadow, start, old_extent, new_extent)
  79. lib.marktree_splice(tree, start[1], start[2], old_extent[1], old_extent[2], new_extent[1], new_extent[2])
  80. shadowsplice(shadow, start, old_extent, new_extent)
  81. end
  82. describe('marktree', function()
  83. itp('works', function()
  84. local tree = ffi.new("MarkTree[1]") -- zero initialized by luajit
  85. local shadow = {}
  86. local iter = ffi.new("MarkTreeIter[1]")
  87. local iter2 = ffi.new("MarkTreeIter[1]")
  88. for i = 1,100 do
  89. for j = 1,100 do
  90. local gravitate = (i%2) > 0
  91. local id = tonumber(lib.marktree_put(tree, j, i, gravitate, 0))
  92. ok(id > 0)
  93. eq(nil, shadow[id])
  94. shadow[id] = {j,i,gravitate}
  95. end
  96. -- checking every insert is too slow, but this is ok
  97. lib.marktree_check(tree)
  98. end
  99. -- ss = lib.mt_inspect_rec(tree)
  100. -- io.stdout:write(ffi.string(ss))
  101. -- io.stdout:flush()
  102. local id2pos, pos2id = shadoworder(tree, shadow, iter)
  103. eq({}, pos2id) -- not set if not requested
  104. eq({}, id2pos)
  105. for i,ipos in pairs(shadow) do
  106. local pos = lib.marktree_lookup(tree, i, iter)
  107. eq(ipos[1], pos.row)
  108. eq(ipos[2], pos.col)
  109. local k = lib.marktree_itr_current(iter)
  110. eq(ipos[1], k.row)
  111. eq(ipos[2], k.col, ipos[1])
  112. lib.marktree_itr_next(tree, iter)
  113. -- TODO(bfredl): use id2pos to check neighbour?
  114. -- local k2 = lib.marktree_itr_current(iter)
  115. end
  116. for i,ipos in pairs(shadow) do
  117. lib.marktree_itr_get(tree, ipos[1], ipos[2], iter)
  118. local k = lib.marktree_itr_current(iter)
  119. eq(i, tonumber(k.id))
  120. eq(ipos[1], k.row)
  121. eq(ipos[2], k.col)
  122. end
  123. ok(lib.marktree_itr_first(tree, iter))
  124. local del = lib.marktree_itr_current(iter)
  125. lib.marktree_del_itr(tree, iter, false)
  126. shadow[tonumber(del.id)] = nil
  127. shadoworder(tree, shadow, iter)
  128. for _, ci in ipairs({0,-1,1,-2,2,-10,10}) do
  129. for i = 1,100 do
  130. lib.marktree_itr_get(tree, i, 50+ci, iter)
  131. local k = lib.marktree_itr_current(iter)
  132. local id = tonumber(k.id)
  133. eq(shadow[id][1], k.row)
  134. eq(shadow[id][2], k.col)
  135. lib.marktree_del_itr(tree, iter, false)
  136. shadow[id] = nil
  137. end
  138. lib.marktree_check(tree)
  139. shadoworder(tree, shadow, iter)
  140. end
  141. -- NB: this is quite rudimentary. We rely on
  142. -- functional tests exercising splicing quite a bit
  143. lib.marktree_check(tree)
  144. dosplice(tree, shadow, {2,2}, {0,5}, {1, 2})
  145. lib.marktree_check(tree)
  146. shadoworder(tree, shadow, iter)
  147. dosplice(tree, shadow, {30,2}, {30,5}, {1, 2})
  148. lib.marktree_check(tree)
  149. shadoworder(tree, shadow, iter)
  150. dosplice(tree, shadow, {5,3}, {0,2}, {0, 5})
  151. shadoworder(tree, shadow, iter)
  152. lib.marktree_check(tree)
  153. -- build then burn (HOORAY! HOORAY!)
  154. while next(shadow) do
  155. lib.marktree_itr_first(tree, iter)
  156. -- delete every other key for fun and profit
  157. while true do
  158. local k = lib.marktree_itr_current(iter)
  159. lib.marktree_del_itr(tree, iter, false)
  160. ok(shadow[tonumber(k.id)] ~= nil)
  161. shadow[tonumber(k.id)] = nil
  162. local stat = lib.marktree_itr_next(tree, iter)
  163. if not stat then
  164. break
  165. end
  166. end
  167. lib.marktree_check(tree)
  168. shadoworder(tree, shadow, iter2)
  169. end
  170. -- Check iterator validity for 2 specific edge cases:
  171. -- https://github.com/neovim/neovim/pull/14719
  172. lib.marktree_clear(tree)
  173. for i = 1,20 do
  174. lib.marktree_put(tree, i, i, false, 0)
  175. end
  176. lib.marktree_itr_get(tree, 10, 10, iter)
  177. lib.marktree_del_itr(tree, iter, false)
  178. eq(11, iter[0].node.key[iter[0].i].pos.col)
  179. lib.marktree_itr_get(tree, 11, 11, iter)
  180. lib.marktree_del_itr(tree, iter, false)
  181. eq(12, iter[0].node.key[iter[0].i].pos.col)
  182. end)
  183. end)