1" Tests for defining text property types and adding text properties to the
2" buffer.
3
4if !has('textprop')
5  finish
6endif
7
8source screendump.vim
9
10" test length zero
11
12func Test_proptype_global()
13  call prop_type_add('comment', {'highlight': 'Directory', 'priority': 123, 'start_incl': 1, 'end_incl': 1})
14  let proptypes = prop_type_list()
15  call assert_equal(1, len(proptypes))
16  call assert_equal('comment', proptypes[0])
17
18  let proptype = prop_type_get('comment')
19  call assert_equal('Directory', proptype['highlight'])
20  call assert_equal(123, proptype['priority'])
21  call assert_equal(1, proptype['start_incl'])
22  call assert_equal(1, proptype['end_incl'])
23
24  call prop_type_delete('comment')
25  call assert_equal(0, len(prop_type_list()))
26
27  call prop_type_add('one', {})
28  call assert_equal(1, len(prop_type_list()))
29  let proptype = prop_type_get('one')
30  call assert_false(has_key(proptype, 'highlight'))
31  call assert_equal(0, proptype['priority'])
32  call assert_equal(0, proptype['start_incl'])
33  call assert_equal(0, proptype['end_incl'])
34
35  call prop_type_add('two', {})
36  call assert_equal(2, len(prop_type_list()))
37  call prop_type_delete('one')
38  call assert_equal(1, len(prop_type_list()))
39  call prop_type_delete('two')
40  call assert_equal(0, len(prop_type_list()))
41endfunc
42
43func Test_proptype_buf()
44  let bufnr = bufnr('')
45  call prop_type_add('comment', {'bufnr': bufnr, 'highlight': 'Directory', 'priority': 123, 'start_incl': 1, 'end_incl': 1})
46  let proptypes = prop_type_list({'bufnr': bufnr})
47  call assert_equal(1, len(proptypes))
48  call assert_equal('comment', proptypes[0])
49
50  let proptype = prop_type_get('comment', {'bufnr': bufnr})
51  call assert_equal('Directory', proptype['highlight'])
52  call assert_equal(123, proptype['priority'])
53  call assert_equal(1, proptype['start_incl'])
54  call assert_equal(1, proptype['end_incl'])
55
56  call prop_type_delete('comment', {'bufnr': bufnr})
57  call assert_equal(0, len(prop_type_list({'bufnr': bufnr})))
58
59  call prop_type_add('one', {'bufnr': bufnr})
60  let proptype = prop_type_get('one', {'bufnr': bufnr})
61  call assert_false(has_key(proptype, 'highlight'))
62  call assert_equal(0, proptype['priority'])
63  call assert_equal(0, proptype['start_incl'])
64  call assert_equal(0, proptype['end_incl'])
65
66  call prop_type_add('two', {'bufnr': bufnr})
67  call assert_equal(2, len(prop_type_list({'bufnr': bufnr})))
68  call prop_type_delete('one', {'bufnr': bufnr})
69  call assert_equal(1, len(prop_type_list({'bufnr': bufnr})))
70  call prop_type_delete('two', {'bufnr': bufnr})
71  call assert_equal(0, len(prop_type_list({'bufnr': bufnr})))
72endfunc
73
74func AddPropTypes()
75  call prop_type_add('one', {})
76  call prop_type_add('two', {})
77  call prop_type_add('three', {})
78  call prop_type_add('whole', {})
79endfunc
80
81func DeletePropTypes()
82  call prop_type_delete('one')
83  call prop_type_delete('two')
84  call prop_type_delete('three')
85  call prop_type_delete('whole')
86endfunc
87
88func SetupPropsInFirstLine()
89  call setline(1, 'one two three')
90  call prop_add(1, 1, {'length': 3, 'id': 11, 'type': 'one'})
91  call prop_add(1, 5, {'length': 3, 'id': 12, 'type': 'two'})
92  call prop_add(1, 9, {'length': 5, 'id': 13, 'type': 'three'})
93  call prop_add(1, 1, {'length': 13, 'id': 14, 'type': 'whole'})
94endfunc
95
96func Get_expected_props()
97  return [
98      \ {'col': 1, 'length': 13, 'id': 14, 'type': 'whole', 'start': 1, 'end': 1},
99      \ {'col': 1, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1},
100      \ {'col': 5, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1},
101      \ {'col': 9, 'length': 5, 'id': 13, 'type': 'three', 'start': 1, 'end': 1},
102      \ ]
103endfunc
104
105func Test_prop_add()
106  new
107  call AddPropTypes()
108  call SetupPropsInFirstLine()
109  let expected_props = Get_expected_props()
110  call assert_equal(expected_props, prop_list(1))
111  call assert_fails("call prop_add(10, 1, {'length': 1, 'id': 14, 'type': 'whole'})", 'E966:')
112  call assert_fails("call prop_add(1, 22, {'length': 1, 'id': 14, 'type': 'whole'})", 'E964:')
113
114  " Insert a line above, text props must still be there.
115  call append(0, 'empty')
116  call assert_equal(expected_props, prop_list(2))
117  " Delete a line above, text props must still be there.
118  1del
119  call assert_equal(expected_props, prop_list(1))
120
121  " Prop without length or end column is zero length
122  call prop_clear(1)
123  call prop_add(1, 5, {'type': 'two'})
124  let expected = [{'col': 5, 'length': 0, 'type': 'two', 'id': 0, 'start': 1, 'end': 1}]
125  call assert_equal(expected, prop_list(1))
126
127  call DeletePropTypes()
128  bwipe!
129endfunc
130
131func Test_prop_remove()
132  new
133  call AddPropTypes()
134  call SetupPropsInFirstLine()
135  let props = Get_expected_props()
136  call assert_equal(props, prop_list(1))
137
138  " remove by id
139  call prop_remove({'id': 12}, 1)
140  unlet props[2]
141  call assert_equal(props, prop_list(1))
142
143  " remove by type
144  call prop_remove({'type': 'one'}, 1)
145  unlet props[1]
146  call assert_equal(props, prop_list(1))
147
148  call DeletePropTypes()
149  bwipe!
150endfunc
151
152func SetupOneLine()
153  call setline(1, 'xonex xtwoxx')
154  call AddPropTypes()
155  call prop_add(1, 2, {'length': 3, 'id': 11, 'type': 'one'})
156  call prop_add(1, 8, {'length': 3, 'id': 12, 'type': 'two'})
157  let expected = [
158	\ {'col': 2, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1},
159	\ {'col': 8, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1},
160	\]
161  call assert_equal(expected, prop_list(1))
162  return expected
163endfunc
164
165func Test_prop_add_remove_buf()
166  new
167  let bufnr = bufnr('')
168  call AddPropTypes()
169  for lnum in range(1, 4)
170    call setline(lnum, 'one two three')
171  endfor
172  wincmd w
173  for lnum in range(1, 4)
174    call prop_add(lnum, 1, {'length': 3, 'id': 11, 'type': 'one', 'bufnr': bufnr})
175    call prop_add(lnum, 5, {'length': 3, 'id': 12, 'type': 'two', 'bufnr': bufnr})
176    call prop_add(lnum, 11, {'length': 3, 'id': 13, 'type': 'three', 'bufnr': bufnr})
177  endfor
178
179  let props = [
180	\ {'col': 1, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1},
181	\ {'col': 5, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1},
182	\ {'col': 11, 'length': 3, 'id': 13, 'type': 'three', 'start': 1, 'end': 1},
183	\]
184  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
185
186  " remove by id
187  let before_props = deepcopy(props)
188  unlet props[1]
189
190  call prop_remove({'id': 12, 'bufnr': bufnr}, 1)
191  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
192  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
193  call assert_equal(before_props, prop_list(3, {'bufnr': bufnr}))
194  call assert_equal(before_props, prop_list(4, {'bufnr': bufnr}))
195
196  call prop_remove({'id': 12, 'bufnr': bufnr}, 3, 4)
197  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
198  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
199  call assert_equal(props, prop_list(3, {'bufnr': bufnr}))
200  call assert_equal(props, prop_list(4, {'bufnr': bufnr}))
201
202  call prop_remove({'id': 12, 'bufnr': bufnr})
203  for lnum in range(1, 4)
204    call assert_equal(props, prop_list(lnum, {'bufnr': bufnr}))
205  endfor
206
207  " remove by type
208  let before_props = deepcopy(props)
209  unlet props[0]
210
211  call prop_remove({'type': 'one', 'bufnr': bufnr}, 1)
212  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
213  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
214  call assert_equal(before_props, prop_list(3, {'bufnr': bufnr}))
215  call assert_equal(before_props, prop_list(4, {'bufnr': bufnr}))
216
217  call prop_remove({'type': 'one', 'bufnr': bufnr}, 3, 4)
218  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
219  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
220  call assert_equal(props, prop_list(3, {'bufnr': bufnr}))
221  call assert_equal(props, prop_list(4, {'bufnr': bufnr}))
222
223  call prop_remove({'type': 'one', 'bufnr': bufnr})
224  for lnum in range(1, 4)
225    call assert_equal(props, prop_list(lnum, {'bufnr': bufnr}))
226  endfor
227
228  call DeletePropTypes()
229  wincmd w
230  bwipe!
231endfunc
232
233func Test_prop_backspace()
234  new
235  set bs=2
236  let expected = SetupOneLine() " 'xonex xtwoxx'
237
238  exe "normal 0li\<BS>\<Esc>fxli\<BS>\<Esc>"
239  call assert_equal('one xtwoxx', getline(1))
240  let expected[0].col = 1
241  let expected[1].col = 6
242  call assert_equal(expected, prop_list(1))
243
244  call DeletePropTypes()
245  bwipe!
246  set bs&
247endfunc
248
249func Test_prop_replace()
250  new
251  set bs=2
252  let expected = SetupOneLine() " 'xonex xtwoxx'
253
254  exe "normal 0Ryyy\<Esc>"
255  call assert_equal('yyyex xtwoxx', getline(1))
256  call assert_equal(expected, prop_list(1))
257
258  exe "normal ftRyy\<BS>"
259  call assert_equal('yyyex xywoxx', getline(1))
260  call assert_equal(expected, prop_list(1))
261
262  exe "normal 0fwRyy\<BS>"
263  call assert_equal('yyyex xyyoxx', getline(1))
264  call assert_equal(expected, prop_list(1))
265
266  exe "normal 0foRyy\<BS>\<BS>"
267  call assert_equal('yyyex xyyoxx', getline(1))
268  call assert_equal(expected, prop_list(1))
269
270  call DeletePropTypes()
271  bwipe!
272  set bs&
273endfunc
274
275func Test_prop_clear()
276  new
277  call AddPropTypes()
278  call SetupPropsInFirstLine()
279  call assert_equal(Get_expected_props(), prop_list(1))
280
281  call prop_clear(1)
282  call assert_equal([], prop_list(1))
283
284  call DeletePropTypes()
285  bwipe!
286endfunc
287
288func Test_prop_clear_buf()
289  new
290  call AddPropTypes()
291  call SetupPropsInFirstLine()
292  let bufnr = bufnr('')
293  wincmd w
294  call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr}))
295
296  call prop_clear(1, 1, {'bufnr': bufnr})
297  call assert_equal([], prop_list(1, {'bufnr': bufnr}))
298
299  wincmd w
300  call DeletePropTypes()
301  bwipe!
302endfunc
303
304func Test_prop_setline()
305  new
306  call AddPropTypes()
307  call SetupPropsInFirstLine()
308  call assert_equal(Get_expected_props(), prop_list(1))
309
310  call setline(1, 'foobar')
311  call assert_equal([], prop_list(1))
312
313  call DeletePropTypes()
314  bwipe!
315endfunc
316
317func Test_prop_setbufline()
318  new
319  call AddPropTypes()
320  call SetupPropsInFirstLine()
321  let bufnr = bufnr('')
322  wincmd w
323  call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr}))
324
325  call setbufline(bufnr, 1, 'foobar')
326  call assert_equal([], prop_list(1, {'bufnr': bufnr}))
327
328  wincmd w
329  call DeletePropTypes()
330  bwipe!
331endfunc
332
333func Test_prop_substitute()
334  new
335  " Set first line to 'one two three'
336  call AddPropTypes()
337  call SetupPropsInFirstLine()
338  let expected_props = Get_expected_props()
339  call assert_equal(expected_props, prop_list(1))
340
341  " Change "n" in "one" to XX: 'oXXe two three'
342  s/n/XX/
343  let expected_props[0].length += 1
344  let expected_props[1].length += 1
345  let expected_props[2].col += 1
346  let expected_props[3].col += 1
347  call assert_equal(expected_props, prop_list(1))
348
349  " Delete "t" in "two" and "three" to XX: 'oXXe wo hree'
350  s/t//g
351  let expected_props[0].length -= 2
352  let expected_props[2].length -= 1
353  let expected_props[3].length -= 1
354  let expected_props[3].col -= 1
355  call assert_equal(expected_props, prop_list(1))
356
357  " Split the line by changing w to line break: 'oXXe ', 'o hree'
358  " The long prop is split and spans both lines.
359  " The props on "two" and "three" move to the next line.
360  s/w/\r/
361  let new_props = [
362	\ copy(expected_props[0]),
363	\ copy(expected_props[2]),
364	\ copy(expected_props[3]),
365	\ ]
366  let expected_props[0].length = 5
367  unlet expected_props[3]
368  unlet expected_props[2]
369  call assert_equal(expected_props, prop_list(1))
370
371  let new_props[0].length = 6
372  let new_props[1].col = 1
373  let new_props[1].length = 1
374  let new_props[2].col = 3
375  call assert_equal(new_props, prop_list(2))
376
377  call DeletePropTypes()
378  bwipe!
379endfunc
380
381func Test_prop_change_indent()
382  call prop_type_add('comment', {'highlight': 'Directory'})
383  new
384  call setline(1, ['    xxx', 'yyyyy'])
385  call prop_add(2, 2, {'length': 2, 'type': 'comment'})
386  let expect = {'col': 2, 'length': 2, 'type': 'comment', 'start': 1, 'end': 1, 'id': 0}
387  call assert_equal([expect], prop_list(2))
388
389  set shiftwidth=3
390  normal 2G>>
391  call assert_equal('   yyyyy', getline(2))
392  let expect.col += 3
393  call assert_equal([expect], prop_list(2))
394
395  normal 2G==
396  call assert_equal('    yyyyy', getline(2))
397  let expect.col = 6
398  call assert_equal([expect], prop_list(2))
399
400  call prop_clear(2)
401  call prop_add(2, 2, {'length': 5, 'type': 'comment'})
402  let expect.col = 2
403  let expect.length = 5
404  call assert_equal([expect], prop_list(2))
405
406  normal 2G<<
407  call assert_equal(' yyyyy', getline(2))
408  let expect.length = 2
409  call assert_equal([expect], prop_list(2))
410
411  set shiftwidth&
412  call prop_type_delete('comment')
413endfunc
414
415" Setup a three line prop in lines 2 - 4.
416" Add short props in line 1 and 5.
417func Setup_three_line_prop()
418  new
419  call setline(1, ['one', 'twotwo', 'three', 'fourfour', 'five'])
420  call prop_add(1, 2, {'length': 1, 'type': 'comment'})
421  call prop_add(2, 4, {'end_lnum': 4, 'end_col': 5, 'type': 'comment'})
422  call prop_add(5, 2, {'length': 1, 'type': 'comment'})
423endfunc
424
425func Test_prop_multiline()
426  call prop_type_add('comment', {'highlight': 'Directory'})
427  new
428  call setline(1, ['xxxxxxx', 'yyyyyyyyy', 'zzzzzzzz'])
429
430  " start halfway line 1, end halfway line 3
431  call prop_add(1, 3, {'end_lnum': 3, 'end_col': 5, 'type': 'comment'})
432  let expect1 = {'col': 3, 'length': 6, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
433  call assert_equal([expect1], prop_list(1))
434  let expect2 = {'col': 1, 'length': 10, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0}
435  call assert_equal([expect2], prop_list(2))
436  let expect3 = {'col': 1, 'length': 4, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0}
437  call assert_equal([expect3], prop_list(3))
438  call prop_clear(1, 3)
439
440  " include all three lines
441  call prop_add(1, 1, {'end_lnum': 3, 'end_col': 999, 'type': 'comment'})
442  let expect1.col = 1
443  let expect1.length = 8
444  call assert_equal([expect1], prop_list(1))
445  call assert_equal([expect2], prop_list(2))
446  let expect3.length = 9
447  call assert_equal([expect3], prop_list(3))
448  call prop_clear(1, 3)
449
450  bwipe!
451
452  " Test deleting the first line of a multi-line prop.
453  call Setup_three_line_prop()
454  let expect_short = {'col': 2, 'length': 1, 'type': 'comment', 'start': 1, 'end': 1, 'id': 0}
455  call assert_equal([expect_short], prop_list(1))
456  let expect2 = {'col': 4, 'length': 4, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
457  call assert_equal([expect2], prop_list(2))
458  2del
459  call assert_equal([expect_short], prop_list(1))
460  let expect2 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
461  call assert_equal([expect2], prop_list(2))
462  bwipe!
463
464  " Test deleting the last line of a multi-line prop.
465  call Setup_three_line_prop()
466  let expect3 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0}
467  call assert_equal([expect3], prop_list(3))
468  let expect4 = {'col': 1, 'length': 4, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0}
469  call assert_equal([expect4], prop_list(4))
470  4del
471  let expect3.end = 1
472  call assert_equal([expect3], prop_list(3))
473  call assert_equal([expect_short], prop_list(4))
474  bwipe!
475
476  " Test appending a line below the multi-line text prop start.
477  call Setup_three_line_prop()
478  let expect2 = {'col': 4, 'length': 4, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
479  call assert_equal([expect2], prop_list(2))
480  call append(2, "new line")
481  call assert_equal([expect2], prop_list(2))
482  let expect3 = {'col': 1, 'length': 9, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0}
483  call assert_equal([expect3], prop_list(3))
484  bwipe!
485
486  call prop_type_delete('comment')
487endfunc
488
489func Test_prop_byteoff()
490  call prop_type_add('comment', {'highlight': 'Directory'})
491  new
492  call setline(1, ['line1', 'second line', ''])
493  set ff=unix
494  call assert_equal(19, line2byte(3))
495  call prop_add(1, 1, {'end_col': 3, 'type': 'comment'})
496  call assert_equal(19, line2byte(3))
497
498  bwipe!
499  call prop_type_delete('comment')
500endfunc
501
502func Test_prop_undo()
503  new
504  call prop_type_add('comment', {'highlight': 'Directory'})
505  call setline(1, ['oneone', 'twotwo', 'three'])
506  " Set 'undolevels' to break changes into undo-able pieces.
507  set ul&
508
509  call prop_add(1, 3, {'end_col': 5, 'type': 'comment'})
510  let expected = [{'col': 3, 'length': 2, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ]
511  call assert_equal(expected, prop_list(1))
512
513  " Insert a character, then undo.
514  exe "normal 0lllix\<Esc>"
515  set ul&
516  let expected[0].length = 3
517  call assert_equal(expected, prop_list(1))
518  undo
519  let expected[0].length = 2
520  call assert_equal(expected, prop_list(1))
521
522  " Delete a character, then undo
523  exe "normal 0lllx"
524  set ul&
525  let expected[0].length = 1
526  call assert_equal(expected, prop_list(1))
527  undo
528  let expected[0].length = 2
529  call assert_equal(expected, prop_list(1))
530
531  " Delete the line, then undo
532  1d
533  set ul&
534  call assert_equal([], prop_list(1))
535  undo
536  call assert_equal(expected, prop_list(1))
537
538  " Insert a character, delete two characters, then undo with "U"
539  exe "normal 0lllix\<Esc>"
540  set ul&
541  let expected[0].length = 3
542  call assert_equal(expected, prop_list(1))
543  exe "normal 0lllxx"
544  set ul&
545  let expected[0].length = 1
546  call assert_equal(expected, prop_list(1))
547  normal U
548  let expected[0].length = 2
549  call assert_equal(expected, prop_list(1))
550
551  bwipe!
552  call prop_type_delete('comment')
553endfunc
554
555" screenshot test with textprop highlighting
556funct Test_textprop_screenshots()
557  " The Vim running in the terminal needs to use utf-8.
558  if !CanRunVimInTerminal() || g:orig_encoding != 'utf-8'
559    return
560  endif
561  call writefile([
562	\ "call setline(1, ['One two', 'Numbér 123 änd thœn 4¾7.', '--aa--bb--cc--dd--'])",
563	\ "hi NumberProp ctermfg=blue",
564	\ "hi LongProp ctermbg=yellow",
565	\ "call prop_type_add('number', {'highlight': 'NumberProp'})",
566	\ "call prop_type_add('long', {'highlight': 'LongProp'})",
567	\ "call prop_type_add('start', {'highlight': 'NumberProp', 'start_incl': 1})",
568	\ "call prop_type_add('end', {'highlight': 'NumberProp', 'end_incl': 1})",
569	\ "call prop_type_add('both', {'highlight': 'NumberProp', 'start_incl': 1, 'end_incl': 1})",
570	\ "call prop_add(1, 4, {'end_lnum': 3, 'end_col': 3, 'type': 'long'})",
571	\ "call prop_add(2, 9, {'length': 3, 'type': 'number'})",
572	\ "call prop_add(2, 24, {'length': 4, 'type': 'number'})",
573	\ "call prop_add(3, 3, {'length': 2, 'type': 'number'})",
574	\ "call prop_add(3, 7, {'length': 2, 'type': 'start'})",
575	\ "call prop_add(3, 11, {'length': 2, 'type': 'end'})",
576	\ "call prop_add(3, 15, {'length': 2, 'type': 'both'})",
577	\ "set number",
578	\ "hi clear SpellBad",
579	\ "set spell",
580	\ "normal 3G0llix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>",
581	\ "normal 3G0lli\<BS>\<Esc>",
582	\], 'XtestProp')
583  let buf = RunVimInTerminal('-S XtestProp', {'rows': 6})
584  call VerifyScreenDump(buf, 'Test_textprop_01', {})
585
586  " clean up
587  call StopVimInTerminal(buf)
588  call delete('XtestProp')
589endfunc
590