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