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 813 bwipe! 814 call prop_type_delete('comment') 815endfunc 816 817func Test_prop_byte2line() 818 new 819 set ff=unix 820 call setline(1, ['one one', 'two two', 'three three', 'four four', 'five']) 821 call assert_equal(4, byte2line(line2byte(4))) 822 call assert_equal(5, byte2line(line2byte(5))) 823 824 call prop_type_add('prop', {'highlight': 'Directory'}) 825 call prop_add(3, 1, {'length': 5, 'type': 'prop'}) 826 call assert_equal(4, byte2line(line2byte(4))) 827 call assert_equal(5, byte2line(line2byte(5))) 828 829 bwipe! 830 call prop_type_delete('prop') 831endfunc 832 833func Test_prop_goto_byte() 834 new 835 call setline(1, '') 836 call setline(2, 'two three') 837 call setline(3, '') 838 call setline(4, 'four five') 839 840 call prop_type_add('testprop', {'highlight': 'Directory'}) 841 call search('^two') 842 call prop_add(line('.'), col('.'), { 843 \ 'length': len('two'), 844 \ 'type': 'testprop' 845 \ }) 846 847 call search('two \zsthree') 848 let expected_pos = line2byte(line('.')) + col('.') - 1 849 exe expected_pos .. 'goto' 850 let actual_pos = line2byte(line('.')) + col('.') - 1 851 eval actual_pos->assert_equal(expected_pos) 852 853 call search('four \zsfive') 854 let expected_pos = line2byte(line('.')) + col('.') - 1 855 exe expected_pos .. 'goto' 856 let actual_pos = line2byte(line('.')) + col('.') - 1 857 eval actual_pos->assert_equal(expected_pos) 858 859 call prop_type_delete('testprop') 860 bwipe! 861endfunc 862 863func Test_prop_undo() 864 new 865 call prop_type_add('comment', {'highlight': 'Directory'}) 866 call setline(1, ['oneone', 'twotwo', 'three']) 867 " Set 'undolevels' to break changes into undo-able pieces. 868 set ul& 869 870 call prop_add(1, 3, {'end_col': 5, 'type': 'comment'}) 871 let expected = [#{type_bufnr: 0, col: 3, length: 2, id: 0, type: 'comment', start: 1, end: 1}] 872 call assert_equal(expected, prop_list(1)) 873 874 " Insert a character, then undo. 875 exe "normal 0lllix\<Esc>" 876 set ul& 877 let expected[0].length = 3 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 a character, then undo 884 exe "normal 0lllx" 885 set ul& 886 let expected[0].length = 1 887 call assert_equal(expected, prop_list(1)) 888 undo 889 let expected[0].length = 2 890 call assert_equal(expected, prop_list(1)) 891 892 " Delete the line, then undo 893 1d 894 set ul& 895 call assert_equal([], prop_list(1)) 896 undo 897 call assert_equal(expected, prop_list(1)) 898 899 " Insert a character, delete two characters, then undo with "U" 900 exe "normal 0lllix\<Esc>" 901 set ul& 902 let expected[0].length = 3 903 call assert_equal(expected, prop_list(1)) 904 exe "normal 0lllxx" 905 set ul& 906 let expected[0].length = 1 907 call assert_equal(expected, prop_list(1)) 908 normal U 909 let expected[0].length = 2 910 call assert_equal(expected, prop_list(1)) 911 912 " substitute a word, then undo 913 call setline(1, 'the number 123 is highlighted.') 914 call prop_add(1, 12, {'length': 3, 'type': 'comment'}) 915 let expected = [#{type_bufnr: 0, col: 12, length: 3, id: 0, type: 'comment', start: 1, end: 1} ] 916 call assert_equal(expected, prop_list(1)) 917 set ul& 918 1s/number/foo 919 let expected[0].col = 9 920 call assert_equal(expected, prop_list(1)) 921 undo 922 let expected[0].col = 12 923 call assert_equal(expected, prop_list(1)) 924 call prop_clear(1) 925 926 " substitute with backslash 927 call setline(1, 'the number 123 is highlighted.') 928 call prop_add(1, 12, {'length': 3, 'type': 'comment'}) 929 let expected = [#{type_bufnr: 0, col: 12, length: 3, id: 0, type: 'comment', start: 1, end: 1} ] 930 call assert_equal(expected, prop_list(1)) 931 1s/the/\The 932 call assert_equal(expected, prop_list(1)) 933 1s/^/\\ 934 let expected[0].col += 1 935 call assert_equal(expected, prop_list(1)) 936 1s/^/\~ 937 let expected[0].col += 1 938 call assert_equal(expected, prop_list(1)) 939 1s/123/12\\3 940 let expected[0].length += 1 941 call assert_equal(expected, prop_list(1)) 942 call prop_clear(1) 943 944 bwipe! 945 call prop_type_delete('comment') 946endfunc 947 948func Test_prop_delete_text() 949 new 950 call prop_type_add('comment', {'highlight': 'Directory'}) 951 call setline(1, ['oneone', 'twotwo', 'three']) 952 953 " zero length property 954 call prop_add(1, 3, {'type': 'comment'}) 955 let expected = [#{type_bufnr: 0, col: 3, length: 0, id: 0, type: 'comment', start: 1, end: 1} ] 956 call assert_equal(expected, prop_list(1)) 957 958 " delete one char moves the property 959 normal! x 960 let expected = [#{type_bufnr: 0, col: 2, length: 0, id: 0, type: 'comment', start: 1, end: 1} ] 961 call assert_equal(expected, prop_list(1)) 962 963 " delete char of the property has no effect 964 normal! lx 965 let expected = [#{type_bufnr: 0, col: 2, length: 0, id: 0, type: 'comment', start: 1, end: 1} ] 966 call assert_equal(expected, prop_list(1)) 967 968 " delete more chars moves property to first column, is not deleted 969 normal! 0xxxx 970 let expected = [#{type_bufnr: 0, col: 1, length: 0, id: 0, type: 'comment', start: 1, end: 1} ] 971 call assert_equal(expected, prop_list(1)) 972 973 bwipe! 974 call prop_type_delete('comment') 975endfunc 976 977" screenshot test with textprop highlighting 978func Test_textprop_screenshot_various() 979 CheckScreendump 980 " The Vim running in the terminal needs to use utf-8. 981 if g:orig_encoding != 'utf-8' 982 throw 'Skipped: not using utf-8' 983 endif 984 call writefile([ 985 \ "call setline(1, [" 986 \ .. "'One two'," 987 \ .. "'Numbér 123 änd thœn 4¾7.'," 988 \ .. "'--aa--bb--cc--dd--'," 989 \ .. "'// comment with error in it'," 990 \ .. "'first line'," 991 \ .. "' second line '," 992 \ .. "'third line'," 993 \ .. "' fourth line'," 994 \ .. "])", 995 \ "hi NumberProp ctermfg=blue", 996 \ "hi LongProp ctermbg=yellow", 997 \ "hi BackgroundProp ctermbg=lightgrey", 998 \ "hi UnderlineProp cterm=underline", 999 \ "call prop_type_add('number', {'highlight': 'NumberProp'})", 1000 \ "call prop_type_add('long', {'highlight': 'NumberProp'})", 1001 \ "call prop_type_change('long', {'highlight': 'LongProp'})", 1002 \ "call prop_type_add('start', {'highlight': 'NumberProp', 'start_incl': 1})", 1003 \ "call prop_type_add('end', {'highlight': 'NumberProp', 'end_incl': 1})", 1004 \ "call prop_type_add('both', {'highlight': 'NumberProp', 'start_incl': 1, 'end_incl': 1})", 1005 \ "call prop_type_add('background', {'highlight': 'BackgroundProp', 'combine': 0})", 1006 \ "call prop_type_add('backgroundcomb', {'highlight': 'NumberProp', 'combine': 1})", 1007 \ "eval 'backgroundcomb'->prop_type_change({'highlight': 'BackgroundProp'})", 1008 \ "call prop_type_add('error', {'highlight': 'UnderlineProp'})", 1009 \ "call prop_add(1, 4, {'end_lnum': 3, 'end_col': 3, 'type': 'long'})", 1010 \ "call prop_add(2, 9, {'length': 3, 'type': 'number'})", 1011 \ "call prop_add(2, 24, {'length': 4, 'type': 'number'})", 1012 \ "call prop_add(3, 3, {'length': 2, 'type': 'number'})", 1013 \ "call prop_add(3, 7, {'length': 2, 'type': 'start'})", 1014 \ "call prop_add(3, 11, {'length': 2, 'type': 'end'})", 1015 \ "call prop_add(3, 15, {'length': 2, 'type': 'both'})", 1016 \ "call prop_add(4, 6, {'length': 3, 'type': 'background'})", 1017 \ "call prop_add(4, 12, {'length': 10, 'type': 'backgroundcomb'})", 1018 \ "call prop_add(4, 17, {'length': 5, 'type': 'error'})", 1019 \ "call prop_add(5, 7, {'length': 4, 'type': 'long'})", 1020 \ "call prop_add(6, 1, {'length': 8, 'type': 'long'})", 1021 \ "call prop_add(8, 1, {'length': 1, 'type': 'long'})", 1022 \ "call prop_add(8, 11, {'length': 4, 'type': 'long'})", 1023 \ "set number cursorline", 1024 \ "hi clear SpellBad", 1025 \ "set spell", 1026 \ "syn match Comment '//.*'", 1027 \ "hi Comment ctermfg=green", 1028 \ "normal 3G0llix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>", 1029 \ "normal 3G0lli\<BS>\<Esc>", 1030 \ "normal 6G0i\<BS>\<Esc>", 1031 \ "normal 3J", 1032 \ "normal 3G", 1033 \], 'XtestProp') 1034 let buf = RunVimInTerminal('-S XtestProp', {'rows': 8}) 1035 call VerifyScreenDump(buf, 'Test_textprop_01', {}) 1036 1037 " clean up 1038 call StopVimInTerminal(buf) 1039 call delete('XtestProp') 1040endfunc 1041 1042func RunTestVisualBlock(width, dump) 1043 call writefile([ 1044 \ "call setline(1, [" 1045 \ .. "'xxxxxxxxx 123 x'," 1046 \ .. "'xxxxxxxx 123 x'," 1047 \ .. "'xxxxxxx 123 x'," 1048 \ .. "'xxxxxx 123 x'," 1049 \ .. "'xxxxx 123 x'," 1050 \ .. "'xxxx 123 xx'," 1051 \ .. "'xxx 123 xxx'," 1052 \ .. "'xx 123 xxxx'," 1053 \ .. "'x 123 xxxxx'," 1054 \ .. "' 123 xxxxxx'," 1055 \ .. "])", 1056 \ "hi SearchProp ctermbg=yellow", 1057 \ "call prop_type_add('search', {'highlight': 'SearchProp'})", 1058 \ "call prop_add(1, 11, {'length': 3, 'type': 'search'})", 1059 \ "call prop_add(2, 10, {'length': 3, 'type': 'search'})", 1060 \ "call prop_add(3, 9, {'length': 3, 'type': 'search'})", 1061 \ "call prop_add(4, 8, {'length': 3, 'type': 'search'})", 1062 \ "call prop_add(5, 7, {'length': 3, 'type': 'search'})", 1063 \ "call prop_add(6, 6, {'length': 3, 'type': 'search'})", 1064 \ "call prop_add(7, 5, {'length': 3, 'type': 'search'})", 1065 \ "call prop_add(8, 4, {'length': 3, 'type': 'search'})", 1066 \ "call prop_add(9, 3, {'length': 3, 'type': 'search'})", 1067 \ "call prop_add(10, 2, {'length': 3, 'type': 'search'})", 1068 \ "normal 1G6|\<C-V>" .. repeat('l', a:width - 1) .. "10jx", 1069 \], 'XtestPropVis') 1070 let buf = RunVimInTerminal('-S XtestPropVis', {'rows': 12}) 1071 call VerifyScreenDump(buf, 'Test_textprop_vis_' .. a:dump, {}) 1072 1073 " clean up 1074 call StopVimInTerminal(buf) 1075 call delete('XtestPropVis') 1076endfunc 1077 1078" screenshot test with Visual block mode operations 1079func Test_textprop_screenshot_visual() 1080 CheckScreendump 1081 1082 " Delete two columns while text props are three chars wide. 1083 call RunTestVisualBlock(2, '01') 1084 1085 " Same, but delete four columns 1086 call RunTestVisualBlock(4, '02') 1087endfunc 1088 1089func Test_textprop_after_tab() 1090 CheckScreendump 1091 1092 let lines =<< trim END 1093 call setline(1, [ 1094 \ "\txxx", 1095 \ "x\txxx", 1096 \ ]) 1097 hi SearchProp ctermbg=yellow 1098 call prop_type_add('search', {'highlight': 'SearchProp'}) 1099 call prop_add(1, 2, {'length': 3, 'type': 'search'}) 1100 call prop_add(2, 3, {'length': 3, 'type': 'search'}) 1101 END 1102 call writefile(lines, 'XtestPropTab') 1103 let buf = RunVimInTerminal('-S XtestPropTab', {'rows': 6}) 1104 call VerifyScreenDump(buf, 'Test_textprop_tab', {}) 1105 1106 " clean up 1107 call StopVimInTerminal(buf) 1108 call delete('XtestPropTab') 1109endfunc 1110 1111func Test_textprop_nowrap_scrolled() 1112 CheckScreendump 1113 1114 let lines =<< trim END 1115 vim9script 1116 set nowrap 1117 setline(1, 'The number 123 is smaller than 4567.' .. repeat('X', &columns)) 1118 prop_type_add('number', {'highlight': 'ErrorMsg'}) 1119 prop_add(1, 12, {'length': 3, 'type': 'number'}) 1120 prop_add(1, 32, {'length': 4, 'type': 'number'}) 1121 feedkeys('gg20zl', 'nxt') 1122 END 1123 call writefile(lines, 'XtestNowrap') 1124 let buf = RunVimInTerminal('-S XtestNowrap', {'rows': 6}) 1125 call VerifyScreenDump(buf, 'Test_textprop_nowrap_01', {}) 1126 1127 call term_sendkeys(buf, "$") 1128 call VerifyScreenDump(buf, 'Test_textprop_nowrap_02', {}) 1129 1130 " clean up 1131 call StopVimInTerminal(buf) 1132 call delete('XtestNowrap') 1133endfunc 1134 1135func Test_textprop_with_syntax() 1136 CheckScreendump 1137 1138 let lines =<< trim END 1139 call setline(1, [ 1140 \ "(abc)", 1141 \ ]) 1142 syn match csParens "[()]" display 1143 hi! link csParens MatchParen 1144 1145 call prop_type_add('TPTitle', #{ highlight: 'Title' }) 1146 call prop_add(1, 2, #{type: 'TPTitle', end_col: 5}) 1147 END 1148 call writefile(lines, 'XtestPropSyn') 1149 let buf = RunVimInTerminal('-S XtestPropSyn', {'rows': 6}) 1150 call VerifyScreenDump(buf, 'Test_textprop_syn_1', {}) 1151 1152 " clean up 1153 call StopVimInTerminal(buf) 1154 call delete('XtestPropSyn') 1155endfunc 1156 1157" Adding a text property to a new buffer should not fail 1158func Test_textprop_empty_buffer() 1159 call prop_type_add('comment', {'highlight': 'Search'}) 1160 new 1161 call prop_add(1, 1, {'type': 'comment'}) 1162 close 1163 call prop_type_delete('comment') 1164endfunc 1165 1166" Adding a text property with invalid highlight should be ignored. 1167func Test_textprop_invalid_highlight() 1168 call assert_fails("call prop_type_add('dni', {'highlight': 'DoesNotExist'})", 'E970:') 1169 new 1170 call setline(1, ['asdf','asdf']) 1171 call prop_add(1, 1, {'length': 4, 'type': 'dni'}) 1172 redraw 1173 bwipe! 1174 call prop_type_delete('dni') 1175endfunc 1176 1177" Adding a text property to an empty buffer and then editing another 1178func Test_textprop_empty_buffer_next() 1179 call prop_type_add("xxx", {}) 1180 call prop_add(1, 1, {"type": "xxx"}) 1181 next X 1182 call prop_type_delete('xxx') 1183endfunc 1184 1185func Test_textprop_remove_from_buf() 1186 new 1187 let buf = bufnr('') 1188 call prop_type_add('one', {'bufnr': buf}) 1189 call prop_add(1, 1, {'type': 'one', 'id': 234}) 1190 file x 1191 edit y 1192 call prop_remove({'id': 234, 'bufnr': buf}, 1) 1193 call prop_type_delete('one', {'bufnr': buf}) 1194 bwipe! x 1195 close 1196endfunc 1197 1198func Test_textprop_in_unloaded_buf() 1199 edit Xaaa 1200 call setline(1, 'aaa') 1201 write 1202 edit Xbbb 1203 call setline(1, 'bbb') 1204 write 1205 let bnr = bufnr('') 1206 edit Xaaa 1207 1208 call prop_type_add('ErrorMsg', #{highlight:'ErrorMsg'}) 1209 call assert_fails("call prop_add(1, 1, #{end_lnum: 1, endcol: 2, type: 'ErrorMsg', bufnr: bnr})", 'E275:') 1210 exe 'buf ' .. bnr 1211 call assert_equal('bbb', getline(1)) 1212 call assert_equal(0, prop_list(1)->len()) 1213 1214 bwipe! Xaaa 1215 bwipe! Xbbb 1216 cal delete('Xaaa') 1217 cal delete('Xbbb') 1218endfunc 1219 1220func Test_proptype_substitute2() 1221 new 1222 " text_prop.vim 1223 call setline(1, [ 1224 \ 'The num 123 is smaller than 4567.', 1225 \ '123 The number 123 is smaller than 4567.', 1226 \ '123 The number 123 is smaller than 4567.']) 1227 1228 call prop_type_add('number', {'highlight': 'ErrorMsg'}) 1229 1230 call prop_add(1, 12, {'length': 3, 'type': 'number'}) 1231 call prop_add(2, 1, {'length': 3, 'type': 'number'}) 1232 call prop_add(3, 36, {'length': 4, 'type': 'number'}) 1233 set ul& 1234 let expected = [ 1235 \ #{type_bufnr: 0, id: 0, col: 13, end: 1, type: 'number', length: 3, start: 1}, 1236 \ #{type_bufnr: 0, id: 0, col: 1, end: 1, type: 'number', length: 3, start: 1}, 1237 \ #{type_bufnr: 0, id: 0, col: 50, end: 1, type: 'number', length: 4, start: 1}] 1238 1239 " TODO 1240 return 1241 " Add some text in between 1242 %s/\s\+/ /g 1243 call assert_equal(expected, prop_list(1) + prop_list(2) + prop_list(3)) 1244 1245 " remove some text 1246 :1s/[a-z]\{3\}//g 1247 let expected = [{'id': 0, 'col': 10, 'end': 1, 'type': 'number', 'length': 3, 'start': 1}] 1248 call assert_equal(expected, prop_list(1)) 1249 bwipe! 1250endfunc 1251 1252" This was causing property corruption. 1253func Test_proptype_substitute3() 1254 new 1255 call setline(1, ['abcxxx', 'def']) 1256 call prop_type_add("test", {"highlight": "Search"}) 1257 call prop_add(1, 2, {"end_lnum": 2, "end_col": 2, "type": "test"}) 1258 %s/x\+$// 1259 redraw 1260 1261 call prop_type_delete('test') 1262 bwipe! 1263endfunc 1264 1265func SaveOptions() 1266 let d = #{tabstop: &tabstop, 1267 \ softtabstop: &softtabstop, 1268 \ shiftwidth: &shiftwidth, 1269 \ expandtab: &expandtab, 1270 \ foldmethod: '"' .. &foldmethod .. '"', 1271 \ } 1272 return d 1273endfunc 1274 1275func RestoreOptions(dict) 1276 for name in keys(a:dict) 1277 exe 'let &' .. name .. ' = ' .. a:dict[name] 1278 endfor 1279endfunc 1280 1281func Test_textprop_noexpandtab() 1282 new 1283 let save_dict = SaveOptions() 1284 1285 set tabstop=8 1286 set softtabstop=4 1287 set shiftwidth=4 1288 set noexpandtab 1289 set foldmethod=marker 1290 1291 call feedkeys("\<esc>\<esc>0Ca\<cr>\<esc>\<up>", "tx") 1292 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1293 call prop_add(1, 1, {'end_col': 2, 'type': 'test'}) 1294 call feedkeys("0i\<tab>", "tx") 1295 call prop_remove({'type': 'test'}) 1296 call prop_add(1, 2, {'end_col': 3, 'type': 'test'}) 1297 call feedkeys("A\<left>\<tab>", "tx") 1298 call prop_remove({'type': 'test'}) 1299 try 1300 " It is correct that this does not pass 1301 call prop_add(1, 6, {'end_col': 7, 'type': 'test'}) 1302 " Has already collapsed here, start_col:6 does not result in an error 1303 call feedkeys("A\<left>\<tab>", "tx") 1304 catch /^Vim\%((\a\+)\)\=:E964/ 1305 endtry 1306 call prop_remove({'type': 'test'}) 1307 call prop_type_delete('test') 1308 1309 call RestoreOptions(save_dict) 1310 bwipe! 1311endfunc 1312 1313func Test_textprop_noexpandtab_redraw() 1314 new 1315 let save_dict = SaveOptions() 1316 1317 set tabstop=8 1318 set softtabstop=4 1319 set shiftwidth=4 1320 set noexpandtab 1321 set foldmethod=marker 1322 1323 call feedkeys("\<esc>\<esc>0Ca\<cr>\<space>\<esc>\<up>", "tx") 1324 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1325 call prop_add(1, 1, {'end_col': 2, 'type': 'test'}) 1326 call feedkeys("0i\<tab>", "tx") 1327 " Internally broken at the next line 1328 call feedkeys("A\<left>\<tab>", "tx") 1329 redraw 1330 " Index calculation failed internally on next line 1331 call prop_add(1, 1, {'end_col': 2, 'type': 'test'}) 1332 call prop_remove({'type': 'test', 'all': v:true}) 1333 call prop_type_delete('test') 1334 call prop_type_delete('test') 1335 1336 call RestoreOptions(save_dict) 1337 bwipe! 1338endfunc 1339 1340func Test_textprop_ins_str() 1341 new 1342 call setline(1, 'just some text') 1343 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1344 call prop_add(1, 1, {'end_col': 2, 'type': 'test'}) 1345 call assert_equal([#{type_bufnr: 0, id: 0, col: 1, end: 1, type: 'test', length: 1, start: 1}], prop_list(1)) 1346 1347 call feedkeys("foi\<F8>\<Esc>", "tx") 1348 call assert_equal('just s<F8>ome text', getline(1)) 1349 call assert_equal([#{type_bufnr: 0, id: 0, col: 1, end: 1, type: 'test', length: 1, start: 1}], prop_list(1)) 1350 1351 bwipe! 1352 call prop_remove({'type': 'test'}) 1353 call prop_type_delete('test') 1354endfunc 1355 1356func Test_find_prop_later_in_line() 1357 new 1358 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1359 call setline(1, 'just some text') 1360 call prop_add(1, 1, {'length': 4, 'type': 'test'}) 1361 call prop_add(1, 10, {'length': 3, 'type': 'test'}) 1362 1363 call assert_equal( 1364 \ #{type_bufnr: 0, id: 0, lnum: 1, col: 10, end: 1, type: 'test', length: 3, start: 1}, 1365 \ prop_find(#{type: 'test', lnum: 1, col: 6})) 1366 1367 bwipe! 1368 call prop_type_delete('test') 1369endfunc 1370 1371func Test_find_zerowidth_prop_sol() 1372 new 1373 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1374 call setline(1, 'just some text') 1375 call prop_add(1, 1, {'length': 0, 'type': 'test'}) 1376 1377 call assert_equal( 1378 \ #{type_bufnr: 0, id: 0, lnum: 1, col: 1, end: 1, type: 'test', length: 0, start: 1}, 1379 \ prop_find(#{type: 'test', lnum: 1})) 1380 1381 bwipe! 1382 call prop_type_delete('test') 1383endfunc 1384 1385" Test for passing invalid arguments to prop_xxx() functions 1386func Test_prop_func_invalid_args() 1387 call assert_fails('call prop_clear(1, 2, [])', 'E715:') 1388 call assert_fails('call prop_clear(-1, 2)', 'E16:') 1389 call assert_fails('call prop_find(test_null_dict())', 'E715:') 1390 call assert_fails('call prop_find({"bufnr" : []})', 'E730:') 1391 call assert_fails('call prop_find({})', 'E968:') 1392 call assert_fails('call prop_find({}, "x")', 'E474:') 1393 call assert_fails('call prop_find({"lnum" : -2})', 'E16:') 1394 call assert_fails('call prop_list(1, [])', 'E715:') 1395 call assert_fails('call prop_list(-1, {})', 'E16:') 1396 call assert_fails('call prop_remove([])', 'E474:') 1397 call assert_fails('call prop_remove({}, -2)', 'E16:') 1398 call assert_fails('call prop_remove({})', 'E968:') 1399 call assert_fails('call prop_type_add([], {})', 'E730:') 1400 call assert_fails("call prop_type_change('long', {'xyz' : 10})", 'E971:') 1401 call assert_fails("call prop_type_delete([])", 'E730:') 1402 call assert_fails("call prop_type_delete('xyz', [])", 'E715:') 1403 call assert_fails("call prop_type_get([])", 'E730:') 1404 call assert_fails("call prop_type_get('', [])", 'E474:') 1405 call assert_fails("call prop_type_list([])", 'E715:') 1406 call assert_fails("call prop_type_add('yyy', 'not_a_dict')", 'E715:') 1407 call assert_fails("call prop_add(1, 5, {'type':'missing_type', 'length':1})", 'E971:') 1408 call assert_fails("call prop_add(1, 5, {'type': ''})", 'E971:') 1409 call assert_fails('call prop_add(1, 1, 0)', 'E715:') 1410 1411 new 1412 call setline(1, ['first', 'second']) 1413 call prop_type_add('xxx', {}) 1414 1415 call assert_fails("call prop_type_add('xxx', {})", 'E969:') 1416 call assert_fails("call prop_add(2, 0, {'type': 'xxx'})", 'E964:') 1417 call assert_fails("call prop_add(2, 3, {'type': 'xxx', 'end_lnum':1})", 'E475:') 1418 call assert_fails("call prop_add(2, 3, {'type': 'xxx', 'end_lnum':3})", 'E966:') 1419 call assert_fails("call prop_add(2, 3, {'type': 'xxx', 'length':-1})", 'E475:') 1420 call assert_fails("call prop_add(2, 3, {'type': 'xxx', 'end_col':0})", 'E475:') 1421 call assert_fails("call prop_add(2, 3, {'length':1})", 'E965:') 1422 1423 call prop_type_delete('xxx') 1424 bwipe! 1425endfunc 1426 1427func Test_prop_split_join() 1428 new 1429 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1430 call setline(1, 'just some text') 1431 call prop_add(1, 6, {'length': 4, 'type': 'test'}) 1432 1433 " Split in middle of "some" 1434 execute "normal! 8|i\<CR>" 1435 call assert_equal( 1436 \ [#{type_bufnr: 0, id: 0, col: 6, end: 0, type: 'test', length: 2, start: 1}], 1437 \ prop_list(1)) 1438 call assert_equal( 1439 \ [#{type_bufnr: 0, id: 0, col: 1, end: 1, type: 'test', length: 2, start: 0}], 1440 \ prop_list(2)) 1441 1442 " Join the two lines back together 1443 normal! 1GJ 1444 call assert_equal([#{type_bufnr: 0, id: 0, col: 6, end: 1, type: 'test', length: 5, start: 1}], prop_list(1)) 1445 1446 bwipe! 1447 call prop_type_delete('test') 1448endfunc 1449 1450func Test_prop_increment_decrement() 1451 new 1452 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1453 call setline(1, 'its 998 times') 1454 call prop_add(1, 5, {'length': 3, 'type': 'test'}) 1455 1456 exe "normal! 0f9\<C-A>" 1457 eval getline(1)->assert_equal('its 999 times') 1458 eval prop_list(1)->assert_equal([ 1459 \ #{type_bufnr: 0, id: 0, col: 5, end: 1, type: 'test', length: 3, start: 1}]) 1460 1461 exe "normal! 0f9\<C-A>" 1462 eval getline(1)->assert_equal('its 1000 times') 1463 eval prop_list(1)->assert_equal([ 1464 \ #{type_bufnr: 0, id: 0, col: 5, end: 1, type: 'test', length: 4, start: 1}]) 1465 1466 bwipe! 1467 call prop_type_delete('test') 1468endfunc 1469 1470func Test_prop_block_insert() 1471 new 1472 call prop_type_add('test', {'highlight': 'ErrorMsg'}) 1473 call setline(1, ['one ', 'two ']) 1474 call prop_add(1, 1, {'length': 3, 'type': 'test'}) 1475 call prop_add(2, 1, {'length': 3, 'type': 'test'}) 1476 1477 " insert "xx" in the first column of both lines 1478 exe "normal! gg0\<C-V>jIxx\<Esc>" 1479 eval getline(1, 2)->assert_equal(['xxone ', 'xxtwo ']) 1480 let expected = [#{type_bufnr: 0, id: 0, col: 3, end: 1, type: 'test', length: 3, start: 1}] 1481 eval prop_list(1)->assert_equal(expected) 1482 eval prop_list(2)->assert_equal(expected) 1483 1484 " insert "yy" inside the text props to make them longer 1485 exe "normal! gg03l\<C-V>jIyy\<Esc>" 1486 eval getline(1, 2)->assert_equal(['xxoyyne ', 'xxtyywo ']) 1487 let expected[0].length = 5 1488 eval prop_list(1)->assert_equal(expected) 1489 eval prop_list(2)->assert_equal(expected) 1490 1491 " insert "zz" after the text props, text props don't change 1492 exe "normal! gg07l\<C-V>jIzz\<Esc>" 1493 eval getline(1, 2)->assert_equal(['xxoyynezz ', 'xxtyywozz ']) 1494 eval prop_list(1)->assert_equal(expected) 1495 eval prop_list(2)->assert_equal(expected) 1496 1497 bwipe! 1498 call prop_type_delete('test') 1499endfunc 1500 1501" this was causing an ml_get error because w_botline was wrong 1502func Test_prop_one_line_window() 1503 enew 1504 call range(2)->setline(1) 1505 call prop_type_add('testprop', {}) 1506 call prop_add(1, 1, {'type': 'testprop'}) 1507 call popup_create('popup', {'textprop': 'testprop'}) 1508 $ 1509 new 1510 wincmd _ 1511 call feedkeys("\r", 'xt') 1512 redraw 1513 1514 call popup_clear() 1515 call prop_type_delete('testprop') 1516 close 1517 bwipe! 1518endfunc 1519 1520" This was calling ml_append_int() and copy a text property from a previous 1521" line at the wrong moment. Exact text length matters. 1522def Test_prop_splits_data_block() 1523 new 1524 var lines: list<string> = [repeat('x', 35)]->repeat(41) 1525 + [repeat('!', 35)] 1526 + [repeat('x', 35)]->repeat(56) 1527 lines->setline(1) 1528 prop_type_add('someprop', {highlight: 'ErrorMsg'}) 1529 prop_add(1, 27, {end_lnum: 1, end_col: 70, type: 'someprop'}) 1530 prop_remove({type: 'someprop'}, 1) 1531 prop_add(35, 22, {end_lnum: 43, end_col: 43, type: 'someprop'}) 1532 prop_remove({type: 'someprop'}, 35, 43) 1533 assert_equal([], prop_list(42)) 1534 1535 bwipe! 1536 prop_type_delete('someprop') 1537enddef 1538 1539" This was calling ml_delete_int() and try to change text properties. 1540def Test_prop_add_delete_line() 1541 new 1542 var a = 10 1543 var b = 20 1544 repeat([''], a)->append('$') 1545 prop_type_add('Test', {highlight: 'ErrorMsg'}) 1546 for lnum in range(1, a) 1547 for col in range(1, b) 1548 prop_add(1, 1, {end_lnum: lnum, end_col: col, type: 'Test'}) 1549 endfor 1550 endfor 1551 1552 # check deleting lines is OK 1553 :5del 1554 :1del 1555 :$del 1556 1557 prop_type_delete('Test') 1558 bwipe! 1559enddef 1560 1561" Buffer number of 0 should be ignored, as if the parameter wasn't passed. 1562def Test_prop_bufnr_zero() 1563 new 1564 try 1565 var bufnr = bufnr('') 1566 setline(1, 'hello') 1567 prop_type_add('bufnr-global', {highlight: 'ErrorMsg'}) 1568 prop_type_add('bufnr-buffer', {highlight: 'StatusLine', bufnr: bufnr}) 1569 1570 prop_add(1, 1, {type: 'bufnr-global', length: 1}) 1571 prop_add(1, 2, {type: 'bufnr-buffer', length: 1}) 1572 1573 var list = prop_list(1) 1574 assert_equal([ 1575 {id: 0, col: 1, type_bufnr: 0, end: 1, type: 'bufnr-global', length: 1, start: 1}, 1576 {id: 0, col: 2, type_bufnr: bufnr, end: 1, type: 'bufnr-buffer', length: 1, start: 1}, 1577 ], list) 1578 1579 assert_equal( 1580 {highlight: 'ErrorMsg', end_incl: 0, start_incl: 0, priority: 0, combine: 1}, 1581 prop_type_get('bufnr-global', {bufnr: list[0].type_bufnr})) 1582 1583 assert_equal( 1584 {highlight: 'StatusLine', end_incl: 0, start_incl: 0, priority: 0, bufnr: bufnr, combine: 1}, 1585 prop_type_get('bufnr-buffer', {bufnr: list[1].type_bufnr})) 1586 finally 1587 bwipe! 1588 prop_type_delete('bufnr-global') 1589 endtry 1590enddef 1591 1592 1593 1594" vim: shiftwidth=2 sts=2 expandtab 1595