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