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