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
73func AddPropTypes()
74  call prop_type_add('one', {})
75  call prop_type_add('two', {})
76  call prop_type_add('three', {})
77  call prop_type_add('whole', {})
78endfunc
79
80func DeletePropTypes()
81  call prop_type_delete('one')
82  call prop_type_delete('two')
83  call prop_type_delete('three')
84  call prop_type_delete('whole')
85endfunc
86
87func SetupPropsInFirstLine()
88  call setline(1, 'one two three')
89  call prop_add(1, 1, {'length': 3, 'id': 11, 'type': 'one'})
90  eval 1->prop_add(5, {'length': 3, 'id': 12, 'type': 'two'})
91  call prop_add(1, 9, {'length': 5, 'id': 13, 'type': 'three'})
92  call prop_add(1, 1, {'length': 13, 'id': 14, 'type': 'whole'})
93endfunc
94
95func Get_expected_props()
96  return [
97      \ {'col': 1, 'length': 13, 'id': 14, 'type': 'whole', 'start': 1, 'end': 1},
98      \ {'col': 1, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1},
99      \ {'col': 5, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1},
100      \ {'col': 9, 'length': 5, 'id': 13, 'type': 'three', 'start': 1, 'end': 1},
101      \ ]
102endfunc
103
104func Test_prop_find()
105  new
106  call setline(1, ['one one one', 'twotwo', 'three', 'fourfour', 'five', 'sixsix'])
107
108  " Add two text props on lines 1 and 5, and one spanning lines 2 to 4.
109  call prop_type_add('prop_name', {'highlight': 'Directory'})
110  call prop_add(1, 5, {'type': 'prop_name', 'id': 10, 'length': 3})
111  call prop_add(2, 4, {'type': 'prop_name', 'id': 11, 'end_lnum': 4, 'end_col': 9})
112  call prop_add(5, 4, {'type': 'prop_name', 'id': 12, 'length': 1})
113
114  let expected = [
115    \ {'lnum': 1, 'col': 5, 'length': 3, 'id': 10, 'type': 'prop_name', 'start': 1, 'end': 1},
116    \ {'lnum': 2, 'col': 4, 'id': 11, 'type': 'prop_name', 'start': 1, 'end': 0},
117    \ {'lnum': 5, 'col': 4, 'length': 1, 'id': 12, 'type': 'prop_name', 'start': 1, 'end': 1}
118    \ ]
119
120  " Starting at line 5 col 1 this should find the prop at line 5 col 4.
121  call cursor(5,1)
122  let result = prop_find({'type': 'prop_name'}, 'f')
123  call assert_equal(expected[2], result)
124
125  " With skipstart left at false (default), this should find the prop at line
126  " 5 col 4.
127  let result = prop_find({'type': 'prop_name', 'lnum': 5, 'col': 4}, 'b')
128  call assert_equal(expected[2], result)
129
130  " With skipstart set to true, this should skip the prop at line 5 col 4.
131  let result = prop_find({'type': 'prop_name', 'lnum': 5, 'col': 4, 'skipstart': 1}, 'b')
132  unlet result.length
133  call assert_equal(expected[1], result)
134
135  " Search backwards from line 1 col 10 to find the prop on the same line.
136  let result = prop_find({'type': 'prop_name', 'lnum': 1, 'col': 10}, 'b')
137  call assert_equal(expected[0], result)
138
139  " with skipstart set to false, if the start position is anywhere between the
140  " start and end lines of a text prop (searching forward or backward), the
141  " result should be the prop on the first line (the line with 'start' set to 1).
142  call cursor(3,1)
143  let result = prop_find({'type': 'prop_name'}, 'f')
144  unlet result.length
145  call assert_equal(expected[1], result)
146  let result = prop_find({'type': 'prop_name'}, 'b')
147  unlet result.length
148  call assert_equal(expected[1], result)
149
150  " with skipstart set to true, if the start position is anywhere between the
151  " start and end lines of a text prop (searching forward or backward), all lines
152  " of the prop will be skipped.
153  let result = prop_find({'type': 'prop_name', 'skipstart': 1}, 'b')
154  call assert_equal(expected[0], result)
155  let result = prop_find({'type': 'prop_name', 'skipstart': 1}, 'f')
156  call assert_equal(expected[2], result)
157
158  " Use skipstart to search through all props with type name 'prop_name'.
159  " First forward...
160  let lnum = 1
161  let col = 1
162  let i = 0
163  for exp in expected
164    let result = prop_find({'type': 'prop_name', 'lnum': lnum, 'col': col, 'skipstart': 1}, 'f')
165    if !has_key(exp, "length")
166      unlet result.length
167    endif
168    call assert_equal(exp, result)
169    let lnum = result.lnum
170    let col = result.col
171    let i = i + 1
172  endfor
173
174  " ...then backwards.
175  let lnum = 6
176  let col = 4
177  let i = 2
178  while i >= 0
179    let result = prop_find({'type': 'prop_name', 'lnum': lnum, 'col': col, 'skipstart': 1}, 'b')
180    if !has_key(expected[i], "length")
181      unlet result.length
182    endif
183    call assert_equal(expected[i], result)
184    let lnum = result.lnum
185    let col = result.col
186    let i = i - 1
187  endwhile
188
189  " Starting from line 6 col 1 search backwards for prop with id 10.
190  call cursor(6,1)
191  let result = prop_find({'id': 10, 'skipstart': 1}, 'b')
192  call assert_equal(expected[0], result)
193
194  " Starting from line 1 col 1 search forwards for prop with id 12.
195  call cursor(1,1)
196  let result = prop_find({'id': 12}, 'f')
197  call assert_equal(expected[2], result)
198
199  " Search for a prop with an unknown id.
200  let result = prop_find({'id': 999}, 'f')
201  call assert_equal({}, result)
202
203  " Search backwards from the proceeding position of the prop with id 11
204  " (at line num 2 col 4). This should return an empty dict.
205  let result = prop_find({'id': 11, 'lnum': 2, 'col': 3}, 'b')
206  call assert_equal({}, result)
207
208  " When lnum is given and col is omitted, use column 1.
209  let result = prop_find({'type': 'prop_name', 'lnum': 1}, 'f')
210  call assert_equal(expected[0], result)
211
212  call prop_clear(1,6)
213  call prop_type_delete('prop_name')
214
215  bwipe!
216endfunc
217
218def Test_prop_find2()
219  # Multiple props per line, start on the first, should find the second.
220  new
221  ['the quikc bronw fox jumsp over the layz dog']->repeat(2)->setline(1)
222  prop_type_add('misspell', #{highlight: 'ErrorMsg'})
223  for lnum in [1, 2]
224    for col in [8, 14, 24, 38]
225      prop_add(lnum, col, #{type: 'misspell', length: 2})
226    endfor
227  endfor
228  cursor(1, 8)
229  let expected = {'lnum': 1, 'id': 0, 'col': 14, 'end': 1, 'type': 'misspell', 'length': 2, 'start': 1}
230  let result = prop_find(#{type: 'misspell', skipstart: true}, 'f')
231  assert_equal(expected, result)
232
233  prop_type_delete('misspell')
234  bwipe!
235enddef
236
237func Test_prop_find_smaller_len_than_match_col()
238  new
239  call prop_type_add('test', {'highlight': 'ErrorMsg'})
240  call setline(1, ['xxxx', 'x'])
241  call prop_add(1, 4, {'type': 'test'})
242  call assert_equal({'id': 0, 'lnum': 1, 'col': 4, 'type': 'test', 'length': 0, 'start': 1, 'end': 1},
243        \ prop_find({'type': 'test', 'lnum': 2, 'col': 1}, 'b'))
244  bwipe!
245  call prop_type_delete('test')
246endfunc
247
248func Test_prop_add()
249  new
250  call AddPropTypes()
251  call SetupPropsInFirstLine()
252  let expected_props = Get_expected_props()
253  call assert_equal(expected_props, prop_list(1))
254  call assert_fails("call prop_add(10, 1, {'length': 1, 'id': 14, 'type': 'whole'})", 'E966:')
255  call assert_fails("call prop_add(1, 22, {'length': 1, 'id': 14, 'type': 'whole'})", 'E964:')
256
257  " Insert a line above, text props must still be there.
258  call append(0, 'empty')
259  call assert_equal(expected_props, prop_list(2))
260  " Delete a line above, text props must still be there.
261  1del
262  call assert_equal(expected_props, prop_list(1))
263
264  " Prop without length or end column is zero length
265  call prop_clear(1)
266  call prop_type_add('included', {'start_incl': 1, 'end_incl': 1})
267  call prop_add(1, 5, #{type: 'included'})
268  let expected = [#{col: 5, length: 0, type: 'included', id: 0, start: 1, end: 1}]
269  call assert_equal(expected, prop_list(1))
270
271  " Inserting text makes the prop bigger.
272  exe "normal 5|ixx\<Esc>"
273  let expected = [#{col: 5, length: 2, type: 'included', id: 0, start: 1, end: 1}]
274  call assert_equal(expected, prop_list(1))
275
276  call assert_fails("call prop_add(1, 5, {'type': 'two', 'bufnr': 234343})", 'E158:')
277
278  call DeletePropTypes()
279  call prop_type_delete('included')
280  bwipe!
281endfunc
282
283func Test_prop_remove()
284  new
285  call AddPropTypes()
286  call SetupPropsInFirstLine()
287  let props = Get_expected_props()
288  call assert_equal(props, prop_list(1))
289
290  " remove by id
291  call assert_equal(1, {'id': 12}->prop_remove(1))
292  unlet props[2]
293  call assert_equal(props, prop_list(1))
294
295  " remove by type
296  call assert_equal(1, prop_remove({'type': 'one'}, 1))
297  unlet props[1]
298  call assert_equal(props, prop_list(1))
299
300  " remove from unknown buffer
301  call assert_fails("call prop_remove({'type': 'one', 'bufnr': 123456}, 1)", 'E158:')
302
303  call DeletePropTypes()
304  bwipe!
305
306  new
307  call AddPropTypes()
308  call SetupPropsInFirstLine()
309  call prop_add(1, 6, {'length': 2, 'id': 11, 'type': 'three'})
310  let props = Get_expected_props()
311  call insert(props, {'col': 6, 'length': 2, 'id': 11, 'type': 'three', 'start': 1, 'end': 1}, 3)
312  call assert_equal(props, prop_list(1))
313  call assert_equal(1, prop_remove({'type': 'three', 'id': 11, 'both': 1, 'all': 1}, 1))
314  unlet props[3]
315  call assert_equal(props, prop_list(1))
316
317  call assert_fails("call prop_remove({'id': 11, 'both': 1})", 'E860')
318  call assert_fails("call prop_remove({'type': 'three', 'both': 1})", 'E860')
319
320  call DeletePropTypes()
321  bwipe!
322endfunc
323
324func SetupOneLine()
325  call setline(1, 'xonex xtwoxx')
326  normal gg0
327  call AddPropTypes()
328  call prop_add(1, 2, {'length': 3, 'id': 11, 'type': 'one'})
329  call prop_add(1, 8, {'length': 3, 'id': 12, 'type': 'two'})
330  let expected = [
331	\ {'col': 2, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1},
332	\ {'col': 8, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1},
333	\]
334  call assert_equal(expected, prop_list(1))
335  return expected
336endfunc
337
338func Test_prop_add_remove_buf()
339  new
340  let bufnr = bufnr('')
341  call AddPropTypes()
342  for lnum in range(1, 4)
343    call setline(lnum, 'one two three')
344  endfor
345  wincmd w
346  for lnum in range(1, 4)
347    call prop_add(lnum, 1, {'length': 3, 'id': 11, 'type': 'one', 'bufnr': bufnr})
348    call prop_add(lnum, 5, {'length': 3, 'id': 12, 'type': 'two', 'bufnr': bufnr})
349    call prop_add(lnum, 11, {'length': 3, 'id': 13, 'type': 'three', 'bufnr': bufnr})
350  endfor
351
352  let props = [
353	\ {'col': 1, 'length': 3, 'id': 11, 'type': 'one', 'start': 1, 'end': 1},
354	\ {'col': 5, 'length': 3, 'id': 12, 'type': 'two', 'start': 1, 'end': 1},
355	\ {'col': 11, 'length': 3, 'id': 13, 'type': 'three', 'start': 1, 'end': 1},
356	\]
357  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
358
359  " remove by id
360  let before_props = deepcopy(props)
361  unlet props[1]
362
363  call prop_remove({'id': 12, 'bufnr': bufnr}, 1)
364  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
365  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
366  call assert_equal(before_props, prop_list(3, {'bufnr': bufnr}))
367  call assert_equal(before_props, prop_list(4, {'bufnr': bufnr}))
368
369  call prop_remove({'id': 12, 'bufnr': bufnr}, 3, 4)
370  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
371  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
372  call assert_equal(props, prop_list(3, {'bufnr': bufnr}))
373  call assert_equal(props, prop_list(4, {'bufnr': bufnr}))
374
375  call prop_remove({'id': 12, 'bufnr': bufnr})
376  for lnum in range(1, 4)
377    call assert_equal(props, prop_list(lnum, {'bufnr': bufnr}))
378  endfor
379
380  " remove by type
381  let before_props = deepcopy(props)
382  unlet props[0]
383
384  call prop_remove({'type': 'one', 'bufnr': bufnr}, 1)
385  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
386  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
387  call assert_equal(before_props, prop_list(3, {'bufnr': bufnr}))
388  call assert_equal(before_props, prop_list(4, {'bufnr': bufnr}))
389
390  call prop_remove({'type': 'one', 'bufnr': bufnr}, 3, 4)
391  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
392  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
393  call assert_equal(props, prop_list(3, {'bufnr': bufnr}))
394  call assert_equal(props, prop_list(4, {'bufnr': bufnr}))
395
396  call prop_remove({'type': 'one', 'bufnr': bufnr})
397  for lnum in range(1, 4)
398    call assert_equal(props, prop_list(lnum, {'bufnr': bufnr}))
399  endfor
400
401  call DeletePropTypes()
402  wincmd w
403  bwipe!
404endfunc
405
406func Test_prop_backspace()
407  new
408  set bs=2
409  let expected = SetupOneLine() " 'xonex xtwoxx'
410
411  exe "normal 0li\<BS>\<Esc>fxli\<BS>\<Esc>"
412  call assert_equal('one xtwoxx', getline(1))
413  let expected[0].col = 1
414  let expected[1].col = 6
415  call assert_equal(expected, prop_list(1))
416
417  call DeletePropTypes()
418  bwipe!
419  set bs&
420endfunc
421
422func Test_prop_replace()
423  new
424  set bs=2
425  let expected = SetupOneLine() " 'xonex xtwoxx'
426
427  exe "normal 0Ryyy\<Esc>"
428  call assert_equal('yyyex xtwoxx', getline(1))
429  call assert_equal(expected, prop_list(1))
430
431  exe "normal ftRyy\<BS>"
432  call assert_equal('yyyex xywoxx', getline(1))
433  call assert_equal(expected, prop_list(1))
434
435  exe "normal 0fwRyy\<BS>"
436  call assert_equal('yyyex xyyoxx', getline(1))
437  call assert_equal(expected, prop_list(1))
438
439  exe "normal 0foRyy\<BS>\<BS>"
440  call assert_equal('yyyex xyyoxx', getline(1))
441  call assert_equal(expected, prop_list(1))
442
443  call DeletePropTypes()
444  bwipe!
445  set bs&
446endfunc
447
448func Test_prop_open_line()
449  new
450
451  " open new line, props stay in top line
452  let expected = SetupOneLine() " 'xonex xtwoxx'
453  exe "normal o\<Esc>"
454  call assert_equal('xonex xtwoxx', getline(1))
455  call assert_equal('', getline(2))
456  call assert_equal(expected, prop_list(1))
457  call DeletePropTypes()
458
459  " move all props to next line
460  let expected = SetupOneLine() " 'xonex xtwoxx'
461  exe "normal 0i\<CR>\<Esc>"
462  call assert_equal('', getline(1))
463  call assert_equal('xonex xtwoxx', getline(2))
464  call assert_equal(expected, prop_list(2))
465  call DeletePropTypes()
466
467  " split just before prop, move all props to next line
468  let expected = SetupOneLine() " 'xonex xtwoxx'
469  exe "normal 0li\<CR>\<Esc>"
470  call assert_equal('x', getline(1))
471  call assert_equal('onex xtwoxx', getline(2))
472  let expected[0].col -= 1
473  let expected[1].col -= 1
474  call assert_equal(expected, prop_list(2))
475  call DeletePropTypes()
476
477  " split inside prop, split first prop
478  let expected = SetupOneLine() " 'xonex xtwoxx'
479  exe "normal 0lli\<CR>\<Esc>"
480  call assert_equal('xo', getline(1))
481  call assert_equal('nex xtwoxx', getline(2))
482  let exp_first = [deepcopy(expected[0])]
483  let exp_first[0].length = 1
484  let exp_first[0].end = 0
485  call assert_equal(exp_first, prop_list(1))
486  let expected[0].col = 1
487  let expected[0].length = 2
488  let expected[0].start = 0
489  let expected[1].col -= 2
490  call assert_equal(expected, prop_list(2))
491  call DeletePropTypes()
492
493  " split just after first prop, second prop move to next line
494  let expected = SetupOneLine() " 'xonex xtwoxx'
495  exe "normal 0fea\<CR>\<Esc>"
496  call assert_equal('xone', getline(1))
497  call assert_equal('x xtwoxx', getline(2))
498  let exp_first = expected[0:0]
499  call assert_equal(exp_first, prop_list(1))
500  let expected = expected[1:1]
501  let expected[0].col -= 4
502  call assert_equal(expected, prop_list(2))
503  call DeletePropTypes()
504
505  bwipe!
506  set bs&
507endfunc
508
509func Test_prop_clear()
510  new
511  call AddPropTypes()
512  call SetupPropsInFirstLine()
513  call assert_equal(Get_expected_props(), prop_list(1))
514
515  eval 1->prop_clear()
516  call assert_equal([], 1->prop_list())
517
518  call DeletePropTypes()
519  bwipe!
520endfunc
521
522func Test_prop_clear_buf()
523  new
524  call AddPropTypes()
525  call SetupPropsInFirstLine()
526  let bufnr = bufnr('')
527  wincmd w
528  call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr}))
529
530  call prop_clear(1, 1, {'bufnr': bufnr})
531  call assert_equal([], prop_list(1, {'bufnr': bufnr}))
532
533  wincmd w
534  call DeletePropTypes()
535  bwipe!
536endfunc
537
538func Test_prop_setline()
539  new
540  call AddPropTypes()
541  call SetupPropsInFirstLine()
542  call assert_equal(Get_expected_props(), prop_list(1))
543
544  call setline(1, 'foobar')
545  call assert_equal([], prop_list(1))
546
547  call DeletePropTypes()
548  bwipe!
549endfunc
550
551func Test_prop_setbufline()
552  new
553  call AddPropTypes()
554  call SetupPropsInFirstLine()
555  let bufnr = bufnr('')
556  wincmd w
557  call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr}))
558
559  call setbufline(bufnr, 1, 'foobar')
560  call assert_equal([], prop_list(1, {'bufnr': bufnr}))
561
562  wincmd w
563  call DeletePropTypes()
564  bwipe!
565endfunc
566
567func Test_prop_substitute()
568  new
569  " Set first line to 'one two three'
570  call AddPropTypes()
571  call SetupPropsInFirstLine()
572  let expected_props = Get_expected_props()
573  call assert_equal(expected_props, prop_list(1))
574
575  " Change "n" in "one" to XX: 'oXXe two three'
576  s/n/XX/
577  let expected_props[0].length += 1
578  let expected_props[1].length += 1
579  let expected_props[2].col += 1
580  let expected_props[3].col += 1
581  call assert_equal(expected_props, prop_list(1))
582
583  " Delete "t" in "two" and "three" to XX: 'oXXe wo hree'
584  s/t//g
585  let expected_props[0].length -= 2
586  let expected_props[2].length -= 1
587  let expected_props[3].length -= 1
588  let expected_props[3].col -= 1
589  call assert_equal(expected_props, prop_list(1))
590
591  " Split the line by changing w to line break: 'oXXe ', 'o hree'
592  " The long prop is split and spans both lines.
593  " The props on "two" and "three" move to the next line.
594  s/w/\r/
595  let new_props = [
596	\ copy(expected_props[0]),
597	\ copy(expected_props[2]),
598	\ copy(expected_props[3]),
599	\ ]
600  let expected_props[0].length = 5
601  let expected_props[0].end = 0
602  unlet expected_props[3]
603  unlet expected_props[2]
604  call assert_equal(expected_props, prop_list(1))
605
606  let new_props[0].length = 6
607  let new_props[0].start = 0
608  let new_props[1].col = 1
609  let new_props[1].length = 1
610  let new_props[2].col = 3
611  call assert_equal(new_props, prop_list(2))
612
613  call DeletePropTypes()
614  bwipe!
615endfunc
616
617func Test_prop_change_indent()
618  call prop_type_add('comment', {'highlight': 'Directory'})
619  new
620  call setline(1, ['    xxx', 'yyyyy'])
621  call prop_add(2, 2, {'length': 2, 'type': 'comment'})
622  let expect = {'col': 2, 'length': 2, 'type': 'comment', 'start': 1, 'end': 1, 'id': 0}
623  call assert_equal([expect], prop_list(2))
624
625  set shiftwidth=3
626  normal 2G>>
627  call assert_equal('   yyyyy', getline(2))
628  let expect.col += 3
629  call assert_equal([expect], prop_list(2))
630
631  normal 2G==
632  call assert_equal('    yyyyy', getline(2))
633  let expect.col = 6
634  call assert_equal([expect], prop_list(2))
635
636  call prop_clear(2)
637  call prop_add(2, 2, {'length': 5, 'type': 'comment'})
638  let expect.col = 2
639  let expect.length = 5
640  call assert_equal([expect], prop_list(2))
641
642  normal 2G<<
643  call assert_equal(' yyyyy', getline(2))
644  let expect.length = 2
645  call assert_equal([expect], prop_list(2))
646
647  set shiftwidth&
648  call prop_type_delete('comment')
649endfunc
650
651" Setup a three line prop in lines 2 - 4.
652" Add short props in line 1 and 5.
653func Setup_three_line_prop()
654  new
655  call setline(1, ['one', 'twotwo', 'three', 'fourfour', 'five'])
656  call prop_add(1, 2, {'length': 1, 'type': 'comment'})
657  call prop_add(2, 4, {'end_lnum': 4, 'end_col': 5, 'type': 'comment'})
658  call prop_add(5, 2, {'length': 1, 'type': 'comment'})
659endfunc
660
661func Test_prop_multiline()
662  eval 'comment'->prop_type_add({'highlight': 'Directory'})
663  new
664  call setline(1, ['xxxxxxx', 'yyyyyyyyy', 'zzzzzzzz'])
665
666  " start halfway line 1, end halfway line 3
667  call prop_add(1, 3, {'end_lnum': 3, 'end_col': 5, 'type': 'comment'})
668  let expect1 = {'col': 3, 'length': 6, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
669  call assert_equal([expect1], prop_list(1))
670  let expect2 = {'col': 1, 'length': 10, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0}
671  call assert_equal([expect2], prop_list(2))
672  let expect3 = {'col': 1, 'length': 4, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0}
673  call assert_equal([expect3], prop_list(3))
674  call prop_clear(1, 3)
675
676  " include all three lines
677  call prop_add(1, 1, {'end_lnum': 3, 'end_col': 999, 'type': 'comment'})
678  let expect1.col = 1
679  let expect1.length = 8
680  call assert_equal([expect1], prop_list(1))
681  call assert_equal([expect2], prop_list(2))
682  let expect3.length = 9
683  call assert_equal([expect3], prop_list(3))
684  call prop_clear(1, 3)
685
686  bwipe!
687
688  " Test deleting the first line of a multi-line prop.
689  call Setup_three_line_prop()
690  let expect_short = {'col': 2, 'length': 1, 'type': 'comment', 'start': 1, 'end': 1, 'id': 0}
691  call assert_equal([expect_short], prop_list(1))
692  let expect2 = {'col': 4, 'length': 4, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
693  call assert_equal([expect2], prop_list(2))
694  2del
695  call assert_equal([expect_short], prop_list(1))
696  let expect2 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
697  call assert_equal([expect2], prop_list(2))
698  bwipe!
699
700  " Test deleting the last line of a multi-line prop.
701  call Setup_three_line_prop()
702  let expect3 = {'col': 1, 'length': 6, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0}
703  call assert_equal([expect3], prop_list(3))
704  let expect4 = {'col': 1, 'length': 4, 'type': 'comment', 'start': 0, 'end': 1, 'id': 0}
705  call assert_equal([expect4], prop_list(4))
706  4del
707  let expect3.end = 1
708  call assert_equal([expect3], prop_list(3))
709  call assert_equal([expect_short], prop_list(4))
710  bwipe!
711
712  " Test appending a line below the multi-line text prop start.
713  call Setup_three_line_prop()
714  let expect2 = {'col': 4, 'length': 4, 'type': 'comment', 'start': 1, 'end': 0, 'id': 0}
715  call assert_equal([expect2], prop_list(2))
716  call append(2, "new line")
717  call assert_equal([expect2], prop_list(2))
718  let expect3 = {'col': 1, 'length': 9, 'type': 'comment', 'start': 0, 'end': 0, 'id': 0}
719  call assert_equal([expect3], prop_list(3))
720  bwipe!
721
722  call prop_type_delete('comment')
723endfunc
724
725func Test_prop_line2byte()
726  call prop_type_add('comment', {'highlight': 'Directory'})
727  new
728  call setline(1, ['line1', 'second line', ''])
729  set ff=unix
730  call assert_equal(19, line2byte(3))
731  call prop_add(1, 1, {'end_col': 3, 'type': 'comment'})
732  call assert_equal(19, line2byte(3))
733
734  bwipe!
735  call prop_type_delete('comment')
736endfunc
737
738func Test_prop_byte2line()
739  new
740  set ff=unix
741  call setline(1, ['one one', 'two two', 'three three', 'four four', 'five'])
742  call assert_equal(4, byte2line(line2byte(4)))
743  call assert_equal(5, byte2line(line2byte(5)))
744
745  call prop_type_add('prop', {'highlight': 'Directory'})
746  call prop_add(3, 1, {'length': 5, 'type': 'prop'})
747  call assert_equal(4, byte2line(line2byte(4)))
748  call assert_equal(5, byte2line(line2byte(5)))
749
750  bwipe!
751  call prop_type_delete('prop')
752endfunc
753
754func Test_prop_undo()
755  new
756  call prop_type_add('comment', {'highlight': 'Directory'})
757  call setline(1, ['oneone', 'twotwo', 'three'])
758  " Set 'undolevels' to break changes into undo-able pieces.
759  set ul&
760
761  call prop_add(1, 3, {'end_col': 5, 'type': 'comment'})
762  let expected = [{'col': 3, 'length': 2, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ]
763  call assert_equal(expected, prop_list(1))
764
765  " Insert a character, then undo.
766  exe "normal 0lllix\<Esc>"
767  set ul&
768  let expected[0].length = 3
769  call assert_equal(expected, prop_list(1))
770  undo
771  let expected[0].length = 2
772  call assert_equal(expected, prop_list(1))
773
774  " Delete a character, then undo
775  exe "normal 0lllx"
776  set ul&
777  let expected[0].length = 1
778  call assert_equal(expected, prop_list(1))
779  undo
780  let expected[0].length = 2
781  call assert_equal(expected, prop_list(1))
782
783  " Delete the line, then undo
784  1d
785  set ul&
786  call assert_equal([], prop_list(1))
787  undo
788  call assert_equal(expected, prop_list(1))
789
790  " Insert a character, delete two characters, then undo with "U"
791  exe "normal 0lllix\<Esc>"
792  set ul&
793  let expected[0].length = 3
794  call assert_equal(expected, prop_list(1))
795  exe "normal 0lllxx"
796  set ul&
797  let expected[0].length = 1
798  call assert_equal(expected, prop_list(1))
799  normal U
800  let expected[0].length = 2
801  call assert_equal(expected, prop_list(1))
802
803  " substitute a word, then undo
804  call setline(1, 'the number 123 is highlighted.')
805  call prop_add(1, 12, {'length': 3, 'type': 'comment'})
806  let expected = [{'col': 12, 'length': 3, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ]
807  call assert_equal(expected, prop_list(1))
808  set ul&
809  1s/number/foo
810  let expected[0].col = 9
811  call assert_equal(expected, prop_list(1))
812  undo
813  let expected[0].col = 12
814  call assert_equal(expected, prop_list(1))
815  call prop_clear(1)
816
817  " substitute with backslash
818  call setline(1, 'the number 123 is highlighted.')
819  call prop_add(1, 12, {'length': 3, 'type': 'comment'})
820  let expected = [{'col': 12, 'length': 3, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ]
821  call assert_equal(expected, prop_list(1))
822  1s/the/\The
823  call assert_equal(expected, prop_list(1))
824  1s/^/\\
825  let expected[0].col += 1
826  call assert_equal(expected, prop_list(1))
827  1s/^/\~
828  let expected[0].col += 1
829  call assert_equal(expected, prop_list(1))
830  1s/123/12\\3
831  let expected[0].length += 1
832  call assert_equal(expected, prop_list(1))
833  call prop_clear(1)
834
835  bwipe!
836  call prop_type_delete('comment')
837endfunc
838
839func Test_prop_delete_text()
840  new
841  call prop_type_add('comment', {'highlight': 'Directory'})
842  call setline(1, ['oneone', 'twotwo', 'three'])
843
844  " zero length property
845  call prop_add(1, 3, {'type': 'comment'})
846  let expected = [{'col': 3, 'length': 0, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ]
847  call assert_equal(expected, prop_list(1))
848
849  " delete one char moves the property
850  normal! x
851  let expected = [{'col': 2, 'length': 0, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ]
852  call assert_equal(expected, prop_list(1))
853
854  " delete char of the property has no effect
855  normal! lx
856  let expected = [{'col': 2, 'length': 0, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ]
857  call assert_equal(expected, prop_list(1))
858
859  " delete more chars moves property to first column, is not deleted
860  normal! 0xxxx
861  let expected = [{'col': 1, 'length': 0, 'id': 0, 'type': 'comment', 'start': 1, 'end': 1} ]
862  call assert_equal(expected, prop_list(1))
863
864  bwipe!
865  call prop_type_delete('comment')
866endfunc
867
868" screenshot test with textprop highlighting
869func Test_textprop_screenshot_various()
870  CheckScreendump
871  " The Vim running in the terminal needs to use utf-8.
872  if g:orig_encoding != 'utf-8'
873    throw 'Skipped: not using utf-8'
874  endif
875  call writefile([
876	\ "call setline(1, ["
877	\	.. "'One two',"
878	\	.. "'Numbér 123 änd thœn 4¾7.',"
879	\	.. "'--aa--bb--cc--dd--',"
880	\	.. "'// comment with error in it',"
881	\	.. "'first line',"
882	\	.. "'  second line  ',"
883	\	.. "'third line',"
884	\	.. "'   fourth line',"
885	\	.. "])",
886	\ "hi NumberProp ctermfg=blue",
887	\ "hi LongProp ctermbg=yellow",
888	\ "hi BackgroundProp ctermbg=lightgrey",
889	\ "hi UnderlineProp cterm=underline",
890	\ "call prop_type_add('number', {'highlight': 'NumberProp'})",
891	\ "call prop_type_add('long', {'highlight': 'NumberProp'})",
892	\ "call prop_type_change('long', {'highlight': 'LongProp'})",
893	\ "call prop_type_add('start', {'highlight': 'NumberProp', 'start_incl': 1})",
894	\ "call prop_type_add('end', {'highlight': 'NumberProp', 'end_incl': 1})",
895	\ "call prop_type_add('both', {'highlight': 'NumberProp', 'start_incl': 1, 'end_incl': 1})",
896	\ "call prop_type_add('background', {'highlight': 'BackgroundProp', 'combine': 0})",
897	\ "call prop_type_add('backgroundcomb', {'highlight': 'NumberProp', 'combine': 1})",
898	\ "eval 'backgroundcomb'->prop_type_change({'highlight': 'BackgroundProp'})",
899	\ "call prop_type_add('error', {'highlight': 'UnderlineProp'})",
900	\ "call prop_add(1, 4, {'end_lnum': 3, 'end_col': 3, 'type': 'long'})",
901	\ "call prop_add(2, 9, {'length': 3, 'type': 'number'})",
902	\ "call prop_add(2, 24, {'length': 4, 'type': 'number'})",
903	\ "call prop_add(3, 3, {'length': 2, 'type': 'number'})",
904	\ "call prop_add(3, 7, {'length': 2, 'type': 'start'})",
905	\ "call prop_add(3, 11, {'length': 2, 'type': 'end'})",
906	\ "call prop_add(3, 15, {'length': 2, 'type': 'both'})",
907	\ "call prop_add(4, 6, {'length': 3, 'type': 'background'})",
908	\ "call prop_add(4, 12, {'length': 10, 'type': 'backgroundcomb'})",
909	\ "call prop_add(4, 17, {'length': 5, 'type': 'error'})",
910	\ "call prop_add(5, 7, {'length': 4, 'type': 'long'})",
911	\ "call prop_add(6, 1, {'length': 8, 'type': 'long'})",
912	\ "call prop_add(8, 1, {'length': 1, 'type': 'long'})",
913	\ "call prop_add(8, 11, {'length': 4, 'type': 'long'})",
914	\ "set number cursorline",
915	\ "hi clear SpellBad",
916	\ "set spell",
917	\ "syn match Comment '//.*'",
918	\ "hi Comment ctermfg=green",
919	\ "normal 3G0llix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>",
920	\ "normal 3G0lli\<BS>\<Esc>",
921	\ "normal 6G0i\<BS>\<Esc>",
922	\ "normal 3J",
923	\ "normal 3G",
924	\], 'XtestProp')
925  let buf = RunVimInTerminal('-S XtestProp', {'rows': 8})
926  call VerifyScreenDump(buf, 'Test_textprop_01', {})
927
928  " clean up
929  call StopVimInTerminal(buf)
930  call delete('XtestProp')
931endfunc
932
933func RunTestVisualBlock(width, dump)
934  call writefile([
935	\ "call setline(1, ["
936	\	.. "'xxxxxxxxx 123 x',"
937	\	.. "'xxxxxxxx 123 x',"
938	\	.. "'xxxxxxx 123 x',"
939	\	.. "'xxxxxx 123 x',"
940	\	.. "'xxxxx 123 x',"
941	\	.. "'xxxx 123 xx',"
942	\	.. "'xxx 123 xxx',"
943	\	.. "'xx 123 xxxx',"
944	\	.. "'x 123 xxxxx',"
945	\	.. "' 123 xxxxxx',"
946	\	.. "])",
947	\ "hi SearchProp ctermbg=yellow",
948	\ "call prop_type_add('search', {'highlight': 'SearchProp'})",
949	\ "call prop_add(1, 11, {'length': 3, 'type': 'search'})",
950	\ "call prop_add(2, 10, {'length': 3, 'type': 'search'})",
951	\ "call prop_add(3, 9, {'length': 3, 'type': 'search'})",
952	\ "call prop_add(4, 8, {'length': 3, 'type': 'search'})",
953	\ "call prop_add(5, 7, {'length': 3, 'type': 'search'})",
954	\ "call prop_add(6, 6, {'length': 3, 'type': 'search'})",
955	\ "call prop_add(7, 5, {'length': 3, 'type': 'search'})",
956	\ "call prop_add(8, 4, {'length': 3, 'type': 'search'})",
957	\ "call prop_add(9, 3, {'length': 3, 'type': 'search'})",
958	\ "call prop_add(10, 2, {'length': 3, 'type': 'search'})",
959	\ "normal 1G6|\<C-V>" .. repeat('l', a:width - 1) .. "10jx",
960	\], 'XtestPropVis')
961  let buf = RunVimInTerminal('-S XtestPropVis', {'rows': 12})
962  call VerifyScreenDump(buf, 'Test_textprop_vis_' .. a:dump, {})
963
964  " clean up
965  call StopVimInTerminal(buf)
966  call delete('XtestPropVis')
967endfunc
968
969" screenshot test with Visual block mode operations
970func Test_textprop_screenshot_visual()
971  CheckScreendump
972
973  " Delete two columns while text props are three chars wide.
974  call RunTestVisualBlock(2, '01')
975
976  " Same, but delete four columns
977  call RunTestVisualBlock(4, '02')
978endfunc
979
980func Test_textprop_after_tab()
981  CheckScreendump
982
983  let lines =<< trim END
984       call setline(1, [
985             \ "\txxx",
986             \ "x\txxx",
987             \ ])
988       hi SearchProp ctermbg=yellow
989       call prop_type_add('search', {'highlight': 'SearchProp'})
990       call prop_add(1, 2, {'length': 3, 'type': 'search'})
991       call prop_add(2, 3, {'length': 3, 'type': 'search'})
992  END
993  call writefile(lines, 'XtestPropTab')
994  let buf = RunVimInTerminal('-S XtestPropTab', {'rows': 6})
995  call VerifyScreenDump(buf, 'Test_textprop_tab', {})
996
997  " clean up
998  call StopVimInTerminal(buf)
999  call delete('XtestPropTab')
1000endfunc
1001
1002func Test_textprop_with_syntax()
1003  CheckScreendump
1004
1005  let lines =<< trim END
1006       call setline(1, [
1007             \ "(abc)",
1008             \ ])
1009       syn match csParens "[()]" display
1010       hi! link csParens MatchParen
1011
1012       call prop_type_add('TPTitle', #{ highlight: 'Title' })
1013       call prop_add(1, 2, #{type: 'TPTitle', end_col: 5})
1014  END
1015  call writefile(lines, 'XtestPropSyn')
1016  let buf = RunVimInTerminal('-S XtestPropSyn', {'rows': 6})
1017  call VerifyScreenDump(buf, 'Test_textprop_syn_1', {})
1018
1019  " clean up
1020  call StopVimInTerminal(buf)
1021  call delete('XtestPropSyn')
1022endfunc
1023
1024" Adding a text property to a new buffer should not fail
1025func Test_textprop_empty_buffer()
1026  call prop_type_add('comment', {'highlight': 'Search'})
1027  new
1028  call prop_add(1, 1, {'type': 'comment'})
1029  close
1030  call prop_type_delete('comment')
1031endfunc
1032
1033" Adding a text property with invalid highlight should be ignored.
1034func Test_textprop_invalid_highlight()
1035  call assert_fails("call prop_type_add('dni', {'highlight': 'DoesNotExist'})", 'E970:')
1036  new
1037  call setline(1, ['asdf','asdf'])
1038  call prop_add(1, 1, {'length': 4, 'type': 'dni'})
1039  redraw
1040  bwipe!
1041  call prop_type_delete('dni')
1042endfunc
1043
1044" Adding a text property to an empty buffer and then editing another
1045func Test_textprop_empty_buffer_next()
1046  call prop_type_add("xxx", {})
1047  call prop_add(1, 1, {"type": "xxx"})
1048  next X
1049  call prop_type_delete('xxx')
1050endfunc
1051
1052func Test_textprop_remove_from_buf()
1053  new
1054  let buf = bufnr('')
1055  call prop_type_add('one', {'bufnr': buf})
1056  call prop_add(1, 1, {'type': 'one', 'id': 234})
1057  file x
1058  edit y
1059  call prop_remove({'id': 234, 'bufnr': buf}, 1)
1060  call prop_type_delete('one', {'bufnr': buf})
1061  bwipe! x
1062  close
1063endfunc
1064
1065func Test_textprop_in_unloaded_buf()
1066  edit Xaaa
1067  call setline(1, 'aaa')
1068  write
1069  edit Xbbb
1070  call setline(1, 'bbb')
1071  write
1072  let bnr = bufnr('')
1073  edit Xaaa
1074
1075  call prop_type_add('ErrorMsg', #{highlight:'ErrorMsg'})
1076  call assert_fails("call prop_add(1, 1, #{end_lnum: 1, endcol: 2, type: 'ErrorMsg', bufnr: bnr})", 'E275:')
1077  exe 'buf ' .. bnr
1078  call assert_equal('bbb', getline(1))
1079  call assert_equal(0, prop_list(1)->len())
1080
1081  bwipe! Xaaa
1082  bwipe! Xbbb
1083  cal delete('Xaaa')
1084  cal delete('Xbbb')
1085endfunc
1086
1087func Test_proptype_substitute2()
1088  new
1089  " text_prop.vim
1090  call setline(1, [
1091        \ 'The   num  123 is smaller than 4567.',
1092        \ '123 The number 123 is smaller than 4567.',
1093        \ '123 The number 123 is smaller than 4567.'])
1094
1095  call prop_type_add('number', {'highlight': 'ErrorMsg'})
1096
1097  call prop_add(1, 12, {'length': 3, 'type': 'number'})
1098  call prop_add(2, 1, {'length': 3, 'type': 'number'})
1099  call prop_add(3, 36, {'length': 4, 'type': 'number'})
1100  set ul&
1101  let expected = [{'id': 0, 'col': 13, 'end': 1, 'type': 'number', 'length': 3, 'start': 1},
1102        \ {'id': 0, 'col': 1, 'end': 1, 'type': 'number', 'length': 3, 'start': 1},
1103        \ {'id': 0, 'col': 50, 'end': 1, 'type': 'number', 'length': 4, 'start': 1}]
1104  " Add some text in between
1105  %s/\s\+/   /g
1106  call assert_equal(expected, prop_list(1) + prop_list(2) + prop_list(3))
1107
1108  " remove some text
1109  :1s/[a-z]\{3\}//g
1110  let expected = [{'id': 0, 'col': 10, 'end': 1, 'type': 'number', 'length': 3, 'start': 1}]
1111  call assert_equal(expected, prop_list(1))
1112  bwipe!
1113endfunc
1114
1115func SaveOptions()
1116  let d = #{tabstop: &tabstop,
1117	  \ softtabstop: &softtabstop,
1118	  \ shiftwidth: &shiftwidth,
1119	  \ expandtab: &expandtab,
1120	  \ foldmethod: '"' .. &foldmethod .. '"',
1121	  \ }
1122  return d
1123endfunc
1124
1125func RestoreOptions(dict)
1126  for name in keys(a:dict)
1127    exe 'let &' .. name .. ' = ' .. a:dict[name]
1128  endfor
1129endfunc
1130
1131func Test_textprop_noexpandtab()
1132  new
1133  let save_dict = SaveOptions()
1134
1135  set tabstop=8
1136  set softtabstop=4
1137  set shiftwidth=4
1138  set noexpandtab
1139  set foldmethod=marker
1140
1141  call feedkeys("\<esc>\<esc>0Ca\<cr>\<esc>\<up>", "tx")
1142  call prop_type_add('test', {'highlight': 'ErrorMsg'})
1143  call prop_add(1, 1, {'end_col': 2, 'type': 'test'})
1144  call feedkeys("0i\<tab>", "tx")
1145  call prop_remove({'type': 'test'})
1146  call prop_add(1, 2, {'end_col': 3, 'type': 'test'})
1147  call feedkeys("A\<left>\<tab>", "tx")
1148  call prop_remove({'type': 'test'})
1149  try
1150    " It is correct that this does not pass
1151    call prop_add(1, 6, {'end_col': 7, 'type': 'test'})
1152    " Has already collapsed here, start_col:6 does not result in an error
1153    call feedkeys("A\<left>\<tab>", "tx")
1154  catch /^Vim\%((\a\+)\)\=:E964/
1155  endtry
1156  call prop_remove({'type': 'test'})
1157  call prop_type_delete('test')
1158
1159  call RestoreOptions(save_dict)
1160  bwipe!
1161endfunc
1162
1163func Test_textprop_noexpandtab_redraw()
1164  new
1165  let save_dict = SaveOptions()
1166
1167  set tabstop=8
1168  set softtabstop=4
1169  set shiftwidth=4
1170  set noexpandtab
1171  set foldmethod=marker
1172
1173  call feedkeys("\<esc>\<esc>0Ca\<cr>\<space>\<esc>\<up>", "tx")
1174  call prop_type_add('test', {'highlight': 'ErrorMsg'})
1175  call prop_add(1, 1, {'end_col': 2, 'type': 'test'})
1176  call feedkeys("0i\<tab>", "tx")
1177  " Internally broken at the next line
1178  call feedkeys("A\<left>\<tab>", "tx")
1179  redraw
1180  " Index calculation failed internally on next line
1181  call prop_add(1, 1, {'end_col': 2, 'type': 'test'})
1182  call prop_remove({'type': 'test', 'all': v:true})
1183  call prop_type_delete('test')
1184  call prop_type_delete('test')
1185
1186  call RestoreOptions(save_dict)
1187  bwipe!
1188endfunc
1189
1190func Test_textprop_ins_str()
1191  new
1192  call setline(1, 'just some text')
1193  call prop_type_add('test', {'highlight': 'ErrorMsg'})
1194  call prop_add(1, 1, {'end_col': 2, 'type': 'test'})
1195  call assert_equal([{'id': 0, 'col': 1, 'end': 1, 'type': 'test', 'length': 1, 'start': 1}], prop_list(1))
1196
1197  call feedkeys("foi\<F8>\<Esc>", "tx")
1198  call assert_equal('just s<F8>ome text', getline(1))
1199  call assert_equal([{'id': 0, 'col': 1, 'end': 1, 'type': 'test', 'length': 1, 'start': 1}], prop_list(1))
1200
1201  bwipe!
1202  call prop_remove({'type': 'test'})
1203  call prop_type_delete('test')
1204endfunc
1205
1206func Test_find_prop_later_in_line()
1207  new
1208  call prop_type_add('test', {'highlight': 'ErrorMsg'})
1209  call setline(1, 'just some text')
1210  call prop_add(1, 1, {'length': 4, 'type': 'test'})
1211  call prop_add(1, 10, {'length': 3, 'type': 'test'})
1212
1213  call assert_equal({'id': 0, 'lnum': 1, 'col': 10, 'end': 1, 'type': 'test', 'length': 3, 'start': 1},
1214			  \ prop_find(#{type: 'test', lnum: 1, col: 6}))
1215
1216  bwipe!
1217  call prop_type_delete('test')
1218endfunc
1219
1220func Test_find_zerowidth_prop_sol()
1221  new
1222  call prop_type_add('test', {'highlight': 'ErrorMsg'})
1223  call setline(1, 'just some text')
1224  call prop_add(1, 1, {'length': 0, 'type': 'test'})
1225
1226  call assert_equal({'id': 0, 'lnum': 1, 'col': 1, 'end': 1, 'type': 'test', 'length': 0, 'start': 1},
1227			  \ prop_find(#{type: 'test', lnum: 1}))
1228
1229  bwipe!
1230  call prop_type_delete('test')
1231endfunc
1232
1233" Test for passing invalid arguments to prop_xxx() functions
1234func Test_prop_func_invalid_args()
1235  call assert_fails('call prop_clear(1, 2, [])', 'E715:')
1236  call assert_fails('call prop_clear(-1, 2)', 'E16:')
1237  call assert_fails('call prop_find(test_null_dict())', 'E474:')
1238  call assert_fails('call prop_find({"bufnr" : []})', 'E730:')
1239  call assert_fails('call prop_find({})', 'E968:')
1240  call assert_fails('call prop_find({}, "x")', 'E474:')
1241  call assert_fails('call prop_find({"lnum" : -2})', 'E16:')
1242  call assert_fails('call prop_list(1, [])', 'E715:')
1243  call assert_fails('call prop_list(-1, {})', 'E16:')
1244  call assert_fails('call prop_remove([])', 'E474:')
1245  call assert_fails('call prop_remove({}, -2)', 'E16:')
1246  call assert_fails('call prop_remove({})', 'E968:')
1247  call assert_fails('call prop_type_add([], {})', 'E730:')
1248  call assert_fails("call prop_type_change('long', {'xyz' : 10})", 'E971:')
1249  call assert_fails("call prop_type_delete([])", 'E730:')
1250  call assert_fails("call prop_type_delete('xyz', [])", 'E715:')
1251  call assert_fails("call prop_type_get([])", 'E730:')
1252  call assert_fails("call prop_type_get('', [])", 'E474:')
1253  call assert_fails("call prop_type_list([])", 'E715:')
1254endfunc
1255
1256func Test_split_join()
1257  new
1258  call prop_type_add('test', {'highlight': 'ErrorMsg'})
1259  call setline(1, 'just some text')
1260  call prop_add(1, 6, {'length': 4, 'type': 'test'})
1261
1262  " Split in middle of "some"
1263  execute "normal! 8|i\<CR>"
1264  call assert_equal([{'id': 0, 'col': 6, 'end': 0, 'type': 'test', 'length': 2, 'start': 1}],
1265			  \ prop_list(1))
1266  call assert_equal([{'id': 0, 'col': 1, 'end': 1, 'type': 'test', 'length': 2, 'start': 0}],
1267			  \ prop_list(2))
1268
1269  " Join the two lines back together
1270  normal! 1GJ
1271  call assert_equal([{'id': 0, 'col': 6, 'end': 1, 'type': 'test', 'length': 5, 'start': 1}], prop_list(1))
1272
1273  bwipe!
1274  call prop_type_delete('test')
1275endfunc
1276
1277" vim: shiftwidth=2 sts=2 expandtab
1278