1" Tests for defining text property types and adding text properties to the 2" buffer. 3 4source check.vim 5CheckFeature textprop 6 7source screendump.vim 8 9" test length zero 10 11func Test_proptype_global() 12 call prop_type_add('comment', {'highlight': 'Directory', 'priority': 123, 'start_incl': 1, 'end_incl': 1}) 13 let proptypes = prop_type_list() 14 call assert_equal(1, len(proptypes)) 15 call assert_equal('comment', proptypes[0]) 16 17 let proptype = prop_type_get('comment') 18 call assert_equal('Directory', proptype['highlight']) 19 call assert_equal(123, proptype['priority']) 20 call assert_equal(1, proptype['start_incl']) 21 call assert_equal(1, proptype['end_incl']) 22 23 call prop_type_delete('comment') 24 call assert_equal(0, len(prop_type_list())) 25 26 call prop_type_add('one', {}) 27 call assert_equal(1, len(prop_type_list())) 28 let proptype = 'one'->prop_type_get() 29 call assert_false(has_key(proptype, 'highlight')) 30 call assert_equal(0, proptype['priority']) 31 call assert_equal(0, proptype['start_incl']) 32 call assert_equal(0, proptype['end_incl']) 33 34 call prop_type_add('two', {}) 35 call assert_equal(2, len(prop_type_list())) 36 call prop_type_delete('one') 37 call assert_equal(1, len(prop_type_list())) 38 call prop_type_delete('two') 39 call assert_equal(0, len(prop_type_list())) 40endfunc 41 42func Test_proptype_buf() 43 let bufnr = bufnr('') 44 call prop_type_add('comment', {'bufnr': bufnr, 'highlight': 'Directory', 'priority': 123, 'start_incl': 1, 'end_incl': 1}) 45 let proptypes = prop_type_list({'bufnr': bufnr}) 46 call assert_equal(1, len(proptypes)) 47 call assert_equal('comment', proptypes[0]) 48 49 let proptype = prop_type_get('comment', {'bufnr': bufnr}) 50 call assert_equal('Directory', proptype['highlight']) 51 call assert_equal(123, proptype['priority']) 52 call assert_equal(1, proptype['start_incl']) 53 call assert_equal(1, proptype['end_incl']) 54 55 call prop_type_delete('comment', {'bufnr': bufnr}) 56 call assert_equal(0, len({'bufnr': bufnr}->prop_type_list())) 57 58 call prop_type_add('one', {'bufnr': bufnr}) 59 let proptype = prop_type_get('one', {'bufnr': bufnr}) 60 call assert_false(has_key(proptype, 'highlight')) 61 call assert_equal(0, proptype['priority']) 62 call assert_equal(0, proptype['start_incl']) 63 call assert_equal(0, proptype['end_incl']) 64 65 call prop_type_add('two', {'bufnr': bufnr}) 66 call assert_equal(2, len(prop_type_list({'bufnr': bufnr}))) 67 call prop_type_delete('one', {'bufnr': bufnr}) 68 call assert_equal(1, len(prop_type_list({'bufnr': bufnr}))) 69 call prop_type_delete('two', {'bufnr': bufnr}) 70 call assert_equal(0, len(prop_type_list({'bufnr': bufnr}))) 71 72 call assert_fails("call prop_type_add('one', {'bufnr': 98764})", "E158:") 73endfunc 74 75func AddPropTypes() 76 call prop_type_add('one', {}) 77 call prop_type_add('two', {}) 78 call prop_type_add('three', {}) 79 call prop_type_add('whole', {}) 80endfunc 81 82func DeletePropTypes() 83 call prop_type_delete('one') 84 call prop_type_delete('two') 85 call prop_type_delete('three') 86 call prop_type_delete('whole') 87endfunc 88 89func SetupPropsInFirstLine() 90 call setline(1, 'one two three') 91 call prop_add(1, 1, {'length': 3, 'id': 11, 'type': 'one'}) 92 eval 1->prop_add(5, {'length': 3, 'id': 12, 'type': 'two'}) 93 call prop_add(1, 9, {'length': 5, 'id': 13, 'type': 'three'}) 94 call prop_add(1, 1, {'length': 13, 'id': 14, 'type': 'whole'}) 95endfunc 96 97func Get_expected_props() 98 return [ 99 \ {'col': 1, 'length': 13, 'id': 14, 'type': 'whole', 'start': 1, 'end': 1}, 100 \ {'col': 1, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1}, 101 \ {'col': 5, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1}, 102 \ {'col': 9, 'length': 5, 'id': 13, 'type': 'three', 'start': 1, 'end': 1}, 103 \ ] 104endfunc 105 106func Test_prop_find() 107 new 108 call setline(1, ['one one one', 'twotwo', 'three', 'fourfour', 'five', 'sixsix']) 109 110 " Add two text props on lines 1 and 5, and one spanning lines 2 to 4. 111 call prop_type_add('prop_name', {'highlight': 'Directory'}) 112 call prop_add(1, 5, {'type': 'prop_name', 'id': 10, 'length': 3}) 113 call prop_add(2, 4, {'type': 'prop_name', 'id': 11, 'end_lnum': 4, 'end_col': 9}) 114 call prop_add(5, 4, {'type': 'prop_name', 'id': 12, 'length': 1}) 115 116 let expected = [ 117 \ {'lnum': 1, 'col': 5, 'length': 3, 'id': 10, 'type': 'prop_name', 'start': 1, 'end': 1}, 118 \ {'lnum': 2, 'col': 4, 'id': 11, 'type': 'prop_name', 'start': 1, 'end': 0}, 119 \ {'lnum': 5, 'col': 4, 'length': 1, 'id': 12, 'type': 'prop_name', 'start': 1, 'end': 1} 120 \ ] 121 122 " Starting at line 5 col 1 this should find the prop at line 5 col 4. 123 call cursor(5,1) 124 let result = prop_find({'type': 'prop_name'}, 'f') 125 call assert_equal(expected[2], result) 126 127 " With skipstart left at false (default), this should find the prop at line 128 " 5 col 4. 129 let result = prop_find({'type': 'prop_name', 'lnum': 5, 'col': 4}, 'b') 130 call assert_equal(expected[2], result) 131 132 " With skipstart set to true, this should skip the prop at line 5 col 4. 133 let result = prop_find({'type': 'prop_name', 'lnum': 5, 'col': 4, 'skipstart': 1}, 'b') 134 unlet result.length 135 call assert_equal(expected[1], result) 136 137 " Search backwards from line 1 col 10 to find the prop on the same line. 138 let result = prop_find({'type': 'prop_name', 'lnum': 1, 'col': 10}, 'b') 139 call assert_equal(expected[0], result) 140 141 " with skipstart set to false, if the start position is anywhere between the 142 " start and end lines of a text prop (searching forward or backward), the 143 " result should be the prop on the first line (the line with 'start' set to 1). 144 call cursor(3,1) 145 let result = prop_find({'type': 'prop_name'}, 'f') 146 unlet result.length 147 call assert_equal(expected[1], result) 148 let result = prop_find({'type': 'prop_name'}, 'b') 149 unlet result.length 150 call assert_equal(expected[1], result) 151 152 " with skipstart set to true, if the start position is anywhere between the 153 " start and end lines of a text prop (searching forward or backward), all lines 154 " of the prop will be skipped. 155 let result = prop_find({'type': 'prop_name', 'skipstart': 1}, 'b') 156 call assert_equal(expected[0], result) 157 let result = prop_find({'type': 'prop_name', 'skipstart': 1}, 'f') 158 call assert_equal(expected[2], result) 159 160 " Use skipstart to search through all props with type name 'prop_name'. 161 " First forward... 162 let lnum = 1 163 let col = 1 164 let i = 0 165 for exp in expected 166 let result = prop_find({'type': 'prop_name', 'lnum': lnum, 'col': col, 'skipstart': 1}, 'f') 167 if !has_key(exp, "length") 168 unlet result.length 169 endif 170 call assert_equal(exp, result) 171 let lnum = result.lnum 172 let col = result.col 173 let i = i + 1 174 endfor 175 176 " ...then backwards. 177 let lnum = 6 178 let col = 4 179 let i = 2 180 while i >= 0 181 let result = prop_find({'type': 'prop_name', 'lnum': lnum, 'col': col, 'skipstart': 1}, 'b') 182 if !has_key(expected[i], "length") 183 unlet result.length 184 endif 185 call assert_equal(expected[i], result) 186 let lnum = result.lnum 187 let col = result.col 188 let i = i - 1 189 endwhile 190 191 " Starting from line 6 col 1 search backwards for prop with id 10. 192 call cursor(6,1) 193 let result = prop_find({'id': 10, 'skipstart': 1}, 'b') 194 call assert_equal(expected[0], result) 195 196 " Starting from line 1 col 1 search forwards for prop with id 12. 197 call cursor(1,1) 198 let result = prop_find({'id': 12}, 'f') 199 call assert_equal(expected[2], result) 200 201 " Search for a prop with an unknown id. 202 let result = prop_find({'id': 999}, 'f') 203 call assert_equal({}, result) 204 205 " Search backwards from the proceeding position of the prop with id 11 206 " (at line num 2 col 4). This should return an empty dict. 207 let result = prop_find({'id': 11, 'lnum': 2, 'col': 3}, 'b') 208 call assert_equal({}, result) 209 210 " When lnum is given and col is omitted, use column 1. 211 let result = prop_find({'type': 'prop_name', 'lnum': 1}, 'f') 212 call assert_equal(expected[0], result) 213 214 call prop_clear(1,6) 215 call prop_type_delete('prop_name') 216endfunc 217 218func Test_prop_add() 219 new 220 call AddPropTypes() 221 call SetupPropsInFirstLine() 222 let expected_props = Get_expected_props() 223 call assert_equal(expected_props, prop_list(1)) 224 call assert_fails("call prop_add(10, 1, {'length': 1, 'id': 14, 'type': 'whole'})", 'E966:') 225 call assert_fails("call prop_add(1, 22, {'length': 1, 'id': 14, 'type': 'whole'})", 'E964:') 226 227 " Insert a line above, text props must still be there. 228 call append(0, 'empty') 229 call assert_equal(expected_props, prop_list(2)) 230 " Delete a line above, text props must still be there. 231 1del 232 call assert_equal(expected_props, prop_list(1)) 233 234 " Prop without length or end column is zero length 235 call prop_clear(1) 236 call prop_add(1, 5, {'type': 'two'}) 237 let expected = [{'col': 5, 'length': 0, 'type': 'two', 'id': 0, 'start': 1, 'end': 1}] 238 call assert_equal(expected, prop_list(1)) 239 240 call assert_fails("call prop_add(1, 5, {'type': 'two', 'bufnr': 234343})", 'E158:') 241 242 call DeletePropTypes() 243 bwipe! 244endfunc 245 246func Test_prop_remove() 247 new 248 call AddPropTypes() 249 call SetupPropsInFirstLine() 250 let props = Get_expected_props() 251 call assert_equal(props, prop_list(1)) 252 253 " remove by id 254 call assert_equal(1, {'id': 12}->prop_remove(1)) 255 unlet props[2] 256 call assert_equal(props, prop_list(1)) 257 258 " remove by type 259 call assert_equal(1, prop_remove({'type': 'one'}, 1)) 260 unlet props[1] 261 call assert_equal(props, prop_list(1)) 262 263 " remove from unknown buffer 264 call assert_fails("call prop_remove({'type': 'one', 'bufnr': 123456}, 1)", 'E158:') 265 266 call DeletePropTypes() 267 bwipe! 268endfunc 269 270func SetupOneLine() 271 call setline(1, 'xonex xtwoxx') 272 normal gg0 273 call AddPropTypes() 274 call prop_add(1, 2, {'length': 3, 'id': 11, 'type': 'one'}) 275 call prop_add(1, 8, {'length': 3, 'id': 12, 'type': 'two'}) 276 let expected = [ 277 \ {'col': 2, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1}, 278 \ {'col': 8, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1}, 279 \] 280 call assert_equal(expected, prop_list(1)) 281 return expected 282endfunc 283 284func Test_prop_add_remove_buf() 285 new 286 let bufnr = bufnr('') 287 call AddPropTypes() 288 for lnum in range(1, 4) 289 call setline(lnum, 'one two three') 290 endfor 291 wincmd w 292 for lnum in range(1, 4) 293 call prop_add(lnum, 1, {'length': 3, 'id': 11, 'type': 'one', 'bufnr': bufnr}) 294 call prop_add(lnum, 5, {'length': 3, 'id': 12, 'type': 'two', 'bufnr': bufnr}) 295 call prop_add(lnum, 11, {'length': 3, 'id': 13, 'type': 'three', 'bufnr': bufnr}) 296 endfor 297 298 let props = [ 299 \ {'col': 1, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1}, 300 \ {'col': 5, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1}, 301 \ {'col': 11, 'length': 3, 'id': 13, 'type': 'three', 'start': 1, 'end': 1}, 302 \] 303 call assert_equal(props, prop_list(1, {'bufnr': bufnr})) 304 305 " remove by id 306 let before_props = deepcopy(props) 307 unlet props[1] 308 309 call prop_remove({'id': 12, 'bufnr': bufnr}, 1) 310 call assert_equal(props, prop_list(1, {'bufnr': bufnr})) 311 call assert_equal(before_props, prop_list(2, {'bufnr': bufnr})) 312 call assert_equal(before_props, prop_list(3, {'bufnr': bufnr})) 313 call assert_equal(before_props, prop_list(4, {'bufnr': bufnr})) 314 315 call prop_remove({'id': 12, 'bufnr': bufnr}, 3, 4) 316 call assert_equal(props, prop_list(1, {'bufnr': bufnr})) 317 call assert_equal(before_props, prop_list(2, {'bufnr': bufnr})) 318 call assert_equal(props, prop_list(3, {'bufnr': bufnr})) 319 call assert_equal(props, prop_list(4, {'bufnr': bufnr})) 320 321 call prop_remove({'id': 12, 'bufnr': bufnr}) 322 for lnum in range(1, 4) 323 call assert_equal(props, prop_list(lnum, {'bufnr': bufnr})) 324 endfor 325 326 " remove by type 327 let before_props = deepcopy(props) 328 unlet props[0] 329 330 call prop_remove({'type': 'one', 'bufnr': bufnr}, 1) 331 call assert_equal(props, prop_list(1, {'bufnr': bufnr})) 332 call assert_equal(before_props, prop_list(2, {'bufnr': bufnr})) 333 call assert_equal(before_props, prop_list(3, {'bufnr': bufnr})) 334 call assert_equal(before_props, prop_list(4, {'bufnr': bufnr})) 335 336 call prop_remove({'type': 'one', 'bufnr': bufnr}, 3, 4) 337 call assert_equal(props, prop_list(1, {'bufnr': bufnr})) 338 call assert_equal(before_props, prop_list(2, {'bufnr': bufnr})) 339 call assert_equal(props, prop_list(3, {'bufnr': bufnr})) 340 call assert_equal(props, prop_list(4, {'bufnr': bufnr})) 341 342 call prop_remove({'type': 'one', 'bufnr': bufnr}) 343 for lnum in range(1, 4) 344 call assert_equal(props, prop_list(lnum, {'bufnr': bufnr})) 345 endfor 346 347 call DeletePropTypes() 348 wincmd w 349 bwipe! 350endfunc 351 352func Test_prop_backspace() 353 new 354 set bs=2 355 let expected = SetupOneLine() " 'xonex xtwoxx' 356 357 exe "normal 0li\<BS>\<Esc>fxli\<BS>\<Esc>" 358 call assert_equal('one xtwoxx', getline(1)) 359 let expected[0].col = 1 360 let expected[1].col = 6 361 call assert_equal(expected, prop_list(1)) 362 363 call DeletePropTypes() 364 bwipe! 365 set bs& 366endfunc 367 368func Test_prop_replace() 369 new 370 set bs=2 371 let expected = SetupOneLine() " 'xonex xtwoxx' 372 373 exe "normal 0Ryyy\<Esc>" 374 call assert_equal('yyyex xtwoxx', getline(1)) 375 call assert_equal(expected, prop_list(1)) 376 377 exe "normal ftRyy\<BS>" 378 call assert_equal('yyyex xywoxx', getline(1)) 379 call assert_equal(expected, prop_list(1)) 380 381 exe "normal 0fwRyy\<BS>" 382 call assert_equal('yyyex xyyoxx', getline(1)) 383 call assert_equal(expected, prop_list(1)) 384 385 exe "normal 0foRyy\<BS>\<BS>" 386 call assert_equal('yyyex xyyoxx', getline(1)) 387 call assert_equal(expected, prop_list(1)) 388 389 call DeletePropTypes() 390 bwipe! 391 set bs& 392endfunc 393 394func Test_prop_open_line() 395 new 396 397 " open new line, props stay in top line 398 let expected = SetupOneLine() " 'xonex xtwoxx' 399 exe "normal o\<Esc>" 400 call assert_equal('xonex xtwoxx', getline(1)) 401 call assert_equal('', getline(2)) 402 call assert_equal(expected, prop_list(1)) 403 call DeletePropTypes() 404 405 " move all props to next line 406 let expected = SetupOneLine() " 'xonex xtwoxx' 407 exe "normal 0i\<CR>\<Esc>" 408 call assert_equal('', getline(1)) 409 call assert_equal('xonex xtwoxx', getline(2)) 410 call assert_equal(expected, prop_list(2)) 411 call DeletePropTypes() 412 413 " split just before prop, move all props to next line 414 let expected = SetupOneLine() " 'xonex xtwoxx' 415 exe "normal 0li\<CR>\<Esc>" 416 call assert_equal('x', getline(1)) 417 call assert_equal('onex xtwoxx', getline(2)) 418 let expected[0].col -= 1 419 let expected[1].col -= 1 420 call assert_equal(expected, prop_list(2)) 421 call DeletePropTypes() 422 423 " split inside prop, split first prop 424 let expected = SetupOneLine() " 'xonex xtwoxx' 425 exe "normal 0lli\<CR>\<Esc>" 426 call assert_equal('xo', getline(1)) 427 call assert_equal('nex xtwoxx', getline(2)) 428 let exp_first = [deepcopy(expected[0])] 429 let exp_first[0].length = 1 430 call assert_equal(exp_first, prop_list(1)) 431 let expected[0].col = 1 432 let expected[0].length = 2 433 let expected[1].col -= 2 434 call assert_equal(expected, prop_list(2)) 435 call DeletePropTypes() 436 437 " split just after first prop, second prop move to next line 438 let expected = SetupOneLine() " 'xonex xtwoxx' 439 exe "normal 0fea\<CR>\<Esc>" 440 call assert_equal('xone', getline(1)) 441 call assert_equal('x xtwoxx', getline(2)) 442 let exp_first = expected[0:0] 443 call assert_equal(exp_first, prop_list(1)) 444 let expected = expected[1:1] 445 let expected[0].col -= 4 446 call assert_equal(expected, prop_list(2)) 447 call DeletePropTypes() 448 449 bwipe! 450 set bs& 451endfunc 452 453func Test_prop_clear() 454 new 455 call AddPropTypes() 456 call SetupPropsInFirstLine() 457 call assert_equal(Get_expected_props(), prop_list(1)) 458 459 eval 1->prop_clear() 460 call assert_equal([], 1->prop_list()) 461 462 call DeletePropTypes() 463 bwipe! 464endfunc 465 466func Test_prop_clear_buf() 467 new 468 call AddPropTypes() 469 call SetupPropsInFirstLine() 470 let bufnr = bufnr('') 471 wincmd w 472 call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr})) 473 474 call prop_clear(1, 1, {'bufnr': bufnr}) 475 call assert_equal([], prop_list(1, {'bufnr': bufnr})) 476 477 wincmd w 478 call DeletePropTypes() 479 bwipe! 480endfunc 481 482func Test_prop_setline() 483 new 484 call AddPropTypes() 485 call SetupPropsInFirstLine() 486 call assert_equal(Get_expected_props(), prop_list(1)) 487 488 call setline(1, 'foobar') 489 call assert_equal([], prop_list(1)) 490 491 call DeletePropTypes() 492 bwipe! 493endfunc 494 495func Test_prop_setbufline() 496 new 497 call AddPropTypes() 498 call SetupPropsInFirstLine() 499 let bufnr = bufnr('') 500 wincmd w 501 call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr})) 502 503 call setbufline(bufnr, 1, 'foobar') 504 call assert_equal([], prop_list(1, {'bufnr': bufnr})) 505 506 wincmd w 507 call DeletePropTypes() 508 bwipe! 509endfunc 510 511func Test_prop_substitute() 512 new 513 " Set first line to 'one two three' 514 call AddPropTypes() 515 call SetupPropsInFirstLine() 516 let expected_props = Get_expected_props() 517 call assert_equal(expected_props, prop_list(1)) 518 519 " Change "n" in "one" to XX: 'oXXe two three' 520 s/n/XX/ 521 let expected_props[0].length += 1 522 let expected_props[1].length += 1 523 let expected_props[2].col += 1 524 let expected_props[3].col += 1 525 call assert_equal(expected_props, prop_list(1)) 526 527 " Delete "t" in "two" and "three" to XX: 'oXXe wo hree' 528 s/t//g 529 let expected_props[0].length -= 2 530 let expected_props[2].length -= 1 531 let expected_props[3].length -= 1 532 let expected_props[3].col -= 1 533 call assert_equal(expected_props, prop_list(1)) 534 535 " Split the line by changing w to line break: 'oXXe ', 'o hree' 536 " The long prop is split and spans both lines. 537 " The props on "two" and "three" move to the next line. 538 s/w/\r/ 539 let new_props = [ 540 \ copy(expected_props[0]), 541 \ copy(expected_props[2]), 542 \ copy(expected_props[3]), 543 \ ] 544 let expected_props[0].length = 5 545 unlet expected_props[3] 546 unlet expected_props[2] 547 call assert_equal(expected_props, prop_list(1)) 548 549 let new_props[0].length = 6 550 let new_props[1].col = 1 551 let new_props[1].length = 1 552 let new_props[2].col = 3 553 call assert_equal(new_props, prop_list(2)) 554 555 call DeletePropTypes() 556 bwipe! 557endfunc 558 559func Test_prop_change_indent() 560 call prop_type_add('comment', {'highlight': 'Directory'}) 561 new 562 call setline(1, [' xxx', 'yyyyy']) 563 call prop_add(2, 2, {'length': 2, 'type': 'comment'}) 564 let expect = {'col': 2, 'length': 2, 'type': 'comment', 'start': 1, 'end': 1, 'id': 0} 565 call assert_equal([expect], prop_list(2)) 566 567 set shiftwidth=3 568 normal 2G>> 569 call assert_equal(' yyyyy', getline(2)) 570 let expect.col += 3 571 call assert_equal([expect], prop_list(2)) 572 573 normal 2G== 574 call assert_equal(' yyyyy', getline(2)) 575 let expect.col = 6 576 call assert_equal([expect], prop_list(2)) 577 578 call prop_clear(2) 579 call prop_add(2, 2, {'length': 5, 'type': 'comment'}) 580 let expect.col = 2 581 let expect.length = 5 582 call assert_equal([expect], prop_list(2)) 583 584 normal 2G<< 585 call assert_equal(' yyyyy', getline(2)) 586 let expect.length = 2 587 call assert_equal([expect], prop_list(2)) 588 589 set shiftwidth& 590 call prop_type_delete('comment') 591endfunc 592 593" Setup a three line prop in lines 2 - 4. 594" Add short props in line 1 and 5. 595func Setup_three_line_prop() 596 new 597 call setline(1, ['one', 'twotwo', 'three', 'fourfour', 'five']) 598 call prop_add(1, 2, {'length': 1, 'type': 'comment'}) 599 call prop_add(2, 4, {'end_lnum': 4, 'end_col': 5, 'type': 'comment'}) 600 call prop_add(5, 2, {'length': 1, 'type': 'comment'}) 601endfunc 602 603func Test_prop_multiline() 604 eval 'comment'->prop_type_add({'highlight': 'Directory'}) 605 new 606 call setline(1, ['xxxxxxx', 'yyyyyyyyy', 'zzzzzzzz']) 607 608 " start halfway line 1, end halfway line 3 609 call prop_add(1, 3, {'end_lnum': 3, 'end_col': 5, 'type': 'comment'}) 610 let expect1 = {'col': 3, 'length': 6, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0} 611 call assert_equal([expect1], prop_list(1)) 612 let expect2 = {'col': 1, 'length': 10, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0} 613 call assert_equal([expect2], prop_list(2)) 614 let expect3 = {'col': 1, 'length': 4, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0} 615 call assert_equal([expect3], prop_list(3)) 616 call prop_clear(1, 3) 617 618 " include all three lines 619 call prop_add(1, 1, {'end_lnum': 3, 'end_col': 999, 'type': 'comment'}) 620 let expect1.col = 1 621 let expect1.length = 8 622 call assert_equal([expect1], prop_list(1)) 623 call assert_equal([expect2], prop_list(2)) 624 let expect3.length = 9 625 call assert_equal([expect3], prop_list(3)) 626 call prop_clear(1, 3) 627 628 bwipe! 629 630 " Test deleting the first line of a multi-line prop. 631 call Setup_three_line_prop() 632 let expect_short = {'col': 2, 'length': 1, 'type': 'comment', 'start': 1, 'end': 1, 'id': 0} 633 call assert_equal([expect_short], prop_list(1)) 634 let expect2 = {'col': 4, 'length': 4, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0} 635 call assert_equal([expect2], prop_list(2)) 636 2del 637 call assert_equal([expect_short], prop_list(1)) 638 let expect2 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0} 639 call assert_equal([expect2], prop_list(2)) 640 bwipe! 641 642 " Test deleting the last line of a multi-line prop. 643 call Setup_three_line_prop() 644 let expect3 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0} 645 call assert_equal([expect3], prop_list(3)) 646 let expect4 = {'col': 1, 'length': 4, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0} 647 call assert_equal([expect4], prop_list(4)) 648 4del 649 let expect3.end = 1 650 call assert_equal([expect3], prop_list(3)) 651 call assert_equal([expect_short], prop_list(4)) 652 bwipe! 653 654 " Test appending a line below the multi-line text prop start. 655 call Setup_three_line_prop() 656 let expect2 = {'col': 4, 'length': 4, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0} 657 call assert_equal([expect2], prop_list(2)) 658 call append(2, "new line") 659 call assert_equal([expect2], prop_list(2)) 660 let expect3 = {'col': 1, 'length': 9, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0} 661 call assert_equal([expect3], prop_list(3)) 662 bwipe! 663 664 call prop_type_delete('comment') 665endfunc 666 667func Test_prop_line2byte() 668 call prop_type_add('comment', {'highlight': 'Directory'}) 669 new 670 call setline(1, ['line1', 'second line', '']) 671 set ff=unix 672 call assert_equal(19, line2byte(3)) 673 call prop_add(1, 1, {'end_col': 3, 'type': 'comment'}) 674 call assert_equal(19, line2byte(3)) 675 676 bwipe! 677 call prop_type_delete('comment') 678endfunc 679 680func Test_prop_byte2line() 681 new 682 set ff=unix 683 call setline(1, ['one one', 'two two', 'three three', 'four four', 'five']) 684 call assert_equal(4, byte2line(line2byte(4))) 685 call assert_equal(5, byte2line(line2byte(5))) 686 687 call prop_type_add('prop', {'highlight': 'Directory'}) 688 call prop_add(3, 1, {'length': 5, 'type': 'prop'}) 689 call assert_equal(4, byte2line(line2byte(4))) 690 call assert_equal(5, byte2line(line2byte(5))) 691 692 bwipe! 693 call prop_type_delete('prop') 694endfunc 695 696func Test_prop_undo() 697 new 698 call prop_type_add('comment', {'highlight': 'Directory'}) 699 call setline(1, ['oneone', 'twotwo', 'three']) 700 " Set 'undolevels' to break changes into undo-able pieces. 701 set ul& 702 703 call prop_add(1, 3, {'end_col': 5, 'type': 'comment'}) 704 let expected = [{'col': 3, 'length': 2, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] 705 call assert_equal(expected, prop_list(1)) 706 707 " Insert a character, then undo. 708 exe "normal 0lllix\<Esc>" 709 set ul& 710 let expected[0].length = 3 711 call assert_equal(expected, prop_list(1)) 712 undo 713 let expected[0].length = 2 714 call assert_equal(expected, prop_list(1)) 715 716 " Delete a character, then undo 717 exe "normal 0lllx" 718 set ul& 719 let expected[0].length = 1 720 call assert_equal(expected, prop_list(1)) 721 undo 722 let expected[0].length = 2 723 call assert_equal(expected, prop_list(1)) 724 725 " Delete the line, then undo 726 1d 727 set ul& 728 call assert_equal([], prop_list(1)) 729 undo 730 call assert_equal(expected, prop_list(1)) 731 732 " Insert a character, delete two characters, then undo with "U" 733 exe "normal 0lllix\<Esc>" 734 set ul& 735 let expected[0].length = 3 736 call assert_equal(expected, prop_list(1)) 737 exe "normal 0lllxx" 738 set ul& 739 let expected[0].length = 1 740 call assert_equal(expected, prop_list(1)) 741 normal U 742 let expected[0].length = 2 743 call assert_equal(expected, prop_list(1)) 744 745 " substitute a word, then undo 746 call setline(1, 'the number 123 is highlighted.') 747 call prop_add(1, 12, {'length': 3, 'type': 'comment'}) 748 let expected = [{'col': 12, 'length': 3, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] 749 call assert_equal(expected, prop_list(1)) 750 set ul& 751 1s/number/foo 752 let expected[0].col = 9 753 call assert_equal(expected, prop_list(1)) 754 undo 755 let expected[0].col = 12 756 call assert_equal(expected, prop_list(1)) 757 call prop_clear(1) 758 759 " substitute with backslash 760 call setline(1, 'the number 123 is highlighted.') 761 call prop_add(1, 12, {'length': 3, 'type': 'comment'}) 762 let expected = [{'col': 12, 'length': 3, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] 763 call assert_equal(expected, prop_list(1)) 764 1s/the/\The 765 call assert_equal(expected, prop_list(1)) 766 1s/^/\\ 767 let expected[0].col += 1 768 call assert_equal(expected, prop_list(1)) 769 1s/^/\~ 770 let expected[0].col += 1 771 call assert_equal(expected, prop_list(1)) 772 1s/123/12\\3 773 let expected[0].length += 1 774 call assert_equal(expected, prop_list(1)) 775 call prop_clear(1) 776 777 bwipe! 778 call prop_type_delete('comment') 779endfunc 780 781func Test_prop_delete_text() 782 new 783 call prop_type_add('comment', {'highlight': 'Directory'}) 784 call setline(1, ['oneone', 'twotwo', 'three']) 785 786 " zero length property 787 call prop_add(1, 3, {'type': 'comment'}) 788 let expected = [{'col': 3, 'length': 0, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] 789 call assert_equal(expected, prop_list(1)) 790 791 " delete one char moves the property 792 normal! x 793 let expected = [{'col': 2, 'length': 0, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] 794 call assert_equal(expected, prop_list(1)) 795 796 " delete char of the property has no effect 797 normal! lx 798 let expected = [{'col': 2, 'length': 0, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] 799 call assert_equal(expected, prop_list(1)) 800 801 " delete more chars moves property to first column, is not deleted 802 normal! 0xxxx 803 let expected = [{'col': 1, 'length': 0, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] 804 call assert_equal(expected, prop_list(1)) 805 806 bwipe! 807 call prop_type_delete('comment') 808endfunc 809 810" screenshot test with textprop highlighting 811func Test_textprop_screenshot_various() 812 CheckScreendump 813 " The Vim running in the terminal needs to use utf-8. 814 if g:orig_encoding != 'utf-8' 815 throw 'Skipped: not using utf-8' 816 endif 817 call writefile([ 818 \ "call setline(1, [" 819 \ .. "'One two'," 820 \ .. "'Numbér 123 änd thœn 4¾7.'," 821 \ .. "'--aa--bb--cc--dd--'," 822 \ .. "'// comment with error in it'," 823 \ .. "'first line'," 824 \ .. "' second line '," 825 \ .. "'third line'," 826 \ .. "' fourth line'," 827 \ .. "])", 828 \ "hi NumberProp ctermfg=blue", 829 \ "hi LongProp ctermbg=yellow", 830 \ "hi BackgroundProp ctermbg=lightgrey", 831 \ "hi UnderlineProp cterm=underline", 832 \ "call prop_type_add('number', {'highlight': 'NumberProp'})", 833 \ "call prop_type_add('long', {'highlight': 'NumberProp'})", 834 \ "call prop_type_change('long', {'highlight': 'LongProp'})", 835 \ "call prop_type_add('start', {'highlight': 'NumberProp', 'start_incl': 1})", 836 \ "call prop_type_add('end', {'highlight': 'NumberProp', 'end_incl': 1})", 837 \ "call prop_type_add('both', {'highlight': 'NumberProp', 'start_incl': 1, 'end_incl': 1})", 838 \ "call prop_type_add('background', {'highlight': 'BackgroundProp', 'combine': 0})", 839 \ "call prop_type_add('backgroundcomb', {'highlight': 'NumberProp', 'combine': 1})", 840 \ "eval 'backgroundcomb'->prop_type_change({'highlight': 'BackgroundProp'})", 841 \ "call prop_type_add('error', {'highlight': 'UnderlineProp'})", 842 \ "call prop_add(1, 4, {'end_lnum': 3, 'end_col': 3, 'type': 'long'})", 843 \ "call prop_add(2, 9, {'length': 3, 'type': 'number'})", 844 \ "call prop_add(2, 24, {'length': 4, 'type': 'number'})", 845 \ "call prop_add(3, 3, {'length': 2, 'type': 'number'})", 846 \ "call prop_add(3, 7, {'length': 2, 'type': 'start'})", 847 \ "call prop_add(3, 11, {'length': 2, 'type': 'end'})", 848 \ "call prop_add(3, 15, {'length': 2, 'type': 'both'})", 849 \ "call prop_add(4, 6, {'length': 3, 'type': 'background'})", 850 \ "call prop_add(4, 12, {'length': 10, 'type': 'backgroundcomb'})", 851 \ "call prop_add(4, 17, {'length': 5, 'type': 'error'})", 852 \ "call prop_add(5, 7, {'length': 4, 'type': 'long'})", 853 \ "call prop_add(6, 1, {'length': 8, 'type': 'long'})", 854 \ "call prop_add(8, 1, {'length': 1, 'type': 'long'})", 855 \ "call prop_add(8, 11, {'length': 4, 'type': 'long'})", 856 \ "set number cursorline", 857 \ "hi clear SpellBad", 858 \ "set spell", 859 \ "syn match Comment '//.*'", 860 \ "hi Comment ctermfg=green", 861 \ "normal 3G0llix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>", 862 \ "normal 3G0lli\<BS>\<Esc>", 863 \ "normal 6G0i\<BS>\<Esc>", 864 \ "normal 3J", 865 \ "normal 3G", 866 \], 'XtestProp') 867 let buf = RunVimInTerminal('-S XtestProp', {'rows': 8}) 868 call VerifyScreenDump(buf, 'Test_textprop_01', {}) 869 870 " clean up 871 call StopVimInTerminal(buf) 872 call delete('XtestProp') 873endfunc 874 875func RunTestVisualBlock(width, dump) 876 call writefile([ 877 \ "call setline(1, [" 878 \ .. "'xxxxxxxxx 123 x'," 879 \ .. "'xxxxxxxx 123 x'," 880 \ .. "'xxxxxxx 123 x'," 881 \ .. "'xxxxxx 123 x'," 882 \ .. "'xxxxx 123 x'," 883 \ .. "'xxxx 123 xx'," 884 \ .. "'xxx 123 xxx'," 885 \ .. "'xx 123 xxxx'," 886 \ .. "'x 123 xxxxx'," 887 \ .. "' 123 xxxxxx'," 888 \ .. "])", 889 \ "hi SearchProp ctermbg=yellow", 890 \ "call prop_type_add('search', {'highlight': 'SearchProp'})", 891 \ "call prop_add(1, 11, {'length': 3, 'type': 'search'})", 892 \ "call prop_add(2, 10, {'length': 3, 'type': 'search'})", 893 \ "call prop_add(3, 9, {'length': 3, 'type': 'search'})", 894 \ "call prop_add(4, 8, {'length': 3, 'type': 'search'})", 895 \ "call prop_add(5, 7, {'length': 3, 'type': 'search'})", 896 \ "call prop_add(6, 6, {'length': 3, 'type': 'search'})", 897 \ "call prop_add(7, 5, {'length': 3, 'type': 'search'})", 898 \ "call prop_add(8, 4, {'length': 3, 'type': 'search'})", 899 \ "call prop_add(9, 3, {'length': 3, 'type': 'search'})", 900 \ "call prop_add(10, 2, {'length': 3, 'type': 'search'})", 901 \ "normal 1G6|\<C-V>" .. repeat('l', a:width - 1) .. "10jx", 902 \], 'XtestPropVis') 903 let buf = RunVimInTerminal('-S XtestPropVis', {'rows': 12}) 904 call VerifyScreenDump(buf, 'Test_textprop_vis_' .. a:dump, {}) 905 906 " clean up 907 call StopVimInTerminal(buf) 908 call delete('XtestPropVis') 909endfunc 910 911" screenshot test with Visual block mode operations 912func Test_textprop_screenshot_visual() 913 CheckScreendump 914 915 " Delete two columns while text props are three chars wide. 916 call RunTestVisualBlock(2, '01') 917 918 " Same, but delete four columns 919 call RunTestVisualBlock(4, '02') 920endfunc 921 922func Test_textprop_after_tab() 923 CheckScreendump 924 925 let lines =<< trim END 926 call setline(1, [ 927 \ "\txxx", 928 \ "x\txxx", 929 \ ]) 930 hi SearchProp ctermbg=yellow 931 call prop_type_add('search', {'highlight': 'SearchProp'}) 932 call prop_add(1, 2, {'length': 3, 'type': 'search'}) 933 call prop_add(2, 3, {'length': 3, 'type': 'search'}) 934 END 935 call writefile(lines, 'XtestPropTab') 936 let buf = RunVimInTerminal('-S XtestPropTab', {'rows': 6}) 937 call VerifyScreenDump(buf, 'Test_textprop_tab', {}) 938 939 " clean up 940 call StopVimInTerminal(buf) 941 call delete('XtestPropTab') 942endfunc 943 944func Test_textprop_with_syntax() 945 CheckScreendump 946 947 let lines =<< trim END 948 call setline(1, [ 949 \ "(abc)", 950 \ ]) 951 syn match csParens "[()]" display 952 hi! link csParens MatchParen 953 954 call prop_type_add('TPTitle', #{ highlight: 'Title' }) 955 call prop_add(1, 2, #{type: 'TPTitle', end_col: 5}) 956 END 957 call writefile(lines, 'XtestPropSyn') 958 let buf = RunVimInTerminal('-S XtestPropSyn', {'rows': 6}) 959 call VerifyScreenDump(buf, 'Test_textprop_syn_1', {}) 960 961 " clean up 962 call StopVimInTerminal(buf) 963 call delete('XtestPropSyn') 964endfunc 965 966" Adding a text property to a new buffer should not fail 967func Test_textprop_empty_buffer() 968 call prop_type_add('comment', {'highlight': 'Search'}) 969 new 970 call prop_add(1, 1, {'type': 'comment'}) 971 close 972 call prop_type_delete('comment') 973endfunc 974 975" Adding a text property with invalid highlight should be ignored. 976func Test_textprop_invalid_highlight() 977 call assert_fails("call prop_type_add('dni', {'highlight': 'DoesNotExist'})", 'E970:') 978 new 979 call setline(1, ['asdf','asdf']) 980 call prop_add(1, 1, {'length': 4, 'type': 'dni'}) 981 redraw 982 bwipe! 983 call prop_type_delete('dni') 984endfunc 985 986" Adding a text property to an empty buffer and then editing another 987func Test_textprop_empty_buffer_next() 988 call prop_type_add("xxx", {}) 989 call prop_add(1, 1, {"type": "xxx"}) 990 next X 991 call prop_type_delete('xxx') 992endfunc 993 994func Test_textprop_remove_from_buf() 995 new 996 let buf = bufnr('') 997 call prop_type_add('one', {'bufnr': buf}) 998 call prop_add(1, 1, {'type': 'one', 'id': 234}) 999 file x 1000 edit y 1001 call prop_remove({'id': 234, 'bufnr': buf}, 1) 1002 call prop_type_delete('one', {'bufnr': buf}) 1003 bwipe! x 1004 close 1005endfunc 1006 1007func Test_textprop_in_unloaded_buf() 1008 edit Xaaa 1009 call setline(1, 'aaa') 1010 write 1011 edit Xbbb 1012 call setline(1, 'bbb') 1013 write 1014 let bnr = bufnr('') 1015 edit Xaaa 1016 1017 call prop_type_add('ErrorMsg', #{highlight:'ErrorMsg'}) 1018 call assert_fails("call prop_add(1, 1, #{end_lnum: 1, endcol: 2, type: 'ErrorMsg', bufnr: bnr})", 'E275:') 1019 exe 'buf ' .. bnr 1020 call assert_equal('bbb', getline(1)) 1021 call assert_equal(0, prop_list(1)->len()) 1022 1023 bwipe! Xaaa 1024 bwipe! Xbbb 1025 cal delete('Xaaa') 1026 cal delete('Xbbb') 1027endfunc 1028 1029func Test_proptype_substitute2() 1030 new 1031 " text_prop.vim 1032 call setline(1, [ 1033 \ 'The num 123 is smaller than 4567.', 1034 \ '123 The number 123 is smaller than 4567.', 1035 \ '123 The number 123 is smaller than 4567.']) 1036 1037 call prop_type_add('number', {'highlight': 'ErrorMsg'}) 1038 1039 call prop_add(1, 12, {'length': 3, 'type': 'number'}) 1040 call prop_add(2, 1, {'length': 3, 'type': 'number'}) 1041 call prop_add(3, 36, {'length': 4, 'type': 'number'}) 1042 set ul& 1043 let expected = [{'id': 0, 'col': 13, 'end': 1, 'type': 'number', 'length': 3, 'start': 1}, 1044 \ {'id': 0, 'col': 1, 'end': 1, 'type': 'number', 'length': 3, 'start': 1}, 1045 \ {'id': 0, 'col': 50, 'end': 1, 'type': 'number', 'length': 4, 'start': 1}] 1046 " Add some text in between 1047 %s/\s\+/ /g 1048 call assert_equal(expected, prop_list(1) + prop_list(2) + prop_list(3)) 1049 1050 " remove some text 1051 :1s/[a-z]\{3\}//g 1052 let expected = [{'id': 0, 'col': 10, 'end': 1, 'type': 'number', 'length': 3, 'start': 1}] 1053 call assert_equal(expected, prop_list(1)) 1054 bwipe! 1055endfunc 1056 1057func SaveOptions() 1058 let d = #{tabstop: &tabstop, 1059 \ softtabstop: &softtabstop, 1060 \ shiftwidth: &shiftwidth, 1061 \ expandtab: &expandtab, 1062 \ foldmethod: '"' .. &foldmethod .. '"', 1063 \ } 1064 return d 1065endfunc 1066 1067func RestoreOptions(dict) 1068 for name in keys(a:dict) 1069 exe 'let &' .. name .. ' = ' .. a:dict[name] 1070 endfor 1071endfunc 1072 1073func Test_textprop_noexpandtab() 1074 new 1075 let save_dict = SaveOptions() 1076 1077 set tabstop=8 1078 set softtabstop=4 1079 set shiftwidth=4 1080 set noexpandtab 1081 set foldmethod=marker 1082 1083 call feedkeys("\<esc>\<esc>0Ca\<cr>\<esc>\<up>", "tx") 1084 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1085 call prop_add(1, 1, {'end_col': 2, 'type': 'test'}) 1086 call feedkeys("0i\<tab>", "tx") 1087 call prop_remove({'type': 'test'}) 1088 call prop_add(1, 2, {'end_col': 3, 'type': 'test'}) 1089 call feedkeys("A\<left>\<tab>", "tx") 1090 call prop_remove({'type': 'test'}) 1091 try 1092 " It is correct that this does not pass 1093 call prop_add(1, 6, {'end_col': 7, 'type': 'test'}) 1094 " Has already collapsed here, start_col:6 does not result in an error 1095 call feedkeys("A\<left>\<tab>", "tx") 1096 catch /^Vim\%((\a\+)\)\=:E964/ 1097 endtry 1098 call prop_remove({'type': 'test'}) 1099 call prop_type_delete('test') 1100 1101 call RestoreOptions(save_dict) 1102 bwipe! 1103endfunc 1104 1105func Test_textprop_noexpandtab_redraw() 1106 new 1107 let save_dict = SaveOptions() 1108 1109 set tabstop=8 1110 set softtabstop=4 1111 set shiftwidth=4 1112 set noexpandtab 1113 set foldmethod=marker 1114 1115 call feedkeys("\<esc>\<esc>0Ca\<cr>\<space>\<esc>\<up>", "tx") 1116 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1117 call prop_add(1, 1, {'end_col': 2, 'type': 'test'}) 1118 call feedkeys("0i\<tab>", "tx") 1119 " Internally broken at the next line 1120 call feedkeys("A\<left>\<tab>", "tx") 1121 redraw 1122 " Index calculation failed internally on next line 1123 call prop_add(1, 1, {'end_col': 2, 'type': 'test'}) 1124 call prop_remove({'type': 'test', 'all': v:true}) 1125 call prop_type_delete('test') 1126 call prop_type_delete('test') 1127 1128 call RestoreOptions(save_dict) 1129 bwipe! 1130endfunc 1131 1132func Test_textprop_ins_str() 1133 new 1134 call setline(1, 'just some text') 1135 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1136 call prop_add(1, 1, {'end_col': 2, 'type': 'test'}) 1137 call assert_equal([{'id': 0, 'col': 1, 'end': 1, 'type': 'test', 'length': 1, 'start': 1}], prop_list(1)) 1138 1139 call feedkeys("foi\<F8>\<Esc>", "tx") 1140 call assert_equal('just s<F8>ome text', getline(1)) 1141 call assert_equal([{'id': 0, 'col': 1, 'end': 1, 'type': 'test', 'length': 1, 'start': 1}], prop_list(1)) 1142 1143 bwipe! 1144 call prop_remove({'type': 'test'}) 1145 call prop_type_delete('test') 1146endfunc 1147