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