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