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