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') 214 215 bwipe! 216endfunc 217 218def Test_prop_find2() 219 # Multiple props per line, start on the first, should find the second. 220 new 221 ['the quikc bronw fox jumsp over the layz dog']->repeat(2)->setline(1) 222 prop_type_add('misspell', {highlight: 'ErrorMsg'}) 223 for lnum in [1, 2] 224 for col in [8, 14, 24, 38] 225 prop_add(lnum, col, {type: 'misspell', length: 2}) 226 endfor 227 endfor 228 cursor(1, 8) 229 var expected = {lnum: 1, id: 0, col: 14, end: 1, type: 'misspell', length: 2, start: 1} 230 var result = prop_find({type: 'misspell', skipstart: true}, 'f') 231 assert_equal(expected, result) 232 233 prop_type_delete('misspell') 234 bwipe! 235enddef 236 237func Test_prop_find_smaller_len_than_match_col() 238 new 239 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 240 call setline(1, ['xxxx', 'x']) 241 call prop_add(1, 4, {'type': 'test'}) 242 call assert_equal({'id': 0, 'lnum': 1, 'col': 4, 'type': 'test', 'length': 0, 'start': 1, 'end': 1}, 243 \ prop_find({'type': 'test', 'lnum': 2, 'col': 1}, 'b')) 244 bwipe! 245 call prop_type_delete('test') 246endfunc 247 248func Test_prop_find_with_both_option_enabled() 249 " Initialize 250 new 251 call AddPropTypes() 252 call SetupPropsInFirstLine() 253 let props = Get_expected_props()->map({_, v -> extend(v, {'lnum': 1})}) 254 " Test 255 call assert_fails("call prop_find({'both': 1})", 'E968:') 256 call assert_fails("call prop_find({'id': 11, 'both': 1})", 'E860:') 257 call assert_fails("call prop_find({'type': 'three', 'both': 1})", 'E860:') 258 call assert_equal({}, prop_find({'id': 11, 'type': 'three', 'both': 1})) 259 call assert_equal({}, prop_find({'id': 130000, 'type': 'one', 'both': 1})) 260 call assert_equal(props[2], prop_find({'id': 12, 'type': 'two', 'both': 1})) 261 call assert_equal(props[0], prop_find({'id': 14, 'type': 'whole', 'both': 1})) 262 " Clean up 263 call DeletePropTypes() 264 bwipe! 265endfunc 266 267func Test_prop_add() 268 new 269 call AddPropTypes() 270 call SetupPropsInFirstLine() 271 let expected_props = Get_expected_props() 272 call assert_equal(expected_props, prop_list(1)) 273 call assert_fails("call prop_add(10, 1, {'length': 1, 'id': 14, 'type': 'whole'})", 'E966:') 274 call assert_fails("call prop_add(1, 22, {'length': 1, 'id': 14, 'type': 'whole'})", 'E964:') 275 276 " Insert a line above, text props must still be there. 277 call append(0, 'empty') 278 call assert_equal(expected_props, prop_list(2)) 279 " Delete a line above, text props must still be there. 280 1del 281 call assert_equal(expected_props, prop_list(1)) 282 283 " Prop without length or end column is zero length 284 call prop_clear(1) 285 call prop_type_add('included', {'start_incl': 1, 'end_incl': 1}) 286 call prop_add(1, 5, #{type: 'included'}) 287 let expected = [#{col: 5, length: 0, type: 'included', id: 0, start: 1, end: 1}] 288 call assert_equal(expected, prop_list(1)) 289 290 " Inserting text makes the prop bigger. 291 exe "normal 5|ixx\<Esc>" 292 let expected = [#{col: 5, length: 2, type: 'included', id: 0, start: 1, end: 1}] 293 call assert_equal(expected, prop_list(1)) 294 295 call assert_fails("call prop_add(1, 5, {'type': 'two', 'bufnr': 234343})", 'E158:') 296 297 call DeletePropTypes() 298 call prop_type_delete('included') 299 bwipe! 300endfunc 301 302func Test_prop_remove() 303 new 304 call AddPropTypes() 305 call SetupPropsInFirstLine() 306 let props = Get_expected_props() 307 call assert_equal(props, prop_list(1)) 308 309 " remove by id 310 call assert_equal(1, {'id': 12}->prop_remove(1)) 311 unlet props[2] 312 call assert_equal(props, prop_list(1)) 313 314 " remove by type 315 call assert_equal(1, prop_remove({'type': 'one'}, 1)) 316 unlet props[1] 317 call assert_equal(props, prop_list(1)) 318 319 " remove from unknown buffer 320 call assert_fails("call prop_remove({'type': 'one', 'bufnr': 123456}, 1)", 'E158:') 321 322 call DeletePropTypes() 323 bwipe! 324 325 new 326 call AddPropTypes() 327 call SetupPropsInFirstLine() 328 call prop_add(1, 6, {'length': 2, 'id': 11, 'type': 'three'}) 329 let props = Get_expected_props() 330 call insert(props, {'col': 6, 'length': 2, 'id': 11, 'type': 'three', 'start': 1, 'end': 1}, 3) 331 call assert_equal(props, prop_list(1)) 332 call assert_equal(1, prop_remove({'type': 'three', 'id': 11, 'both': 1, 'all': 1}, 1)) 333 unlet props[3] 334 call assert_equal(props, prop_list(1)) 335 336 call assert_fails("call prop_remove({'id': 11, 'both': 1})", 'E860:') 337 call assert_fails("call prop_remove({'type': 'three', 'both': 1})", 'E860:') 338 339 call DeletePropTypes() 340 bwipe! 341endfunc 342 343def Test_prop_add_vim9() 344 prop_type_add('comment', { 345 highlight: 'Directory', 346 priority: 123, 347 start_incl: true, 348 end_incl: true, 349 combine: false, 350 }) 351 prop_type_delete('comment') 352enddef 353 354def Test_prop_remove_vim9() 355 new 356 AddPropTypes() 357 SetupPropsInFirstLine() 358 assert_equal(1, prop_remove({type: 'three', id: 13, both: true, all: true})) 359 DeletePropTypes() 360 bwipe! 361enddef 362 363func SetupOneLine() 364 call setline(1, 'xonex xtwoxx') 365 normal gg0 366 call AddPropTypes() 367 call prop_add(1, 2, {'length': 3, 'id': 11, 'type': 'one'}) 368 call prop_add(1, 8, {'length': 3, 'id': 12, 'type': 'two'}) 369 let expected = [ 370 \ {'col': 2, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1}, 371 \ {'col': 8, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1}, 372 \] 373 call assert_equal(expected, prop_list(1)) 374 return expected 375endfunc 376 377func Test_prop_add_remove_buf() 378 new 379 let bufnr = bufnr('') 380 call AddPropTypes() 381 for lnum in range(1, 4) 382 call setline(lnum, 'one two three') 383 endfor 384 wincmd w 385 for lnum in range(1, 4) 386 call prop_add(lnum, 1, {'length': 3, 'id': 11, 'type': 'one', 'bufnr': bufnr}) 387 call prop_add(lnum, 5, {'length': 3, 'id': 12, 'type': 'two', 'bufnr': bufnr}) 388 call prop_add(lnum, 11, {'length': 3, 'id': 13, 'type': 'three', 'bufnr': bufnr}) 389 endfor 390 391 let props = [ 392 \ {'col': 1, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1}, 393 \ {'col': 5, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1}, 394 \ {'col': 11, 'length': 3, 'id': 13, 'type': 'three', 'start': 1, 'end': 1}, 395 \] 396 call assert_equal(props, prop_list(1, {'bufnr': bufnr})) 397 398 " remove by id 399 let before_props = deepcopy(props) 400 unlet props[1] 401 402 call prop_remove({'id': 12, 'bufnr': bufnr}, 1) 403 call assert_equal(props, prop_list(1, {'bufnr': bufnr})) 404 call assert_equal(before_props, prop_list(2, {'bufnr': bufnr})) 405 call assert_equal(before_props, prop_list(3, {'bufnr': bufnr})) 406 call assert_equal(before_props, prop_list(4, {'bufnr': bufnr})) 407 408 call prop_remove({'id': 12, 'bufnr': bufnr}, 3, 4) 409 call assert_equal(props, prop_list(1, {'bufnr': bufnr})) 410 call assert_equal(before_props, prop_list(2, {'bufnr': bufnr})) 411 call assert_equal(props, prop_list(3, {'bufnr': bufnr})) 412 call assert_equal(props, prop_list(4, {'bufnr': bufnr})) 413 414 call prop_remove({'id': 12, 'bufnr': bufnr}) 415 for lnum in range(1, 4) 416 call assert_equal(props, prop_list(lnum, {'bufnr': bufnr})) 417 endfor 418 419 " remove by type 420 let before_props = deepcopy(props) 421 unlet props[0] 422 423 call prop_remove({'type': 'one', 'bufnr': bufnr}, 1) 424 call assert_equal(props, prop_list(1, {'bufnr': bufnr})) 425 call assert_equal(before_props, prop_list(2, {'bufnr': bufnr})) 426 call assert_equal(before_props, prop_list(3, {'bufnr': bufnr})) 427 call assert_equal(before_props, prop_list(4, {'bufnr': bufnr})) 428 429 call prop_remove({'type': 'one', 'bufnr': bufnr}, 3, 4) 430 call assert_equal(props, prop_list(1, {'bufnr': bufnr})) 431 call assert_equal(before_props, prop_list(2, {'bufnr': bufnr})) 432 call assert_equal(props, prop_list(3, {'bufnr': bufnr})) 433 call assert_equal(props, prop_list(4, {'bufnr': bufnr})) 434 435 call prop_remove({'type': 'one', 'bufnr': bufnr}) 436 for lnum in range(1, 4) 437 call assert_equal(props, prop_list(lnum, {'bufnr': bufnr})) 438 endfor 439 440 call DeletePropTypes() 441 wincmd w 442 bwipe! 443endfunc 444 445func Test_prop_backspace() 446 new 447 set bs=2 448 let expected = SetupOneLine() " 'xonex xtwoxx' 449 450 exe "normal 0li\<BS>\<Esc>fxli\<BS>\<Esc>" 451 call assert_equal('one xtwoxx', getline(1)) 452 let expected[0].col = 1 453 let expected[1].col = 6 454 call assert_equal(expected, prop_list(1)) 455 456 call DeletePropTypes() 457 bwipe! 458 set bs& 459endfunc 460 461func Test_prop_replace() 462 new 463 set bs=2 464 let expected = SetupOneLine() " 'xonex xtwoxx' 465 466 exe "normal 0Ryyy\<Esc>" 467 call assert_equal('yyyex xtwoxx', getline(1)) 468 call assert_equal(expected, prop_list(1)) 469 470 exe "normal ftRyy\<BS>" 471 call assert_equal('yyyex xywoxx', getline(1)) 472 call assert_equal(expected, prop_list(1)) 473 474 exe "normal 0fwRyy\<BS>" 475 call assert_equal('yyyex xyyoxx', getline(1)) 476 call assert_equal(expected, prop_list(1)) 477 478 exe "normal 0foRyy\<BS>\<BS>" 479 call assert_equal('yyyex xyyoxx', getline(1)) 480 call assert_equal(expected, prop_list(1)) 481 482 call DeletePropTypes() 483 bwipe! 484 set bs& 485endfunc 486 487func Test_prop_open_line() 488 new 489 490 " open new line, props stay in top line 491 let expected = SetupOneLine() " 'xonex xtwoxx' 492 exe "normal o\<Esc>" 493 call assert_equal('xonex xtwoxx', getline(1)) 494 call assert_equal('', getline(2)) 495 call assert_equal(expected, prop_list(1)) 496 call DeletePropTypes() 497 498 " move all props to next line 499 let expected = SetupOneLine() " 'xonex xtwoxx' 500 exe "normal 0i\<CR>\<Esc>" 501 call assert_equal('', getline(1)) 502 call assert_equal('xonex xtwoxx', getline(2)) 503 call assert_equal(expected, prop_list(2)) 504 call DeletePropTypes() 505 506 " split just before prop, move all props to next line 507 let expected = SetupOneLine() " 'xonex xtwoxx' 508 exe "normal 0li\<CR>\<Esc>" 509 call assert_equal('x', getline(1)) 510 call assert_equal('onex xtwoxx', getline(2)) 511 let expected[0].col -= 1 512 let expected[1].col -= 1 513 call assert_equal(expected, prop_list(2)) 514 call DeletePropTypes() 515 516 " split inside prop, split first prop 517 let expected = SetupOneLine() " 'xonex xtwoxx' 518 exe "normal 0lli\<CR>\<Esc>" 519 call assert_equal('xo', getline(1)) 520 call assert_equal('nex xtwoxx', getline(2)) 521 let exp_first = [deepcopy(expected[0])] 522 let exp_first[0].length = 1 523 let exp_first[0].end = 0 524 call assert_equal(exp_first, prop_list(1)) 525 let expected[0].col = 1 526 let expected[0].length = 2 527 let expected[0].start = 0 528 let expected[1].col -= 2 529 call assert_equal(expected, prop_list(2)) 530 call DeletePropTypes() 531 532 " split just after first prop, second prop move to next line 533 let expected = SetupOneLine() " 'xonex xtwoxx' 534 exe "normal 0fea\<CR>\<Esc>" 535 call assert_equal('xone', getline(1)) 536 call assert_equal('x xtwoxx', getline(2)) 537 let exp_first = expected[0:0] 538 call assert_equal(exp_first, prop_list(1)) 539 let expected = expected[1:1] 540 let expected[0].col -= 4 541 call assert_equal(expected, prop_list(2)) 542 call DeletePropTypes() 543 544 bwipe! 545 set bs& 546endfunc 547 548func Test_prop_clear() 549 new 550 call AddPropTypes() 551 call SetupPropsInFirstLine() 552 call assert_equal(Get_expected_props(), prop_list(1)) 553 554 eval 1->prop_clear() 555 call assert_equal([], 1->prop_list()) 556 557 call DeletePropTypes() 558 bwipe! 559endfunc 560 561func Test_prop_clear_buf() 562 new 563 call AddPropTypes() 564 call SetupPropsInFirstLine() 565 let bufnr = bufnr('') 566 wincmd w 567 call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr})) 568 569 call prop_clear(1, 1, {'bufnr': bufnr}) 570 call assert_equal([], prop_list(1, {'bufnr': bufnr})) 571 572 wincmd w 573 call DeletePropTypes() 574 bwipe! 575endfunc 576 577func Test_prop_setline() 578 new 579 call AddPropTypes() 580 call SetupPropsInFirstLine() 581 call assert_equal(Get_expected_props(), prop_list(1)) 582 583 call setline(1, 'foobar') 584 call assert_equal([], prop_list(1)) 585 586 call DeletePropTypes() 587 bwipe! 588endfunc 589 590func Test_prop_setbufline() 591 new 592 call AddPropTypes() 593 call SetupPropsInFirstLine() 594 let bufnr = bufnr('') 595 wincmd w 596 call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr})) 597 598 call setbufline(bufnr, 1, 'foobar') 599 call assert_equal([], prop_list(1, {'bufnr': bufnr})) 600 601 wincmd w 602 call DeletePropTypes() 603 bwipe! 604endfunc 605 606func Test_prop_substitute() 607 new 608 " Set first line to 'one two three' 609 call AddPropTypes() 610 call SetupPropsInFirstLine() 611 let expected_props = Get_expected_props() 612 call assert_equal(expected_props, prop_list(1)) 613 614 " Change "n" in "one" to XX: 'oXXe two three' 615 s/n/XX/ 616 let expected_props[0].length += 1 617 let expected_props[1].length += 1 618 let expected_props[2].col += 1 619 let expected_props[3].col += 1 620 call assert_equal(expected_props, prop_list(1)) 621 622 " Delete "t" in "two" and "three" to XX: 'oXXe wo hree' 623 s/t//g 624 let expected_props[0].length -= 2 625 let expected_props[2].length -= 1 626 let expected_props[3].length -= 1 627 let expected_props[3].col -= 1 628 call assert_equal(expected_props, prop_list(1)) 629 630 " Split the line by changing w to line break: 'oXXe ', 'o hree' 631 " The long prop is split and spans both lines. 632 " The props on "two" and "three" move to the next line. 633 s/w/\r/ 634 let new_props = [ 635 \ copy(expected_props[0]), 636 \ copy(expected_props[2]), 637 \ copy(expected_props[3]), 638 \ ] 639 let expected_props[0].length = 5 640 let expected_props[0].end = 0 641 unlet expected_props[3] 642 unlet expected_props[2] 643 call assert_equal(expected_props, prop_list(1)) 644 645 let new_props[0].length = 6 646 let new_props[0].start = 0 647 let new_props[1].col = 1 648 let new_props[1].length = 1 649 let new_props[2].col = 3 650 call assert_equal(new_props, prop_list(2)) 651 652 call DeletePropTypes() 653 bwipe! 654endfunc 655 656func Test_prop_change_indent() 657 call prop_type_add('comment', {'highlight': 'Directory'}) 658 new 659 call setline(1, [' xxx', 'yyyyy']) 660 call prop_add(2, 2, {'length': 2, 'type': 'comment'}) 661 let expect = {'col': 2, 'length': 2, 'type': 'comment', 'start': 1, 'end': 1, 'id': 0} 662 call assert_equal([expect], prop_list(2)) 663 664 set shiftwidth=3 665 normal 2G>> 666 call assert_equal(' yyyyy', getline(2)) 667 let expect.col += 3 668 call assert_equal([expect], prop_list(2)) 669 670 normal 2G== 671 call assert_equal(' yyyyy', getline(2)) 672 let expect.col = 6 673 call assert_equal([expect], prop_list(2)) 674 675 call prop_clear(2) 676 call prop_add(2, 2, {'length': 5, 'type': 'comment'}) 677 let expect.col = 2 678 let expect.length = 5 679 call assert_equal([expect], prop_list(2)) 680 681 normal 2G<< 682 call assert_equal(' yyyyy', getline(2)) 683 let expect.length = 2 684 call assert_equal([expect], prop_list(2)) 685 686 set shiftwidth& 687 call prop_type_delete('comment') 688endfunc 689 690" Setup a three line prop in lines 2 - 4. 691" Add short props in line 1 and 5. 692func Setup_three_line_prop() 693 new 694 call setline(1, ['one', 'twotwo', 'three', 'fourfour', 'five']) 695 call prop_add(1, 2, {'length': 1, 'type': 'comment'}) 696 call prop_add(2, 4, {'end_lnum': 4, 'end_col': 5, 'type': 'comment'}) 697 call prop_add(5, 2, {'length': 1, 'type': 'comment'}) 698endfunc 699 700func Test_prop_multiline() 701 eval 'comment'->prop_type_add({'highlight': 'Directory'}) 702 new 703 call setline(1, ['xxxxxxx', 'yyyyyyyyy', 'zzzzzzzz']) 704 705 " start halfway line 1, end halfway line 3 706 call prop_add(1, 3, {'end_lnum': 3, 'end_col': 5, 'type': 'comment'}) 707 let expect1 = {'col': 3, 'length': 6, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0} 708 call assert_equal([expect1], prop_list(1)) 709 let expect2 = {'col': 1, 'length': 10, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0} 710 call assert_equal([expect2], prop_list(2)) 711 let expect3 = {'col': 1, 'length': 4, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0} 712 call assert_equal([expect3], prop_list(3)) 713 call prop_clear(1, 3) 714 715 " include all three lines 716 call prop_add(1, 1, {'end_lnum': 3, 'end_col': 999, 'type': 'comment'}) 717 let expect1.col = 1 718 let expect1.length = 8 719 call assert_equal([expect1], prop_list(1)) 720 call assert_equal([expect2], prop_list(2)) 721 let expect3.length = 9 722 call assert_equal([expect3], prop_list(3)) 723 call prop_clear(1, 3) 724 725 bwipe! 726 727 " Test deleting the first line of a multi-line prop. 728 call Setup_three_line_prop() 729 let expect_short = {'col': 2, 'length': 1, 'type': 'comment', 'start': 1, 'end': 1, 'id': 0} 730 call assert_equal([expect_short], prop_list(1)) 731 let expect2 = {'col': 4, 'length': 4, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0} 732 call assert_equal([expect2], prop_list(2)) 733 2del 734 call assert_equal([expect_short], prop_list(1)) 735 let expect2 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0} 736 call assert_equal([expect2], prop_list(2)) 737 bwipe! 738 739 " Test deleting the last line of a multi-line prop. 740 call Setup_three_line_prop() 741 let expect3 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0} 742 call assert_equal([expect3], prop_list(3)) 743 let expect4 = {'col': 1, 'length': 4, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0} 744 call assert_equal([expect4], prop_list(4)) 745 4del 746 let expect3.end = 1 747 call assert_equal([expect3], prop_list(3)) 748 call assert_equal([expect_short], prop_list(4)) 749 bwipe! 750 751 " Test appending a line below the multi-line text prop start. 752 call Setup_three_line_prop() 753 let expect2 = {'col': 4, 'length': 4, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0} 754 call assert_equal([expect2], prop_list(2)) 755 call append(2, "new line") 756 call assert_equal([expect2], prop_list(2)) 757 let expect3 = {'col': 1, 'length': 9, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0} 758 call assert_equal([expect3], prop_list(3)) 759 bwipe! 760 761 call prop_type_delete('comment') 762endfunc 763 764func Test_prop_line2byte() 765 call prop_type_add('comment', {'highlight': 'Directory'}) 766 new 767 call setline(1, ['line1', 'second line', '']) 768 set ff=unix 769 call assert_equal(19, line2byte(3)) 770 call prop_add(1, 1, {'end_col': 3, 'type': 'comment'}) 771 call assert_equal(19, line2byte(3)) 772 773 bwipe! 774 call prop_type_delete('comment') 775endfunc 776 777func Test_prop_byte2line() 778 new 779 set ff=unix 780 call setline(1, ['one one', 'two two', 'three three', 'four four', 'five']) 781 call assert_equal(4, byte2line(line2byte(4))) 782 call assert_equal(5, byte2line(line2byte(5))) 783 784 call prop_type_add('prop', {'highlight': 'Directory'}) 785 call prop_add(3, 1, {'length': 5, 'type': 'prop'}) 786 call assert_equal(4, byte2line(line2byte(4))) 787 call assert_equal(5, byte2line(line2byte(5))) 788 789 bwipe! 790 call prop_type_delete('prop') 791endfunc 792 793func Test_prop_goto_byte() 794 new 795 call setline(1, '') 796 call setline(2, 'two three') 797 call setline(3, '') 798 call setline(4, 'four five') 799 800 call prop_type_add('testprop', {'highlight': 'Directory'}) 801 call search('^two') 802 call prop_add(line('.'), col('.'), { 803 \ 'length': len('two'), 804 \ 'type': 'testprop' 805 \ }) 806 807 call search('two \zsthree') 808 let expected_pos = line2byte(line('.')) + col('.') - 1 809 exe expected_pos .. 'goto' 810 let actual_pos = line2byte(line('.')) + col('.') - 1 811 eval actual_pos->assert_equal(expected_pos) 812 813 call search('four \zsfive') 814 let expected_pos = line2byte(line('.')) + col('.') - 1 815 exe expected_pos .. 'goto' 816 let actual_pos = line2byte(line('.')) + col('.') - 1 817 eval actual_pos->assert_equal(expected_pos) 818 819 call prop_type_delete('testprop') 820 bwipe! 821endfunc 822 823func Test_prop_undo() 824 new 825 call prop_type_add('comment', {'highlight': 'Directory'}) 826 call setline(1, ['oneone', 'twotwo', 'three']) 827 " Set 'undolevels' to break changes into undo-able pieces. 828 set ul& 829 830 call prop_add(1, 3, {'end_col': 5, 'type': 'comment'}) 831 let expected = [{'col': 3, 'length': 2, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] 832 call assert_equal(expected, prop_list(1)) 833 834 " Insert a character, then undo. 835 exe "normal 0lllix\<Esc>" 836 set ul& 837 let expected[0].length = 3 838 call assert_equal(expected, prop_list(1)) 839 undo 840 let expected[0].length = 2 841 call assert_equal(expected, prop_list(1)) 842 843 " Delete a character, then undo 844 exe "normal 0lllx" 845 set ul& 846 let expected[0].length = 1 847 call assert_equal(expected, prop_list(1)) 848 undo 849 let expected[0].length = 2 850 call assert_equal(expected, prop_list(1)) 851 852 " Delete the line, then undo 853 1d 854 set ul& 855 call assert_equal([], prop_list(1)) 856 undo 857 call assert_equal(expected, prop_list(1)) 858 859 " Insert a character, delete two characters, then undo with "U" 860 exe "normal 0lllix\<Esc>" 861 set ul& 862 let expected[0].length = 3 863 call assert_equal(expected, prop_list(1)) 864 exe "normal 0lllxx" 865 set ul& 866 let expected[0].length = 1 867 call assert_equal(expected, prop_list(1)) 868 normal U 869 let expected[0].length = 2 870 call assert_equal(expected, prop_list(1)) 871 872 " substitute a word, then undo 873 call setline(1, 'the number 123 is highlighted.') 874 call prop_add(1, 12, {'length': 3, 'type': 'comment'}) 875 let expected = [{'col': 12, 'length': 3, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] 876 call assert_equal(expected, prop_list(1)) 877 set ul& 878 1s/number/foo 879 let expected[0].col = 9 880 call assert_equal(expected, prop_list(1)) 881 undo 882 let expected[0].col = 12 883 call assert_equal(expected, prop_list(1)) 884 call prop_clear(1) 885 886 " substitute with backslash 887 call setline(1, 'the number 123 is highlighted.') 888 call prop_add(1, 12, {'length': 3, 'type': 'comment'}) 889 let expected = [{'col': 12, 'length': 3, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] 890 call assert_equal(expected, prop_list(1)) 891 1s/the/\The 892 call assert_equal(expected, prop_list(1)) 893 1s/^/\\ 894 let expected[0].col += 1 895 call assert_equal(expected, prop_list(1)) 896 1s/^/\~ 897 let expected[0].col += 1 898 call assert_equal(expected, prop_list(1)) 899 1s/123/12\\3 900 let expected[0].length += 1 901 call assert_equal(expected, prop_list(1)) 902 call prop_clear(1) 903 904 bwipe! 905 call prop_type_delete('comment') 906endfunc 907 908func Test_prop_delete_text() 909 new 910 call prop_type_add('comment', {'highlight': 'Directory'}) 911 call setline(1, ['oneone', 'twotwo', 'three']) 912 913 " zero length property 914 call prop_add(1, 3, {'type': 'comment'}) 915 let expected = [{'col': 3, 'length': 0, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] 916 call assert_equal(expected, prop_list(1)) 917 918 " delete one char moves the property 919 normal! x 920 let expected = [{'col': 2, 'length': 0, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] 921 call assert_equal(expected, prop_list(1)) 922 923 " delete char of the property has no effect 924 normal! lx 925 let expected = [{'col': 2, 'length': 0, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] 926 call assert_equal(expected, prop_list(1)) 927 928 " delete more chars moves property to first column, is not deleted 929 normal! 0xxxx 930 let expected = [{'col': 1, 'length': 0, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ] 931 call assert_equal(expected, prop_list(1)) 932 933 bwipe! 934 call prop_type_delete('comment') 935endfunc 936 937" screenshot test with textprop highlighting 938func Test_textprop_screenshot_various() 939 CheckScreendump 940 " The Vim running in the terminal needs to use utf-8. 941 if g:orig_encoding != 'utf-8' 942 throw 'Skipped: not using utf-8' 943 endif 944 call writefile([ 945 \ "call setline(1, [" 946 \ .. "'One two'," 947 \ .. "'Numbér 123 änd thœn 4¾7.'," 948 \ .. "'--aa--bb--cc--dd--'," 949 \ .. "'// comment with error in it'," 950 \ .. "'first line'," 951 \ .. "' second line '," 952 \ .. "'third line'," 953 \ .. "' fourth line'," 954 \ .. "])", 955 \ "hi NumberProp ctermfg=blue", 956 \ "hi LongProp ctermbg=yellow", 957 \ "hi BackgroundProp ctermbg=lightgrey", 958 \ "hi UnderlineProp cterm=underline", 959 \ "call prop_type_add('number', {'highlight': 'NumberProp'})", 960 \ "call prop_type_add('long', {'highlight': 'NumberProp'})", 961 \ "call prop_type_change('long', {'highlight': 'LongProp'})", 962 \ "call prop_type_add('start', {'highlight': 'NumberProp', 'start_incl': 1})", 963 \ "call prop_type_add('end', {'highlight': 'NumberProp', 'end_incl': 1})", 964 \ "call prop_type_add('both', {'highlight': 'NumberProp', 'start_incl': 1, 'end_incl': 1})", 965 \ "call prop_type_add('background', {'highlight': 'BackgroundProp', 'combine': 0})", 966 \ "call prop_type_add('backgroundcomb', {'highlight': 'NumberProp', 'combine': 1})", 967 \ "eval 'backgroundcomb'->prop_type_change({'highlight': 'BackgroundProp'})", 968 \ "call prop_type_add('error', {'highlight': 'UnderlineProp'})", 969 \ "call prop_add(1, 4, {'end_lnum': 3, 'end_col': 3, 'type': 'long'})", 970 \ "call prop_add(2, 9, {'length': 3, 'type': 'number'})", 971 \ "call prop_add(2, 24, {'length': 4, 'type': 'number'})", 972 \ "call prop_add(3, 3, {'length': 2, 'type': 'number'})", 973 \ "call prop_add(3, 7, {'length': 2, 'type': 'start'})", 974 \ "call prop_add(3, 11, {'length': 2, 'type': 'end'})", 975 \ "call prop_add(3, 15, {'length': 2, 'type': 'both'})", 976 \ "call prop_add(4, 6, {'length': 3, 'type': 'background'})", 977 \ "call prop_add(4, 12, {'length': 10, 'type': 'backgroundcomb'})", 978 \ "call prop_add(4, 17, {'length': 5, 'type': 'error'})", 979 \ "call prop_add(5, 7, {'length': 4, 'type': 'long'})", 980 \ "call prop_add(6, 1, {'length': 8, 'type': 'long'})", 981 \ "call prop_add(8, 1, {'length': 1, 'type': 'long'})", 982 \ "call prop_add(8, 11, {'length': 4, 'type': 'long'})", 983 \ "set number cursorline", 984 \ "hi clear SpellBad", 985 \ "set spell", 986 \ "syn match Comment '//.*'", 987 \ "hi Comment ctermfg=green", 988 \ "normal 3G0llix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>", 989 \ "normal 3G0lli\<BS>\<Esc>", 990 \ "normal 6G0i\<BS>\<Esc>", 991 \ "normal 3J", 992 \ "normal 3G", 993 \], 'XtestProp') 994 let buf = RunVimInTerminal('-S XtestProp', {'rows': 8}) 995 call VerifyScreenDump(buf, 'Test_textprop_01', {}) 996 997 " clean up 998 call StopVimInTerminal(buf) 999 call delete('XtestProp') 1000endfunc 1001 1002func RunTestVisualBlock(width, dump) 1003 call writefile([ 1004 \ "call setline(1, [" 1005 \ .. "'xxxxxxxxx 123 x'," 1006 \ .. "'xxxxxxxx 123 x'," 1007 \ .. "'xxxxxxx 123 x'," 1008 \ .. "'xxxxxx 123 x'," 1009 \ .. "'xxxxx 123 x'," 1010 \ .. "'xxxx 123 xx'," 1011 \ .. "'xxx 123 xxx'," 1012 \ .. "'xx 123 xxxx'," 1013 \ .. "'x 123 xxxxx'," 1014 \ .. "' 123 xxxxxx'," 1015 \ .. "])", 1016 \ "hi SearchProp ctermbg=yellow", 1017 \ "call prop_type_add('search', {'highlight': 'SearchProp'})", 1018 \ "call prop_add(1, 11, {'length': 3, 'type': 'search'})", 1019 \ "call prop_add(2, 10, {'length': 3, 'type': 'search'})", 1020 \ "call prop_add(3, 9, {'length': 3, 'type': 'search'})", 1021 \ "call prop_add(4, 8, {'length': 3, 'type': 'search'})", 1022 \ "call prop_add(5, 7, {'length': 3, 'type': 'search'})", 1023 \ "call prop_add(6, 6, {'length': 3, 'type': 'search'})", 1024 \ "call prop_add(7, 5, {'length': 3, 'type': 'search'})", 1025 \ "call prop_add(8, 4, {'length': 3, 'type': 'search'})", 1026 \ "call prop_add(9, 3, {'length': 3, 'type': 'search'})", 1027 \ "call prop_add(10, 2, {'length': 3, 'type': 'search'})", 1028 \ "normal 1G6|\<C-V>" .. repeat('l', a:width - 1) .. "10jx", 1029 \], 'XtestPropVis') 1030 let buf = RunVimInTerminal('-S XtestPropVis', {'rows': 12}) 1031 call VerifyScreenDump(buf, 'Test_textprop_vis_' .. a:dump, {}) 1032 1033 " clean up 1034 call StopVimInTerminal(buf) 1035 call delete('XtestPropVis') 1036endfunc 1037 1038" screenshot test with Visual block mode operations 1039func Test_textprop_screenshot_visual() 1040 CheckScreendump 1041 1042 " Delete two columns while text props are three chars wide. 1043 call RunTestVisualBlock(2, '01') 1044 1045 " Same, but delete four columns 1046 call RunTestVisualBlock(4, '02') 1047endfunc 1048 1049func Test_textprop_after_tab() 1050 CheckScreendump 1051 1052 let lines =<< trim END 1053 call setline(1, [ 1054 \ "\txxx", 1055 \ "x\txxx", 1056 \ ]) 1057 hi SearchProp ctermbg=yellow 1058 call prop_type_add('search', {'highlight': 'SearchProp'}) 1059 call prop_add(1, 2, {'length': 3, 'type': 'search'}) 1060 call prop_add(2, 3, {'length': 3, 'type': 'search'}) 1061 END 1062 call writefile(lines, 'XtestPropTab') 1063 let buf = RunVimInTerminal('-S XtestPropTab', {'rows': 6}) 1064 call VerifyScreenDump(buf, 'Test_textprop_tab', {}) 1065 1066 " clean up 1067 call StopVimInTerminal(buf) 1068 call delete('XtestPropTab') 1069endfunc 1070 1071func Test_textprop_nowrap_scrolled() 1072 CheckScreendump 1073 1074 let lines =<< trim END 1075 vim9script 1076 set nowrap 1077 setline(1, 'The number 123 is smaller than 4567.' .. repeat('X', &columns)) 1078 prop_type_add('number', {'highlight': 'ErrorMsg'}) 1079 prop_add(1, 12, {'length': 3, 'type': 'number'}) 1080 prop_add(1, 32, {'length': 4, 'type': 'number'}) 1081 feedkeys('gg20zl', 'nxt') 1082 END 1083 call writefile(lines, 'XtestNowrap') 1084 let buf = RunVimInTerminal('-S XtestNowrap', {'rows': 6}) 1085 call VerifyScreenDump(buf, 'Test_textprop_nowrap_01', {}) 1086 1087 call term_sendkeys(buf, "$") 1088 call VerifyScreenDump(buf, 'Test_textprop_nowrap_02', {}) 1089 1090 " clean up 1091 call StopVimInTerminal(buf) 1092 call delete('XtestNowrap') 1093endfunc 1094 1095func Test_textprop_with_syntax() 1096 CheckScreendump 1097 1098 let lines =<< trim END 1099 call setline(1, [ 1100 \ "(abc)", 1101 \ ]) 1102 syn match csParens "[()]" display 1103 hi! link csParens MatchParen 1104 1105 call prop_type_add('TPTitle', #{ highlight: 'Title' }) 1106 call prop_add(1, 2, #{type: 'TPTitle', end_col: 5}) 1107 END 1108 call writefile(lines, 'XtestPropSyn') 1109 let buf = RunVimInTerminal('-S XtestPropSyn', {'rows': 6}) 1110 call VerifyScreenDump(buf, 'Test_textprop_syn_1', {}) 1111 1112 " clean up 1113 call StopVimInTerminal(buf) 1114 call delete('XtestPropSyn') 1115endfunc 1116 1117" Adding a text property to a new buffer should not fail 1118func Test_textprop_empty_buffer() 1119 call prop_type_add('comment', {'highlight': 'Search'}) 1120 new 1121 call prop_add(1, 1, {'type': 'comment'}) 1122 close 1123 call prop_type_delete('comment') 1124endfunc 1125 1126" Adding a text property with invalid highlight should be ignored. 1127func Test_textprop_invalid_highlight() 1128 call assert_fails("call prop_type_add('dni', {'highlight': 'DoesNotExist'})", 'E970:') 1129 new 1130 call setline(1, ['asdf','asdf']) 1131 call prop_add(1, 1, {'length': 4, 'type': 'dni'}) 1132 redraw 1133 bwipe! 1134 call prop_type_delete('dni') 1135endfunc 1136 1137" Adding a text property to an empty buffer and then editing another 1138func Test_textprop_empty_buffer_next() 1139 call prop_type_add("xxx", {}) 1140 call prop_add(1, 1, {"type": "xxx"}) 1141 next X 1142 call prop_type_delete('xxx') 1143endfunc 1144 1145func Test_textprop_remove_from_buf() 1146 new 1147 let buf = bufnr('') 1148 call prop_type_add('one', {'bufnr': buf}) 1149 call prop_add(1, 1, {'type': 'one', 'id': 234}) 1150 file x 1151 edit y 1152 call prop_remove({'id': 234, 'bufnr': buf}, 1) 1153 call prop_type_delete('one', {'bufnr': buf}) 1154 bwipe! x 1155 close 1156endfunc 1157 1158func Test_textprop_in_unloaded_buf() 1159 edit Xaaa 1160 call setline(1, 'aaa') 1161 write 1162 edit Xbbb 1163 call setline(1, 'bbb') 1164 write 1165 let bnr = bufnr('') 1166 edit Xaaa 1167 1168 call prop_type_add('ErrorMsg', #{highlight:'ErrorMsg'}) 1169 call assert_fails("call prop_add(1, 1, #{end_lnum: 1, endcol: 2, type: 'ErrorMsg', bufnr: bnr})", 'E275:') 1170 exe 'buf ' .. bnr 1171 call assert_equal('bbb', getline(1)) 1172 call assert_equal(0, prop_list(1)->len()) 1173 1174 bwipe! Xaaa 1175 bwipe! Xbbb 1176 cal delete('Xaaa') 1177 cal delete('Xbbb') 1178endfunc 1179 1180func Test_proptype_substitute2() 1181 new 1182 " text_prop.vim 1183 call setline(1, [ 1184 \ 'The num 123 is smaller than 4567.', 1185 \ '123 The number 123 is smaller than 4567.', 1186 \ '123 The number 123 is smaller than 4567.']) 1187 1188 call prop_type_add('number', {'highlight': 'ErrorMsg'}) 1189 1190 call prop_add(1, 12, {'length': 3, 'type': 'number'}) 1191 call prop_add(2, 1, {'length': 3, 'type': 'number'}) 1192 call prop_add(3, 36, {'length': 4, 'type': 'number'}) 1193 set ul& 1194 let expected = [{'id': 0, 'col': 13, 'end': 1, 'type': 'number', 'length': 3, 'start': 1}, 1195 \ {'id': 0, 'col': 1, 'end': 1, 'type': 'number', 'length': 3, 'start': 1}, 1196 \ {'id': 0, 'col': 50, 'end': 1, 'type': 'number', 'length': 4, 'start': 1}] 1197 " Add some text in between 1198 %s/\s\+/ /g 1199 call assert_equal(expected, prop_list(1) + prop_list(2) + prop_list(3)) 1200 1201 " remove some text 1202 :1s/[a-z]\{3\}//g 1203 let expected = [{'id': 0, 'col': 10, 'end': 1, 'type': 'number', 'length': 3, 'start': 1}] 1204 call assert_equal(expected, prop_list(1)) 1205 bwipe! 1206endfunc 1207 1208" This was causing property corruption. 1209func Test_proptype_substitute3() 1210 new 1211 call setline(1, ['abcxxx', 'def']) 1212 call prop_type_add("test", {"highlight": "Search"}) 1213 call prop_add(1, 2, {"end_lnum": 2, "end_col": 2, "type": "test"}) 1214 %s/x\+$// 1215 redraw 1216 1217 call prop_type_delete('test') 1218 bwipe! 1219endfunc 1220 1221func SaveOptions() 1222 let d = #{tabstop: &tabstop, 1223 \ softtabstop: &softtabstop, 1224 \ shiftwidth: &shiftwidth, 1225 \ expandtab: &expandtab, 1226 \ foldmethod: '"' .. &foldmethod .. '"', 1227 \ } 1228 return d 1229endfunc 1230 1231func RestoreOptions(dict) 1232 for name in keys(a:dict) 1233 exe 'let &' .. name .. ' = ' .. a:dict[name] 1234 endfor 1235endfunc 1236 1237func Test_textprop_noexpandtab() 1238 new 1239 let save_dict = SaveOptions() 1240 1241 set tabstop=8 1242 set softtabstop=4 1243 set shiftwidth=4 1244 set noexpandtab 1245 set foldmethod=marker 1246 1247 call feedkeys("\<esc>\<esc>0Ca\<cr>\<esc>\<up>", "tx") 1248 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1249 call prop_add(1, 1, {'end_col': 2, 'type': 'test'}) 1250 call feedkeys("0i\<tab>", "tx") 1251 call prop_remove({'type': 'test'}) 1252 call prop_add(1, 2, {'end_col': 3, 'type': 'test'}) 1253 call feedkeys("A\<left>\<tab>", "tx") 1254 call prop_remove({'type': 'test'}) 1255 try 1256 " It is correct that this does not pass 1257 call prop_add(1, 6, {'end_col': 7, 'type': 'test'}) 1258 " Has already collapsed here, start_col:6 does not result in an error 1259 call feedkeys("A\<left>\<tab>", "tx") 1260 catch /^Vim\%((\a\+)\)\=:E964/ 1261 endtry 1262 call prop_remove({'type': 'test'}) 1263 call prop_type_delete('test') 1264 1265 call RestoreOptions(save_dict) 1266 bwipe! 1267endfunc 1268 1269func Test_textprop_noexpandtab_redraw() 1270 new 1271 let save_dict = SaveOptions() 1272 1273 set tabstop=8 1274 set softtabstop=4 1275 set shiftwidth=4 1276 set noexpandtab 1277 set foldmethod=marker 1278 1279 call feedkeys("\<esc>\<esc>0Ca\<cr>\<space>\<esc>\<up>", "tx") 1280 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1281 call prop_add(1, 1, {'end_col': 2, 'type': 'test'}) 1282 call feedkeys("0i\<tab>", "tx") 1283 " Internally broken at the next line 1284 call feedkeys("A\<left>\<tab>", "tx") 1285 redraw 1286 " Index calculation failed internally on next line 1287 call prop_add(1, 1, {'end_col': 2, 'type': 'test'}) 1288 call prop_remove({'type': 'test', 'all': v:true}) 1289 call prop_type_delete('test') 1290 call prop_type_delete('test') 1291 1292 call RestoreOptions(save_dict) 1293 bwipe! 1294endfunc 1295 1296func Test_textprop_ins_str() 1297 new 1298 call setline(1, 'just some text') 1299 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1300 call prop_add(1, 1, {'end_col': 2, 'type': 'test'}) 1301 call assert_equal([{'id': 0, 'col': 1, 'end': 1, 'type': 'test', 'length': 1, 'start': 1}], prop_list(1)) 1302 1303 call feedkeys("foi\<F8>\<Esc>", "tx") 1304 call assert_equal('just s<F8>ome text', getline(1)) 1305 call assert_equal([{'id': 0, 'col': 1, 'end': 1, 'type': 'test', 'length': 1, 'start': 1}], prop_list(1)) 1306 1307 bwipe! 1308 call prop_remove({'type': 'test'}) 1309 call prop_type_delete('test') 1310endfunc 1311 1312func Test_find_prop_later_in_line() 1313 new 1314 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1315 call setline(1, 'just some text') 1316 call prop_add(1, 1, {'length': 4, 'type': 'test'}) 1317 call prop_add(1, 10, {'length': 3, 'type': 'test'}) 1318 1319 call assert_equal({'id': 0, 'lnum': 1, 'col': 10, 'end': 1, 'type': 'test', 'length': 3, 'start': 1}, 1320 \ prop_find(#{type: 'test', lnum: 1, col: 6})) 1321 1322 bwipe! 1323 call prop_type_delete('test') 1324endfunc 1325 1326func Test_find_zerowidth_prop_sol() 1327 new 1328 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1329 call setline(1, 'just some text') 1330 call prop_add(1, 1, {'length': 0, 'type': 'test'}) 1331 1332 call assert_equal({'id': 0, 'lnum': 1, 'col': 1, 'end': 1, 'type': 'test', 'length': 0, 'start': 1}, 1333 \ prop_find(#{type: 'test', lnum: 1})) 1334 1335 bwipe! 1336 call prop_type_delete('test') 1337endfunc 1338 1339" Test for passing invalid arguments to prop_xxx() functions 1340func Test_prop_func_invalid_args() 1341 call assert_fails('call prop_clear(1, 2, [])', 'E715:') 1342 call assert_fails('call prop_clear(-1, 2)', 'E16:') 1343 call assert_fails('call prop_find(test_null_dict())', 'E474:') 1344 call assert_fails('call prop_find({"bufnr" : []})', 'E730:') 1345 call assert_fails('call prop_find({})', 'E968:') 1346 call assert_fails('call prop_find({}, "x")', 'E474:') 1347 call assert_fails('call prop_find({"lnum" : -2})', 'E16:') 1348 call assert_fails('call prop_list(1, [])', 'E715:') 1349 call assert_fails('call prop_list(-1, {})', 'E16:') 1350 call assert_fails('call prop_remove([])', 'E474:') 1351 call assert_fails('call prop_remove({}, -2)', 'E16:') 1352 call assert_fails('call prop_remove({})', 'E968:') 1353 call assert_fails('call prop_type_add([], {})', 'E730:') 1354 call assert_fails("call prop_type_change('long', {'xyz' : 10})", 'E971:') 1355 call assert_fails("call prop_type_delete([])", 'E730:') 1356 call assert_fails("call prop_type_delete('xyz', [])", 'E715:') 1357 call assert_fails("call prop_type_get([])", 'E730:') 1358 call assert_fails("call prop_type_get('', [])", 'E474:') 1359 call assert_fails("call prop_type_list([])", 'E715:') 1360 call assert_fails("call prop_type_add('yyy', 'not_a_dict')", 'E715:') 1361 call assert_fails("call prop_add(1, 5, {'type':'missing_type', 'length':1})", 'E971:') 1362 call assert_fails("call prop_add(1, 5, {'type': ''})", 'E971:') 1363 call assert_fails('call prop_add(1, 1, 0)', 'E715:') 1364 1365 new 1366 call setline(1, ['first', 'second']) 1367 call prop_type_add('xxx', {}) 1368 1369 call assert_fails("call prop_type_add('xxx', {})", 'E969:') 1370 call assert_fails("call prop_add(2, 0, {'type': 'xxx'})", 'E964:') 1371 call assert_fails("call prop_add(2, 3, {'type': 'xxx', 'end_lnum':1})", 'E475:') 1372 call assert_fails("call prop_add(2, 3, {'type': 'xxx', 'end_lnum':3})", 'E966:') 1373 call assert_fails("call prop_add(2, 3, {'type': 'xxx', 'length':-1})", 'E475:') 1374 call assert_fails("call prop_add(2, 3, {'type': 'xxx', 'end_col':0})", 'E475:') 1375 call assert_fails("call prop_add(2, 3, {'length':1})", 'E965:') 1376 1377 call prop_type_delete('xxx') 1378 bwipe! 1379endfunc 1380 1381func Test_prop_split_join() 1382 new 1383 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1384 call setline(1, 'just some text') 1385 call prop_add(1, 6, {'length': 4, 'type': 'test'}) 1386 1387 " Split in middle of "some" 1388 execute "normal! 8|i\<CR>" 1389 call assert_equal([{'id': 0, 'col': 6, 'end': 0, 'type': 'test', 'length': 2, 'start': 1}], 1390 \ prop_list(1)) 1391 call assert_equal([{'id': 0, 'col': 1, 'end': 1, 'type': 'test', 'length': 2, 'start': 0}], 1392 \ prop_list(2)) 1393 1394 " Join the two lines back together 1395 normal! 1GJ 1396 call assert_equal([{'id': 0, 'col': 6, 'end': 1, 'type': 'test', 'length': 5, 'start': 1}], prop_list(1)) 1397 1398 bwipe! 1399 call prop_type_delete('test') 1400endfunc 1401 1402func Test_prop_increment_decrement() 1403 new 1404 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1405 call setline(1, 'its 998 times') 1406 call prop_add(1, 5, {'length': 3, 'type': 'test'}) 1407 1408 exe "normal! 0f9\<C-A>" 1409 eval getline(1)->assert_equal('its 999 times') 1410 eval prop_list(1)->assert_equal([ 1411 \ #{id: 0, col: 5, end: 1, type: 'test', length: 3, start: 1}]) 1412 1413 exe "normal! 0f9\<C-A>" 1414 eval getline(1)->assert_equal('its 1000 times') 1415 eval prop_list(1)->assert_equal([ 1416 \ #{id: 0, col: 5, end: 1, type: 'test', length: 4, start: 1}]) 1417 1418 bwipe! 1419 call prop_type_delete('test') 1420endfunc 1421 1422func Test_prop_block_insert() 1423 new 1424 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1425 call setline(1, ['one ', 'two ']) 1426 call prop_add(1, 1, {'length': 3, 'type': 'test'}) 1427 call prop_add(2, 1, {'length': 3, 'type': 'test'}) 1428 1429 " insert "xx" in the first column of both lines 1430 exe "normal! gg0\<C-V>jIxx\<Esc>" 1431 eval getline(1, 2)->assert_equal(['xxone ', 'xxtwo ']) 1432 let expected = [#{id: 0, col: 3, end: 1, type: 'test', length: 3, start: 1}] 1433 eval prop_list(1)->assert_equal(expected) 1434 eval prop_list(2)->assert_equal(expected) 1435 1436 " insert "yy" inside the text props to make them longer 1437 exe "normal! gg03l\<C-V>jIyy\<Esc>" 1438 eval getline(1, 2)->assert_equal(['xxoyyne ', 'xxtyywo ']) 1439 let expected[0].length = 5 1440 eval prop_list(1)->assert_equal(expected) 1441 eval prop_list(2)->assert_equal(expected) 1442 1443 " insert "zz" after the text props, text props don't change 1444 exe "normal! gg07l\<C-V>jIzz\<Esc>" 1445 eval getline(1, 2)->assert_equal(['xxoyynezz ', 'xxtyywozz ']) 1446 eval prop_list(1)->assert_equal(expected) 1447 eval prop_list(2)->assert_equal(expected) 1448 1449 bwipe! 1450 call prop_type_delete('test') 1451endfunc 1452 1453" this was causing an ml_get error because w_botline was wrong 1454func Test_prop_one_line_window() 1455 enew 1456 call range(2)->setline(1) 1457 call prop_type_add('testprop', {}) 1458 call prop_add(1, 1, {'type': 'testprop'}) 1459 call popup_create('popup', {'textprop': 'testprop'}) 1460 $ 1461 new 1462 wincmd _ 1463 call feedkeys("\r", 'xt') 1464 redraw 1465 1466 call popup_clear() 1467 call prop_type_delete('testprop') 1468 close 1469 bwipe! 1470endfunc 1471 1472" This was calling ml_append_int() and copy a text property from a previous 1473" line at the wrong moment. Exact text length matters. 1474def Test_prop_splits_data_block() 1475 new 1476 var lines: list<string> = [repeat('x', 35)]->repeat(41) 1477 + [repeat('!', 35)] 1478 + [repeat('x', 35)]->repeat(56) 1479 lines->setline(1) 1480 prop_type_add('someprop', {highlight: 'ErrorMsg'}) 1481 prop_add(1, 27, {end_lnum: 1, end_col: 70, type: 'someprop'}) 1482 prop_remove({type: 'someprop'}, 1) 1483 prop_add(35, 22, {end_lnum: 43, end_col: 43, type: 'someprop'}) 1484 prop_remove({type: 'someprop'}, 35, 43) 1485 assert_equal([], prop_list(42)) 1486 1487 bwipe! 1488 prop_type_delete('someprop') 1489enddef 1490 1491 1492" vim: shiftwidth=2 sts=2 expandtab 1493