extmark_spec.lua (73786B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 local Screen = require('test.functional.ui.screen') 4 5 local request = n.request 6 local eq = t.eq 7 local ok = t.ok 8 local pcall_err = t.pcall_err 9 local insert = n.insert 10 local feed = n.feed 11 local clear = n.clear 12 local command = n.command 13 local exec = n.exec 14 local exec_lua = n.exec_lua 15 local api = n.api 16 local fn = n.fn 17 local assert_alive = n.assert_alive 18 19 local function expect(contents) 20 return eq(contents, n.curbuf_contents()) 21 end 22 23 local function set_extmark(ns_id, id, line, col, opts) 24 if opts == nil then 25 opts = {} 26 end 27 if id ~= nil and id ~= 0 then 28 opts.id = id 29 end 30 return api.nvim_buf_set_extmark(0, ns_id, line, col, opts) 31 end 32 33 local function get_extmarks(ns_id, start, end_, opts) 34 if opts == nil then 35 opts = {} 36 end 37 return api.nvim_buf_get_extmarks(0, ns_id, start, end_, opts) 38 end 39 40 local function get_extmark_by_id(ns_id, id, opts) 41 if opts == nil then 42 opts = {} 43 end 44 return api.nvim_buf_get_extmark_by_id(0, ns_id, id, opts) 45 end 46 47 local function check_undo_redo(ns, mark, sr, sc, er, ec) --s = start, e = end 48 local rv = get_extmark_by_id(ns, mark) 49 eq({ er, ec }, rv) 50 feed('u') 51 rv = get_extmark_by_id(ns, mark) 52 eq({ sr, sc }, rv) 53 feed('<c-r>') 54 rv = get_extmark_by_id(ns, mark) 55 eq({ er, ec }, rv) 56 end 57 58 local function batch_set(ns_id, positions) 59 local ids = {} 60 for _, pos in ipairs(positions) do 61 table.insert(ids, set_extmark(ns_id, 0, pos[1], pos[2])) 62 end 63 return ids 64 end 65 66 local function batch_check(ns_id, ids, positions) 67 local actual, expected = {}, {} 68 for i, id in ipairs(ids) do 69 expected[id] = positions[i] 70 end 71 for _, mark in pairs(get_extmarks(ns_id, 0, -1, {})) do 72 actual[mark[1]] = { mark[2], mark[3] } 73 end 74 eq(expected, actual) 75 end 76 77 local function batch_check_undo_redo(ns_id, ids, before, after) 78 batch_check(ns_id, ids, after) 79 feed('u') 80 batch_check(ns_id, ids, before) 81 feed('<c-r>') 82 batch_check(ns_id, ids, after) 83 end 84 85 describe('API/extmarks', function() 86 local screen 87 local marks, positions, init_text, row, col 88 local ns, ns2 ---@type integer, integer 89 90 before_each(function() 91 -- Initialize some namespaces and insert 12345 into a buffer 92 marks = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 } 93 positions = { { 0, 0 }, { 0, 2 }, { 0, 3 } } 94 95 init_text = '12345' 96 row = 0 97 col = 2 98 99 clear() 100 101 insert(init_text) 102 ns = request('nvim_create_namespace', 'my-fancy-plugin') 103 ns2 = request('nvim_create_namespace', 'my-fancy-plugin2') 104 end) 105 106 it('validation', function() 107 eq( 108 "Invalid 'end_col': expected Integer, got Array", 109 pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = {}, end_row = 1 }) 110 ) 111 eq( 112 "Invalid 'end_row': expected Integer, got Array", 113 pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = {} }) 114 ) 115 eq( 116 "Invalid 'virt_text_pos': expected String, got Integer", 117 pcall_err(set_extmark, ns, marks[2], 0, 0, { virt_text_pos = 0 }) 118 ) 119 eq( 120 "Invalid 'virt_text_pos': 'foo'", 121 pcall_err(set_extmark, ns, marks[2], 0, 0, { virt_text_pos = 'foo' }) 122 ) 123 eq( 124 "Invalid 'hl_mode': expected String, got Integer", 125 pcall_err(set_extmark, ns, marks[2], 0, 0, { hl_mode = 0 }) 126 ) 127 eq("Invalid 'hl_mode': 'foo'", pcall_err(set_extmark, ns, marks[2], 0, 0, { hl_mode = 'foo' })) 128 eq( 129 "Invalid 'virt_lines_overflow': 'foo'", 130 pcall_err(set_extmark, ns, marks[2], 0, 0, { virt_lines_overflow = 'foo' }) 131 ) 132 eq( 133 "Invalid 'id': expected Integer, got Array", 134 pcall_err(set_extmark, ns, {}, 0, 0, { end_col = 1, end_row = 1 }) 135 ) 136 eq( 137 'Invalid mark position: expected 2 Integer items', 138 pcall_err(get_extmarks, ns, {}, { -1, -1 }) 139 ) 140 eq( 141 'Invalid mark position: expected mark id Integer or 2-item Array', 142 pcall_err(get_extmarks, ns, true, { -1, -1 }) 143 ) 144 -- No memory leak with virt_text, virt_lines, sign_text 145 eq( 146 'right_gravity is not a boolean', 147 pcall_err(set_extmark, ns, marks[2], 0, 0, { 148 virt_text = { { 'foo', 'Normal' } }, 149 virt_lines = { { { 'bar', 'Normal' } } }, 150 sign_text = 'a', 151 right_gravity = 'baz', 152 }) 153 ) 154 end) 155 156 it('can end extranges past final newline using end_col = 0', function() 157 set_extmark(ns, marks[1], 0, 0, { 158 end_col = 0, 159 end_row = 1, 160 }) 161 eq( 162 "Invalid 'end_col': out of range", 163 pcall_err(set_extmark, ns, marks[2], 0, 0, { end_col = 1, end_row = 1 }) 164 ) 165 end) 166 167 it('can end extranges past final newline when strict mode is false', function() 168 set_extmark(ns, marks[1], 0, 0, { 169 end_col = 1, 170 end_row = 1, 171 strict = false, 172 }) 173 end) 174 175 it('can end extranges past final column when strict mode is false', function() 176 set_extmark(ns, marks[1], 0, 0, { 177 end_col = 6, 178 end_row = 0, 179 strict = false, 180 }) 181 end) 182 183 it('adds, updates and deletes marks', function() 184 local rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2]) 185 eq(marks[1], rv) 186 rv = get_extmark_by_id(ns, marks[1]) 187 eq({ positions[1][1], positions[1][2] }, rv) 188 -- Test adding a second mark on same row works 189 rv = set_extmark(ns, marks[2], positions[2][1], positions[2][2]) 190 eq(marks[2], rv) 191 192 -- Test an update, (same pos) 193 rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2]) 194 eq(marks[1], rv) 195 rv = get_extmark_by_id(ns, marks[2]) 196 eq({ positions[2][1], positions[2][2] }, rv) 197 -- Test an update, (new pos) 198 row = positions[1][1] 199 col = positions[1][2] + 1 200 rv = set_extmark(ns, marks[1], row, col) 201 eq(marks[1], rv) 202 rv = get_extmark_by_id(ns, marks[1]) 203 eq({ row, col }, rv) 204 205 -- remove the test marks 206 eq(true, api.nvim_buf_del_extmark(0, ns, marks[1])) 207 eq(false, api.nvim_buf_del_extmark(0, ns, marks[1])) 208 eq(true, api.nvim_buf_del_extmark(0, ns, marks[2])) 209 eq(false, api.nvim_buf_del_extmark(0, ns, marks[3])) 210 eq(false, api.nvim_buf_del_extmark(0, ns, 1000)) 211 end) 212 213 it('can clear a specific namespace range', function() 214 set_extmark(ns, 1, 0, 1) 215 set_extmark(ns2, 1, 0, 1) 216 -- force a new undo buffer 217 feed('o<esc>') 218 api.nvim_buf_clear_namespace(0, ns2, 0, -1) 219 eq({ { 1, 0, 1 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 })) 220 eq({}, get_extmarks(ns2, { 0, 0 }, { -1, -1 })) 221 feed('u') 222 eq({ { 1, 0, 1 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 })) 223 eq({}, get_extmarks(ns2, { 0, 0 }, { -1, -1 })) 224 feed('<c-r>') 225 eq({ { 1, 0, 1 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 })) 226 eq({}, get_extmarks(ns2, { 0, 0 }, { -1, -1 })) 227 end) 228 229 it('can clear a namespace range using 0,-1', function() 230 set_extmark(ns, 1, 0, 1) 231 set_extmark(ns2, 1, 0, 1) 232 -- force a new undo buffer 233 feed('o<esc>') 234 api.nvim_buf_clear_namespace(0, -1, 0, -1) 235 eq({}, get_extmarks(ns, { 0, 0 }, { -1, -1 })) 236 eq({}, get_extmarks(ns2, { 0, 0 }, { -1, -1 })) 237 feed('u') 238 eq({}, get_extmarks(ns, { 0, 0 }, { -1, -1 })) 239 eq({}, get_extmarks(ns2, { 0, 0 }, { -1, -1 })) 240 feed('<c-r>') 241 eq({}, get_extmarks(ns, { 0, 0 }, { -1, -1 })) 242 eq({}, get_extmarks(ns2, { 0, 0 }, { -1, -1 })) 243 end) 244 245 it('can undo with extmarks (#25147)', function() 246 feed('itest<esc>') 247 set_extmark(ns, 1, 0, 0) 248 set_extmark(ns, 2, 1, 0) 249 eq({ { 1, 0, 0 }, { 2, 1, 0 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 })) 250 feed('dd') 251 eq({ { 1, 1, 0 }, { 2, 1, 0 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 })) 252 api.nvim_buf_clear_namespace(0, ns, 0, -1) 253 eq({}, get_extmarks(ns, { 0, 0 }, { -1, -1 })) 254 set_extmark(ns, 1, 0, 0, { right_gravity = false }) 255 set_extmark(ns, 2, 1, 0, { right_gravity = false }) 256 eq({ { 1, 0, 0 }, { 2, 1, 0 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 })) 257 feed('u') 258 eq({ { 1, 0, 0 }, { 2, 0, 0 } }, get_extmarks(ns, { 0, 0 }, { -1, -1 })) 259 api.nvim_buf_clear_namespace(0, ns, 0, -1) 260 end) 261 262 it('querying for information and ranges', function() 263 --marks = {1, 2, 3} 264 --positions = {{0, 0,}, {0, 2}, {0, 3}} 265 -- add some more marks 266 for i, m in ipairs(marks) do 267 if positions[i] ~= nil then 268 local rv = set_extmark(ns, m, positions[i][1], positions[i][2]) 269 eq(m, rv) 270 end 271 end 272 273 -- {0, 0} and {-1, -1} work as extreme values 274 eq({ { 1, 0, 0 } }, get_extmarks(ns, { 0, 0 }, { 0, 0 })) 275 eq({}, get_extmarks(ns, { -1, -1 }, { -1, -1 })) 276 local rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }) 277 for i, m in ipairs(marks) do 278 if positions[i] ~= nil then 279 eq({ m, positions[i][1], positions[i][2] }, rv[i]) 280 end 281 end 282 283 -- 0 and -1 works as short hand extreme values 284 eq({ { 1, 0, 0 } }, get_extmarks(ns, 0, 0)) 285 eq({}, get_extmarks(ns, -1, -1)) 286 rv = get_extmarks(ns, 0, -1) 287 for i, m in ipairs(marks) do 288 if positions[i] ~= nil then 289 eq({ m, positions[i][1], positions[i][2] }, rv[i]) 290 end 291 end 292 293 -- next with mark id 294 rv = get_extmarks(ns, marks[1], { -1, -1 }, { limit = 1 }) 295 eq({ { marks[1], positions[1][1], positions[1][2] } }, rv) 296 rv = get_extmarks(ns, marks[2], { -1, -1 }, { limit = 1 }) 297 eq({ { marks[2], positions[2][1], positions[2][2] } }, rv) 298 -- next with positional when mark exists at position 299 rv = get_extmarks(ns, positions[1], { -1, -1 }, { limit = 1 }) 300 eq({ { marks[1], positions[1][1], positions[1][2] } }, rv) 301 -- next with positional index (no mark at position) 302 rv = get_extmarks(ns, { positions[1][1], positions[1][2] + 1 }, { -1, -1 }, { limit = 1 }) 303 eq({ { marks[2], positions[2][1], positions[2][2] } }, rv) 304 -- next with Extremity index 305 rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }, { limit = 1 }) 306 eq({ { marks[1], positions[1][1], positions[1][2] } }, rv) 307 308 -- nextrange with mark id 309 rv = get_extmarks(ns, marks[1], marks[3]) 310 eq({ marks[1], positions[1][1], positions[1][2] }, rv[1]) 311 eq({ marks[2], positions[2][1], positions[2][2] }, rv[2]) 312 -- nextrange with `limit` 313 rv = get_extmarks(ns, marks[1], marks[3], { limit = 2 }) 314 eq(2, #rv) 315 -- nextrange with positional when mark exists at position 316 rv = get_extmarks(ns, positions[1], positions[3]) 317 eq({ marks[1], positions[1][1], positions[1][2] }, rv[1]) 318 eq({ marks[2], positions[2][1], positions[2][2] }, rv[2]) 319 rv = get_extmarks(ns, positions[2], positions[3]) 320 eq(2, #rv) 321 -- nextrange with positional index (no mark at position) 322 local lower = { positions[1][1], positions[2][2] - 1 } 323 local upper = { positions[2][1], positions[3][2] - 1 } 324 rv = get_extmarks(ns, lower, upper) 325 eq({ { marks[2], positions[2][1], positions[2][2] } }, rv) 326 lower = { positions[3][1], positions[3][2] + 1 } 327 upper = { positions[3][1], positions[3][2] + 2 } 328 rv = get_extmarks(ns, lower, upper) 329 eq({}, rv) 330 -- nextrange with extremity index 331 lower = { positions[2][1], positions[2][2] + 1 } 332 upper = { -1, -1 } 333 rv = get_extmarks(ns, lower, upper) 334 eq({ { marks[3], positions[3][1], positions[3][2] } }, rv) 335 336 -- reverse with mark id 337 rv = get_extmarks(ns, marks[3], { 0, 0 }, { limit = 1 }) 338 eq({ { marks[3], positions[3][1], positions[3][2] } }, rv) 339 rv = get_extmarks(ns, marks[2], { 0, 0 }, { limit = 1 }) 340 eq({ { marks[2], positions[2][1], positions[2][2] } }, rv) 341 -- reverse with positional when mark exists at position 342 rv = get_extmarks(ns, positions[3], { 0, 0 }, { limit = 1 }) 343 eq({ { marks[3], positions[3][1], positions[3][2] } }, rv) 344 -- reverse with positional index (no mark at position) 345 rv = get_extmarks(ns, { positions[1][1], positions[1][2] + 1 }, { 0, 0 }, { limit = 1 }) 346 eq({ { marks[1], positions[1][1], positions[1][2] } }, rv) 347 -- reverse with Extremity index 348 rv = get_extmarks(ns, { -1, -1 }, { 0, 0 }, { limit = 1 }) 349 eq({ { marks[3], positions[3][1], positions[3][2] } }, rv) 350 351 -- reverse with mark id 352 rv = get_extmarks(ns, marks[3], marks[1]) 353 eq({ marks[3], positions[3][1], positions[3][2] }, rv[1]) 354 eq({ marks[2], positions[2][1], positions[2][2] }, rv[2]) 355 eq({ marks[1], positions[1][1], positions[1][2] }, rv[3]) 356 -- reverse with limit 357 rv = get_extmarks(ns, marks[3], marks[1], { limit = 2 }) 358 eq(2, #rv) 359 -- reverse with positional when mark exists at position 360 rv = get_extmarks(ns, positions[3], positions[1]) 361 eq({ 362 { marks[3], positions[3][1], positions[3][2] }, 363 { marks[2], positions[2][1], positions[2][2] }, 364 { marks[1], positions[1][1], positions[1][2] }, 365 }, rv) 366 rv = get_extmarks(ns, positions[2], positions[1]) 367 eq(2, #rv) 368 -- reverse with positional index (no mark at position) 369 lower = { positions[2][1], positions[2][2] + 1 } 370 upper = { positions[3][1], positions[3][2] + 1 } 371 rv = get_extmarks(ns, upper, lower) 372 eq({ { marks[3], positions[3][1], positions[3][2] } }, rv) 373 lower = { positions[3][1], positions[3][2] + 1 } 374 upper = { positions[3][1], positions[3][2] + 2 } 375 rv = get_extmarks(ns, upper, lower) 376 eq({}, rv) 377 -- reverse with extremity index 378 lower = { 0, 0 } 379 upper = { positions[2][1], positions[2][2] - 1 } 380 rv = get_extmarks(ns, upper, lower) 381 eq({ { marks[1], positions[1][1], positions[1][2] } }, rv) 382 end) 383 384 it('querying for information with limit', function() 385 -- add some more marks 386 for i, m in ipairs(marks) do 387 if positions[i] ~= nil then 388 local rv = set_extmark(ns, m, positions[i][1], positions[i][2]) 389 eq(m, rv) 390 end 391 end 392 393 local rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }, { limit = 1 }) 394 eq(1, #rv) 395 rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }, { limit = 2 }) 396 eq(2, #rv) 397 rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }, { limit = 3 }) 398 eq(3, #rv) 399 400 -- now in reverse 401 rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }, { limit = 1 }) 402 eq(1, #rv) 403 rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }, { limit = 2 }) 404 eq(2, #rv) 405 rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }, { limit = 3 }) 406 eq(3, #rv) 407 end) 408 409 it('get_marks works when mark col > upper col', function() 410 feed('A<cr>12345<esc>') 411 feed('A<cr>12345<esc>') 412 set_extmark(ns, 10, 0, 2) -- this shouldn't be found 413 set_extmark(ns, 11, 2, 1) -- this shouldn't be found 414 set_extmark(ns, marks[1], 0, 4) -- check col > our upper bound 415 set_extmark(ns, marks[2], 1, 1) -- check col < lower bound 416 set_extmark(ns, marks[3], 2, 0) -- check is inclusive 417 eq( 418 { { marks[1], 0, 4 }, { marks[2], 1, 1 }, { marks[3], 2, 0 } }, 419 get_extmarks(ns, { 0, 3 }, { 2, 0 }) 420 ) 421 end) 422 423 it('get_marks works in reverse when mark col < lower col', function() 424 feed('A<cr>12345<esc>') 425 feed('A<cr>12345<esc>') 426 set_extmark(ns, 10, 0, 1) -- this shouldn't be found 427 set_extmark(ns, 11, 2, 4) -- this shouldn't be found 428 set_extmark(ns, marks[1], 2, 1) -- check col < our lower bound 429 set_extmark(ns, marks[2], 1, 4) -- check col > upper bound 430 set_extmark(ns, marks[3], 0, 2) -- check is inclusive 431 local rv = get_extmarks(ns, { 2, 3 }, { 0, 2 }) 432 eq({ { marks[1], 2, 1 }, { marks[2], 1, 4 }, { marks[3], 0, 2 } }, rv) 433 -- doesn't include paired marks whose start pos > lower bound twice 434 -- and returns mark overlapping start pos but not end pos 435 local m1 = set_extmark(ns, nil, 0, 0, { end_row = 1, end_col = 4 }) 436 local m2 = set_extmark(ns, nil, 0, 0, { end_row = 1, end_col = 2 }) 437 local m3 = set_extmark(ns, nil, 1, 0, { end_row = 1, end_col = 4 }) 438 local m4 = set_extmark(ns, nil, 1, 2, { end_row = 1, end_col = 4 }) 439 rv = get_extmarks(ns, { 1, 3 }, { 1, 2 }, { overlap = true }) 440 eq({ { m4, 1, 2 }, { m3, 1, 0 }, { m2, 0, 0 }, { m1, 0, 0 } }, rv) 441 end) 442 443 it('get_marks limit=0 returns nothing', function() 444 set_extmark(ns, marks[1], positions[1][1], positions[1][2]) 445 local rv = get_extmarks(ns, { -1, -1 }, { -1, -1 }, { limit = 0 }) 446 eq({}, rv) 447 end) 448 449 it('marks move with line insertations', function() 450 set_extmark(ns, marks[1], 0, 0) 451 feed('yyP') 452 check_undo_redo(ns, marks[1], 0, 0, 1, 0) 453 end) 454 455 it('marks move with multiline insertations', function() 456 feed('a<cr>22<cr>33<esc>') 457 set_extmark(ns, marks[1], 1, 1) 458 feed('ggVGyP') 459 check_undo_redo(ns, marks[1], 1, 1, 4, 1) 460 end) 461 462 it('marks move with line join', function() 463 -- do_join in ops.c 464 feed('a<cr>222<esc>') 465 set_extmark(ns, marks[1], 1, 0) 466 feed('ggJ') 467 check_undo_redo(ns, marks[1], 1, 0, 0, 6) 468 end) 469 470 it('join works when no marks are present', function() 471 screen = Screen.new(15, 10) 472 feed('a<cr>1<esc>') 473 feed('kJ') 474 -- This shouldn't seg fault 475 screen:expect([[ 476 12345^ 1 | 477 {1:~ }|*8 478 | 479 ]]) 480 end) 481 482 it('marks move with multiline join', function() 483 -- do_join in ops.c 484 feed('a<cr>222<cr>333<cr>444<esc>') 485 set_extmark(ns, marks[1], 3, 0) 486 feed('2GVGJ') 487 check_undo_redo(ns, marks[1], 3, 0, 1, 8) 488 end) 489 490 it('marks move with line deletes', function() 491 feed('a<cr>222<cr>333<cr>444<esc>') 492 set_extmark(ns, marks[1], 2, 1) 493 feed('ggjdd') 494 check_undo_redo(ns, marks[1], 2, 1, 1, 1) 495 end) 496 497 it('marks move with multiline deletes', function() 498 feed('a<cr>222<cr>333<cr>444<esc>') 499 set_extmark(ns, marks[1], 3, 0) 500 feed('gg2dd') 501 check_undo_redo(ns, marks[1], 3, 0, 1, 0) 502 -- regression test, undoing multiline delete when mark is on row 1 503 feed('ugg3dd') 504 check_undo_redo(ns, marks[1], 3, 0, 0, 0) 505 end) 506 507 it('marks move with open line', function() 508 -- open_line in change.c 509 -- testing marks below are also moved 510 feed('yyP') 511 set_extmark(ns, marks[1], 0, 4) 512 set_extmark(ns, marks[2], 1, 4) 513 feed('1G<s-o><esc>') 514 check_undo_redo(ns, marks[1], 0, 4, 1, 4) 515 check_undo_redo(ns, marks[2], 1, 4, 2, 4) 516 feed('2Go<esc>') 517 check_undo_redo(ns, marks[1], 1, 4, 1, 4) 518 check_undo_redo(ns, marks[2], 2, 4, 3, 4) 519 end) 520 521 it('marks move with char inserts', function() 522 -- insertchar in edit.c (the ins_str branch) 523 screen = Screen.new(15, 10) 524 set_extmark(ns, marks[1], 0, 3) 525 feed('0') 526 insert('abc') 527 screen:expect([[ 528 ab^c12345 | 529 {1:~ }|*8 530 | 531 ]]) 532 local rv = get_extmark_by_id(ns, marks[1]) 533 eq({ 0, 6 }, rv) 534 check_undo_redo(ns, marks[1], 0, 3, 0, 6) 535 end) 536 537 -- gravity right as definted in tk library 538 it('marks have gravity right', function() 539 -- insertchar in edit.c (the ins_str branch) 540 set_extmark(ns, marks[1], 0, 2) 541 feed('03l') 542 insert('X') 543 check_undo_redo(ns, marks[1], 0, 2, 0, 2) 544 545 -- check multibyte chars 546 feed('03l<esc>') 547 insert('~~') 548 check_undo_redo(ns, marks[1], 0, 2, 0, 2) 549 end) 550 551 it('we can insert multibyte chars', function() 552 -- insertchar in edit.c 553 feed('a<cr>12345<esc>') 554 set_extmark(ns, marks[1], 1, 2) 555 -- Insert a fullwidth (two col) tilde, NICE 556 feed('0i~<esc>') 557 check_undo_redo(ns, marks[1], 1, 2, 1, 5) 558 end) 559 560 it('marks move with blockwise inserts', function() 561 -- op_insert in ops.c 562 feed('a<cr>12345<esc>') 563 set_extmark(ns, marks[1], 1, 2) 564 feed('0<c-v>lkI9<esc>') 565 check_undo_redo(ns, marks[1], 1, 2, 1, 3) 566 end) 567 568 it('marks move with line splits (using enter)', function() 569 -- open_line in change.c 570 -- testing marks below are also moved 571 feed('yyP') 572 set_extmark(ns, marks[1], 0, 4) 573 set_extmark(ns, marks[2], 1, 4) 574 feed('1Gla<cr><esc>') 575 check_undo_redo(ns, marks[1], 0, 4, 1, 2) 576 check_undo_redo(ns, marks[2], 1, 4, 2, 4) 577 end) 578 579 it('marks at last line move on insert new line', function() 580 -- open_line in change.c 581 set_extmark(ns, marks[1], 0, 4) 582 feed('0i<cr><esc>') 583 check_undo_redo(ns, marks[1], 0, 4, 1, 4) 584 end) 585 586 it('yet again marks move with line splits', function() 587 -- the first test above wasn't catching all errors.. 588 feed('A67890<esc>') 589 set_extmark(ns, marks[1], 0, 4) 590 feed('04li<cr><esc>') 591 check_undo_redo(ns, marks[1], 0, 4, 1, 0) 592 end) 593 594 it('and one last time line splits...', function() 595 set_extmark(ns, marks[1], 0, 1) 596 set_extmark(ns, marks[2], 0, 2) 597 feed('02li<cr><esc>') 598 check_undo_redo(ns, marks[1], 0, 1, 0, 1) 599 check_undo_redo(ns, marks[2], 0, 2, 1, 0) 600 end) 601 602 it('multiple marks move with mark splits', function() 603 set_extmark(ns, marks[1], 0, 1) 604 set_extmark(ns, marks[2], 0, 3) 605 feed('0li<cr><esc>') 606 check_undo_redo(ns, marks[1], 0, 1, 1, 0) 607 check_undo_redo(ns, marks[2], 0, 3, 1, 2) 608 end) 609 610 it('deleting right before a mark works', function() 611 -- op_delete in ops.c 612 set_extmark(ns, marks[1], 0, 2) 613 feed('0lx') 614 check_undo_redo(ns, marks[1], 0, 2, 0, 1) 615 end) 616 617 it('deleting right after a mark works', function() 618 -- op_delete in ops.c 619 set_extmark(ns, marks[1], 0, 2) 620 feed('02lx') 621 check_undo_redo(ns, marks[1], 0, 2, 0, 2) 622 end) 623 624 it('marks move with char deletes', function() 625 -- op_delete in ops.c 626 set_extmark(ns, marks[1], 0, 2) 627 feed('02dl') 628 check_undo_redo(ns, marks[1], 0, 2, 0, 0) 629 -- from the other side (nothing should happen) 630 feed('$x') 631 check_undo_redo(ns, marks[1], 0, 0, 0, 0) 632 end) 633 634 it('marks move with char deletes over a range', function() 635 -- op_delete in ops.c 636 set_extmark(ns, marks[1], 0, 2) 637 set_extmark(ns, marks[2], 0, 3) 638 feed('0l3dl<esc>') 639 check_undo_redo(ns, marks[1], 0, 2, 0, 1) 640 check_undo_redo(ns, marks[2], 0, 3, 0, 1) 641 -- delete 1, nothing should happen to our marks 642 feed('u') 643 feed('$x') 644 check_undo_redo(ns, marks[2], 0, 3, 0, 3) 645 end) 646 647 it('deleting marks at end of line works', function() 648 set_extmark(ns, marks[1], 0, 4) 649 feed('$x') 650 check_undo_redo(ns, marks[1], 0, 4, 0, 4) 651 -- check the copy happened correctly on delete at eol 652 feed('$x') 653 check_undo_redo(ns, marks[1], 0, 4, 0, 3) 654 feed('u') 655 check_undo_redo(ns, marks[1], 0, 4, 0, 4) 656 end) 657 658 it('marks move with blockwise deletes', function() 659 -- op_delete in ops.c 660 feed('a<cr>12345<esc>') 661 set_extmark(ns, marks[1], 1, 4) 662 feed('h<c-v>hhkd') 663 check_undo_redo(ns, marks[1], 1, 4, 1, 1) 664 end) 665 666 it('marks move with blockwise deletes over a range', function() 667 -- op_delete in ops.c 668 feed('a<cr>12345<esc>') 669 set_extmark(ns, marks[1], 0, 1) 670 set_extmark(ns, marks[2], 0, 3) 671 set_extmark(ns, marks[3], 1, 2) 672 feed('0<c-v>k3lx') 673 check_undo_redo(ns, marks[1], 0, 1, 0, 0) 674 check_undo_redo(ns, marks[2], 0, 3, 0, 0) 675 check_undo_redo(ns, marks[3], 1, 2, 1, 0) 676 -- delete 1, nothing should happen to our marks 677 feed('u') 678 feed('$<c-v>jx') 679 check_undo_redo(ns, marks[2], 0, 3, 0, 3) 680 check_undo_redo(ns, marks[3], 1, 2, 1, 2) 681 end) 682 683 it('works with char deletes over multilines', function() 684 feed('a<cr>12345<cr>test-me<esc>') 685 set_extmark(ns, marks[1], 2, 5) 686 feed('gg') 687 feed('dv?-m?<cr>') 688 check_undo_redo(ns, marks[1], 2, 5, 0, 0) 689 end) 690 691 it('marks outside of deleted range move with visual char deletes', function() 692 -- op_delete in ops.c 693 set_extmark(ns, marks[1], 0, 3) 694 feed('0vx<esc>') 695 check_undo_redo(ns, marks[1], 0, 3, 0, 2) 696 697 feed('u') 698 feed('0vlx<esc>') 699 check_undo_redo(ns, marks[1], 0, 3, 0, 1) 700 701 feed('u') 702 feed('0v2lx<esc>') 703 check_undo_redo(ns, marks[1], 0, 3, 0, 0) 704 705 -- from the other side (nothing should happen) 706 feed('$vx') 707 check_undo_redo(ns, marks[1], 0, 0, 0, 0) 708 end) 709 710 it('marks outside of deleted range move with char deletes', function() 711 -- op_delete in ops.c 712 set_extmark(ns, marks[1], 0, 3) 713 feed('0x<esc>') 714 check_undo_redo(ns, marks[1], 0, 3, 0, 2) 715 716 feed('u') 717 feed('02x<esc>') 718 check_undo_redo(ns, marks[1], 0, 3, 0, 1) 719 720 feed('u') 721 feed('0v3lx<esc>') 722 check_undo_redo(ns, marks[1], 0, 3, 0, 0) 723 724 -- from the other side (nothing should happen) 725 feed('u') 726 feed('$vx') 727 check_undo_redo(ns, marks[1], 0, 3, 0, 3) 728 end) 729 730 it('marks move with P(backward) paste', function() 731 -- do_put in ops.c 732 feed('0iabc<esc>') 733 set_extmark(ns, marks[1], 0, 7) 734 feed('0veyP') 735 check_undo_redo(ns, marks[1], 0, 7, 0, 15) 736 end) 737 738 it('marks move with p(forward) paste', function() 739 -- do_put in ops.c 740 feed('0iabc<esc>') 741 set_extmark(ns, marks[1], 0, 7) 742 feed('0veyp') 743 check_undo_redo(ns, marks[1], 0, 7, 0, 15) 744 end) 745 746 it('marks move with blockwise P(backward) paste', function() 747 -- do_put in ops.c 748 feed('a<cr>12345<esc>') 749 set_extmark(ns, marks[1], 1, 4) 750 feed('<c-v>hhkyP<esc>') 751 check_undo_redo(ns, marks[1], 1, 4, 1, 7) 752 end) 753 754 it('marks move with blockwise p(forward) paste', function() 755 -- do_put in ops.c 756 feed('a<cr>12345<esc>') 757 set_extmark(ns, marks[1], 1, 4) 758 feed('<c-v>hhkyp<esc>') 759 check_undo_redo(ns, marks[1], 1, 4, 1, 7) 760 end) 761 762 describe('multiline regions', function() 763 before_each(function() 764 feed('dd') 765 -- Achtung: code has been spiced with some unicode, 766 -- to make life more interesting. 767 -- luacheck whines about TABs inside strings for whatever reason. 768 -- luacheck: push ignore 621 769 insert([[ 770 static int nlua_rpcrequest(lua_State *lstate) 771 { 772 Ïf (!nlua_is_deferred_safe(lstate)) { 773 // strictly not allowed 774 Яetörn luaL_error(lstate, e_fast_api_disabled, "rpcrequest"); 775 } 776 return nlua_rpc(lstate, true); 777 }]]) 778 -- luacheck: pop 779 end) 780 781 it('delete', function() 782 local pos1 = { 783 { 2, 4 }, 784 { 2, 12 }, 785 { 2, 13 }, 786 { 2, 14 }, 787 { 2, 25 }, 788 { 4, 8 }, 789 { 4, 10 }, 790 { 4, 20 }, 791 { 5, 3 }, 792 { 6, 10 }, 793 } 794 local ids = batch_set(ns, pos1) 795 batch_check(ns, ids, pos1) 796 feed('3Gfiv2+ftd') 797 batch_check_undo_redo(ns, ids, pos1, { 798 { 2, 4 }, 799 { 2, 12 }, 800 { 2, 13 }, 801 { 2, 13 }, 802 { 2, 13 }, 803 { 2, 13 }, 804 { 2, 15 }, 805 { 2, 25 }, 806 { 3, 3 }, 807 { 4, 10 }, 808 }) 809 end) 810 811 it('can get overlapping extmarks', function() 812 set_extmark(ns, 1, 0, 0, { end_row = 5, end_col = 0 }) 813 set_extmark(ns, 2, 2, 5, { end_row = 2, end_col = 30 }) 814 set_extmark(ns, 3, 0, 5, { end_row = 2, end_col = 10 }) 815 set_extmark(ns, 4, 0, 0, { end_row = 1, end_col = 0 }) 816 eq({ { 2, 2, 5 } }, get_extmarks(ns, { 2, 0 }, { 2, -1 }, { overlap = false })) 817 eq( 818 { { 1, 0, 0 }, { 3, 0, 5 }, { 2, 2, 5 } }, 819 get_extmarks(ns, { 2, 0 }, { 2, -1 }, { overlap = true }) 820 ) 821 end) 822 823 it('limits overlap results', function() 824 set_extmark(ns, 1, 0, 0, { end_row = 5, end_col = 0 }) 825 set_extmark(ns, 2, 2, 5, { end_row = 2, end_col = 30 }) 826 set_extmark(ns, 3, 0, 5, { end_row = 2, end_col = 10 }) 827 set_extmark(ns, 4, 0, 0, { end_row = 1, end_col = 0 }) 828 local rv = get_extmarks(ns, { 2, 0 }, { 2, -1 }, { overlap = true, limit = 1 }) 829 eq(1, #rv) 830 end) 831 end) 832 833 it('replace works', function() 834 set_extmark(ns, marks[1], 0, 2) 835 feed('0r2') 836 check_undo_redo(ns, marks[1], 0, 2, 0, 2) 837 end) 838 839 it('blockwise replace works', function() 840 feed('a<cr>12345<esc>') 841 set_extmark(ns, marks[1], 0, 2) 842 feed('0<c-v>llkr1<esc>') 843 check_undo_redo(ns, marks[1], 0, 2, 0, 3) 844 end) 845 846 it('shift line', function() 847 -- shift_line in ops.c 848 feed(':set shiftwidth=4<cr><esc>') 849 set_extmark(ns, marks[1], 0, 2) 850 feed('0>>') 851 check_undo_redo(ns, marks[1], 0, 2, 0, 6) 852 expect(' 12345') 853 854 feed('>>') 855 -- this is counter-intuitive. But what happens 856 -- is that 4 spaces gets extended to one tab (== 8 spaces) 857 check_undo_redo(ns, marks[1], 0, 6, 0, 3) 858 expect('\t12345') 859 860 feed('<LT><LT>') -- have to escape, same as << 861 check_undo_redo(ns, marks[1], 0, 3, 0, 6) 862 end) 863 864 it('blockwise shift', function() 865 -- shift_block in ops.c 866 feed(':set shiftwidth=4<cr><esc>') 867 feed('a<cr>12345<esc>') 868 set_extmark(ns, marks[1], 1, 2) 869 feed('0<c-v>k>') 870 check_undo_redo(ns, marks[1], 1, 2, 1, 6) 871 feed('<c-v>j>') 872 expect('\t12345\n\t12345') 873 check_undo_redo(ns, marks[1], 1, 6, 1, 3) 874 875 feed('<c-v>j<LT>') 876 check_undo_redo(ns, marks[1], 1, 3, 1, 6) 877 end) 878 879 it('tab works with expandtab', function() 880 -- ins_tab in edit.c 881 feed(':set expandtab<cr><esc>') 882 feed(':set shiftwidth=2<cr><esc>') 883 set_extmark(ns, marks[1], 0, 2) 884 feed('0i<tab><tab><esc>') 885 check_undo_redo(ns, marks[1], 0, 2, 0, 6) 886 end) 887 888 it('tabs work', function() 889 -- ins_tab in edit.c 890 feed(':set noexpandtab<cr><esc>') 891 feed(':set shiftwidth=2<cr><esc>') 892 feed(':set softtabstop=2<cr><esc>') 893 feed(':set tabstop=8<cr><esc>') 894 set_extmark(ns, marks[1], 0, 2) 895 feed('0i<tab><esc>') 896 check_undo_redo(ns, marks[1], 0, 2, 0, 4) 897 feed('0iX<tab><esc>') 898 check_undo_redo(ns, marks[1], 0, 4, 0, 6) 899 end) 900 901 it('marks move when using :move', function() 902 set_extmark(ns, marks[1], 0, 0) 903 feed('A<cr>2<esc>:1move 2<cr><esc>') 904 check_undo_redo(ns, marks[1], 0, 0, 1, 0) 905 -- test codepath when moving lines up 906 feed(':2move 0<cr><esc>') 907 check_undo_redo(ns, marks[1], 1, 0, 0, 0) 908 end) 909 910 it('marks move when using :move part 2', function() 911 -- make sure we didn't get lucky with the math... 912 feed('A<cr>2<cr>3<cr>4<cr>5<cr>6<esc>') 913 set_extmark(ns, marks[1], 1, 0) 914 feed(':2,3move 5<cr><esc>') 915 check_undo_redo(ns, marks[1], 1, 0, 3, 0) 916 -- test codepath when moving lines up 917 feed(':4,5move 1<cr><esc>') 918 check_undo_redo(ns, marks[1], 3, 0, 1, 0) 919 end) 920 921 it('undo and redo of set and unset marks', function() 922 -- Force a new undo head 923 feed('o<esc>') 924 set_extmark(ns, marks[1], 0, 1) 925 feed('o<esc>') 926 set_extmark(ns, marks[2], 0, -1) 927 set_extmark(ns, marks[3], 0, -1) 928 929 feed('u') 930 local rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }) 931 eq(3, #rv) 932 933 feed('<c-r>') 934 rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }) 935 eq(3, #rv) 936 937 -- Test updates 938 feed('o<esc>') 939 set_extmark(ns, marks[1], positions[1][1], positions[1][2]) 940 rv = get_extmarks(ns, marks[1], marks[1], { limit = 1 }) 941 eq(1, #rv) 942 feed('u') 943 feed('<c-r>') 944 -- old value is NOT kept in history 945 check_undo_redo( 946 ns, 947 marks[1], 948 positions[1][1], 949 positions[1][2], 950 positions[1][1], 951 positions[1][2] 952 ) 953 954 -- Test unset 955 feed('o<esc>') 956 api.nvim_buf_del_extmark(0, ns, marks[3]) 957 feed('u') 958 rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }) 959 -- undo does NOT restore deleted marks 960 eq(2, #rv) 961 feed('<c-r>') 962 rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }) 963 eq(2, #rv) 964 end) 965 966 it('undo and redo of marks deleted during edits', function() 967 -- test extmark_adjust 968 feed('A<cr>12345<esc>') 969 set_extmark(ns, marks[1], 1, 2) 970 feed('dd') 971 check_undo_redo(ns, marks[1], 1, 2, 1, 0) 972 end) 973 974 it('namespaces work properly', function() 975 local rv = set_extmark(ns, marks[1], positions[1][1], positions[1][2]) 976 eq(1, rv) 977 rv = set_extmark(ns2, marks[1], positions[1][1], positions[1][2]) 978 eq(1, rv) 979 rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }) 980 eq(1, #rv) 981 rv = get_extmarks(ns2, { 0, 0 }, { -1, -1 }) 982 eq(1, #rv) 983 984 -- Set more marks for testing the ranges 985 set_extmark(ns, marks[2], positions[2][1], positions[2][2]) 986 set_extmark(ns, marks[3], positions[3][1], positions[3][2]) 987 set_extmark(ns2, marks[2], positions[2][1], positions[2][2]) 988 set_extmark(ns2, marks[3], positions[3][1], positions[3][2]) 989 990 -- get_next (limit set) 991 rv = get_extmarks(ns, { 0, 0 }, positions[2], { limit = 1 }) 992 eq(1, #rv) 993 rv = get_extmarks(ns2, { 0, 0 }, positions[2], { limit = 1 }) 994 eq(1, #rv) 995 -- reverse (limit set) 996 rv = get_extmarks(ns, positions[1], { 0, 0 }, { limit = 1 }) 997 eq(1, #rv) 998 rv = get_extmarks(ns2, positions[1], { 0, 0 }, { limit = 1 }) 999 eq(1, #rv) 1000 1001 -- get_next (no limit) 1002 rv = get_extmarks(ns, positions[1], positions[2]) 1003 eq(2, #rv) 1004 rv = get_extmarks(ns2, positions[1], positions[2]) 1005 eq(2, #rv) 1006 -- reverse (no limit) 1007 rv = get_extmarks(ns, positions[2], positions[1]) 1008 eq(2, #rv) 1009 rv = get_extmarks(ns2, positions[2], positions[1]) 1010 eq(2, #rv) 1011 1012 api.nvim_buf_del_extmark(0, ns, marks[1]) 1013 rv = get_extmarks(ns, { 0, 0 }, { -1, -1 }) 1014 eq(2, #rv) 1015 api.nvim_buf_del_extmark(0, ns2, marks[1]) 1016 rv = get_extmarks(ns2, { 0, 0 }, { -1, -1 }) 1017 eq(2, #rv) 1018 end) 1019 1020 it('mark set can create unique identifiers', function() 1021 -- create mark with id 1 1022 eq(1, set_extmark(ns, 1, positions[1][1], positions[1][2])) 1023 -- ask for unique id, it should be the next one, i e 2 1024 eq(2, set_extmark(ns, 0, positions[1][1], positions[1][2])) 1025 eq(3, set_extmark(ns, 3, positions[2][1], positions[2][2])) 1026 eq(4, set_extmark(ns, 0, positions[1][1], positions[1][2])) 1027 1028 -- mixing manual and allocated id:s are not recommended, but it should 1029 -- do something reasonable 1030 eq(6, set_extmark(ns, 6, positions[2][1], positions[2][2])) 1031 eq(7, set_extmark(ns, 0, positions[1][1], positions[1][2])) 1032 eq(8, set_extmark(ns, 0, positions[1][1], positions[1][2])) 1033 end) 1034 1035 it('auto indenting with enter works', function() 1036 -- op_reindent in ops.c 1037 feed(':set cindent<cr><esc>') 1038 feed(':set autoindent<cr><esc>') 1039 feed(':set shiftwidth=2<cr><esc>') 1040 feed('0iint <esc>A {1M1<esc>b<esc>') 1041 -- Set the mark on the M, should move.. 1042 set_extmark(ns, marks[1], 0, 12) 1043 -- Set the mark before the cursor, should stay there 1044 set_extmark(ns, marks[2], 0, 10) 1045 feed('i<cr><esc>') 1046 local rv = get_extmark_by_id(ns, marks[1]) 1047 eq({ 1, 3 }, rv) 1048 rv = get_extmark_by_id(ns, marks[2]) 1049 eq({ 0, 10 }, rv) 1050 check_undo_redo(ns, marks[1], 0, 12, 1, 3) 1051 end) 1052 1053 it('auto indenting entire line works', function() 1054 feed(':set cindent<cr><esc>') 1055 feed(':set autoindent<cr><esc>') 1056 feed(':set shiftwidth=2<cr><esc>') 1057 -- <c-f> will force an indent of 2 1058 feed('0iint <esc>A {<cr><esc>0i1M1<esc>') 1059 set_extmark(ns, marks[1], 1, 1) 1060 feed('0i<c-f><esc>') 1061 local rv = get_extmark_by_id(ns, marks[1]) 1062 eq({ 1, 3 }, rv) 1063 check_undo_redo(ns, marks[1], 1, 1, 1, 3) 1064 -- now check when cursor at eol 1065 feed('uA<c-f><esc>') 1066 rv = get_extmark_by_id(ns, marks[1]) 1067 eq({ 1, 3 }, rv) 1068 end) 1069 1070 it('removing auto indenting with <C-D> works', function() 1071 feed(':set cindent<cr><esc>') 1072 feed(':set autoindent<cr><esc>') 1073 feed(':set shiftwidth=2<cr><esc>') 1074 feed('0i<tab><esc>') 1075 set_extmark(ns, marks[1], 0, 3) 1076 feed('bi<c-d><esc>') 1077 local rv = get_extmark_by_id(ns, marks[1]) 1078 eq({ 0, 1 }, rv) 1079 check_undo_redo(ns, marks[1], 0, 3, 0, 1) 1080 -- check when cursor at eol 1081 feed('uA<c-d><esc>') 1082 rv = get_extmark_by_id(ns, marks[1]) 1083 eq({ 0, 1 }, rv) 1084 end) 1085 1086 it('indenting multiple lines with = works', function() 1087 feed(':set cindent<cr><esc>') 1088 feed(':set autoindent<cr><esc>') 1089 feed(':set shiftwidth=2<cr><esc>') 1090 feed('0iint <esc>A {<cr><bs>1M1<cr><bs>2M2<esc>') 1091 set_extmark(ns, marks[1], 1, 1) 1092 set_extmark(ns, marks[2], 2, 1) 1093 feed('=gg') 1094 check_undo_redo(ns, marks[1], 1, 1, 1, 3) 1095 check_undo_redo(ns, marks[2], 2, 1, 2, 5) 1096 end) 1097 1098 it('substitutes by deleting inside the replace matches', function() 1099 -- do_sub in ex_cmds.c 1100 set_extmark(ns, marks[1], 0, 2) 1101 set_extmark(ns, marks[2], 0, 3) 1102 feed(':s/34/xx<cr>') 1103 check_undo_redo(ns, marks[1], 0, 2, 0, 4) 1104 check_undo_redo(ns, marks[2], 0, 3, 0, 4) 1105 end) 1106 1107 it('substitutes when insert text > deleted', function() 1108 -- do_sub in ex_cmds.c 1109 set_extmark(ns, marks[1], 0, 2) 1110 set_extmark(ns, marks[2], 0, 3) 1111 feed(':s/34/xxx<cr>') 1112 check_undo_redo(ns, marks[1], 0, 2, 0, 5) 1113 check_undo_redo(ns, marks[2], 0, 3, 0, 5) 1114 end) 1115 1116 it('substitutes when marks around eol', function() 1117 -- do_sub in ex_cmds.c 1118 set_extmark(ns, marks[1], 0, 4) 1119 set_extmark(ns, marks[2], 0, 5) 1120 feed(':s/5/xxx<cr>') 1121 check_undo_redo(ns, marks[1], 0, 4, 0, 7) 1122 check_undo_redo(ns, marks[2], 0, 5, 0, 7) 1123 end) 1124 1125 it('substitutes over range insert text > deleted', function() 1126 -- do_sub in ex_cmds.c 1127 feed('A<cr>x34xx<esc>') 1128 feed('A<cr>xxx34<esc>') 1129 set_extmark(ns, marks[1], 0, 2) 1130 set_extmark(ns, marks[2], 1, 1) 1131 set_extmark(ns, marks[3], 2, 4) 1132 feed(':1,3s/34/xxx<cr><esc>') 1133 check_undo_redo(ns, marks[1], 0, 2, 0, 5) 1134 check_undo_redo(ns, marks[2], 1, 1, 1, 4) 1135 check_undo_redo(ns, marks[3], 2, 4, 2, 6) 1136 end) 1137 1138 it('substitutes multiple matches in a line', function() 1139 -- do_sub in ex_cmds.c 1140 feed('ddi3x3x3<esc>') 1141 set_extmark(ns, marks[1], 0, 0) 1142 set_extmark(ns, marks[2], 0, 2) 1143 set_extmark(ns, marks[3], 0, 4) 1144 feed(':s/3/yy/g<cr><esc>') 1145 check_undo_redo(ns, marks[1], 0, 0, 0, 2) 1146 check_undo_redo(ns, marks[2], 0, 2, 0, 5) 1147 check_undo_redo(ns, marks[3], 0, 4, 0, 8) 1148 end) 1149 1150 it('substitutes over multiple lines with newline in pattern', function() 1151 feed('A<cr>67890<cr>xx<esc>') 1152 set_extmark(ns, marks[1], 0, 3) 1153 set_extmark(ns, marks[2], 0, 4) 1154 set_extmark(ns, marks[3], 1, 0) 1155 set_extmark(ns, marks[4], 1, 5) 1156 set_extmark(ns, marks[5], 2, 0) 1157 feed([[:1,2s:5\n:5 <cr>]]) 1158 check_undo_redo(ns, marks[1], 0, 3, 0, 3) 1159 check_undo_redo(ns, marks[2], 0, 4, 0, 6) 1160 check_undo_redo(ns, marks[3], 1, 0, 0, 6) 1161 check_undo_redo(ns, marks[4], 1, 5, 0, 11) 1162 check_undo_redo(ns, marks[5], 2, 0, 1, 0) 1163 end) 1164 1165 it('inserting', function() 1166 feed('A<cr>67890<cr>xx<esc>') 1167 set_extmark(ns, marks[1], 0, 3) 1168 set_extmark(ns, marks[2], 0, 4) 1169 set_extmark(ns, marks[3], 1, 0) 1170 set_extmark(ns, marks[4], 1, 5) 1171 set_extmark(ns, marks[5], 2, 0) 1172 set_extmark(ns, marks[6], 1, 2) 1173 feed([[:1,2s:5\n67:X<cr>]]) 1174 check_undo_redo(ns, marks[1], 0, 3, 0, 3) 1175 check_undo_redo(ns, marks[2], 0, 4, 0, 5) 1176 check_undo_redo(ns, marks[3], 1, 0, 0, 5) 1177 check_undo_redo(ns, marks[4], 1, 5, 0, 8) 1178 check_undo_redo(ns, marks[5], 2, 0, 1, 0) 1179 check_undo_redo(ns, marks[6], 1, 2, 0, 5) 1180 end) 1181 1182 it('substitutes with multiple newlines in pattern', function() 1183 feed('A<cr>67890<cr>xx<esc>') 1184 set_extmark(ns, marks[1], 0, 4) 1185 set_extmark(ns, marks[2], 0, 5) 1186 set_extmark(ns, marks[3], 1, 0) 1187 set_extmark(ns, marks[4], 1, 5) 1188 set_extmark(ns, marks[5], 2, 0) 1189 feed([[:1,2s:\n.*\n:X<cr>]]) 1190 check_undo_redo(ns, marks[1], 0, 4, 0, 4) 1191 check_undo_redo(ns, marks[2], 0, 5, 0, 6) 1192 check_undo_redo(ns, marks[3], 1, 0, 0, 6) 1193 check_undo_redo(ns, marks[4], 1, 5, 0, 6) 1194 check_undo_redo(ns, marks[5], 2, 0, 0, 6) 1195 end) 1196 1197 it('substitutes over multiple lines with replace in substitution', function() 1198 feed('A<cr>67890<cr>xx<esc>') 1199 set_extmark(ns, marks[1], 0, 1) 1200 set_extmark(ns, marks[2], 0, 2) 1201 set_extmark(ns, marks[3], 0, 4) 1202 set_extmark(ns, marks[4], 1, 0) 1203 set_extmark(ns, marks[5], 2, 0) 1204 feed([[:1,2s:3:\r<cr>]]) 1205 check_undo_redo(ns, marks[1], 0, 1, 0, 1) 1206 check_undo_redo(ns, marks[2], 0, 2, 1, 0) 1207 check_undo_redo(ns, marks[3], 0, 4, 1, 1) 1208 check_undo_redo(ns, marks[4], 1, 0, 2, 0) 1209 check_undo_redo(ns, marks[5], 2, 0, 3, 0) 1210 feed('u') 1211 feed([[:1,2s:3:\rxx<cr>]]) 1212 eq({ 1, 3 }, get_extmark_by_id(ns, marks[3])) 1213 end) 1214 1215 it('substitutes over multiple lines with replace in substitution', function() 1216 feed('A<cr>x3<cr>xx<esc>') 1217 set_extmark(ns, marks[1], 1, 0) 1218 set_extmark(ns, marks[2], 1, 1) 1219 set_extmark(ns, marks[3], 1, 2) 1220 feed([[:2,2s:3:\r<cr>]]) 1221 check_undo_redo(ns, marks[1], 1, 0, 1, 0) 1222 check_undo_redo(ns, marks[2], 1, 1, 2, 0) 1223 check_undo_redo(ns, marks[3], 1, 2, 2, 0) 1224 end) 1225 1226 it('substitutes over multiple lines with replace in substitution', function() 1227 feed('A<cr>x3<cr>xx<esc>') 1228 set_extmark(ns, marks[1], 0, 1) 1229 set_extmark(ns, marks[2], 0, 2) 1230 set_extmark(ns, marks[3], 0, 4) 1231 set_extmark(ns, marks[4], 1, 1) 1232 set_extmark(ns, marks[5], 2, 0) 1233 feed([[:1,2s:3:\r<cr>]]) 1234 check_undo_redo(ns, marks[1], 0, 1, 0, 1) 1235 check_undo_redo(ns, marks[2], 0, 2, 1, 0) 1236 check_undo_redo(ns, marks[3], 0, 4, 1, 1) 1237 check_undo_redo(ns, marks[4], 1, 1, 3, 0) 1238 check_undo_redo(ns, marks[5], 2, 0, 4, 0) 1239 feed('u') 1240 feed([[:1,2s:3:\rxx<cr>]]) 1241 check_undo_redo(ns, marks[3], 0, 4, 1, 3) 1242 end) 1243 1244 it('substitutes with newline in match and sub, delta is 0', function() 1245 feed('A<cr>67890<cr>xx<esc>') 1246 set_extmark(ns, marks[1], 0, 3) 1247 set_extmark(ns, marks[2], 0, 4) 1248 set_extmark(ns, marks[3], 0, 5) 1249 set_extmark(ns, marks[4], 1, 0) 1250 set_extmark(ns, marks[5], 1, 5) 1251 set_extmark(ns, marks[6], 2, 0) 1252 feed([[:1,1s:5\n:\r<cr>]]) 1253 check_undo_redo(ns, marks[1], 0, 3, 0, 3) 1254 check_undo_redo(ns, marks[2], 0, 4, 1, 0) 1255 check_undo_redo(ns, marks[3], 0, 5, 1, 0) 1256 check_undo_redo(ns, marks[4], 1, 0, 1, 0) 1257 check_undo_redo(ns, marks[5], 1, 5, 1, 5) 1258 check_undo_redo(ns, marks[6], 2, 0, 2, 0) 1259 end) 1260 1261 it('substitutes with newline in match and sub, delta > 0', function() 1262 feed('A<cr>67890<cr>xx<esc>') 1263 set_extmark(ns, marks[1], 0, 3) 1264 set_extmark(ns, marks[2], 0, 4) 1265 set_extmark(ns, marks[3], 0, 5) 1266 set_extmark(ns, marks[4], 1, 0) 1267 set_extmark(ns, marks[5], 1, 5) 1268 set_extmark(ns, marks[6], 2, 0) 1269 feed([[:1,1s:5\n:\r\r<cr>]]) 1270 check_undo_redo(ns, marks[1], 0, 3, 0, 3) 1271 check_undo_redo(ns, marks[2], 0, 4, 2, 0) 1272 check_undo_redo(ns, marks[3], 0, 5, 2, 0) 1273 check_undo_redo(ns, marks[4], 1, 0, 2, 0) 1274 check_undo_redo(ns, marks[5], 1, 5, 2, 5) 1275 check_undo_redo(ns, marks[6], 2, 0, 3, 0) 1276 end) 1277 1278 it('substitutes with newline in match and sub, delta < 0', function() 1279 feed('A<cr>67890<cr>xx<cr>xx<esc>') 1280 set_extmark(ns, marks[1], 0, 3) 1281 set_extmark(ns, marks[2], 0, 4) 1282 set_extmark(ns, marks[3], 0, 5) 1283 set_extmark(ns, marks[4], 1, 0) 1284 set_extmark(ns, marks[5], 1, 5) 1285 set_extmark(ns, marks[6], 2, 1) 1286 set_extmark(ns, marks[7], 3, 0) 1287 feed([[:1,2s:5\n.*\n:\r<cr>]]) 1288 check_undo_redo(ns, marks[1], 0, 3, 0, 3) 1289 check_undo_redo(ns, marks[2], 0, 4, 1, 0) 1290 check_undo_redo(ns, marks[3], 0, 5, 1, 0) 1291 check_undo_redo(ns, marks[4], 1, 0, 1, 0) 1292 check_undo_redo(ns, marks[5], 1, 5, 1, 0) 1293 check_undo_redo(ns, marks[6], 2, 1, 1, 1) 1294 check_undo_redo(ns, marks[7], 3, 0, 2, 0) 1295 end) 1296 1297 it('substitutes with backrefs, newline inserted into sub', function() 1298 feed('A<cr>67890<cr>xx<cr>xx<esc>') 1299 set_extmark(ns, marks[1], 0, 3) 1300 set_extmark(ns, marks[2], 0, 4) 1301 set_extmark(ns, marks[3], 0, 5) 1302 set_extmark(ns, marks[4], 1, 0) 1303 set_extmark(ns, marks[5], 1, 5) 1304 set_extmark(ns, marks[6], 2, 0) 1305 feed([[:1,1s:5\(\n\):\0\1<cr>]]) 1306 check_undo_redo(ns, marks[1], 0, 3, 0, 3) 1307 check_undo_redo(ns, marks[2], 0, 4, 2, 0) 1308 check_undo_redo(ns, marks[3], 0, 5, 2, 0) 1309 check_undo_redo(ns, marks[4], 1, 0, 2, 0) 1310 check_undo_redo(ns, marks[5], 1, 5, 2, 5) 1311 check_undo_redo(ns, marks[6], 2, 0, 3, 0) 1312 end) 1313 1314 it('substitutes a ^', function() 1315 set_extmark(ns, marks[1], 0, 0) 1316 set_extmark(ns, marks[2], 0, 1) 1317 feed([[:s:^:x<cr>]]) 1318 check_undo_redo(ns, marks[1], 0, 0, 0, 1) 1319 check_undo_redo(ns, marks[2], 0, 1, 0, 2) 1320 end) 1321 1322 it('using <c-a> without increase in order of magnitude', function() 1323 -- do_addsub in ops.c 1324 feed('ddiabc998xxx<esc>Tc') 1325 set_extmark(ns, marks[1], 0, 2) 1326 set_extmark(ns, marks[2], 0, 3) 1327 set_extmark(ns, marks[3], 0, 5) 1328 set_extmark(ns, marks[4], 0, 6) 1329 set_extmark(ns, marks[5], 0, 7) 1330 feed('<c-a>') 1331 check_undo_redo(ns, marks[1], 0, 2, 0, 2) 1332 check_undo_redo(ns, marks[2], 0, 3, 0, 6) 1333 check_undo_redo(ns, marks[3], 0, 5, 0, 6) 1334 check_undo_redo(ns, marks[4], 0, 6, 0, 6) 1335 check_undo_redo(ns, marks[5], 0, 7, 0, 7) 1336 end) 1337 1338 it('using <c-a> when increase in order of magnitude', function() 1339 -- do_addsub in ops.c 1340 feed('ddiabc999xxx<esc>Tc') 1341 set_extmark(ns, marks[1], 0, 2) 1342 set_extmark(ns, marks[2], 0, 3) 1343 set_extmark(ns, marks[3], 0, 5) 1344 set_extmark(ns, marks[4], 0, 6) 1345 set_extmark(ns, marks[5], 0, 7) 1346 feed('<c-a>') 1347 check_undo_redo(ns, marks[1], 0, 2, 0, 2) 1348 check_undo_redo(ns, marks[2], 0, 3, 0, 7) 1349 check_undo_redo(ns, marks[3], 0, 5, 0, 7) 1350 check_undo_redo(ns, marks[4], 0, 6, 0, 7) 1351 check_undo_redo(ns, marks[5], 0, 7, 0, 8) 1352 end) 1353 1354 it('using <c-a> when negative and without decrease in order of magnitude', function() 1355 feed('ddiabc-999xxx<esc>T-') 1356 set_extmark(ns, marks[1], 0, 2) 1357 set_extmark(ns, marks[2], 0, 3) 1358 set_extmark(ns, marks[3], 0, 6) 1359 set_extmark(ns, marks[4], 0, 7) 1360 set_extmark(ns, marks[5], 0, 8) 1361 feed('<c-a>') 1362 check_undo_redo(ns, marks[1], 0, 2, 0, 2) 1363 check_undo_redo(ns, marks[2], 0, 3, 0, 7) 1364 check_undo_redo(ns, marks[3], 0, 6, 0, 7) 1365 check_undo_redo(ns, marks[4], 0, 7, 0, 7) 1366 check_undo_redo(ns, marks[5], 0, 8, 0, 8) 1367 end) 1368 1369 it('using <c-a> when negative and decrease in order of magnitude', function() 1370 feed('ddiabc-1000xxx<esc>T-') 1371 set_extmark(ns, marks[1], 0, 2) 1372 set_extmark(ns, marks[2], 0, 3) 1373 set_extmark(ns, marks[3], 0, 7) 1374 set_extmark(ns, marks[4], 0, 8) 1375 set_extmark(ns, marks[5], 0, 9) 1376 feed('<c-a>') 1377 check_undo_redo(ns, marks[1], 0, 2, 0, 2) 1378 check_undo_redo(ns, marks[2], 0, 3, 0, 7) 1379 check_undo_redo(ns, marks[3], 0, 7, 0, 7) 1380 check_undo_redo(ns, marks[4], 0, 8, 0, 7) 1381 check_undo_redo(ns, marks[5], 0, 9, 0, 8) 1382 end) 1383 1384 it('using <c-x> without decrease in order of magnitude', function() 1385 -- do_addsub in ops.c 1386 feed('ddiabc999xxx<esc>Tc') 1387 set_extmark(ns, marks[1], 0, 2) 1388 set_extmark(ns, marks[2], 0, 3) 1389 set_extmark(ns, marks[3], 0, 5) 1390 set_extmark(ns, marks[4], 0, 6) 1391 set_extmark(ns, marks[5], 0, 7) 1392 feed('<c-x>') 1393 check_undo_redo(ns, marks[1], 0, 2, 0, 2) 1394 check_undo_redo(ns, marks[2], 0, 3, 0, 6) 1395 check_undo_redo(ns, marks[3], 0, 5, 0, 6) 1396 check_undo_redo(ns, marks[4], 0, 6, 0, 6) 1397 check_undo_redo(ns, marks[5], 0, 7, 0, 7) 1398 end) 1399 1400 it('using <c-x> when decrease in order of magnitude', function() 1401 -- do_addsub in ops.c 1402 feed('ddiabc1000xxx<esc>Tc') 1403 set_extmark(ns, marks[1], 0, 2) 1404 set_extmark(ns, marks[2], 0, 3) 1405 set_extmark(ns, marks[3], 0, 6) 1406 set_extmark(ns, marks[4], 0, 7) 1407 set_extmark(ns, marks[5], 0, 8) 1408 feed('<c-x>') 1409 check_undo_redo(ns, marks[1], 0, 2, 0, 2) 1410 check_undo_redo(ns, marks[2], 0, 3, 0, 6) 1411 check_undo_redo(ns, marks[3], 0, 6, 0, 6) 1412 check_undo_redo(ns, marks[4], 0, 7, 0, 6) 1413 check_undo_redo(ns, marks[5], 0, 8, 0, 7) 1414 end) 1415 1416 it('using <c-x> when negative and without increase in order of magnitude', function() 1417 feed('ddiabc-998xxx<esc>T-') 1418 set_extmark(ns, marks[1], 0, 2) 1419 set_extmark(ns, marks[2], 0, 3) 1420 set_extmark(ns, marks[3], 0, 6) 1421 set_extmark(ns, marks[4], 0, 7) 1422 set_extmark(ns, marks[5], 0, 8) 1423 feed('<c-x>') 1424 check_undo_redo(ns, marks[1], 0, 2, 0, 2) 1425 check_undo_redo(ns, marks[2], 0, 3, 0, 7) 1426 check_undo_redo(ns, marks[3], 0, 6, 0, 7) 1427 check_undo_redo(ns, marks[4], 0, 7, 0, 7) 1428 check_undo_redo(ns, marks[5], 0, 8, 0, 8) 1429 end) 1430 1431 it('using <c-x> when negative and increase in order of magnitude', function() 1432 feed('ddiabc-999xxx<esc>T-') 1433 set_extmark(ns, marks[1], 0, 2) 1434 set_extmark(ns, marks[2], 0, 3) 1435 set_extmark(ns, marks[3], 0, 6) 1436 set_extmark(ns, marks[4], 0, 7) 1437 set_extmark(ns, marks[5], 0, 8) 1438 feed('<c-x>') 1439 check_undo_redo(ns, marks[1], 0, 2, 0, 2) 1440 check_undo_redo(ns, marks[2], 0, 3, 0, 8) 1441 check_undo_redo(ns, marks[3], 0, 6, 0, 8) 1442 check_undo_redo(ns, marks[4], 0, 7, 0, 8) 1443 check_undo_redo(ns, marks[5], 0, 8, 0, 9) 1444 end) 1445 1446 it('throws consistent error codes', function() 1447 local ns_invalid = ns2 + 1 ---@type integer 1448 local err = string.format("Invalid 'ns_id': %d", ns_invalid) 1449 eq(err, pcall_err(set_extmark, ns_invalid, marks[1], positions[1][1], positions[1][2])) 1450 eq(err, pcall_err(api.nvim_buf_del_extmark, 0, ns_invalid, marks[1])) 1451 eq(err, pcall_err(get_extmarks, ns_invalid, positions[1], positions[2])) 1452 eq(err, pcall_err(get_extmark_by_id, ns_invalid, marks[1])) 1453 end) 1454 1455 it('when col = line-length, set the mark on eol', function() 1456 set_extmark(ns, marks[1], 0, -1) 1457 local rv = get_extmark_by_id(ns, marks[1]) 1458 eq({ 0, init_text:len() }, rv) 1459 -- Test another 1460 set_extmark(ns, marks[1], 0, -1) 1461 rv = get_extmark_by_id(ns, marks[1]) 1462 eq({ 0, init_text:len() }, rv) 1463 end) 1464 1465 it('when col = line-length, set the mark on eol', function() 1466 local invalid_col = init_text:len() + 1 1467 eq("Invalid 'col': out of range", pcall_err(set_extmark, ns, marks[1], 0, invalid_col)) 1468 end) 1469 1470 it('fails when line > line_count', function() 1471 local invalid_col = init_text:len() + 1 1472 local invalid_lnum = 3 1473 eq( 1474 "Invalid 'line': out of range", 1475 pcall_err(set_extmark, ns, marks[1], invalid_lnum, invalid_col) 1476 ) 1477 eq({}, get_extmark_by_id(ns, marks[1])) 1478 end) 1479 1480 it('bug from check_col in extmark_set', function() 1481 -- This bug was caused by extmark_set always using check_col. check_col 1482 -- always uses the current buffer. This wasn't working during undo so we 1483 -- now use check_col and check_lnum only when they are required. 1484 feed('A<cr>67890<cr>xx<esc>') 1485 feed('A<cr>12345<cr>67890<cr>xx<esc>') 1486 set_extmark(ns, marks[1], 3, 4) 1487 feed([[:1,5s:5\n:5 <cr>]]) 1488 check_undo_redo(ns, marks[1], 3, 4, 2, 6) 1489 end) 1490 1491 it('in read-only buffer', function() 1492 command('view! runtime/doc/help.txt') 1493 eq(true, api.nvim_get_option_value('ro', {})) 1494 local id = set_extmark(ns, 0, 0, 2) 1495 eq({ { id, 0, 2 } }, get_extmarks(ns, 0, -1)) 1496 end) 1497 1498 it('can set a mark to other buffer', function() 1499 local buf = request('nvim_create_buf', 0, 1) 1500 request('nvim_buf_set_lines', buf, 0, -1, 1, { '', '' }) 1501 local id = api.nvim_buf_set_extmark(buf, ns, 1, 0, {}) 1502 eq({ { id, 1, 0 } }, api.nvim_buf_get_extmarks(buf, ns, 0, -1, {})) 1503 end) 1504 1505 it('does not crash with append/delete/undo sequence', function() 1506 exec([[ 1507 let ns = nvim_create_namespace('myplugin') 1508 call nvim_buf_set_extmark(0, ns, 0, 0, {}) 1509 call append(0, '') 1510 %delete 1511 undo]]) 1512 assert_alive() 1513 end) 1514 1515 it('works with left and right gravity', function() 1516 -- right gravity should move with inserted text, while 1517 -- left gravity should stay in place. 1518 api.nvim_buf_set_extmark(0, ns, 0, 5, { right_gravity = false }) 1519 api.nvim_buf_set_extmark(0, ns, 0, 5, { right_gravity = true }) 1520 feed([[Aasdfasdf]]) 1521 1522 eq({ { 1, 0, 5 }, { 2, 0, 13 } }, api.nvim_buf_get_extmarks(0, ns, 0, -1, {})) 1523 1524 -- but both move when text is inserted before 1525 feed([[<esc>Iasdf<esc>]]) 1526 -- eq({}, api.nvim_buf_get_lines(0, 0, -1, true)) 1527 eq({ { 1, 0, 9 }, { 2, 0, 17 } }, api.nvim_buf_get_extmarks(0, ns, 0, -1, {})) 1528 1529 -- clear text 1530 api.nvim_buf_set_text(0, 0, 0, 0, 17, {}) 1531 1532 -- handles set_text correctly as well 1533 eq({ { 1, 0, 0 }, { 2, 0, 0 } }, api.nvim_buf_get_extmarks(0, ns, 0, -1, {})) 1534 api.nvim_buf_set_text(0, 0, 0, 0, 0, { 'asdfasdf' }) 1535 eq({ { 1, 0, 0 }, { 2, 0, 8 } }, api.nvim_buf_get_extmarks(0, ns, 0, -1, {})) 1536 1537 feed('u') 1538 -- handles pasting 1539 exec([[let @a='asdfasdf']]) 1540 feed([["ap]]) 1541 eq({ { 1, 0, 0 }, { 2, 0, 8 } }, api.nvim_buf_get_extmarks(0, ns, 0, -1, {})) 1542 end) 1543 1544 it('can accept "end_row" or "end_line" #16548', function() 1545 set_extmark(ns, marks[1], 0, 0, { 1546 end_col = 0, 1547 end_line = 1, 1548 }) 1549 eq({ 1550 { 1551 1, 1552 0, 1553 0, 1554 { 1555 ns_id = ns, 1556 end_col = 0, 1557 end_row = 1, 1558 right_gravity = true, 1559 end_right_gravity = false, 1560 }, 1561 }, 1562 }, get_extmarks(ns, 0, -1, { details = true })) 1563 end) 1564 1565 it('in prompt buffer', function() 1566 feed('dd') 1567 set_extmark(ns, marks[1], 0, 0, {}) 1568 api.nvim_set_option_value('buftype', 'prompt', {}) 1569 feed('i<esc>') 1570 eq({ { marks[1], 0, 2 } }, get_extmarks(ns, 0, -1)) 1571 fn.prompt_setprompt('', 'foo > ') 1572 eq({ { marks[1], 0, 6 } }, get_extmarks(ns, 0, -1)) 1573 feed('ihello') 1574 eq({ { marks[1], 0, 11 } }, get_extmarks(ns, 0, -1)) 1575 1576 local function get_extmark_range(id) 1577 local rv = get_extmark_by_id(ns, id, { details = true }) 1578 return rv[3].invalid and 'invalid' or { rv[1], rv[2], rv[3].end_row, rv[3].end_col } 1579 end 1580 1581 set_extmark(ns, marks[2], 0, 0, { invalidate = true, end_col = 6 }) 1582 set_extmark(ns, marks[3], 0, 6, { invalidate = true, end_col = 11 }) 1583 set_extmark(ns, marks[4], 0, 0, { invalidate = true, end_col = 11 }) 1584 set_extmark(ns, marks[5], 0, 0, { invalidate = true, end_row = 1 }) 1585 fn.prompt_setprompt('', 'floob > ') 1586 eq({ 0, 13 }, get_extmark_range(marks[1])) 1587 eq('invalid', get_extmark_range(marks[2])) -- extmark spanning old prompt invalidated 1588 eq({ 0, 8, 0, 13 }, get_extmark_range(marks[3])) 1589 eq({ 0, 8, 0, 13 }, get_extmark_range(marks[4])) 1590 eq({ 0, 8, 1, 0 }, get_extmark_range(marks[5])) 1591 1592 set_extmark(ns, marks[2], 0, 0, { invalidate = true, end_col = 8 }) 1593 set_extmark(ns, marks[3], 0, 8, { invalidate = true, end_col = 13 }) 1594 set_extmark(ns, marks[4], 0, 0, { invalidate = true, end_col = 13 }) 1595 set_extmark(ns, marks[5], 0, 0, { invalidate = true, end_row = 1 }) 1596 -- Do this in the same event. 1597 exec_lua(function() 1598 vim.fn.setpos("':", { 0, 1, 999, 0 }) 1599 vim.fn.prompt_setprompt('', 'discard > ') 1600 end) 1601 eq({ 0, 10 }, get_extmark_range(marks[1])) 1602 eq('invalid', get_extmark_range(marks[2])) -- all spans on line invalidated 1603 eq('invalid', get_extmark_range(marks[3])) 1604 eq('invalid', get_extmark_range(marks[4])) 1605 eq({ 0, 10, 1, 0 }, get_extmark_range(marks[5])) 1606 1607 feed('hello') 1608 eq({ 0, 15 }, get_extmark_range(marks[1])) 1609 eq({ 0, 15, 1, 0 }, get_extmark_range(marks[5])) 1610 -- init_prompt uses correct range for inserted_bytes when fixing empty prompt. 1611 fn.setline('.', { '', 'last line' }) 1612 eq({ 'discard > ', 'last line' }, api.nvim_buf_get_lines(0, 0, -1, true)) 1613 eq({ 0, 10 }, get_extmark_range(marks[1])) 1614 eq({ 0, 10, 1, 0 }, get_extmark_range(marks[5])) 1615 end) 1616 1617 it('can get details', function() 1618 set_extmark(ns, marks[1], 0, 0, { 1619 conceal = 'c', 1620 conceal_lines = '', 1621 cursorline_hl_group = 'Statement', 1622 end_col = 0, 1623 end_right_gravity = true, 1624 end_row = 1, 1625 hl_eol = true, 1626 hl_group = 'String', 1627 hl_mode = 'blend', 1628 line_hl_group = 'Statement', 1629 number_hl_group = 'Statement', 1630 priority = 0, 1631 right_gravity = false, 1632 sign_hl_group = 'Statement', 1633 sign_text = '>>', 1634 spell = true, 1635 virt_lines = { 1636 { { 'lines', 'Macro' }, { '???' }, { ';;;', '' } }, 1637 { { 'stack', { 'Type', 'Search' } }, { '!!!' } }, 1638 }, 1639 virt_lines_above = true, 1640 virt_lines_leftcol = true, 1641 virt_text = { { 'text', 'Macro' }, { '???' }, { 'stack', { 'Type', 'Search' } } }, 1642 virt_text_hide = true, 1643 virt_text_pos = 'right_align', 1644 }) 1645 eq({ 1646 0, 1647 0, 1648 { 1649 conceal = 'c', 1650 conceal_lines = '', 1651 cursorline_hl_group = 'Statement', 1652 end_col = 0, 1653 end_right_gravity = true, 1654 end_row = 1, 1655 hl_eol = true, 1656 hl_group = 'String', 1657 hl_mode = 'blend', 1658 line_hl_group = 'Statement', 1659 ns_id = ns, 1660 number_hl_group = 'Statement', 1661 priority = 0, 1662 right_gravity = false, 1663 sign_hl_group = 'Statement', 1664 sign_text = '>>', 1665 spell = true, 1666 virt_lines = { 1667 { { 'lines', 'Macro' }, { '???' }, { ';;;', '' } }, 1668 { { 'stack', { 'Type', 'Search' } }, { '!!!' } }, 1669 }, 1670 virt_lines_above = true, 1671 virt_lines_leftcol = true, 1672 virt_lines_overflow = 'trunc', 1673 virt_text = { { 'text', 'Macro' }, { '???' }, { 'stack', { 'Type', 'Search' } } }, 1674 virt_text_repeat_linebreak = false, 1675 virt_text_hide = true, 1676 virt_text_pos = 'right_align', 1677 }, 1678 }, get_extmark_by_id(ns, marks[1], { details = true })) 1679 1680 set_extmark(ns, marks[2], 0, 0, { 1681 priority = 0, 1682 virt_text = { { '', 'Macro' }, { '', { 'Type', 'Search' } }, { '' } }, 1683 virt_text_repeat_linebreak = true, 1684 virt_text_win_col = 1, 1685 }) 1686 eq({ 1687 0, 1688 0, 1689 { 1690 ns_id = ns, 1691 right_gravity = true, 1692 priority = 0, 1693 virt_text = { { '', 'Macro' }, { '', { 'Type', 'Search' } }, { '' } }, 1694 virt_text_repeat_linebreak = true, 1695 virt_text_hide = false, 1696 virt_text_pos = 'win_col', 1697 virt_text_win_col = 1, 1698 }, 1699 }, get_extmark_by_id(ns, marks[2], { details = true })) 1700 1701 set_extmark(ns, marks[3], 0, 0, { 1702 priority = 0, 1703 ui_watched = true, 1704 virt_lines = { { { '', 'Macro' }, { '' }, { '', '' } } }, 1705 virt_lines_overflow = 'scroll', 1706 }) 1707 eq({ 1708 0, 1709 0, 1710 { 1711 ns_id = ns, 1712 right_gravity = true, 1713 ui_watched = true, 1714 priority = 0, 1715 virt_lines = { { { '', 'Macro' }, { '' }, { '', '' } } }, 1716 virt_lines_above = false, 1717 virt_lines_leftcol = false, 1718 virt_lines_overflow = 'scroll', 1719 }, 1720 }, get_extmark_by_id(ns, marks[3], { details = true })) 1721 1722 set_extmark(ns, marks[4], 0, 0, { cursorline_hl_group = 'Statement' }) 1723 eq({ 1724 0, 1725 0, 1726 { 1727 ns_id = ns, 1728 cursorline_hl_group = 'Statement', 1729 priority = 4096, 1730 right_gravity = true, 1731 }, 1732 }, get_extmark_by_id(ns, marks[4], { details = true })) 1733 1734 set_extmark(ns, marks[5], 0, 0, { 1735 end_col = 1, 1736 conceal = 'a', 1737 spell = true, 1738 }) 1739 eq({ 1740 0, 1741 0, 1742 { 1743 conceal = 'a', 1744 end_col = 1, 1745 end_right_gravity = false, 1746 end_row = 0, 1747 ns_id = ns, 1748 right_gravity = true, 1749 spell = true, 1750 }, 1751 }, get_extmark_by_id(ns, marks[5], { details = true })) 1752 1753 set_extmark(ns, marks[6], 0, 0, { 1754 end_col = 1, 1755 spell = false, 1756 }) 1757 eq({ 1758 0, 1759 0, 1760 { 1761 end_col = 1, 1762 end_right_gravity = false, 1763 end_row = 0, 1764 ns_id = ns, 1765 right_gravity = true, 1766 spell = false, 1767 }, 1768 }, get_extmark_by_id(ns, marks[6], { details = true })) 1769 1770 api.nvim_buf_clear_namespace(0, ns, 0, -1) 1771 -- legacy sign mark includes sign name 1772 command('sign define sign1 text=s1 texthl=Title linehl=LineNR numhl=Normal culhl=CursorLine') 1773 command('sign place 1 name=sign1 line=1') 1774 eq({ 1775 { 1776 1, 1777 0, 1778 0, 1779 { 1780 cursorline_hl_group = 'CursorLine', 1781 invalidate = true, 1782 line_hl_group = 'LineNr', 1783 ns_id = 0, 1784 number_hl_group = 'Normal', 1785 priority = 10, 1786 right_gravity = true, 1787 sign_hl_group = 'Title', 1788 sign_name = 'sign1', 1789 sign_text = 's1', 1790 undo_restore = false, 1791 }, 1792 }, 1793 }, get_extmarks(-1, 0, -1, { details = true })) 1794 end) 1795 1796 it('can get marks from anonymous namespaces', function() 1797 ns = request('nvim_create_namespace', '') 1798 ns2 = request('nvim_create_namespace', '') 1799 set_extmark(ns, 1, 0, 0, {}) 1800 set_extmark(ns2, 2, 1, 0, {}) 1801 eq({ 1802 { 1, 0, 0, { ns_id = ns, right_gravity = true } }, 1803 { 2, 1, 0, { ns_id = ns2, right_gravity = true } }, 1804 }, get_extmarks(-1, 0, -1, { details = true })) 1805 end) 1806 1807 it('can filter by extmark properties', function() 1808 set_extmark(ns, 1, 0, 0, {}) 1809 set_extmark(ns, 2, 0, 0, { hl_group = 'Normal' }) 1810 set_extmark(ns, 3, 0, 0, { sign_text = '>>' }) 1811 set_extmark(ns, 4, 0, 0, { virt_text = { { 'text', 'Normal' } } }) 1812 set_extmark(ns, 5, 0, 0, { virt_lines = { { { 'line', 'Normal' } } } }) 1813 eq(5, #get_extmarks(-1, 0, -1, {})) 1814 eq({ { 2, 0, 0 } }, get_extmarks(-1, 0, -1, { type = 'highlight' })) 1815 eq({ { 3, 0, 0 } }, get_extmarks(-1, 0, -1, { type = 'sign' })) 1816 eq({ { 4, 0, 0 } }, get_extmarks(-1, 0, -1, { type = 'virt_text' })) 1817 eq({ { 5, 0, 0 } }, get_extmarks(-1, 0, -1, { type = 'virt_lines' })) 1818 end) 1819 1820 it('invalidated marks are deleted', function() 1821 screen = Screen.new(40, 6) 1822 feed('dd6iaaa bbb ccc<CR><ESC>gg') 1823 api.nvim_set_option_value('signcolumn', 'auto:3', {}) 1824 set_extmark(ns, 1, 0, 0, { invalidate = true, sign_text = 'S1', end_row = 1 }) 1825 set_extmark(ns, 2, 1, 0, { invalidate = true, sign_text = 'S2', end_row = 2, end_col = 0 }) 1826 set_extmark(ns, 3, 1, 0, { invalidate = true, sign_text = 'S3', end_row = 2, end_col = 1 }) 1827 -- mark with invalidate is removed 1828 command('d2') 1829 screen:expect([[ 1830 {7:S3}^aaa bbb ccc | 1831 {7: }aaa bbb ccc |*3 1832 {7: } | 1833 | 1834 ]]) 1835 -- mark is restored with undo_restore == true 1836 command('silent undo') 1837 screen:expect([[ 1838 {7:S1 }^aaa bbb ccc | 1839 {7:S3S2S1}aaa bbb ccc | 1840 {7:S3S2 }aaa bbb ccc | 1841 {7: }aaa bbb ccc |*2 1842 | 1843 ]]) 1844 -- decor is not removed twice 1845 command('d3') 1846 api.nvim_buf_del_extmark(0, ns, 1) 1847 api.nvim_buf_del_extmark(0, ns, 3) 1848 command('silent undo') 1849 -- mark is deleted with undo_restore == false 1850 set_extmark(ns, 1, 0, 0, { invalidate = true, undo_restore = false, sign_text = 'S1' }) 1851 set_extmark(ns, 2, 1, 0, { invalidate = true, undo_restore = false, sign_text = 'S2' }) 1852 command('1d 2') 1853 eq(0, #get_extmarks(-1, 0, -1, {})) 1854 -- mark is not removed when deleting bytes before the range 1855 set_extmark(ns, 3, 0, 4, { 1856 invalidate = true, 1857 hl_group = 'Error', 1858 end_col = 7, 1859 right_gravity = false, 1860 }) 1861 feed('dw') 1862 eq(3, get_extmark_by_id(ns, 3, { details = true })[3].end_col) 1863 -- mark is not removed when deleting bytes at the start of the range 1864 feed('x') 1865 eq(2, get_extmark_by_id(ns, 3, { details = true })[3].end_col) 1866 -- mark is not removed when deleting bytes from the end of the range 1867 feed('lx') 1868 eq(1, get_extmark_by_id(ns, 3, { details = true })[3].end_col) 1869 -- mark is not removed when deleting bytes beyond end of the range 1870 feed('x') 1871 eq(1, get_extmark_by_id(ns, 3, { details = true })[3].end_col) 1872 -- mark is removed when all bytes in the range are deleted 1873 feed('hx') 1874 eq(true, get_extmark_by_id(ns, 3, { details = true })[3].invalid) 1875 -- mark is restored with undo_restore == true if pos did not change 1876 command('undo') 1877 eq(nil, get_extmark_by_id(ns, 3, { details = true })[3].invalid) 1878 -- multiline mark is not removed when start of its range is deleted 1879 set_extmark(ns, 4, 1, 4, { 1880 undo_restore = false, 1881 invalidate = true, 1882 hl_group = 'Error', 1883 end_col = 7, 1884 end_row = 3, 1885 }) 1886 feed('ddDdd') 1887 eq({ 0, 0 }, get_extmark_by_id(ns, 4, {})) 1888 -- multiline mark is removed when entirety of its range is deleted 1889 feed('vj2ed') 1890 eq({}, get_extmark_by_id(ns, 4, {})) 1891 end) 1892 1893 it('no crash checking invalidated flag of sign pair end key #31856', function() 1894 api.nvim_buf_set_lines(0, 0, 1, false, { '', '' }) 1895 api.nvim_set_option_value('signcolumn', 'auto:2', {}) 1896 set_extmark(ns, 1, 0, 0, { sign_text = 'S1', invalidate = true, end_row = 0 }) 1897 set_extmark(ns, 2, 1, 0, { sign_text = 'S2', end_row = 1 }) 1898 command('d') 1899 api.nvim_buf_clear_namespace(0, ns, 0, -1) 1900 n.assert_alive() 1901 end) 1902 1903 it('can set a URL', function() 1904 local url1 = 'https://example.com' 1905 local url2 = 'http://127.0.0.1' 1906 set_extmark(ns, 1, 0, 0, { url = url1, end_col = 3 }) 1907 set_extmark(ns, 2, 0, 3, { url = url2, hl_group = 'Search', end_col = 5 }) 1908 local extmarks = get_extmarks(ns, 0, -1, { details = true }) 1909 eq(2, #extmarks) 1910 eq(url1, extmarks[1][4].url) 1911 eq(url2, extmarks[2][4].url) 1912 eq('Search', extmarks[2][4].hl_group) 1913 end) 1914 1915 it('respects priority', function() 1916 screen = Screen.new(15, 10) 1917 1918 set_extmark(ns, marks[1], 0, 0, { 1919 hl_group = 'Comment', 1920 end_col = 2, 1921 priority = 20, 1922 }) 1923 1924 -- Extmark defined after first extmark but has lower priority, first extmark "wins" 1925 set_extmark(ns, marks[2], 0, 0, { 1926 hl_group = 'String', 1927 end_col = 2, 1928 priority = 10, 1929 }) 1930 1931 screen:expect { 1932 grid = [[ 1933 {1:12}34^5 | 1934 {2:~ }|*8 1935 | 1936 ]], 1937 attr_ids = { 1938 [1] = { foreground = Screen.colors.Blue1 }, 1939 [2] = { foreground = Screen.colors.Blue1, bold = true }, 1940 }, 1941 } 1942 end) 1943 end) 1944 1945 describe('Extmarks buffer api with many marks', function() 1946 local ns1 1947 local ns2 1948 local ns_marks = {} 1949 before_each(function() 1950 clear() 1951 ns1 = request('nvim_create_namespace', 'ns1') 1952 ns2 = request('nvim_create_namespace', 'ns2') 1953 ns_marks = { [ns1] = {}, [ns2] = {} } 1954 local lines = {} 1955 for i = 1, 30 do 1956 lines[#lines + 1] = string.rep('x ', i) 1957 end 1958 api.nvim_buf_set_lines(0, 0, -1, true, lines) 1959 local ns = ns1 1960 local q = 0 1961 for i = 0, 29 do 1962 for j = 0, i do 1963 local id = set_extmark(ns, 0, i, j) 1964 eq(nil, ns_marks[ns][id]) 1965 ok(id > 0) 1966 ns_marks[ns][id] = { i, j } 1967 ns = ns1 + ns2 - ns 1968 q = q + 1 1969 end 1970 end 1971 eq(233, #ns_marks[ns1]) 1972 eq(232, #ns_marks[ns2]) 1973 end) 1974 1975 local function get_marks(ns) 1976 local mark_list = get_extmarks(ns, 0, -1) 1977 local marks = {} 1978 for _, mark in ipairs(mark_list) do 1979 local id, row, col = unpack(mark) 1980 eq(nil, marks[id], 'duplicate mark') 1981 marks[id] = { row, col } 1982 end 1983 return marks 1984 end 1985 1986 it('can get marks', function() 1987 eq(ns_marks[ns1], get_marks(ns1)) 1988 eq(ns_marks[ns2], get_marks(ns2)) 1989 end) 1990 1991 it('can clear all marks in ns', function() 1992 api.nvim_buf_clear_namespace(0, ns1, 0, -1) 1993 eq({}, get_marks(ns1)) 1994 eq(ns_marks[ns2], get_marks(ns2)) 1995 api.nvim_buf_clear_namespace(0, ns2, 0, -1) 1996 eq({}, get_marks(ns1)) 1997 eq({}, get_marks(ns2)) 1998 end) 1999 2000 it('can clear line range', function() 2001 api.nvim_buf_clear_namespace(0, ns1, 10, 20) 2002 for id, mark in pairs(ns_marks[ns1]) do 2003 if 10 <= mark[1] and mark[1] < 20 then 2004 ns_marks[ns1][id] = nil 2005 end 2006 end 2007 eq(ns_marks[ns1], get_marks(ns1)) 2008 eq(ns_marks[ns2], get_marks(ns2)) 2009 2010 api.nvim_buf_clear_namespace(0, ns1, 0, 10) 2011 for id, mark in pairs(ns_marks[ns1]) do 2012 if mark[1] < 10 then 2013 ns_marks[ns1][id] = nil 2014 end 2015 end 2016 eq(ns_marks[ns1], get_marks(ns1)) 2017 eq(ns_marks[ns2], get_marks(ns2)) 2018 2019 api.nvim_buf_clear_namespace(0, ns1, 20, -1) 2020 for id, mark in pairs(ns_marks[ns1]) do 2021 if mark[1] >= 20 then 2022 ns_marks[ns1][id] = nil 2023 end 2024 end 2025 eq(ns_marks[ns1], get_marks(ns1)) 2026 eq(ns_marks[ns2], get_marks(ns2)) 2027 end) 2028 2029 it('can delete line', function() 2030 feed('10Gdd') 2031 for _, marks in pairs(ns_marks) do 2032 for id, mark in pairs(marks) do 2033 if mark[1] == 9 then 2034 marks[id] = { 9, 0 } 2035 elseif mark[1] >= 10 then 2036 mark[1] = mark[1] - 1 2037 end 2038 end 2039 end 2040 eq(ns_marks[ns1], get_marks(ns1)) 2041 eq(ns_marks[ns2], get_marks(ns2)) 2042 end) 2043 2044 it('can delete lines', function() 2045 feed('10G10dd') 2046 for _, marks in pairs(ns_marks) do 2047 for id, mark in pairs(marks) do 2048 if 9 <= mark[1] and mark[1] < 19 then 2049 marks[id] = { 9, 0 } 2050 elseif mark[1] >= 19 then 2051 mark[1] = mark[1] - 10 2052 end 2053 end 2054 end 2055 eq(ns_marks[ns1], get_marks(ns1)) 2056 eq(ns_marks[ns2], get_marks(ns2)) 2057 end) 2058 2059 it('can wipe buffer', function() 2060 command('bwipe!') 2061 eq({}, get_marks(ns1)) 2062 eq({}, get_marks(ns2)) 2063 end) 2064 end) 2065 2066 describe('API/win_extmark', function() 2067 local screen 2068 local marks, line1, line2 2069 local ns 2070 2071 before_each(function() 2072 -- Initialize some namespaces and insert text into a buffer 2073 marks = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 } 2074 2075 line1 = 'non ui-watched line' 2076 line2 = 'ui-watched line' 2077 2078 clear() 2079 2080 insert(line1) 2081 feed('o<esc>') 2082 insert(line2) 2083 ns = request('nvim_create_namespace', 'extmark-ui') 2084 end) 2085 2086 it('sends and only sends ui-watched marks to ui', function() 2087 screen = Screen.new(20, 4) 2088 -- should send this 2089 set_extmark(ns, marks[1], 1, 0, { ui_watched = true }) 2090 -- should not send this 2091 set_extmark(ns, marks[2], 0, 0, { ui_watched = false }) 2092 screen:expect({ 2093 grid = [[ 2094 non ui-watched line | 2095 ui-watched lin^e | 2096 {1:~ }| 2097 | 2098 ]], 2099 extmarks = { 2100 [2] = { 2101 -- positioned at the end of the 2nd line 2102 { 1000, ns, marks[1], 1, 16 }, 2103 }, 2104 }, 2105 }) 2106 end) 2107 2108 it('sends multiple ui-watched marks to ui', function() 2109 screen = Screen.new(20, 4) 2110 feed('15A!<Esc>') 2111 -- should send all of these 2112 set_extmark(ns, marks[1], 1, 0, { ui_watched = true, virt_text_pos = 'overlay' }) 2113 set_extmark(ns, marks[2], 1, 2, { ui_watched = true, virt_text_pos = 'overlay' }) 2114 set_extmark(ns, marks[3], 1, 4, { ui_watched = true, virt_text_pos = 'overlay' }) 2115 set_extmark(ns, marks[4], 1, 6, { ui_watched = true, virt_text_pos = 'overlay' }) 2116 set_extmark(ns, marks[5], 1, 8, { ui_watched = true }) 2117 screen:expect({ 2118 grid = [[ 2119 non ui-watched line | 2120 ui-watched line!!!!!| 2121 !!!!!!!!!^! | 2122 | 2123 ]], 2124 extmarks = { 2125 [2] = { 2126 -- notification from 1st call 2127 { 1000, ns, marks[1], 1, 0 }, 2128 -- notifications from 2nd call 2129 { 1000, ns, marks[1], 1, 0 }, 2130 { 1000, ns, marks[2], 1, 2 }, 2131 -- notifications from 3rd call 2132 { 1000, ns, marks[1], 1, 0 }, 2133 { 1000, ns, marks[2], 1, 2 }, 2134 { 1000, ns, marks[3], 1, 4 }, 2135 -- notifications from 4th call 2136 { 1000, ns, marks[1], 1, 0 }, 2137 { 1000, ns, marks[2], 1, 2 }, 2138 { 1000, ns, marks[3], 1, 4 }, 2139 { 1000, ns, marks[4], 1, 6 }, 2140 -- final 2141 -- overlay 2142 { 1000, ns, marks[1], 1, 0 }, 2143 { 1000, ns, marks[2], 1, 2 }, 2144 { 1000, ns, marks[3], 1, 4 }, 2145 { 1000, ns, marks[4], 1, 6 }, 2146 -- eol 2147 { 1000, ns, marks[5], 2, 11 }, 2148 }, 2149 }, 2150 }) 2151 end) 2152 2153 it('updates ui-watched marks', function() 2154 screen = Screen.new(20, 4) 2155 -- should send this 2156 set_extmark(ns, marks[1], 1, 0, { ui_watched = true }) 2157 -- should not send this 2158 set_extmark(ns, marks[2], 0, 0, { ui_watched = false }) 2159 -- make some changes 2160 insert(' update') 2161 screen:expect({ 2162 grid = [[ 2163 non ui-watched line | 2164 ui-watched linupdat^e| 2165 e | 2166 | 2167 ]], 2168 extmarks = { 2169 [2] = { 2170 -- positioned at the end of the 2nd line 2171 { 1000, ns, marks[1], 1, 16 }, 2172 -- updated and wrapped to 3rd line 2173 { 1000, ns, marks[1], 2, 2 }, 2174 }, 2175 }, 2176 }) 2177 feed('<c-e>') 2178 screen:expect({ 2179 grid = [[ 2180 ui-watched linupdat^e| 2181 e | 2182 {1:~ }| 2183 | 2184 ]], 2185 extmarks = { 2186 [2] = { 2187 -- positioned at the end of the 2nd line 2188 { 1000, ns, marks[1], 1, 16 }, 2189 -- updated and wrapped to 3rd line 2190 { 1000, ns, marks[1], 2, 2 }, 2191 -- scrolled up one line, should be handled by grid scroll 2192 }, 2193 }, 2194 }) 2195 end) 2196 2197 it('sends ui-watched to splits', function() 2198 screen = Screen.new(20, 8, { ext_multigrid = true }) 2199 -- should send this 2200 set_extmark(ns, marks[1], 1, 0, { ui_watched = true }) 2201 -- should not send this 2202 set_extmark(ns, marks[2], 0, 0, { ui_watched = false }) 2203 command('split') 2204 screen:expect({ 2205 grid = [[ 2206 ## grid 1 2207 [4:--------------------]|*3 2208 {3:[No Name] [+] }| 2209 [2:--------------------]|*2 2210 {2:[No Name] [+] }| 2211 [3:--------------------]| 2212 ## grid 2 2213 non ui-watched line | 2214 ui-watched line | 2215 ## grid 3 2216 | 2217 ## grid 4 2218 non ui-watched line | 2219 ui-watched lin^e | 2220 {1:~ }| 2221 ]], 2222 extmarks = { 2223 [2] = { 2224 -- positioned at the end of the 2nd line 2225 { 1000, ns, marks[1], 1, 16 }, 2226 -- updated after split 2227 { 1000, ns, marks[1], 1, 16 }, 2228 }, 2229 [4] = { 2230 -- only after split 2231 { 1001, ns, marks[1], 1, 16 }, 2232 }, 2233 }, 2234 }) 2235 -- make some changes 2236 insert(' update') 2237 screen:expect({ 2238 grid = [[ 2239 ## grid 1 2240 [4:--------------------]|*3 2241 {3:[No Name] [+] }| 2242 [2:--------------------]|*2 2243 {2:[No Name] [+] }| 2244 [3:--------------------]| 2245 ## grid 2 2246 non ui-watched line | 2247 ui-watched linupd{1:@@@}| 2248 ## grid 3 2249 | 2250 ## grid 4 2251 non ui-watched line | 2252 ui-watched linupdat^e| 2253 e | 2254 ]], 2255 extmarks = { 2256 [2] = { 2257 -- positioned at the end of the 2nd line 2258 { 1000, ns, marks[1], 1, 16 }, 2259 -- updated after split 2260 { 1000, ns, marks[1], 1, 16 }, 2261 }, 2262 [4] = { 2263 { 1001, ns, marks[1], 1, 16 }, 2264 -- updated 2265 { 1001, ns, marks[1], 2, 2 }, 2266 }, 2267 }, 2268 }) 2269 end) 2270 end)