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