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
9" test length zero
10
11func Test_proptype_global()
12  call prop_type_add('comment', {'highlight': 'Directory', 'priority': 123, 'start_incl': 1, 'end_incl': 1})
13  let proptypes = prop_type_list()
14  call assert_equal(1, len(proptypes))
15  call assert_equal('comment', proptypes[0])
16
17  let proptype = prop_type_get('comment')
18  call assert_equal('Directory', proptype['highlight'])
19  call assert_equal(123, proptype['priority'])
20  call assert_equal(1, proptype['start_incl'])
21  call assert_equal(1, proptype['end_incl'])
22
23  call prop_type_delete('comment')
24  call assert_equal(0, len(prop_type_list()))
25
26  call prop_type_add('one', {})
27  call assert_equal(1, len(prop_type_list()))
28  let proptype = 'one'->prop_type_get()
29  call assert_false(has_key(proptype, 'highlight'))
30  call assert_equal(0, proptype['priority'])
31  call assert_equal(0, proptype['start_incl'])
32  call assert_equal(0, proptype['end_incl'])
33
34  call prop_type_add('two', {})
35  call assert_equal(2, len(prop_type_list()))
36  call prop_type_delete('one')
37  call assert_equal(1, len(prop_type_list()))
38  call prop_type_delete('two')
39  call assert_equal(0, len(prop_type_list()))
40endfunc
41
42func Test_proptype_buf()
43  let bufnr = bufnr('')
44  call prop_type_add('comment', {'bufnr': bufnr, 'highlight': 'Directory', 'priority': 123, 'start_incl': 1, 'end_incl': 1})
45  let proptypes = prop_type_list({'bufnr': bufnr})
46  call assert_equal(1, len(proptypes))
47  call assert_equal('comment', proptypes[0])
48
49  let proptype = prop_type_get('comment', {'bufnr': bufnr})
50  call assert_equal('Directory', proptype['highlight'])
51  call assert_equal(123, proptype['priority'])
52  call assert_equal(1, proptype['start_incl'])
53  call assert_equal(1, proptype['end_incl'])
54
55  call prop_type_delete('comment', {'bufnr': bufnr})
56  call assert_equal(0, len({'bufnr': bufnr}->prop_type_list()))
57
58  call prop_type_add('one', {'bufnr': bufnr})
59  let proptype = prop_type_get('one', {'bufnr': bufnr})
60  call assert_false(has_key(proptype, 'highlight'))
61  call assert_equal(0, proptype['priority'])
62  call assert_equal(0, proptype['start_incl'])
63  call assert_equal(0, proptype['end_incl'])
64
65  call prop_type_add('two', {'bufnr': bufnr})
66  call assert_equal(2, len(prop_type_list({'bufnr': bufnr})))
67  call prop_type_delete('one', {'bufnr': bufnr})
68  call assert_equal(1, len(prop_type_list({'bufnr': bufnr})))
69  call prop_type_delete('two', {'bufnr': bufnr})
70  call assert_equal(0, len(prop_type_list({'bufnr': bufnr})))
71
72  call assert_fails("call prop_type_add('one', {'bufnr': 98764})", "E158:")
73endfunc
74
75func AddPropTypes()
76  call prop_type_add('one', {})
77  call prop_type_add('two', {})
78  call prop_type_add('three', {})
79  call prop_type_add('whole', {})
80endfunc
81
82func DeletePropTypes()
83  call prop_type_delete('one')
84  call prop_type_delete('two')
85  call prop_type_delete('three')
86  call prop_type_delete('whole')
87endfunc
88
89func SetupPropsInFirstLine()
90  call setline(1, 'one two three')
91  call prop_add(1, 1, {'length': 3, 'id': 11, 'type': 'one'})
92  eval 1->prop_add(5, {'length': 3, 'id': 12, 'type': 'two'})
93  call prop_add(1, 9, {'length': 5, 'id': 13, 'type': 'three'})
94  call prop_add(1, 1, {'length': 13, 'id': 14, 'type': 'whole'})
95endfunc
96
97func Get_expected_props()
98  return [
99      \ {'col': 1, 'length': 13, 'id': 14, 'type': 'whole', 'start': 1, 'end': 1},
100      \ {'col': 1, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1},
101      \ {'col': 5, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1},
102      \ {'col': 9, 'length': 5, 'id': 13, 'type': 'three', 'start': 1, 'end': 1},
103      \ ]
104endfunc
105
106func Test_prop_add()
107  new
108  call AddPropTypes()
109  call SetupPropsInFirstLine()
110  let expected_props = Get_expected_props()
111  call assert_equal(expected_props, prop_list(1))
112  call assert_fails("call prop_add(10, 1, {'length': 1, 'id': 14, 'type': 'whole'})", 'E966:')
113  call assert_fails("call prop_add(1, 22, {'length': 1, 'id': 14, 'type': 'whole'})", 'E964:')
114
115  " Insert a line above, text props must still be there.
116  call append(0, 'empty')
117  call assert_equal(expected_props, prop_list(2))
118  " Delete a line above, text props must still be there.
119  1del
120  call assert_equal(expected_props, prop_list(1))
121
122  " Prop without length or end column is zero length
123  call prop_clear(1)
124  call prop_add(1, 5, {'type': 'two'})
125  let expected = [{'col': 5, 'length': 0, 'type': 'two', 'id': 0, 'start': 1, 'end': 1}]
126  call assert_equal(expected, prop_list(1))
127
128  call assert_fails("call prop_add(1, 5, {'type': 'two', 'bufnr': 234343})", 'E158:')
129
130  call DeletePropTypes()
131  bwipe!
132endfunc
133
134func Test_prop_remove()
135  new
136  call AddPropTypes()
137  call SetupPropsInFirstLine()
138  let props = Get_expected_props()
139  call assert_equal(props, prop_list(1))
140
141  " remove by id
142  call assert_equal(1, {'id': 12}->prop_remove(1))
143  unlet props[2]
144  call assert_equal(props, prop_list(1))
145
146  " remove by type
147  call assert_equal(1, prop_remove({'type': 'one'}, 1))
148  unlet props[1]
149  call assert_equal(props, prop_list(1))
150
151  " remove from unknown buffer
152  call assert_fails("call prop_remove({'type': 'one', 'bufnr': 123456}, 1)", 'E158:')
153
154  call DeletePropTypes()
155  bwipe!
156endfunc
157
158func SetupOneLine()
159  call setline(1, 'xonex xtwoxx')
160  normal gg0
161  call AddPropTypes()
162  call prop_add(1, 2, {'length': 3, 'id': 11, 'type': 'one'})
163  call prop_add(1, 8, {'length': 3, 'id': 12, 'type': 'two'})
164  let expected = [
165	\ {'col': 2, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1},
166	\ {'col': 8, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1},
167	\]
168  call assert_equal(expected, prop_list(1))
169  return expected
170endfunc
171
172func Test_prop_add_remove_buf()
173  new
174  let bufnr = bufnr('')
175  call AddPropTypes()
176  for lnum in range(1, 4)
177    call setline(lnum, 'one two three')
178  endfor
179  wincmd w
180  for lnum in range(1, 4)
181    call prop_add(lnum, 1, {'length': 3, 'id': 11, 'type': 'one', 'bufnr': bufnr})
182    call prop_add(lnum, 5, {'length': 3, 'id': 12, 'type': 'two', 'bufnr': bufnr})
183    call prop_add(lnum, 11, {'length': 3, 'id': 13, 'type': 'three', 'bufnr': bufnr})
184  endfor
185
186  let props = [
187	\ {'col': 1, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1},
188	\ {'col': 5, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1},
189	\ {'col': 11, 'length': 3, 'id': 13, 'type': 'three', 'start': 1, 'end': 1},
190	\]
191  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
192
193  " remove by id
194  let before_props = deepcopy(props)
195  unlet props[1]
196
197  call prop_remove({'id': 12, 'bufnr': bufnr}, 1)
198  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
199  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
200  call assert_equal(before_props, prop_list(3, {'bufnr': bufnr}))
201  call assert_equal(before_props, prop_list(4, {'bufnr': bufnr}))
202
203  call prop_remove({'id': 12, 'bufnr': bufnr}, 3, 4)
204  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
205  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
206  call assert_equal(props, prop_list(3, {'bufnr': bufnr}))
207  call assert_equal(props, prop_list(4, {'bufnr': bufnr}))
208
209  call prop_remove({'id': 12, 'bufnr': bufnr})
210  for lnum in range(1, 4)
211    call assert_equal(props, prop_list(lnum, {'bufnr': bufnr}))
212  endfor
213
214  " remove by type
215  let before_props = deepcopy(props)
216  unlet props[0]
217
218  call prop_remove({'type': 'one', 'bufnr': bufnr}, 1)
219  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
220  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
221  call assert_equal(before_props, prop_list(3, {'bufnr': bufnr}))
222  call assert_equal(before_props, prop_list(4, {'bufnr': bufnr}))
223
224  call prop_remove({'type': 'one', 'bufnr': bufnr}, 3, 4)
225  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
226  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
227  call assert_equal(props, prop_list(3, {'bufnr': bufnr}))
228  call assert_equal(props, prop_list(4, {'bufnr': bufnr}))
229
230  call prop_remove({'type': 'one', 'bufnr': bufnr})
231  for lnum in range(1, 4)
232    call assert_equal(props, prop_list(lnum, {'bufnr': bufnr}))
233  endfor
234
235  call DeletePropTypes()
236  wincmd w
237  bwipe!
238endfunc
239
240func Test_prop_backspace()
241  new
242  set bs=2
243  let expected = SetupOneLine() " 'xonex xtwoxx'
244
245  exe "normal 0li\<BS>\<Esc>fxli\<BS>\<Esc>"
246  call assert_equal('one xtwoxx', getline(1))
247  let expected[0].col = 1
248  let expected[1].col = 6
249  call assert_equal(expected, prop_list(1))
250
251  call DeletePropTypes()
252  bwipe!
253  set bs&
254endfunc
255
256func Test_prop_replace()
257  new
258  set bs=2
259  let expected = SetupOneLine() " 'xonex xtwoxx'
260
261  exe "normal 0Ryyy\<Esc>"
262  call assert_equal('yyyex xtwoxx', getline(1))
263  call assert_equal(expected, prop_list(1))
264
265  exe "normal ftRyy\<BS>"
266  call assert_equal('yyyex xywoxx', getline(1))
267  call assert_equal(expected, prop_list(1))
268
269  exe "normal 0fwRyy\<BS>"
270  call assert_equal('yyyex xyyoxx', getline(1))
271  call assert_equal(expected, prop_list(1))
272
273  exe "normal 0foRyy\<BS>\<BS>"
274  call assert_equal('yyyex xyyoxx', getline(1))
275  call assert_equal(expected, prop_list(1))
276
277  call DeletePropTypes()
278  bwipe!
279  set bs&
280endfunc
281
282func Test_prop_open_line()
283  new
284
285  " open new line, props stay in top line
286  let expected = SetupOneLine() " 'xonex xtwoxx'
287  exe "normal o\<Esc>"
288  call assert_equal('xonex xtwoxx', getline(1))
289  call assert_equal('', getline(2))
290  call assert_equal(expected, prop_list(1))
291  call DeletePropTypes()
292
293  " move all props to next line
294  let expected = SetupOneLine() " 'xonex xtwoxx'
295  exe "normal 0i\<CR>\<Esc>"
296  call assert_equal('', getline(1))
297  call assert_equal('xonex xtwoxx', getline(2))
298  call assert_equal(expected, prop_list(2))
299  call DeletePropTypes()
300
301  " split just before prop, move all props to next line
302  let expected = SetupOneLine() " 'xonex xtwoxx'
303  exe "normal 0li\<CR>\<Esc>"
304  call assert_equal('x', getline(1))
305  call assert_equal('onex xtwoxx', getline(2))
306  let expected[0].col -= 1
307  let expected[1].col -= 1
308  call assert_equal(expected, prop_list(2))
309  call DeletePropTypes()
310
311  " split inside prop, split first prop
312  let expected = SetupOneLine() " 'xonex xtwoxx'
313  exe "normal 0lli\<CR>\<Esc>"
314  call assert_equal('xo', getline(1))
315  call assert_equal('nex xtwoxx', getline(2))
316  let exp_first = [deepcopy(expected[0])]
317  let exp_first[0].length = 1
318  call assert_equal(exp_first, prop_list(1))
319  let expected[0].col = 1
320  let expected[0].length = 2
321  let expected[1].col -= 2
322  call assert_equal(expected, prop_list(2))
323  call DeletePropTypes()
324
325  " split just after first prop, second prop move to next line
326  let expected = SetupOneLine() " 'xonex xtwoxx'
327  exe "normal 0fea\<CR>\<Esc>"
328  call assert_equal('xone', getline(1))
329  call assert_equal('x xtwoxx', getline(2))
330  let exp_first = expected[0:0]
331  call assert_equal(exp_first, prop_list(1))
332  let expected = expected[1:1]
333  let expected[0].col -= 4
334  call assert_equal(expected, prop_list(2))
335  call DeletePropTypes()
336
337  bwipe!
338  set bs&
339endfunc
340
341func Test_prop_clear()
342  new
343  call AddPropTypes()
344  call SetupPropsInFirstLine()
345  call assert_equal(Get_expected_props(), prop_list(1))
346
347  eval 1->prop_clear()
348  call assert_equal([], 1->prop_list())
349
350  call DeletePropTypes()
351  bwipe!
352endfunc
353
354func Test_prop_clear_buf()
355  new
356  call AddPropTypes()
357  call SetupPropsInFirstLine()
358  let bufnr = bufnr('')
359  wincmd w
360  call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr}))
361
362  call prop_clear(1, 1, {'bufnr': bufnr})
363  call assert_equal([], prop_list(1, {'bufnr': bufnr}))
364
365  wincmd w
366  call DeletePropTypes()
367  bwipe!
368endfunc
369
370func Test_prop_setline()
371  new
372  call AddPropTypes()
373  call SetupPropsInFirstLine()
374  call assert_equal(Get_expected_props(), prop_list(1))
375
376  call setline(1, 'foobar')
377  call assert_equal([], prop_list(1))
378
379  call DeletePropTypes()
380  bwipe!
381endfunc
382
383func Test_prop_setbufline()
384  new
385  call AddPropTypes()
386  call SetupPropsInFirstLine()
387  let bufnr = bufnr('')
388  wincmd w
389  call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr}))
390
391  call setbufline(bufnr, 1, 'foobar')
392  call assert_equal([], prop_list(1, {'bufnr': bufnr}))
393
394  wincmd w
395  call DeletePropTypes()
396  bwipe!
397endfunc
398
399func Test_prop_substitute()
400  new
401  " Set first line to 'one two three'
402  call AddPropTypes()
403  call SetupPropsInFirstLine()
404  let expected_props = Get_expected_props()
405  call assert_equal(expected_props, prop_list(1))
406
407  " Change "n" in "one" to XX: 'oXXe two three'
408  s/n/XX/
409  let expected_props[0].length += 1
410  let expected_props[1].length += 1
411  let expected_props[2].col += 1
412  let expected_props[3].col += 1
413  call assert_equal(expected_props, prop_list(1))
414
415  " Delete "t" in "two" and "three" to XX: 'oXXe wo hree'
416  s/t//g
417  let expected_props[0].length -= 2
418  let expected_props[2].length -= 1
419  let expected_props[3].length -= 1
420  let expected_props[3].col -= 1
421  call assert_equal(expected_props, prop_list(1))
422
423  " Split the line by changing w to line break: 'oXXe ', 'o hree'
424  " The long prop is split and spans both lines.
425  " The props on "two" and "three" move to the next line.
426  s/w/\r/
427  let new_props = [
428	\ copy(expected_props[0]),
429	\ copy(expected_props[2]),
430	\ copy(expected_props[3]),
431	\ ]
432  let expected_props[0].length = 5
433  unlet expected_props[3]
434  unlet expected_props[2]
435  call assert_equal(expected_props, prop_list(1))
436
437  let new_props[0].length = 6
438  let new_props[1].col = 1
439  let new_props[1].length = 1
440  let new_props[2].col = 3
441  call assert_equal(new_props, prop_list(2))
442
443  call DeletePropTypes()
444  bwipe!
445endfunc
446
447func Test_prop_change_indent()
448  call prop_type_add('comment', {'highlight': 'Directory'})
449  new
450  call setline(1, ['    xxx', 'yyyyy'])
451  call prop_add(2, 2, {'length': 2, 'type': 'comment'})
452  let expect = {'col': 2, 'length': 2, 'type': 'comment', 'start': 1, 'end': 1, 'id': 0}
453  call assert_equal([expect], prop_list(2))
454
455  set shiftwidth=3
456  normal 2G>>
457  call assert_equal('   yyyyy', getline(2))
458  let expect.col += 3
459  call assert_equal([expect], prop_list(2))
460
461  normal 2G==
462  call assert_equal('    yyyyy', getline(2))
463  let expect.col = 6
464  call assert_equal([expect], prop_list(2))
465
466  call prop_clear(2)
467  call prop_add(2, 2, {'length': 5, 'type': 'comment'})
468  let expect.col = 2
469  let expect.length = 5
470  call assert_equal([expect], prop_list(2))
471
472  normal 2G<<
473  call assert_equal(' yyyyy', getline(2))
474  let expect.length = 2
475  call assert_equal([expect], prop_list(2))
476
477  set shiftwidth&
478  call prop_type_delete('comment')
479endfunc
480
481" Setup a three line prop in lines 2 - 4.
482" Add short props in line 1 and 5.
483func Setup_three_line_prop()
484  new
485  call setline(1, ['one', 'twotwo', 'three', 'fourfour', 'five'])
486  call prop_add(1, 2, {'length': 1, 'type': 'comment'})
487  call prop_add(2, 4, {'end_lnum': 4, 'end_col': 5, 'type': 'comment'})
488  call prop_add(5, 2, {'length': 1, 'type': 'comment'})
489endfunc
490
491func Test_prop_multiline()
492  eval 'comment'->prop_type_add({'highlight': 'Directory'})
493  new
494  call setline(1, ['xxxxxxx', 'yyyyyyyyy', 'zzzzzzzz'])
495
496  " start halfway line 1, end halfway line 3
497  call prop_add(1, 3, {'end_lnum': 3, 'end_col': 5, 'type': 'comment'})
498  let expect1 = {'col': 3, 'length': 6, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
499  call assert_equal([expect1], prop_list(1))
500  let expect2 = {'col': 1, 'length': 10, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0}
501  call assert_equal([expect2], prop_list(2))
502  let expect3 = {'col': 1, 'length': 4, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0}
503  call assert_equal([expect3], prop_list(3))
504  call prop_clear(1, 3)
505
506  " include all three lines
507  call prop_add(1, 1, {'end_lnum': 3, 'end_col': 999, 'type': 'comment'})
508  let expect1.col = 1
509  let expect1.length = 8
510  call assert_equal([expect1], prop_list(1))
511  call assert_equal([expect2], prop_list(2))
512  let expect3.length = 9
513  call assert_equal([expect3], prop_list(3))
514  call prop_clear(1, 3)
515
516  bwipe!
517
518  " Test deleting the first line of a multi-line prop.
519  call Setup_three_line_prop()
520  let expect_short = {'col': 2, 'length': 1, 'type': 'comment', 'start': 1, 'end': 1, 'id': 0}
521  call assert_equal([expect_short], prop_list(1))
522  let expect2 = {'col': 4, 'length': 4, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
523  call assert_equal([expect2], prop_list(2))
524  2del
525  call assert_equal([expect_short], prop_list(1))
526  let expect2 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
527  call assert_equal([expect2], prop_list(2))
528  bwipe!
529
530  " Test deleting the last line of a multi-line prop.
531  call Setup_three_line_prop()
532  let expect3 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0}
533  call assert_equal([expect3], prop_list(3))
534  let expect4 = {'col': 1, 'length': 4, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0}
535  call assert_equal([expect4], prop_list(4))
536  4del
537  let expect3.end = 1
538  call assert_equal([expect3], prop_list(3))
539  call assert_equal([expect_short], prop_list(4))
540  bwipe!
541
542  " Test appending a line below the multi-line text prop start.
543  call Setup_three_line_prop()
544  let expect2 = {'col': 4, 'length': 4, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
545  call assert_equal([expect2], prop_list(2))
546  call append(2, "new line")
547  call assert_equal([expect2], prop_list(2))
548  let expect3 = {'col': 1, 'length': 9, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0}
549  call assert_equal([expect3], prop_list(3))
550  bwipe!
551
552  call prop_type_delete('comment')
553endfunc
554
555func Test_prop_byteoff()
556  call prop_type_add('comment', {'highlight': 'Directory'})
557  new
558  call setline(1, ['line1', 'second line', ''])
559  set ff=unix
560  call assert_equal(19, line2byte(3))
561  call prop_add(1, 1, {'end_col': 3, 'type': 'comment'})
562  call assert_equal(19, line2byte(3))
563
564  bwipe!
565  call prop_type_delete('comment')
566endfunc
567
568func Test_prop_undo()
569  new
570  call prop_type_add('comment', {'highlight': 'Directory'})
571  call setline(1, ['oneone', 'twotwo', 'three'])
572  " Set 'undolevels' to break changes into undo-able pieces.
573  set ul&
574
575  call prop_add(1, 3, {'end_col': 5, 'type': 'comment'})
576  let expected = [{'col': 3, 'length': 2, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ]
577  call assert_equal(expected, prop_list(1))
578
579  " Insert a character, then undo.
580  exe "normal 0lllix\<Esc>"
581  set ul&
582  let expected[0].length = 3
583  call assert_equal(expected, prop_list(1))
584  undo
585  let expected[0].length = 2
586  call assert_equal(expected, prop_list(1))
587
588  " Delete a character, then undo
589  exe "normal 0lllx"
590  set ul&
591  let expected[0].length = 1
592  call assert_equal(expected, prop_list(1))
593  undo
594  let expected[0].length = 2
595  call assert_equal(expected, prop_list(1))
596
597  " Delete the line, then undo
598  1d
599  set ul&
600  call assert_equal([], prop_list(1))
601  undo
602  call assert_equal(expected, prop_list(1))
603
604  " Insert a character, delete two characters, then undo with "U"
605  exe "normal 0lllix\<Esc>"
606  set ul&
607  let expected[0].length = 3
608  call assert_equal(expected, prop_list(1))
609  exe "normal 0lllxx"
610  set ul&
611  let expected[0].length = 1
612  call assert_equal(expected, prop_list(1))
613  normal U
614  let expected[0].length = 2
615  call assert_equal(expected, prop_list(1))
616
617  " substitute a word, then undo
618  call setline(1, 'the number 123 is highlighted.')
619  call prop_add(1, 12, {'length': 3, 'type': 'comment'})
620  let expected = [{'col': 12, 'length': 3, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ]
621  call assert_equal(expected, prop_list(1))
622  set ul&
623  1s/number/foo
624  let expected[0].col = 9
625  call assert_equal(expected, prop_list(1))
626  undo
627  let expected[0].col = 12
628  call assert_equal(expected, prop_list(1))
629  call prop_clear(1)
630
631  " substitute with backslash
632  call setline(1, 'the number 123 is highlighted.')
633  call prop_add(1, 12, {'length': 3, 'type': 'comment'})
634  let expected = [{'col': 12, 'length': 3, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ]
635  call assert_equal(expected, prop_list(1))
636  1s/the/\The
637  call assert_equal(expected, prop_list(1))
638  1s/^/\\
639  let expected[0].col += 1
640  call assert_equal(expected, prop_list(1))
641  1s/^/\~
642  let expected[0].col += 1
643  call assert_equal(expected, prop_list(1))
644  1s/123/12\\3
645  let expected[0].length += 1
646  call assert_equal(expected, prop_list(1))
647  call prop_clear(1)
648
649  bwipe!
650  call prop_type_delete('comment')
651endfunc
652
653" screenshot test with textprop highlighting
654func Test_textprop_screenshot_various()
655  CheckScreendump
656  " The Vim running in the terminal needs to use utf-8.
657  if g:orig_encoding != 'utf-8'
658    throw 'Skipped: not using utf-8'
659  endif
660  call writefile([
661	\ "call setline(1, ["
662	\	.. "'One two',"
663	\	.. "'Numbér 123 änd thœn 4¾7.',"
664	\	.. "'--aa--bb--cc--dd--',"
665	\	.. "'// comment with error in it',"
666	\	.. "'first line',"
667	\	.. "'  second line  ',"
668	\	.. "'third line',"
669	\	.. "'   fourth line',"
670	\	.. "])",
671	\ "hi NumberProp ctermfg=blue",
672	\ "hi LongProp ctermbg=yellow",
673	\ "hi BackgroundProp ctermbg=lightgrey",
674	\ "hi UnderlineProp cterm=underline",
675	\ "call prop_type_add('number', {'highlight': 'NumberProp'})",
676	\ "call prop_type_add('long', {'highlight': 'NumberProp'})",
677	\ "call prop_type_change('long', {'highlight': 'LongProp'})",
678	\ "call prop_type_add('start', {'highlight': 'NumberProp', 'start_incl': 1})",
679	\ "call prop_type_add('end', {'highlight': 'NumberProp', 'end_incl': 1})",
680	\ "call prop_type_add('both', {'highlight': 'NumberProp', 'start_incl': 1, 'end_incl': 1})",
681	\ "call prop_type_add('background', {'highlight': 'NumberProp', 'combine': 1})",
682	\ "eval 'background'->prop_type_change({'highlight': 'BackgroundProp'})",
683	\ "call prop_type_add('error', {'highlight': 'UnderlineProp', 'combine': 1})",
684	\ "call prop_add(1, 4, {'end_lnum': 3, 'end_col': 3, 'type': 'long'})",
685	\ "call prop_add(2, 9, {'length': 3, 'type': 'number'})",
686	\ "call prop_add(2, 24, {'length': 4, 'type': 'number'})",
687	\ "call prop_add(3, 3, {'length': 2, 'type': 'number'})",
688	\ "call prop_add(3, 7, {'length': 2, 'type': 'start'})",
689	\ "call prop_add(3, 11, {'length': 2, 'type': 'end'})",
690	\ "call prop_add(3, 15, {'length': 2, 'type': 'both'})",
691	\ "call prop_add(4, 12, {'length': 10, 'type': 'background'})",
692	\ "call prop_add(4, 17, {'length': 5, 'type': 'error'})",
693	\ "call prop_add(5, 7, {'length': 4, 'type': 'long'})",
694	\ "call prop_add(6, 1, {'length': 8, 'type': 'long'})",
695	\ "call prop_add(8, 1, {'length': 1, 'type': 'long'})",
696	\ "call prop_add(8, 11, {'length': 4, 'type': 'long'})",
697	\ "set number cursorline",
698	\ "hi clear SpellBad",
699	\ "set spell",
700	\ "syn match Comment '//.*'",
701	\ "hi Comment ctermfg=green",
702	\ "normal 3G0llix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>",
703	\ "normal 3G0lli\<BS>\<Esc>",
704	\ "normal 6G0i\<BS>\<Esc>",
705	\ "normal 3J",
706	\ "normal 3G",
707	\], 'XtestProp')
708  let buf = RunVimInTerminal('-S XtestProp', {'rows': 8})
709  call VerifyScreenDump(buf, 'Test_textprop_01', {})
710
711  " clean up
712  call StopVimInTerminal(buf)
713  call delete('XtestProp')
714endfunc
715
716func RunTestVisualBlock(width, dump)
717  call writefile([
718	\ "call setline(1, ["
719	\	.. "'xxxxxxxxx 123 x',"
720	\	.. "'xxxxxxxx 123 x',"
721	\	.. "'xxxxxxx 123 x',"
722	\	.. "'xxxxxx 123 x',"
723	\	.. "'xxxxx 123 x',"
724	\	.. "'xxxx 123 xx',"
725	\	.. "'xxx 123 xxx',"
726	\	.. "'xx 123 xxxx',"
727	\	.. "'x 123 xxxxx',"
728	\	.. "' 123 xxxxxx',"
729	\	.. "])",
730	\ "hi SearchProp ctermbg=yellow",
731	\ "call prop_type_add('search', {'highlight': 'SearchProp'})",
732	\ "call prop_add(1, 11, {'length': 3, 'type': 'search'})",
733	\ "call prop_add(2, 10, {'length': 3, 'type': 'search'})",
734	\ "call prop_add(3, 9, {'length': 3, 'type': 'search'})",
735	\ "call prop_add(4, 8, {'length': 3, 'type': 'search'})",
736	\ "call prop_add(5, 7, {'length': 3, 'type': 'search'})",
737	\ "call prop_add(6, 6, {'length': 3, 'type': 'search'})",
738	\ "call prop_add(7, 5, {'length': 3, 'type': 'search'})",
739	\ "call prop_add(8, 4, {'length': 3, 'type': 'search'})",
740	\ "call prop_add(9, 3, {'length': 3, 'type': 'search'})",
741	\ "call prop_add(10, 2, {'length': 3, 'type': 'search'})",
742	\ "normal 1G6|\<C-V>" .. repeat('l', a:width - 1) .. "10jx",
743	\], 'XtestPropVis')
744  let buf = RunVimInTerminal('-S XtestPropVis', {'rows': 12})
745  call VerifyScreenDump(buf, 'Test_textprop_vis_' .. a:dump, {})
746
747  " clean up
748  call StopVimInTerminal(buf)
749  call delete('XtestPropVis')
750endfunc
751
752" screenshot test with Visual block mode operations
753func Test_textprop_screenshot_visual()
754  CheckScreendump
755
756  " Delete two columns while text props are three chars wide.
757  call RunTestVisualBlock(2, '01')
758
759  " Same, but delete four columns
760  call RunTestVisualBlock(4, '02')
761endfunc
762
763func Test_textprop_after_tab()
764  CheckScreendump
765
766  let lines =<< trim END
767       call setline(1, [
768             \ "\txxx",
769             \ "x\txxx",
770             \ ])
771       hi SearchProp ctermbg=yellow
772       call prop_type_add('search', {'highlight': 'SearchProp'})
773       call prop_add(1, 2, {'length': 3, 'type': 'search'})
774       call prop_add(2, 3, {'length': 3, 'type': 'search'})
775  END
776  call writefile(lines, 'XtestPropTab')
777  let buf = RunVimInTerminal('-S XtestPropTab', {'rows': 6})
778  call VerifyScreenDump(buf, 'Test_textprop_tab', {})
779
780  " clean up
781  call StopVimInTerminal(buf)
782  call delete('XtestPropTab')
783endfunc
784
785func Test_textprop_with_syntax()
786  CheckScreendump
787
788  let lines =<< trim END
789       call setline(1, [
790             \ "(abc)",
791             \ ])
792       syn match csParens "[()]" display
793       hi! link csParens MatchParen
794
795       call prop_type_add('TPTitle', #{ highlight: 'Title' })
796       call prop_add(1, 2, #{type: 'TPTitle', end_col: 5})
797  END
798  call writefile(lines, 'XtestPropSyn')
799  let buf = RunVimInTerminal('-S XtestPropSyn', {'rows': 6})
800  call VerifyScreenDump(buf, 'Test_textprop_syn_1', {})
801
802  " clean up
803  call StopVimInTerminal(buf)
804  call delete('XtestPropSyn')
805endfunc
806
807" Adding a text property to a new buffer should not fail
808func Test_textprop_empty_buffer()
809  call prop_type_add('comment', {'highlight': 'Search'})
810  new
811  call prop_add(1, 1, {'type': 'comment'})
812  close
813  call prop_type_delete('comment')
814endfunc
815
816" Adding a text property with invalid highlight should be ignored.
817func Test_textprop_invalid_highlight()
818  call assert_fails("call prop_type_add('dni', {'highlight': 'DoesNotExist'})", 'E970:')
819  new
820  call setline(1, ['asdf','asdf'])
821  call prop_add(1, 1, {'length': 4, 'type': 'dni'})
822  redraw
823  bwipe!
824  call prop_type_delete('dni')
825endfunc
826
827" Adding a text property to an empty buffer and then editing another
828func Test_textprop_empty_buffer_next()
829  call prop_type_add("xxx", {})
830  call prop_add(1, 1, {"type": "xxx"})
831  next X
832  call prop_type_delete('xxx')
833endfunc
834
835func Test_textprop_remove_from_buf()
836  new
837  let buf = bufnr('')
838  call prop_type_add('one', {'bufnr': buf})
839  call prop_add(1, 1, {'type': 'one', 'id': 234})
840  file x
841  edit y
842  call prop_remove({'id': 234, 'bufnr': buf}, 1)
843  call prop_type_delete('one', {'bufnr': buf})
844  bwipe! x
845  close
846endfunc
847
848func Test_textprop_in_unloaded_buf()
849  edit Xaaa
850  call setline(1, 'aaa')
851  write
852  edit Xbbb
853  call setline(1, 'bbb')
854  write
855  let bnr = bufnr('')
856  edit Xaaa
857
858  call prop_type_add('ErrorMsg', #{highlight:'ErrorMsg'})
859  call assert_fails("call prop_add(1, 1, #{end_lnum: 1, endcol: 2, type: 'ErrorMsg', bufnr: bnr})", 'E275:')
860  exe 'buf ' .. bnr
861  call assert_equal('bbb', getline(1))
862  call assert_equal(0, prop_list(1)->len())
863
864  bwipe! Xaaa
865  bwipe! Xbbb
866  cal delete('Xaaa')
867  cal delete('Xbbb')
868endfunc
869