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  call setline(1, 'one two three')
170  wincmd w
171  call prop_add(1, 1, {'length': 3, 'id': 11, 'type': 'one', 'bufnr': bufnr})
172  call prop_add(1, 5, {'length': 3, 'id': 12, 'type': 'two', 'bufnr': bufnr})
173  call prop_add(1, 11, {'length': 3, 'id': 13, 'type': 'three', 'bufnr': bufnr})
174
175  let props = [
176	\ {'col': 1, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1},
177	\ {'col': 5, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1},
178	\ {'col': 11, 'length': 3, 'id': 13, 'type': 'three', 'start': 1, 'end': 1},
179	\]
180  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
181
182  " remove by id
183  call prop_remove({'id': 12, 'bufnr': bufnr}, 1)
184  unlet props[1]
185  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
186
187  " remove by type
188  call prop_remove({'type': 'one', 'bufnr': bufnr}, 1)
189  unlet props[0]
190  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
191
192  call DeletePropTypes()
193  wincmd w
194  bwipe!
195endfunc
196
197func Test_prop_backspace()
198  new
199  set bs=2
200  let expected = SetupOneLine() " 'xonex xtwoxx'
201
202  exe "normal 0li\<BS>\<Esc>fxli\<BS>\<Esc>"
203  call assert_equal('one xtwoxx', getline(1))
204  let expected[0].col = 1
205  let expected[1].col = 6
206  call assert_equal(expected, prop_list(1))
207
208  call DeletePropTypes()
209  bwipe!
210  set bs&
211endfunc
212
213func Test_prop_replace()
214  new
215  set bs=2
216  let expected = SetupOneLine() " 'xonex xtwoxx'
217
218  exe "normal 0Ryyy\<Esc>"
219  call assert_equal('yyyex xtwoxx', getline(1))
220  call assert_equal(expected, prop_list(1))
221
222  exe "normal ftRyy\<BS>"
223  call assert_equal('yyyex xywoxx', getline(1))
224  call assert_equal(expected, prop_list(1))
225
226  exe "normal 0fwRyy\<BS>"
227  call assert_equal('yyyex xyyoxx', getline(1))
228  call assert_equal(expected, prop_list(1))
229
230  exe "normal 0foRyy\<BS>\<BS>"
231  call assert_equal('yyyex xyyoxx', getline(1))
232  call assert_equal(expected, prop_list(1))
233
234  call DeletePropTypes()
235  bwipe!
236  set bs&
237endfunc
238
239func Test_prop_clear()
240  new
241  call AddPropTypes()
242  call SetupPropsInFirstLine()
243  call assert_equal(Get_expected_props(), prop_list(1))
244
245  call prop_clear(1)
246  call assert_equal([], prop_list(1))
247
248  call DeletePropTypes()
249  bwipe!
250endfunc
251
252func Test_prop_clear_buf()
253  new
254  call AddPropTypes()
255  call SetupPropsInFirstLine()
256  let bufnr = bufnr('')
257  wincmd w
258  call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr}))
259
260  call prop_clear(1, 1, {'bufnr': bufnr})
261  call assert_equal([], prop_list(1, {'bufnr': bufnr}))
262
263  wincmd w
264  call DeletePropTypes()
265  bwipe!
266endfunc
267
268func Test_prop_setline()
269  new
270  call AddPropTypes()
271  call SetupPropsInFirstLine()
272  call assert_equal(Get_expected_props(), prop_list(1))
273
274  call setline(1, 'foobar')
275  call assert_equal([], prop_list(1))
276
277  call DeletePropTypes()
278  bwipe!
279endfunc
280
281func Test_prop_setbufline()
282  new
283  call AddPropTypes()
284  call SetupPropsInFirstLine()
285  let bufnr = bufnr('')
286  wincmd w
287  call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr}))
288
289  call setbufline(bufnr, 1, 'foobar')
290  call assert_equal([], prop_list(1, {'bufnr': bufnr}))
291
292  wincmd w
293  call DeletePropTypes()
294  bwipe!
295endfunc
296
297func Test_prop_substitute()
298  new
299  " Set first line to 'one two three'
300  call AddPropTypes()
301  call SetupPropsInFirstLine()
302  let expected_props = Get_expected_props()
303  call assert_equal(expected_props, prop_list(1))
304
305  " Change "n" in "one" to XX: 'oXXe two three'
306  s/n/XX/
307  let expected_props[0].length += 1
308  let expected_props[1].length += 1
309  let expected_props[2].col += 1
310  let expected_props[3].col += 1
311  call assert_equal(expected_props, prop_list(1))
312
313  " Delete "t" in "two" and "three" to XX: 'oXXe wo hree'
314  s/t//g
315  let expected_props[0].length -= 2
316  let expected_props[2].length -= 1
317  let expected_props[3].length -= 1
318  let expected_props[3].col -= 1
319  call assert_equal(expected_props, prop_list(1))
320
321  " Split the line by changing w to line break: 'oXXe ', 'o hree'
322  " The long prop is split and spans both lines.
323  " The props on "two" and "three" move to the next line.
324  s/w/\r/
325  let new_props = [
326	\ copy(expected_props[0]),
327	\ copy(expected_props[2]),
328	\ copy(expected_props[3]),
329	\ ]
330  let expected_props[0].length = 5
331  unlet expected_props[3]
332  unlet expected_props[2]
333  call assert_equal(expected_props, prop_list(1))
334
335  let new_props[0].length = 6
336  let new_props[1].col = 1
337  let new_props[1].length = 1
338  let new_props[2].col = 3
339  call assert_equal(new_props, prop_list(2))
340
341  call DeletePropTypes()
342  bwipe!
343endfunc
344
345func Test_prop_change_indent()
346  call prop_type_add('comment', {'highlight': 'Directory'})
347  new
348  call setline(1, ['    xxx', 'yyyyy'])
349  call prop_add(2, 2, {'length': 2, 'type': 'comment'})
350  let expect = {'col': 2, 'length': 2, 'type': 'comment', 'start': 1, 'end': 1, 'id': 0}
351  call assert_equal([expect], prop_list(2))
352
353  set shiftwidth=3
354  normal 2G>>
355  call assert_equal('   yyyyy', getline(2))
356  let expect.col += 3
357  call assert_equal([expect], prop_list(2))
358
359  normal 2G==
360  call assert_equal('    yyyyy', getline(2))
361  let expect.col = 6
362  call assert_equal([expect], prop_list(2))
363
364  call prop_clear(2)
365  call prop_add(2, 2, {'length': 5, 'type': 'comment'})
366  let expect.col = 2
367  let expect.length = 5
368  call assert_equal([expect], prop_list(2))
369
370  normal 2G<<
371  call assert_equal(' yyyyy', getline(2))
372  let expect.length = 2
373  call assert_equal([expect], prop_list(2))
374
375  set shiftwidth&
376  call prop_type_delete('comment')
377endfunc
378
379" Setup a three line prop in lines 2 - 4.
380" Add short props in line 1 and 5.
381func Setup_three_line_prop()
382  new
383  call setline(1, ['one', 'twotwo', 'three', 'fourfour', 'five'])
384  call prop_add(1, 2, {'length': 1, 'type': 'comment'})
385  call prop_add(2, 4, {'end_lnum': 4, 'end_col': 5, 'type': 'comment'})
386  call prop_add(5, 2, {'length': 1, 'type': 'comment'})
387endfunc
388
389func Test_prop_multiline()
390  call prop_type_add('comment', {'highlight': 'Directory'})
391  new
392  call setline(1, ['xxxxxxx', 'yyyyyyyyy', 'zzzzzzzz'])
393
394  " start halfway line 1, end halfway line 3
395  call prop_add(1, 3, {'end_lnum': 3, 'end_col': 5, 'type': 'comment'})
396  let expect1 = {'col': 3, 'length': 6, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
397  call assert_equal([expect1], prop_list(1))
398  let expect2 = {'col': 1, 'length': 10, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0}
399  call assert_equal([expect2], prop_list(2))
400  let expect3 = {'col': 1, 'length': 4, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0}
401  call assert_equal([expect3], prop_list(3))
402  call prop_clear(1, 3)
403
404  " include all three lines
405  call prop_add(1, 1, {'end_lnum': 3, 'end_col': 999, 'type': 'comment'})
406  let expect1.col = 1
407  let expect1.length = 8
408  call assert_equal([expect1], prop_list(1))
409  call assert_equal([expect2], prop_list(2))
410  let expect3.length = 9
411  call assert_equal([expect3], prop_list(3))
412  call prop_clear(1, 3)
413
414  bwipe!
415
416  " Test deleting the first line of a multi-line prop.
417  call Setup_three_line_prop()
418  let expect_short = {'col': 2, 'length': 1, 'type': 'comment', 'start': 1, 'end': 1, 'id': 0}
419  call assert_equal([expect_short], prop_list(1))
420  let expect2 = {'col': 4, 'length': 4, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
421  call assert_equal([expect2], prop_list(2))
422  2del
423  call assert_equal([expect_short], prop_list(1))
424  let expect2 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
425  call assert_equal([expect2], prop_list(2))
426  bwipe!
427
428  " Test deleting the last line of a multi-line prop.
429  call Setup_three_line_prop()
430  let expect3 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0}
431  call assert_equal([expect3], prop_list(3))
432  let expect4 = {'col': 1, 'length': 4, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0}
433  call assert_equal([expect4], prop_list(4))
434  4del
435  let expect3.end = 1
436  call assert_equal([expect3], prop_list(3))
437  call assert_equal([expect_short], prop_list(4))
438  bwipe!
439
440  " Test appending a line below the multi-line text prop start.
441  call Setup_three_line_prop()
442  let expect2 = {'col': 4, 'length': 4, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
443  call assert_equal([expect2], prop_list(2))
444  call append(2, "new line")
445  call assert_equal([expect2], prop_list(2))
446  let expect3 = {'col': 1, 'length': 9, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0}
447  call assert_equal([expect3], prop_list(3))
448  bwipe!
449
450  call prop_type_delete('comment')
451endfunc
452
453func Test_prop_byteoff()
454  call prop_type_add('comment', {'highlight': 'Directory'})
455  new
456  call setline(1, ['line1', 'second line', ''])
457  set ff=unix
458  call assert_equal(19, line2byte(3))
459  call prop_add(1, 1, {'end_col': 3, 'type': 'comment'})
460  call assert_equal(19, line2byte(3))
461
462  bwipe!
463  call prop_type_delete('comment')
464endfunc
465
466func Test_prop_undo()
467  new
468  call prop_type_add('comment', {'highlight': 'Directory'})
469  call setline(1, ['oneone', 'twotwo', 'three'])
470  " Set 'undolevels' to break changes into undo-able pieces.
471  set ul&
472
473  call prop_add(1, 3, {'end_col': 5, 'type': 'comment'})
474  let expected = [{'col': 3, 'length': 2, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ]
475  call assert_equal(expected, prop_list(1))
476
477  " Insert a character, then undo.
478  exe "normal 0lllix\<Esc>"
479  set ul&
480  let expected[0].length = 3
481  call assert_equal(expected, prop_list(1))
482  undo
483  let expected[0].length = 2
484  call assert_equal(expected, prop_list(1))
485
486  " Delete a character, then undo
487  exe "normal 0lllx"
488  set ul&
489  let expected[0].length = 1
490  call assert_equal(expected, prop_list(1))
491  undo
492  let expected[0].length = 2
493  call assert_equal(expected, prop_list(1))
494
495  " Delete the line, then undo
496  1d
497  set ul&
498  call assert_equal([], prop_list(1))
499  undo
500  call assert_equal(expected, prop_list(1))
501
502  " Insert a character, delete two characters, then undo with "U"
503  exe "normal 0lllix\<Esc>"
504  set ul&
505  let expected[0].length = 3
506  call assert_equal(expected, prop_list(1))
507  exe "normal 0lllxx"
508  set ul&
509  let expected[0].length = 1
510  call assert_equal(expected, prop_list(1))
511  normal U
512  let expected[0].length = 2
513  call assert_equal(expected, prop_list(1))
514
515  bwipe!
516  call prop_type_delete('comment')
517endfunc
518
519" screenshot test with textprop highlighting
520funct Test_textprop_screenshots()
521  " The Vim running in the terminal needs to use utf-8.
522  if !CanRunVimInTerminal() || g:orig_encoding != 'utf-8'
523    return
524  endif
525  call writefile([
526	\ "call setline(1, ['One two', 'Numbér 123 änd thœn 4¾7.', '--aa--bb--cc--dd--'])",
527	\ "hi NumberProp ctermfg=blue",
528	\ "hi LongProp ctermbg=yellow",
529	\ "call prop_type_add('number', {'highlight': 'NumberProp'})",
530	\ "call prop_type_add('long', {'highlight': 'LongProp'})",
531	\ "call prop_type_add('start', {'highlight': 'NumberProp', 'start_incl': 1})",
532	\ "call prop_type_add('end', {'highlight': 'NumberProp', 'end_incl': 1})",
533	\ "call prop_type_add('both', {'highlight': 'NumberProp', 'start_incl': 1, 'end_incl': 1})",
534	\ "call prop_add(1, 4, {'end_lnum': 3, 'end_col': 3, 'type': 'long'})",
535	\ "call prop_add(2, 9, {'length': 3, 'type': 'number'})",
536	\ "call prop_add(2, 24, {'length': 4, 'type': 'number'})",
537	\ "call prop_add(3, 3, {'length': 2, 'type': 'number'})",
538	\ "call prop_add(3, 7, {'length': 2, 'type': 'start'})",
539	\ "call prop_add(3, 11, {'length': 2, 'type': 'end'})",
540	\ "call prop_add(3, 15, {'length': 2, 'type': 'both'})",
541	\ "set number",
542	\ "hi clear SpellBad",
543	\ "set spell",
544	\ "normal 3G0llix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>",
545	\ "normal 3G0lli\<BS>\<Esc>",
546	\], 'XtestProp')
547  let buf = RunVimInTerminal('-S XtestProp', {'rows': 6})
548  call VerifyScreenDump(buf, 'Test_textprop_01', {})
549
550  " clean up
551  call StopVimInTerminal(buf)
552  call delete('XtestProp')
553endfunc
554