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
8source vim9.vim
9
10func Test_proptype_global()
11  call prop_type_add('comment', {'highlight': 'Directory', 'priority': 123, 'start_incl': 1, 'end_incl': 1})
12  let proptypes = prop_type_list()
13  call assert_equal(1, len(proptypes))
14  call assert_equal('comment', proptypes[0])
15
16  let proptype = prop_type_get('comment')
17  call assert_equal('Directory', proptype['highlight'])
18  call assert_equal(123, proptype['priority'])
19  call assert_equal(1, proptype['start_incl'])
20  call assert_equal(1, proptype['end_incl'])
21
22  call prop_type_delete('comment')
23  call assert_equal(0, len(prop_type_list()))
24
25  call prop_type_add('one', {})
26  call assert_equal(1, len(prop_type_list()))
27  let proptype = 'one'->prop_type_get()
28  call assert_false(has_key(proptype, 'highlight'))
29  call assert_equal(0, proptype['priority'])
30  call assert_equal(0, proptype['start_incl'])
31  call assert_equal(0, proptype['end_incl'])
32
33  call prop_type_add('two', {})
34  call assert_equal(2, len(prop_type_list()))
35  call prop_type_delete('one')
36  call assert_equal(1, len(prop_type_list()))
37  call prop_type_delete('two')
38  call assert_equal(0, len(prop_type_list()))
39endfunc
40
41func Test_proptype_buf()
42  let bufnr = bufnr('')
43  call prop_type_add('comment', #{bufnr: bufnr, highlight: 'Directory', priority: 123, start_incl: 1, end_incl: 1})
44  let proptypes = prop_type_list({'bufnr': bufnr})
45  call assert_equal(1, len(proptypes))
46  call assert_equal('comment', proptypes[0])
47
48  let proptype = prop_type_get('comment', {'bufnr': bufnr})
49  call assert_equal('Directory', proptype['highlight'])
50  call assert_equal(123, proptype['priority'])
51  call assert_equal(1, proptype['start_incl'])
52  call assert_equal(1, proptype['end_incl'])
53
54  call prop_type_delete('comment', {'bufnr': bufnr})
55  call assert_equal(0, len({'bufnr': bufnr}->prop_type_list()))
56
57  call prop_type_add('one', {'bufnr': bufnr})
58  let proptype = prop_type_get('one', {'bufnr': bufnr})
59  call assert_false(has_key(proptype, 'highlight'))
60  call assert_equal(0, proptype['priority'])
61  call assert_equal(0, proptype['start_incl'])
62  call assert_equal(0, proptype['end_incl'])
63
64  call prop_type_add('two', {'bufnr': bufnr})
65  call assert_equal(2, len(prop_type_list({'bufnr': bufnr})))
66  call prop_type_delete('one', {'bufnr': bufnr})
67  call assert_equal(1, len(prop_type_list({'bufnr': bufnr})))
68  call prop_type_delete('two', {'bufnr': bufnr})
69  call assert_equal(0, len(prop_type_list({'bufnr': bufnr})))
70
71  call assert_fails("call prop_type_add('one', {'bufnr': 98764})", "E158:")
72endfunc
73
74def Test_proptype_buf_list()
75  new
76  var bufnr = bufnr('')
77  try
78    prop_type_add('global', {})
79    prop_type_add('local', {bufnr: bufnr})
80
81    prop_add(1, 1, {type: 'global'})
82    prop_add(1, 1, {type: 'local'})
83
84    assert_equal([
85      {type: 'local',  type_bufnr: bufnr, id: 0, col: 1, end: 1, length: 0, start: 1},
86      {type: 'global', type_bufnr: 0,     id: 0, col: 1, end: 1, length: 0, start: 1},
87    ], prop_list(1))
88    assert_equal(
89      {lnum: 1, id: 0, col: 1, type_bufnr: bufnr, end: 1, type: 'local', length: 0, start: 1},
90      prop_find({lnum: 1, type: 'local'}))
91    assert_equal(
92      {lnum: 1, id: 0, col: 1, type_bufnr: 0, end: 1, type: 'global', length: 0, start: 1},
93      prop_find({lnum: 1, type: 'global'}))
94
95    prop_remove({type: 'global'}, 1)
96    prop_remove({type: 'local'}, 1)
97  finally
98    prop_type_delete('global')
99    prop_type_delete('local', {bufnr: bufnr})
100    bwipe!
101  endtry
102enddef
103
104func AddPropTypes()
105  call prop_type_add('one', {})
106  call prop_type_add('two', {})
107  call prop_type_add('three', {})
108  call prop_type_add('whole', {})
109endfunc
110
111func DeletePropTypes()
112  call prop_type_delete('one')
113  call prop_type_delete('two')
114  call prop_type_delete('three')
115  call prop_type_delete('whole')
116endfunc
117
118func SetupPropsInFirstLine()
119  call setline(1, 'one two three')
120  call prop_add(1, 1, {'length': 3, 'id': 11, 'type': 'one'})
121  eval 1->prop_add(5, {'length': 3, 'id': 12, 'type': 'two'})
122  call prop_add(1, 9, {'length': 5, 'id': 13, 'type': 'three'})
123  call prop_add(1, 1, {'length': 13, 'id': 14, 'type': 'whole'})
124endfunc
125
126func Get_expected_props()
127  return [
128      \ #{type_bufnr: 0, col: 1, length: 13, id: 14, type: 'whole', start: 1, end: 1},
129      \ #{type_bufnr: 0, col: 1, length: 3,  id: 11, type: 'one',   start: 1, end: 1},
130      \ #{type_bufnr: 0, col: 5, length: 3,  id: 12, type: 'two',   start: 1, end: 1},
131      \ #{type_bufnr: 0, col: 9, length: 5,  id: 13, type: 'three', start: 1, end: 1},
132      \ ]
133endfunc
134
135func Test_prop_find()
136  new
137  call setline(1, ['one one one', 'twotwo', 'three', 'fourfour', 'five', 'sixsix'])
138
139  " Add two text props on lines 1 and 5, and one spanning lines 2 to 4.
140  call prop_type_add('prop_name', {'highlight': 'Directory'})
141  call prop_add(1, 5, {'type': 'prop_name', 'id': 10, 'length': 3})
142  call prop_add(2, 4, {'type': 'prop_name', 'id': 11, 'end_lnum': 4, 'end_col': 9})
143  call prop_add(5, 4, {'type': 'prop_name', 'id': 12, 'length': 1})
144
145  let expected = [
146    \ #{type_bufnr: 0, lnum: 1, col: 5, length: 3, id: 10, type: 'prop_name', start: 1, end: 1},
147    \ #{type_bufnr: 0, lnum: 2, col: 4, id: 11, type: 'prop_name', start: 1, end: 0},
148    \ #{type_bufnr: 0, lnum: 5, col: 4, length: 1, id: 12, type: 'prop_name', start: 1, end: 1}
149    \ ]
150
151  " Starting at line 5 col 1 this should find the prop at line 5 col 4.
152  call cursor(5,1)
153  let result = prop_find({'type': 'prop_name'}, 'f')
154  call assert_equal(expected[2], result)
155
156  " With skipstart left at false (default), this should find the prop at line
157  " 5 col 4.
158  let result = prop_find({'type': 'prop_name', 'lnum': 5, 'col': 4}, 'b')
159  call assert_equal(expected[2], result)
160
161  " With skipstart set to true, this should skip the prop at line 5 col 4.
162  let result = prop_find({'type': 'prop_name', 'lnum': 5, 'col': 4, 'skipstart': 1}, 'b')
163  unlet result.length
164  call assert_equal(expected[1], result)
165
166  " Search backwards from line 1 col 10 to find the prop on the same line.
167  let result = prop_find({'type': 'prop_name', 'lnum': 1, 'col': 10}, 'b')
168  call assert_equal(expected[0], result)
169
170  " with skipstart set to false, if the start position is anywhere between the
171  " start and end lines of a text prop (searching forward or backward), the
172  " result should be the prop on the first line (the line with 'start' set to 1).
173  call cursor(3,1)
174  let result = prop_find({'type': 'prop_name'}, 'f')
175  unlet result.length
176  call assert_equal(expected[1], result)
177  let result = prop_find({'type': 'prop_name'}, 'b')
178  unlet result.length
179  call assert_equal(expected[1], result)
180
181  " with skipstart set to true, if the start position is anywhere between the
182  " start and end lines of a text prop (searching forward or backward), all lines
183  " of the prop will be skipped.
184  let result = prop_find({'type': 'prop_name', 'skipstart': 1}, 'b')
185  call assert_equal(expected[0], result)
186  let result = prop_find({'type': 'prop_name', 'skipstart': 1}, 'f')
187  call assert_equal(expected[2], result)
188
189  " Use skipstart to search through all props with type name 'prop_name'.
190  " First forward...
191  let lnum = 1
192  let col = 1
193  let i = 0
194  for exp in expected
195    let result = prop_find({'type': 'prop_name', 'lnum': lnum, 'col': col, 'skipstart': 1}, 'f')
196    if !has_key(exp, "length")
197      unlet result.length
198    endif
199    call assert_equal(exp, result)
200    let lnum = result.lnum
201    let col = result.col
202    let i = i + 1
203  endfor
204
205  " ...then backwards.
206  let lnum = 6
207  let col = 4
208  let i = 2
209  while i >= 0
210    let result = prop_find({'type': 'prop_name', 'lnum': lnum, 'col': col, 'skipstart': 1}, 'b')
211    if !has_key(expected[i], "length")
212      unlet result.length
213    endif
214    call assert_equal(expected[i], result)
215    let lnum = result.lnum
216    let col = result.col
217    let i = i - 1
218  endwhile
219
220  " Starting from line 6 col 1 search backwards for prop with id 10.
221  call cursor(6,1)
222  let result = prop_find({'id': 10, 'skipstart': 1}, 'b')
223  call assert_equal(expected[0], result)
224
225  " Starting from line 1 col 1 search forwards for prop with id 12.
226  call cursor(1,1)
227  let result = prop_find({'id': 12}, 'f')
228  call assert_equal(expected[2], result)
229
230  " Search for a prop with an unknown id.
231  let result = prop_find({'id': 999}, 'f')
232  call assert_equal({}, result)
233
234  " Search backwards from the proceeding position of the prop with id 11
235  " (at line num 2 col 4). This should return an empty dict.
236  let result = prop_find({'id': 11, 'lnum': 2, 'col': 3}, 'b')
237  call assert_equal({}, result)
238
239  " When lnum is given and col is omitted, use column 1.
240  let result = prop_find({'type': 'prop_name', 'lnum': 1}, 'f')
241  call assert_equal(expected[0], result)
242
243  " Negative ID is possible, just like prop is not found.
244  call assert_equal({}, prop_find({'id': -1}))
245  call assert_equal({}, prop_find({'id': -2}))
246
247  call prop_clear(1, 6)
248
249  " Default ID is zero
250  call prop_add(5, 4, {'type': 'prop_name', 'length': 1})
251  call assert_equal(#{lnum: 5, id: 0, col: 4, type_bufnr: 0, end: 1, type: 'prop_name', length: 1, start: 1}, prop_find({'id': 0}))
252
253  call prop_type_delete('prop_name')
254  call prop_clear(1, 6)
255  bwipe!
256endfunc
257
258def Test_prop_find2()
259  # Multiple props per line, start on the first, should find the second.
260  new
261  ['the quikc bronw fox jumsp over the layz dog']->repeat(2)->setline(1)
262  prop_type_add('misspell', {highlight: 'ErrorMsg'})
263  for lnum in [1, 2]
264    for col in [8, 14, 24, 38]
265      prop_add(lnum, col, {type: 'misspell', length: 2})
266    endfor
267  endfor
268  cursor(1, 8)
269  var expected = {type_bufnr: 0, lnum: 1, id: 0, col: 14, end: 1, type: 'misspell', length: 2, start: 1}
270  var result = prop_find({type: 'misspell', skipstart: true}, 'f')
271  assert_equal(expected, result)
272
273  prop_type_delete('misspell')
274  bwipe!
275enddef
276
277func Test_prop_find_smaller_len_than_match_col()
278  new
279  call prop_type_add('test', {'highlight': 'ErrorMsg'})
280  call setline(1, ['xxxx', 'x'])
281  call prop_add(1, 4, {'type': 'test'})
282  call assert_equal(
283        \ #{type_bufnr: 0, id: 0, lnum: 1, col: 4, type: 'test', length: 0, start: 1, end: 1},
284        \ prop_find({'type': 'test', 'lnum': 2, 'col': 1}, 'b'))
285  bwipe!
286  call prop_type_delete('test')
287endfunc
288
289func Test_prop_find_with_both_option_enabled()
290  " Initialize
291  new
292  call AddPropTypes()
293  call SetupPropsInFirstLine()
294  let props = Get_expected_props()->map({_, v -> extend(v, {'lnum': 1})})
295  " Test
296  call assert_fails("call prop_find({'both': 1})", 'E968:')
297  call assert_fails("call prop_find({'id': 11, 'both': 1})", 'E860:')
298  call assert_fails("call prop_find({'type': 'three', 'both': 1})", 'E860:')
299  call assert_equal({}, prop_find({'id': 11, 'type': 'three', 'both': 1}))
300  call assert_equal({}, prop_find({'id': 130000, 'type': 'one', 'both': 1}))
301  call assert_equal(props[2], prop_find({'id': 12, 'type': 'two', 'both': 1}))
302  call assert_equal(props[0], prop_find({'id': 14, 'type': 'whole', 'both': 1}))
303  " Clean up
304  call DeletePropTypes()
305  bwipe!
306endfunc
307
308func Test_prop_add()
309  new
310  call AddPropTypes()
311  call SetupPropsInFirstLine()
312  let expected_props = Get_expected_props()
313  call assert_equal(expected_props, prop_list(1))
314  call assert_fails("call prop_add(10, 1, {'length': 1, 'id': 14, 'type': 'whole'})", 'E966:')
315  call assert_fails("call prop_add(1, 22, {'length': 1, 'id': 14, 'type': 'whole'})", 'E964:')
316
317  " Insert a line above, text props must still be there.
318  call append(0, 'empty')
319  call assert_equal(expected_props, prop_list(2))
320  " Delete a line above, text props must still be there.
321  1del
322  call assert_equal(expected_props, prop_list(1))
323
324  " Prop without length or end column is zero length
325  call prop_clear(1)
326  call prop_type_add('included', {'start_incl': 1, 'end_incl': 1})
327  call prop_add(1, 5, #{type: 'included'})
328  let expected = [#{type_bufnr: 0, col: 5, length: 0, type: 'included', id: 0, start: 1, end: 1}]
329  call assert_equal(expected, prop_list(1))
330
331  " Inserting text makes the prop bigger.
332  exe "normal 5|ixx\<Esc>"
333  let expected = [#{type_bufnr: 0, col: 5, length: 2, type: 'included', id: 0, start: 1, end: 1}]
334  call assert_equal(expected, prop_list(1))
335
336  call assert_fails("call prop_add(1, 5, {'type': 'two', 'bufnr': 234343})", 'E158:')
337
338  call DeletePropTypes()
339  call prop_type_delete('included')
340  bwipe!
341endfunc
342
343" Test for the prop_add_list() function
344func Test_prop_add_list()
345  new
346  call AddPropTypes()
347  call setline(1, ['one one one', 'two two two', 'six six six', 'ten ten ten'])
348  call prop_add_list(#{type: 'one', id: 2},
349        \ [[1, 1, 1, 3], [2, 5, 2, 7], [3, 6, 4, 6]])
350  call assert_equal([#{id: 2, col: 1, type_bufnr: 0, end: 1, type: 'one',
351        \ length: 2, start: 1}], prop_list(1))
352  call assert_equal([#{id: 2, col: 5, type_bufnr: 0, end: 1, type: 'one',
353        \ length: 2, start: 1}], prop_list(2))
354  call assert_equal([#{id: 2, col: 6, type_bufnr: 0, end: 0, type: 'one',
355        \ length: 7, start: 1}], prop_list(3))
356  call assert_equal([#{id: 2, col: 1, type_bufnr: 0, end: 1, type: 'one',
357        \ length: 5, start: 0}], prop_list(4))
358  call assert_fails('call prop_add_list([1, 2], [[1, 1, 3]])', 'E1206:')
359  call assert_fails('call prop_add_list({}, {})', 'E1211:')
360  call assert_fails('call prop_add_list({}, [[1, 1, 3]])', 'E965:')
361  call assert_fails('call prop_add_list(#{type: "abc"}, [[1, 1, 1, 3]])', 'E971:')
362  call assert_fails('call prop_add_list(#{type: "one"}, [[]])', 'E474:')
363  call assert_fails('call prop_add_list(#{type: "one"}, [[1, 1, 1, 1], {}])', 'E714:')
364  call assert_fails('call prop_add_list(#{type: "one"}, [[1, 1, "a"]])', 'E474:')
365  call assert_fails('call prop_add_list(#{type: "one"}, [[2, 2]])', 'E474:')
366  call assert_fails('call prop_add_list(#{type: "one"}, [[1, 1, 2], [2, 2]])', 'E474:')
367  call assert_fails('call prop_add_list(#{type: "one"}, [[1, 1, 1, 2], [4, 1, 5, 2]])', 'E966:')
368  call assert_fails('call prop_add_list(#{type: "one"}, [[3, 1, 1, 2]])', 'E966:')
369  call assert_fails('call prop_add_list(#{type: "one"}, [[2, 2, 2, 2], [3, 20, 3, 22]])', 'E964:')
370  call assert_fails('eval #{type: "one"}->prop_add_list([[2, 2, 2, 2], [3, 20, 3, 22]])', 'E964:')
371  call assert_fails('call prop_add_list(test_null_dict(), [[2, 2, 2]])', 'E965:')
372  call assert_fails('call prop_add_list(#{type: "one"}, test_null_list())', 'E714:')
373  call assert_fails('call prop_add_list(#{type: "one"}, [test_null_list()])', 'E714:')
374  call DeletePropTypes()
375  bw!
376endfunc
377
378func Test_prop_remove()
379  new
380  call AddPropTypes()
381  call SetupPropsInFirstLine()
382  let props = Get_expected_props()
383  call assert_equal(props, prop_list(1))
384
385  " remove by id
386  call assert_equal(1, {'id': 12}->prop_remove(1))
387  unlet props[2]
388  call assert_equal(props, prop_list(1))
389
390  " remove by type
391  call assert_equal(1, prop_remove({'type': 'one'}, 1))
392  unlet props[1]
393  call assert_equal(props, prop_list(1))
394
395  " remove from unknown buffer
396  call assert_fails("call prop_remove({'type': 'one', 'bufnr': 123456}, 1)", 'E158:')
397
398  call DeletePropTypes()
399  bwipe!
400
401  new
402  call AddPropTypes()
403  call SetupPropsInFirstLine()
404  call prop_add(1, 6, {'length': 2, 'id': 11, 'type': 'three'})
405  let props = Get_expected_props()
406  call insert(props, #{type_bufnr: 0, col: 6, length: 2, id: 11, type: 'three', start: 1, end: 1}, 3)
407  call assert_equal(props, prop_list(1))
408  call assert_equal(1, prop_remove({'type': 'three', 'id': 11, 'both': 1, 'all': 1}, 1))
409  unlet props[3]
410  call assert_equal(props, prop_list(1))
411
412  call assert_fails("call prop_remove({'id': 11, 'both': 1})", 'E860:')
413  call assert_fails("call prop_remove({'type': 'three', 'both': 1})", 'E860:')
414
415  call DeletePropTypes()
416  bwipe!
417endfunc
418
419def Test_prop_add_vim9()
420  prop_type_add('comment', {
421      highlight: 'Directory',
422      priority: 123,
423      start_incl: true,
424      end_incl: true,
425      combine: false,
426    })
427  prop_type_delete('comment')
428enddef
429
430def Test_prop_remove_vim9()
431  new
432  AddPropTypes()
433  SetupPropsInFirstLine()
434  assert_equal(1, prop_remove({type: 'three', id: 13, both: true, all: true}))
435  DeletePropTypes()
436  bwipe!
437enddef
438
439func SetupOneLine()
440  call setline(1, 'xonex xtwoxx')
441  normal gg0
442  call AddPropTypes()
443  call prop_add(1, 2, {'length': 3, 'id': 11, 'type': 'one'})
444  call prop_add(1, 8, {'length': 3, 'id': 12, 'type': 'two'})
445  let expected = [
446	\ #{type_bufnr: 0, col: 2, length: 3, id: 11, type: 'one', start: 1, end: 1},
447	\ #{type_bufnr: 0, col: 8, length: 3, id: 12, type: 'two', start: 1, end: 1},
448	\]
449  call assert_equal(expected, prop_list(1))
450  return expected
451endfunc
452
453func Test_prop_add_remove_buf()
454  new
455  let bufnr = bufnr('')
456  call AddPropTypes()
457  for lnum in range(1, 4)
458    call setline(lnum, 'one two three')
459  endfor
460  wincmd w
461  for lnum in range(1, 4)
462    call prop_add(lnum, 1, {'length': 3, 'id': 11, 'type': 'one', 'bufnr': bufnr})
463    call prop_add(lnum, 5, {'length': 3, 'id': 12, 'type': 'two', 'bufnr': bufnr})
464    call prop_add(lnum, 11, {'length': 3, 'id': 13, 'type': 'three', 'bufnr': bufnr})
465  endfor
466
467  let props = [
468	\ #{type_bufnr: 0, col: 1, length: 3, id: 11, type: 'one', start: 1, end: 1},
469	\ #{type_bufnr: 0, col: 5, length: 3, id: 12, type: 'two', start: 1, end: 1},
470	\ #{type_bufnr: 0, col: 11, length: 3, id: 13, type: 'three', start: 1, end: 1},
471	\]
472  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
473
474  " remove by id
475  let before_props = deepcopy(props)
476  unlet props[1]
477
478  call prop_remove({'id': 12, 'bufnr': bufnr}, 1)
479  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
480  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
481  call assert_equal(before_props, prop_list(3, {'bufnr': bufnr}))
482  call assert_equal(before_props, prop_list(4, {'bufnr': bufnr}))
483
484  call prop_remove({'id': 12, 'bufnr': bufnr}, 3, 4)
485  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
486  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
487  call assert_equal(props, prop_list(3, {'bufnr': bufnr}))
488  call assert_equal(props, prop_list(4, {'bufnr': bufnr}))
489
490  call prop_remove({'id': 12, 'bufnr': bufnr})
491  for lnum in range(1, 4)
492    call assert_equal(props, prop_list(lnum, {'bufnr': bufnr}))
493  endfor
494
495  " remove by type
496  let before_props = deepcopy(props)
497  unlet props[0]
498
499  call prop_remove({'type': 'one', 'bufnr': bufnr}, 1)
500  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
501  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
502  call assert_equal(before_props, prop_list(3, {'bufnr': bufnr}))
503  call assert_equal(before_props, prop_list(4, {'bufnr': bufnr}))
504
505  call prop_remove({'type': 'one', 'bufnr': bufnr}, 3, 4)
506  call assert_equal(props, prop_list(1, {'bufnr': bufnr}))
507  call assert_equal(before_props, prop_list(2, {'bufnr': bufnr}))
508  call assert_equal(props, prop_list(3, {'bufnr': bufnr}))
509  call assert_equal(props, prop_list(4, {'bufnr': bufnr}))
510
511  call prop_remove({'type': 'one', 'bufnr': bufnr})
512  for lnum in range(1, 4)
513    call assert_equal(props, prop_list(lnum, {'bufnr': bufnr}))
514  endfor
515
516  call DeletePropTypes()
517  wincmd w
518  bwipe!
519endfunc
520
521func Test_prop_backspace()
522  new
523  set bs=2
524  let expected = SetupOneLine() " 'xonex xtwoxx'
525
526  exe "normal 0li\<BS>\<Esc>fxli\<BS>\<Esc>"
527  call assert_equal('one xtwoxx', getline(1))
528  let expected[0].col = 1
529  let expected[1].col = 6
530  call assert_equal(expected, prop_list(1))
531
532  call DeletePropTypes()
533  bwipe!
534  set bs&
535endfunc
536
537func Test_prop_replace()
538  new
539  set bs=2
540  let expected = SetupOneLine() " 'xonex xtwoxx'
541
542  exe "normal 0Ryyy\<Esc>"
543  call assert_equal('yyyex xtwoxx', getline(1))
544  call assert_equal(expected, prop_list(1))
545
546  exe "normal ftRyy\<BS>"
547  call assert_equal('yyyex xywoxx', getline(1))
548  call assert_equal(expected, prop_list(1))
549
550  exe "normal 0fwRyy\<BS>"
551  call assert_equal('yyyex xyyoxx', getline(1))
552  call assert_equal(expected, prop_list(1))
553
554  exe "normal 0foRyy\<BS>\<BS>"
555  call assert_equal('yyyex xyyoxx', getline(1))
556  call assert_equal(expected, prop_list(1))
557
558  call DeletePropTypes()
559  bwipe!
560  set bs&
561endfunc
562
563func Test_prop_open_line()
564  new
565
566  " open new line, props stay in top line
567  let expected = SetupOneLine() " 'xonex xtwoxx'
568  exe "normal o\<Esc>"
569  call assert_equal('xonex xtwoxx', getline(1))
570  call assert_equal('', getline(2))
571  call assert_equal(expected, prop_list(1))
572  call DeletePropTypes()
573
574  " move all props to next line
575  let expected = SetupOneLine() " 'xonex xtwoxx'
576  exe "normal 0i\<CR>\<Esc>"
577  call assert_equal('', getline(1))
578  call assert_equal('xonex xtwoxx', getline(2))
579  call assert_equal(expected, prop_list(2))
580  call DeletePropTypes()
581
582  " split just before prop, move all props to next line
583  let expected = SetupOneLine() " 'xonex xtwoxx'
584  exe "normal 0li\<CR>\<Esc>"
585  call assert_equal('x', getline(1))
586  call assert_equal('onex xtwoxx', getline(2))
587  let expected[0].col -= 1
588  let expected[1].col -= 1
589  call assert_equal(expected, prop_list(2))
590  call DeletePropTypes()
591
592  " split inside prop, split first prop
593  let expected = SetupOneLine() " 'xonex xtwoxx'
594  exe "normal 0lli\<CR>\<Esc>"
595  call assert_equal('xo', getline(1))
596  call assert_equal('nex xtwoxx', getline(2))
597  let exp_first = [deepcopy(expected[0])]
598  let exp_first[0].length = 1
599  let exp_first[0].end = 0
600  call assert_equal(exp_first, prop_list(1))
601  let expected[0].col = 1
602  let expected[0].length = 2
603  let expected[0].start = 0
604  let expected[1].col -= 2
605  call assert_equal(expected, prop_list(2))
606  call DeletePropTypes()
607
608  " split just after first prop, second prop move to next line
609  let expected = SetupOneLine() " 'xonex xtwoxx'
610  exe "normal 0fea\<CR>\<Esc>"
611  call assert_equal('xone', getline(1))
612  call assert_equal('x xtwoxx', getline(2))
613  let exp_first = expected[0:0]
614  call assert_equal(exp_first, prop_list(1))
615  let expected = expected[1:1]
616  let expected[0].col -= 4
617  call assert_equal(expected, prop_list(2))
618  call DeletePropTypes()
619
620  bwipe!
621  set bs&
622endfunc
623
624func Test_prop_clear()
625  new
626  call AddPropTypes()
627  call SetupPropsInFirstLine()
628  call assert_equal(Get_expected_props(), prop_list(1))
629
630  eval 1->prop_clear()
631  call assert_equal([], 1->prop_list())
632
633  call DeletePropTypes()
634  bwipe!
635endfunc
636
637func Test_prop_clear_buf()
638  new
639  call AddPropTypes()
640  call SetupPropsInFirstLine()
641  let bufnr = bufnr('')
642  wincmd w
643  call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr}))
644
645  call prop_clear(1, 1, {'bufnr': bufnr})
646  call assert_equal([], prop_list(1, {'bufnr': bufnr}))
647
648  wincmd w
649  call DeletePropTypes()
650  bwipe!
651endfunc
652
653func Test_prop_setline()
654  new
655  call AddPropTypes()
656  call SetupPropsInFirstLine()
657  call assert_equal(Get_expected_props(), prop_list(1))
658
659  call setline(1, 'foobar')
660  call assert_equal([], prop_list(1))
661
662  call DeletePropTypes()
663  bwipe!
664endfunc
665
666func Test_prop_setbufline()
667  new
668  call AddPropTypes()
669  call SetupPropsInFirstLine()
670  let bufnr = bufnr('')
671  wincmd w
672  call assert_equal(Get_expected_props(), prop_list(1, {'bufnr': bufnr}))
673
674  call setbufline(bufnr, 1, 'foobar')
675  call assert_equal([], prop_list(1, {'bufnr': bufnr}))
676
677  wincmd w
678  call DeletePropTypes()
679  bwipe!
680endfunc
681
682func Test_prop_substitute()
683  new
684  " Set first line to 'one two three'
685  call AddPropTypes()
686  call SetupPropsInFirstLine()
687  let expected_props = Get_expected_props()
688  call assert_equal(expected_props, prop_list(1))
689
690  " Change "n" in "one" to XX: 'oXXe two three'
691  s/n/XX/
692  let expected_props[0].length += 1
693  let expected_props[1].length += 1
694  let expected_props[2].col += 1
695  let expected_props[3].col += 1
696  call assert_equal(expected_props, prop_list(1))
697
698  " Delete "t" in "two" and "three" to XX: 'oXXe wo hree'
699  s/t//g
700  let expected_props[0].length -= 2
701  let expected_props[2].length -= 1
702  let expected_props[3].length -= 1
703  let expected_props[3].col -= 1
704  call assert_equal(expected_props, prop_list(1))
705
706  " Split the line by changing w to line break: 'oXXe ', 'o hree'
707  " The long prop is split and spans both lines.
708  " The props on "two" and "three" move to the next line.
709  s/w/\r/
710  let new_props = [
711	\ copy(expected_props[0]),
712	\ copy(expected_props[2]),
713	\ copy(expected_props[3]),
714	\ ]
715  let expected_props[0].length = 5
716  let expected_props[0].end = 0
717  unlet expected_props[3]
718  unlet expected_props[2]
719  call assert_equal(expected_props, prop_list(1))
720
721  let new_props[0].length = 6
722  let new_props[0].start = 0
723  let new_props[1].col = 1
724  let new_props[1].length = 1
725  let new_props[2].col = 3
726  call assert_equal(new_props, prop_list(2))
727
728  call DeletePropTypes()
729  bwipe!
730endfunc
731
732func Test_prop_change_indent()
733  call prop_type_add('comment', {'highlight': 'Directory'})
734  new
735  call setline(1, ['    xxx', 'yyyyy'])
736  call prop_add(2, 2, {'length': 2, 'type': 'comment'})
737  let expect = #{type_bufnr: 0, col: 2, length: 2, type: 'comment', start: 1, end: 1, id: 0}
738  call assert_equal([expect], prop_list(2))
739
740  set shiftwidth=3
741  normal 2G>>
742  call assert_equal('   yyyyy', getline(2))
743  let expect.col += 3
744  call assert_equal([expect], prop_list(2))
745
746  normal 2G==
747  call assert_equal('    yyyyy', getline(2))
748  let expect.col = 6
749  call assert_equal([expect], prop_list(2))
750
751  call prop_clear(2)
752  call prop_add(2, 2, {'length': 5, 'type': 'comment'})
753  let expect.col = 2
754  let expect.length = 5
755  call assert_equal([expect], prop_list(2))
756
757  normal 2G<<
758  call assert_equal(' yyyyy', getline(2))
759  let expect.length = 2
760  call assert_equal([expect], prop_list(2))
761
762  set shiftwidth&
763  call prop_type_delete('comment')
764endfunc
765
766" Setup a three line prop in lines 2 - 4.
767" Add short props in line 1 and 5.
768func Setup_three_line_prop()
769  new
770  call setline(1, ['one', 'twotwo', 'three', 'fourfour', 'five'])
771  call prop_add(1, 2, {'length': 1, 'type': 'comment'})
772  call prop_add(2, 4, {'end_lnum': 4, 'end_col': 5, 'type': 'comment'})
773  call prop_add(5, 2, {'length': 1, 'type': 'comment'})
774endfunc
775
776func Test_prop_multiline()
777  eval 'comment'->prop_type_add({'highlight': 'Directory'})
778  new
779  call setline(1, ['xxxxxxx', 'yyyyyyyyy', 'zzzzzzzz'])
780
781  " start halfway line 1, end halfway line 3
782  call prop_add(1, 3, {'end_lnum': 3, 'end_col': 5, 'type': 'comment'})
783  let expect1 = #{type_bufnr: 0, col: 3, length: 6, type: 'comment', start: 1, end: 0, id: 0}
784  call assert_equal([expect1], prop_list(1))
785  let expect2 = #{type_bufnr: 0, col: 1, length: 10, type: 'comment', start: 0, end: 0, id: 0}
786  call assert_equal([expect2], prop_list(2))
787  let expect3 = #{type_bufnr: 0, col: 1, length: 4, type: 'comment', start: 0, end: 1, id: 0}
788  call assert_equal([expect3], prop_list(3))
789  call prop_clear(1, 3)
790
791  " include all three lines
792  call prop_add(1, 1, {'end_lnum': 3, 'end_col': 999, 'type': 'comment'})
793  let expect1.col = 1
794  let expect1.length = 8
795  call assert_equal([expect1], prop_list(1))
796  call assert_equal([expect2], prop_list(2))
797  let expect3.length = 9
798  call assert_equal([expect3], prop_list(3))
799  call prop_clear(1, 3)
800
801  bwipe!
802
803  " Test deleting the first line of a multi-line prop.
804  call Setup_three_line_prop()
805  let expect_short = #{type_bufnr: 0, col: 2, length: 1, type: 'comment', start: 1, end: 1, id: 0}
806  call assert_equal([expect_short], prop_list(1))
807  let expect2 = #{type_bufnr: 0, col: 4, length: 4, type: 'comment', start: 1, end: 0, id: 0}
808  call assert_equal([expect2], prop_list(2))
809  2del
810  call assert_equal([expect_short], prop_list(1))
811  let expect2 = #{type_bufnr: 0, col: 1, length: 6, type: 'comment', start: 1, end: 0, id: 0}
812  call assert_equal([expect2], prop_list(2))
813  bwipe!
814
815  " Test deleting the last line of a multi-line prop.
816  call Setup_three_line_prop()
817  let expect3 = #{type_bufnr: 0, col: 1, length: 6, type: 'comment', start: 0, end: 0, id: 0}
818  call assert_equal([expect3], prop_list(3))
819  let expect4 = #{type_bufnr: 0, col: 1, length: 4, type: 'comment', start: 0, end: 1, id: 0}
820  call assert_equal([expect4], prop_list(4))
821  4del
822  let expect3.end = 1
823  call assert_equal([expect3], prop_list(3))
824  call assert_equal([expect_short], prop_list(4))
825  bwipe!
826
827  " Test appending a line below the multi-line text prop start.
828  call Setup_three_line_prop()
829  let expect2 = #{type_bufnr: 0, col: 4, length: 4, type: 'comment', start: 1, end: 0, id: 0}
830  call assert_equal([expect2], prop_list(2))
831  call append(2, "new line")
832  call assert_equal([expect2], prop_list(2))
833  let expect3 = #{type_bufnr: 0, col: 1, length: 9, type: 'comment', start: 0, end: 0, id: 0}
834  call assert_equal([expect3], prop_list(3))
835  bwipe!
836
837  call prop_type_delete('comment')
838endfunc
839
840func Test_prop_line2byte()
841  call prop_type_add('comment', {'highlight': 'Directory'})
842  new
843  call setline(1, ['line1', 'second line', ''])
844  set ff=unix
845  call assert_equal(19, line2byte(3))
846  call prop_add(1, 1, {'end_col': 3, 'type': 'comment'})
847  call assert_equal(19, line2byte(3))
848  bwipe!
849
850  new
851  setlocal ff=unix
852  call setline(1, range(500))
853  call assert_equal(1491, line2byte(401))
854  call prop_add(2, 1, {'type': 'comment'})
855  call prop_add(222, 1, {'type': 'comment'})
856  call assert_equal(1491, line2byte(401))
857  call prop_remove({'type': 'comment'})
858  call assert_equal(1491, line2byte(401))
859  bwipe!
860
861  new
862  setlocal ff=unix
863  call setline(1, range(520))
864  call assert_equal(1491, line2byte(401))
865  call prop_add(2, 1, {'type': 'comment'})
866  call assert_equal(1491, line2byte(401))
867  2delete
868  call assert_equal(1489, line2byte(400))
869  bwipe!
870
871  call prop_type_delete('comment')
872endfunc
873
874func Test_prop_byte2line()
875  new
876  set ff=unix
877  call setline(1, ['one one', 'two two', 'three three', 'four four', 'five'])
878  call assert_equal(4, byte2line(line2byte(4)))
879  call assert_equal(5, byte2line(line2byte(5)))
880
881  call prop_type_add('prop', {'highlight': 'Directory'})
882  call prop_add(3, 1, {'length': 5, 'type': 'prop'})
883  call assert_equal(4, byte2line(line2byte(4)))
884  call assert_equal(5, byte2line(line2byte(5)))
885
886  bwipe!
887  call prop_type_delete('prop')
888endfunc
889
890func Test_prop_goto_byte()
891  new
892  call setline(1, '')
893  call setline(2, 'two three')
894  call setline(3, '')
895  call setline(4, 'four five')
896
897  call prop_type_add('testprop', {'highlight': 'Directory'})
898  call search('^two')
899  call prop_add(line('.'), col('.'), {
900        \ 'length': len('two'),
901        \ 'type':   'testprop'
902        \ })
903
904  call search('two \zsthree')
905  let expected_pos = line2byte(line('.')) + col('.') - 1
906  exe expected_pos .. 'goto'
907  let actual_pos = line2byte(line('.')) + col('.') - 1
908  eval actual_pos->assert_equal(expected_pos)
909
910  call search('four \zsfive')
911  let expected_pos = line2byte(line('.')) + col('.') - 1
912  exe expected_pos .. 'goto'
913  let actual_pos = line2byte(line('.')) + col('.') - 1
914  eval actual_pos->assert_equal(expected_pos)
915
916  call prop_type_delete('testprop')
917  bwipe!
918endfunc
919
920func Test_prop_undo()
921  new
922  call prop_type_add('comment', {'highlight': 'Directory'})
923  call setline(1, ['oneone', 'twotwo', 'three'])
924  " Set 'undolevels' to break changes into undo-able pieces.
925  set ul&
926
927  call prop_add(1, 3, {'end_col': 5, 'type': 'comment'})
928  let expected = [#{type_bufnr: 0, col: 3, length: 2, id: 0, type: 'comment', start: 1, end: 1}]
929  call assert_equal(expected, prop_list(1))
930
931  " Insert a character, then undo.
932  exe "normal 0lllix\<Esc>"
933  set ul&
934  let expected[0].length = 3
935  call assert_equal(expected, prop_list(1))
936  undo
937  let expected[0].length = 2
938  call assert_equal(expected, prop_list(1))
939
940  " Delete a character, then undo
941  exe "normal 0lllx"
942  set ul&
943  let expected[0].length = 1
944  call assert_equal(expected, prop_list(1))
945  undo
946  let expected[0].length = 2
947  call assert_equal(expected, prop_list(1))
948
949  " Delete the line, then undo
950  1d
951  set ul&
952  call assert_equal([], prop_list(1))
953  undo
954  call assert_equal(expected, prop_list(1))
955
956  " Insert a character, delete two characters, then undo with "U"
957  exe "normal 0lllix\<Esc>"
958  set ul&
959  let expected[0].length = 3
960  call assert_equal(expected, prop_list(1))
961  exe "normal 0lllxx"
962  set ul&
963  let expected[0].length = 1
964  call assert_equal(expected, prop_list(1))
965  normal U
966  let expected[0].length = 2
967  call assert_equal(expected, prop_list(1))
968
969  " substitute a word, then undo
970  call setline(1, 'the number 123 is highlighted.')
971  call prop_add(1, 12, {'length': 3, 'type': 'comment'})
972  let expected = [#{type_bufnr: 0, col: 12, length: 3, id: 0, type: 'comment', start: 1, end: 1} ]
973  call assert_equal(expected, prop_list(1))
974  set ul&
975  1s/number/foo
976  let expected[0].col = 9
977  call assert_equal(expected, prop_list(1))
978  undo
979  let expected[0].col = 12
980  call assert_equal(expected, prop_list(1))
981  call prop_clear(1)
982
983  " substitute with backslash
984  call setline(1, 'the number 123 is highlighted.')
985  call prop_add(1, 12, {'length': 3, 'type': 'comment'})
986  let expected = [#{type_bufnr: 0, col: 12, length: 3, id: 0, type: 'comment', start: 1, end: 1} ]
987  call assert_equal(expected, prop_list(1))
988  1s/the/\The
989  call assert_equal(expected, prop_list(1))
990  1s/^/\\
991  let expected[0].col += 1
992  call assert_equal(expected, prop_list(1))
993  1s/^/\~
994  let expected[0].col += 1
995  call assert_equal(expected, prop_list(1))
996  1s/123/12\\3
997  let expected[0].length += 1
998  call assert_equal(expected, prop_list(1))
999  call prop_clear(1)
1000
1001  bwipe!
1002  call prop_type_delete('comment')
1003endfunc
1004
1005func Test_prop_delete_text()
1006  new
1007  call prop_type_add('comment', {'highlight': 'Directory'})
1008  call setline(1, ['oneone', 'twotwo', 'three'])
1009
1010  " zero length property
1011  call prop_add(1, 3, {'type': 'comment'})
1012  let expected = [#{type_bufnr: 0, col: 3, length: 0, id: 0, type: 'comment', start: 1, end: 1} ]
1013  call assert_equal(expected, prop_list(1))
1014
1015  " delete one char moves the property
1016  normal! x
1017  let expected = [#{type_bufnr: 0, col: 2, length: 0, id: 0, type: 'comment', start: 1, end: 1} ]
1018  call assert_equal(expected, prop_list(1))
1019
1020  " delete char of the property has no effect
1021  normal! lx
1022  let expected = [#{type_bufnr: 0, col: 2, length: 0, id: 0, type: 'comment', start: 1, end: 1} ]
1023  call assert_equal(expected, prop_list(1))
1024
1025  " delete more chars moves property to first column, is not deleted
1026  normal! 0xxxx
1027  let expected = [#{type_bufnr: 0, col: 1, length: 0, id: 0, type: 'comment', start: 1, end: 1} ]
1028  call assert_equal(expected, prop_list(1))
1029
1030  bwipe!
1031  call prop_type_delete('comment')
1032endfunc
1033
1034" screenshot test with textprop highlighting
1035func Test_textprop_screenshot_various()
1036  CheckScreendump
1037  " The Vim running in the terminal needs to use utf-8.
1038  if g:orig_encoding != 'utf-8'
1039    throw 'Skipped: not using utf-8'
1040  endif
1041  call writefile([
1042	\ "call setline(1, ["
1043	\	.. "'One two',"
1044	\	.. "'Numbér 123 änd thœn 4¾7.',"
1045	\	.. "'--aa--bb--cc--dd--',"
1046	\	.. "'// comment with error in it',"
1047	\	.. "'first line',"
1048	\	.. "'  second line  ',"
1049	\	.. "'third line',"
1050	\	.. "'   fourth line',"
1051	\	.. "])",
1052	\ "hi NumberProp ctermfg=blue",
1053	\ "hi LongProp ctermbg=yellow",
1054	\ "hi BackgroundProp ctermbg=lightgrey",
1055	\ "hi UnderlineProp cterm=underline",
1056	\ "call prop_type_add('number', {'highlight': 'NumberProp'})",
1057	\ "call prop_type_add('long', {'highlight': 'NumberProp'})",
1058	\ "call prop_type_change('long', {'highlight': 'LongProp'})",
1059	\ "call prop_type_add('start', {'highlight': 'NumberProp', 'start_incl': 1})",
1060	\ "call prop_type_add('end', {'highlight': 'NumberProp', 'end_incl': 1})",
1061	\ "call prop_type_add('both', {'highlight': 'NumberProp', 'start_incl': 1, 'end_incl': 1})",
1062	\ "call prop_type_add('background', {'highlight': 'BackgroundProp', 'combine': 0})",
1063	\ "call prop_type_add('backgroundcomb', {'highlight': 'NumberProp', 'combine': 1})",
1064	\ "eval 'backgroundcomb'->prop_type_change({'highlight': 'BackgroundProp'})",
1065	\ "call prop_type_add('error', {'highlight': 'UnderlineProp'})",
1066	\ "call prop_add(1, 4, {'end_lnum': 3, 'end_col': 3, 'type': 'long'})",
1067	\ "call prop_add(2, 9, {'length': 3, 'type': 'number'})",
1068	\ "call prop_add(2, 24, {'length': 4, 'type': 'number'})",
1069	\ "call prop_add(3, 3, {'length': 2, 'type': 'number'})",
1070	\ "call prop_add(3, 7, {'length': 2, 'type': 'start'})",
1071	\ "call prop_add(3, 11, {'length': 2, 'type': 'end'})",
1072	\ "call prop_add(3, 15, {'length': 2, 'type': 'both'})",
1073	\ "call prop_add(4, 6, {'length': 3, 'type': 'background'})",
1074	\ "call prop_add(4, 12, {'length': 10, 'type': 'backgroundcomb'})",
1075	\ "call prop_add(4, 17, {'length': 5, 'type': 'error'})",
1076	\ "call prop_add(5, 7, {'length': 4, 'type': 'long'})",
1077	\ "call prop_add(6, 1, {'length': 8, 'type': 'long'})",
1078	\ "call prop_add(8, 1, {'length': 1, 'type': 'long'})",
1079	\ "call prop_add(8, 11, {'length': 4, 'type': 'long'})",
1080	\ "set number cursorline",
1081	\ "hi clear SpellBad",
1082	\ "set spell",
1083	\ "syn match Comment '//.*'",
1084	\ "hi Comment ctermfg=green",
1085	\ "normal 3G0llix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>lllix\<Esc>",
1086	\ "normal 3G0lli\<BS>\<Esc>",
1087	\ "normal 6G0i\<BS>\<Esc>",
1088	\ "normal 3J",
1089	\ "normal 3G",
1090	\], 'XtestProp')
1091  let buf = RunVimInTerminal('-S XtestProp', {'rows': 8})
1092  call VerifyScreenDump(buf, 'Test_textprop_01', {})
1093
1094  " clean up
1095  call StopVimInTerminal(buf)
1096  call delete('XtestProp')
1097endfunc
1098
1099func RunTestVisualBlock(width, dump)
1100  call writefile([
1101	\ "call setline(1, ["
1102	\	.. "'xxxxxxxxx 123 x',"
1103	\	.. "'xxxxxxxx 123 x',"
1104	\	.. "'xxxxxxx 123 x',"
1105	\	.. "'xxxxxx 123 x',"
1106	\	.. "'xxxxx 123 x',"
1107	\	.. "'xxxx 123 xx',"
1108	\	.. "'xxx 123 xxx',"
1109	\	.. "'xx 123 xxxx',"
1110	\	.. "'x 123 xxxxx',"
1111	\	.. "' 123 xxxxxx',"
1112	\	.. "])",
1113	\ "hi SearchProp ctermbg=yellow",
1114	\ "call prop_type_add('search', {'highlight': 'SearchProp'})",
1115	\ "call prop_add(1, 11, {'length': 3, 'type': 'search'})",
1116	\ "call prop_add(2, 10, {'length': 3, 'type': 'search'})",
1117	\ "call prop_add(3, 9, {'length': 3, 'type': 'search'})",
1118	\ "call prop_add(4, 8, {'length': 3, 'type': 'search'})",
1119	\ "call prop_add(5, 7, {'length': 3, 'type': 'search'})",
1120	\ "call prop_add(6, 6, {'length': 3, 'type': 'search'})",
1121	\ "call prop_add(7, 5, {'length': 3, 'type': 'search'})",
1122	\ "call prop_add(8, 4, {'length': 3, 'type': 'search'})",
1123	\ "call prop_add(9, 3, {'length': 3, 'type': 'search'})",
1124	\ "call prop_add(10, 2, {'length': 3, 'type': 'search'})",
1125	\ "normal 1G6|\<C-V>" .. repeat('l', a:width - 1) .. "10jx",
1126	\], 'XtestPropVis')
1127  let buf = RunVimInTerminal('-S XtestPropVis', {'rows': 12})
1128  call VerifyScreenDump(buf, 'Test_textprop_vis_' .. a:dump, {})
1129
1130  " clean up
1131  call StopVimInTerminal(buf)
1132  call delete('XtestPropVis')
1133endfunc
1134
1135" screenshot test with Visual block mode operations
1136func Test_textprop_screenshot_visual()
1137  CheckScreendump
1138
1139  " Delete two columns while text props are three chars wide.
1140  call RunTestVisualBlock(2, '01')
1141
1142  " Same, but delete four columns
1143  call RunTestVisualBlock(4, '02')
1144endfunc
1145
1146func Test_textprop_after_tab()
1147  CheckScreendump
1148
1149  let lines =<< trim END
1150       call setline(1, [
1151             \ "\txxx",
1152             \ "x\txxx",
1153             \ ])
1154       hi SearchProp ctermbg=yellow
1155       call prop_type_add('search', {'highlight': 'SearchProp'})
1156       call prop_add(1, 2, {'length': 3, 'type': 'search'})
1157       call prop_add(2, 3, {'length': 3, 'type': 'search'})
1158  END
1159  call writefile(lines, 'XtestPropTab')
1160  let buf = RunVimInTerminal('-S XtestPropTab', {'rows': 6})
1161  call VerifyScreenDump(buf, 'Test_textprop_tab', {})
1162
1163  " clean up
1164  call StopVimInTerminal(buf)
1165  call delete('XtestPropTab')
1166endfunc
1167
1168func Test_textprop_nowrap_scrolled()
1169  CheckScreendump
1170
1171  let lines =<< trim END
1172       vim9script
1173       set nowrap
1174       setline(1, 'The number 123 is smaller than 4567.' .. repeat('X', &columns))
1175       prop_type_add('number', {'highlight': 'ErrorMsg'})
1176       prop_add(1, 12, {'length': 3, 'type': 'number'})
1177       prop_add(1, 32, {'length': 4, 'type': 'number'})
1178       feedkeys('gg20zl', 'nxt')
1179  END
1180  call writefile(lines, 'XtestNowrap')
1181  let buf = RunVimInTerminal('-S XtestNowrap', {'rows': 6})
1182  call VerifyScreenDump(buf, 'Test_textprop_nowrap_01', {})
1183
1184  call term_sendkeys(buf, "$")
1185  call VerifyScreenDump(buf, 'Test_textprop_nowrap_02', {})
1186
1187  " clean up
1188  call StopVimInTerminal(buf)
1189  call delete('XtestNowrap')
1190endfunc
1191
1192func Test_textprop_with_syntax()
1193  CheckScreendump
1194
1195  let lines =<< trim END
1196       call setline(1, [
1197             \ "(abc)",
1198             \ ])
1199       syn match csParens "[()]" display
1200       hi! link csParens MatchParen
1201
1202       call prop_type_add('TPTitle', #{ highlight: 'Title' })
1203       call prop_add(1, 2, #{type: 'TPTitle', end_col: 5})
1204  END
1205  call writefile(lines, 'XtestPropSyn')
1206  let buf = RunVimInTerminal('-S XtestPropSyn', {'rows': 6})
1207  call VerifyScreenDump(buf, 'Test_textprop_syn_1', {})
1208
1209  " clean up
1210  call StopVimInTerminal(buf)
1211  call delete('XtestPropSyn')
1212endfunc
1213
1214" Adding a text property to a new buffer should not fail
1215func Test_textprop_empty_buffer()
1216  call prop_type_add('comment', {'highlight': 'Search'})
1217  new
1218  call prop_add(1, 1, {'type': 'comment'})
1219  close
1220  call prop_type_delete('comment')
1221endfunc
1222
1223" Adding a text property with invalid highlight should be ignored.
1224func Test_textprop_invalid_highlight()
1225  call assert_fails("call prop_type_add('dni', {'highlight': 'DoesNotExist'})", 'E970:')
1226  new
1227  call setline(1, ['asdf','asdf'])
1228  call prop_add(1, 1, {'length': 4, 'type': 'dni'})
1229  redraw
1230  bwipe!
1231  call prop_type_delete('dni')
1232endfunc
1233
1234" Adding a text property to an empty buffer and then editing another
1235func Test_textprop_empty_buffer_next()
1236  call prop_type_add("xxx", {})
1237  call prop_add(1, 1, {"type": "xxx"})
1238  next X
1239  call prop_type_delete('xxx')
1240endfunc
1241
1242func Test_textprop_remove_from_buf()
1243  new
1244  let buf = bufnr('')
1245  call prop_type_add('one', {'bufnr': buf})
1246  call prop_add(1, 1, {'type': 'one', 'id': 234})
1247  file x
1248  edit y
1249  call prop_remove({'id': 234, 'bufnr': buf}, 1)
1250  call prop_type_delete('one', {'bufnr': buf})
1251  bwipe! x
1252  close
1253endfunc
1254
1255func Test_textprop_in_unloaded_buf()
1256  edit Xaaa
1257  call setline(1, 'aaa')
1258  write
1259  edit Xbbb
1260  call setline(1, 'bbb')
1261  write
1262  let bnr = bufnr('')
1263  edit Xaaa
1264
1265  call prop_type_add('ErrorMsg', #{highlight:'ErrorMsg'})
1266  call assert_fails("call prop_add(1, 1, #{end_lnum: 1, endcol: 2, type: 'ErrorMsg', bufnr: bnr})", 'E275:')
1267  exe 'buf ' .. bnr
1268  call assert_equal('bbb', getline(1))
1269  call assert_equal(0, prop_list(1)->len())
1270
1271  bwipe! Xaaa
1272  bwipe! Xbbb
1273  cal delete('Xaaa')
1274  cal delete('Xbbb')
1275endfunc
1276
1277func Test_proptype_substitute2()
1278  new
1279  " text_prop.vim
1280  call setline(1, [
1281        \ 'The   num  123 is smaller than 4567.',
1282        \ '123 The number 123 is smaller than 4567.',
1283        \ '123 The number 123 is smaller than 4567.'])
1284
1285  call prop_type_add('number', {'highlight': 'ErrorMsg'})
1286
1287  call prop_add(1, 12, {'length': 3, 'type': 'number'})
1288  call prop_add(2, 1, {'length': 3, 'type': 'number'})
1289  call prop_add(3, 36, {'length': 4, 'type': 'number'})
1290  set ul&
1291  let expected = [
1292        \ #{type_bufnr: 0, id: 0, col: 13, end: 1, type: 'number', length: 3, start: 1},
1293        \ #{type_bufnr: 0, id: 0, col: 1,  end: 1, type: 'number', length: 3, start: 1},
1294        \ #{type_bufnr: 0, id: 0, col: 50, end: 1, type: 'number', length: 4, start: 1}]
1295
1296  " TODO
1297  return
1298  " Add some text in between
1299  %s/\s\+/   /g
1300  call assert_equal(expected, prop_list(1) + prop_list(2) + prop_list(3))
1301
1302  " remove some text
1303  :1s/[a-z]\{3\}//g
1304  let expected = [{'id': 0, 'col': 10, 'end': 1, 'type': 'number', 'length': 3, 'start': 1}]
1305  call assert_equal(expected, prop_list(1))
1306  bwipe!
1307endfunc
1308
1309" This was causing property corruption.
1310func Test_proptype_substitute3()
1311  new
1312  call setline(1, ['abcxxx', 'def'])
1313  call prop_type_add("test", {"highlight": "Search"})
1314  call prop_add(1, 2, {"end_lnum": 2, "end_col": 2, "type": "test"})
1315  %s/x\+$//
1316  redraw
1317
1318  call prop_type_delete('test')
1319  bwipe!
1320endfunc
1321
1322func SaveOptions()
1323  let d = #{tabstop: &tabstop,
1324	  \ softtabstop: &softtabstop,
1325	  \ shiftwidth: &shiftwidth,
1326	  \ expandtab: &expandtab,
1327	  \ foldmethod: '"' .. &foldmethod .. '"',
1328	  \ }
1329  return d
1330endfunc
1331
1332func RestoreOptions(dict)
1333  for name in keys(a:dict)
1334    exe 'let &' .. name .. ' = ' .. a:dict[name]
1335  endfor
1336endfunc
1337
1338func Test_textprop_noexpandtab()
1339  new
1340  let save_dict = SaveOptions()
1341
1342  set tabstop=8
1343  set softtabstop=4
1344  set shiftwidth=4
1345  set noexpandtab
1346  set foldmethod=marker
1347
1348  call feedkeys("\<esc>\<esc>0Ca\<cr>\<esc>\<up>", "tx")
1349  call prop_type_add('test', {'highlight': 'ErrorMsg'})
1350  call prop_add(1, 1, {'end_col': 2, 'type': 'test'})
1351  call feedkeys("0i\<tab>", "tx")
1352  call prop_remove({'type': 'test'})
1353  call prop_add(1, 2, {'end_col': 3, 'type': 'test'})
1354  call feedkeys("A\<left>\<tab>", "tx")
1355  call prop_remove({'type': 'test'})
1356  try
1357    " It is correct that this does not pass
1358    call prop_add(1, 6, {'end_col': 7, 'type': 'test'})
1359    " Has already collapsed here, start_col:6 does not result in an error
1360    call feedkeys("A\<left>\<tab>", "tx")
1361  catch /^Vim\%((\a\+)\)\=:E964/
1362  endtry
1363  call prop_remove({'type': 'test'})
1364  call prop_type_delete('test')
1365
1366  call RestoreOptions(save_dict)
1367  bwipe!
1368endfunc
1369
1370func Test_textprop_noexpandtab_redraw()
1371  new
1372  let save_dict = SaveOptions()
1373
1374  set tabstop=8
1375  set softtabstop=4
1376  set shiftwidth=4
1377  set noexpandtab
1378  set foldmethod=marker
1379
1380  call feedkeys("\<esc>\<esc>0Ca\<cr>\<space>\<esc>\<up>", "tx")
1381  call prop_type_add('test', {'highlight': 'ErrorMsg'})
1382  call prop_add(1, 1, {'end_col': 2, 'type': 'test'})
1383  call feedkeys("0i\<tab>", "tx")
1384  " Internally broken at the next line
1385  call feedkeys("A\<left>\<tab>", "tx")
1386  redraw
1387  " Index calculation failed internally on next line
1388  call prop_add(1, 1, {'end_col': 2, 'type': 'test'})
1389  call prop_remove({'type': 'test', 'all': v:true})
1390  call prop_type_delete('test')
1391  call prop_type_delete('test')
1392
1393  call RestoreOptions(save_dict)
1394  bwipe!
1395endfunc
1396
1397func Test_textprop_ins_str()
1398  new
1399  call setline(1, 'just some text')
1400  call prop_type_add('test', {'highlight': 'ErrorMsg'})
1401  call prop_add(1, 1, {'end_col': 2, 'type': 'test'})
1402  call assert_equal([#{type_bufnr: 0, id: 0, col: 1, end: 1, type: 'test', length: 1, start: 1}], prop_list(1))
1403
1404  call feedkeys("foi\<F8>\<Esc>", "tx")
1405  call assert_equal('just s<F8>ome text', getline(1))
1406  call assert_equal([#{type_bufnr: 0, id: 0, col: 1, end: 1, type: 'test', length: 1, start: 1}], prop_list(1))
1407
1408  bwipe!
1409  call prop_remove({'type': 'test'})
1410  call prop_type_delete('test')
1411endfunc
1412
1413func Test_find_prop_later_in_line()
1414  new
1415  call prop_type_add('test', {'highlight': 'ErrorMsg'})
1416  call setline(1, 'just some text')
1417  call prop_add(1, 1, {'length': 4, 'type': 'test'})
1418  call prop_add(1, 10, {'length': 3, 'type': 'test'})
1419
1420  call assert_equal(
1421        \ #{type_bufnr: 0, id: 0, lnum: 1, col: 10, end: 1, type: 'test', length: 3, start: 1},
1422        \ prop_find(#{type: 'test', lnum: 1, col: 6}))
1423
1424  bwipe!
1425  call prop_type_delete('test')
1426endfunc
1427
1428func Test_find_zerowidth_prop_sol()
1429  new
1430  call prop_type_add('test', {'highlight': 'ErrorMsg'})
1431  call setline(1, 'just some text')
1432  call prop_add(1, 1, {'length': 0, 'type': 'test'})
1433
1434  call assert_equal(
1435        \ #{type_bufnr: 0, id: 0, lnum: 1, col: 1, end: 1, type: 'test', length: 0, start: 1},
1436        \ prop_find(#{type: 'test', lnum: 1}))
1437
1438  bwipe!
1439  call prop_type_delete('test')
1440endfunc
1441
1442" Test for passing invalid arguments to prop_xxx() functions
1443func Test_prop_func_invalid_args()
1444  call assert_fails('call prop_clear(1, 2, [])', 'E715:')
1445  call assert_fails('call prop_clear(-1, 2)', 'E16:')
1446  call assert_fails('call prop_find(test_null_dict())', 'E715:')
1447  call assert_fails('call prop_find({"bufnr" : []})', 'E730:')
1448  call assert_fails('call prop_find({})', 'E968:')
1449  call assert_fails('call prop_find({}, "x")', 'E474:')
1450  call assert_fails('call prop_find({"lnum" : -2})', 'E16:')
1451  call assert_fails('call prop_list(1, [])', 'E715:')
1452  call assert_fails('call prop_list(-1, {})', 'E16:')
1453  call assert_fails('call prop_remove([])', 'E474:')
1454  call assert_fails('call prop_remove({}, -2)', 'E16:')
1455  call assert_fails('call prop_remove({})', 'E968:')
1456  call assert_fails('call prop_type_add([], {})', 'E730:')
1457  call assert_fails("call prop_type_change('long', {'xyz' : 10})", 'E971:')
1458  call assert_fails("call prop_type_delete([])", 'E730:')
1459  call assert_fails("call prop_type_delete('xyz', [])", 'E715:')
1460  call assert_fails("call prop_type_get([])", 'E730:')
1461  call assert_fails("call prop_type_get('', [])", 'E474:')
1462  call assert_fails("call prop_type_list([])", 'E715:')
1463  call assert_fails("call prop_type_add('yyy', 'not_a_dict')", 'E715:')
1464  call assert_fails("call prop_add(1, 5, {'type':'missing_type', 'length':1})", 'E971:')
1465  call assert_fails("call prop_add(1, 5, {'type': ''})", 'E971:')
1466  call assert_fails('call prop_add(1, 1, 0)', 'E715:')
1467
1468  new
1469  call setline(1, ['first', 'second'])
1470  call prop_type_add('xxx', {})
1471
1472  call assert_fails("call prop_type_add('xxx', {})", 'E969:')
1473  call assert_fails("call prop_add(2, 0, {'type': 'xxx'})", 'E964:')
1474  call assert_fails("call prop_add(2, 3, {'type': 'xxx', 'end_lnum':1})", 'E475:')
1475  call assert_fails("call prop_add(2, 3, {'type': 'xxx', 'end_lnum':3})", 'E966:')
1476  call assert_fails("call prop_add(2, 3, {'type': 'xxx', 'length':-1})", 'E475:')
1477  call assert_fails("call prop_add(2, 3, {'type': 'xxx', 'end_col':0})", 'E475:')
1478  call assert_fails("call prop_add(2, 3, {'length':1})", 'E965:')
1479
1480  call prop_type_delete('xxx')
1481  bwipe!
1482endfunc
1483
1484func Test_prop_split_join()
1485  new
1486  call prop_type_add('test', {'highlight': 'ErrorMsg'})
1487  call setline(1, 'just some text')
1488  call prop_add(1, 6, {'length': 4, 'type': 'test'})
1489
1490  " Split in middle of "some"
1491  execute "normal! 8|i\<CR>"
1492  call assert_equal(
1493        \ [#{type_bufnr: 0, id: 0, col: 6, end: 0, type: 'test', length: 2, start: 1}],
1494        \ prop_list(1))
1495  call assert_equal(
1496        \ [#{type_bufnr: 0, id: 0, col: 1, end: 1, type: 'test', length: 2, start: 0}],
1497        \ prop_list(2))
1498
1499  " Join the two lines back together
1500  normal! 1GJ
1501  call assert_equal([#{type_bufnr: 0, id: 0, col: 6, end: 1, type: 'test', length: 5, start: 1}], prop_list(1))
1502
1503  bwipe!
1504  call prop_type_delete('test')
1505endfunc
1506
1507func Test_prop_increment_decrement()
1508  new
1509  call prop_type_add('test', {'highlight': 'ErrorMsg'})
1510  call setline(1, 'its 998 times')
1511  call prop_add(1, 5, {'length': 3, 'type': 'test'})
1512
1513  exe "normal! 0f9\<C-A>"
1514  eval getline(1)->assert_equal('its 999 times')
1515  eval prop_list(1)->assert_equal([
1516        \ #{type_bufnr: 0, id: 0, col: 5, end: 1, type: 'test', length: 3, start: 1}])
1517
1518  exe "normal! 0f9\<C-A>"
1519  eval getline(1)->assert_equal('its 1000 times')
1520  eval prop_list(1)->assert_equal([
1521        \ #{type_bufnr: 0, id: 0, col: 5, end: 1, type: 'test', length: 4, start: 1}])
1522
1523  bwipe!
1524  call prop_type_delete('test')
1525endfunc
1526
1527func Test_prop_block_insert()
1528  new
1529  call prop_type_add('test', {'highlight': 'ErrorMsg'})
1530  call setline(1, ['one ', 'two '])
1531  call prop_add(1, 1, {'length': 3, 'type': 'test'})
1532  call prop_add(2, 1, {'length': 3, 'type': 'test'})
1533
1534  " insert "xx" in the first column of both lines
1535  exe "normal! gg0\<C-V>jIxx\<Esc>"
1536  eval getline(1, 2)->assert_equal(['xxone ', 'xxtwo '])
1537  let expected = [#{type_bufnr: 0, id: 0, col: 3, end: 1, type: 'test', length: 3, start: 1}]
1538  eval prop_list(1)->assert_equal(expected)
1539  eval prop_list(2)->assert_equal(expected)
1540
1541  " insert "yy" inside the text props to make them longer
1542  exe "normal! gg03l\<C-V>jIyy\<Esc>"
1543  eval getline(1, 2)->assert_equal(['xxoyyne ', 'xxtyywo '])
1544  let expected[0].length = 5
1545  eval prop_list(1)->assert_equal(expected)
1546  eval prop_list(2)->assert_equal(expected)
1547
1548  " insert "zz" after the text props, text props don't change
1549  exe "normal! gg07l\<C-V>jIzz\<Esc>"
1550  eval getline(1, 2)->assert_equal(['xxoyynezz ', 'xxtyywozz '])
1551  eval prop_list(1)->assert_equal(expected)
1552  eval prop_list(2)->assert_equal(expected)
1553
1554  bwipe!
1555  call prop_type_delete('test')
1556endfunc
1557
1558" this was causing an ml_get error because w_botline was wrong
1559func Test_prop_one_line_window()
1560  enew
1561  call range(2)->setline(1)
1562  call prop_type_add('testprop', {})
1563  call prop_add(1, 1, {'type': 'testprop'})
1564  call popup_create('popup', {'textprop': 'testprop'})
1565  $
1566  new
1567  wincmd _
1568  call feedkeys("\r", 'xt')
1569  redraw
1570
1571  call popup_clear()
1572  call prop_type_delete('testprop')
1573  close
1574  bwipe!
1575endfunc
1576
1577" This was calling ml_append_int() and copy a text property from a previous
1578" line at the wrong moment.  Exact text length matters.
1579def Test_prop_splits_data_block()
1580  new
1581  var lines: list<string> = [repeat('x', 35)]->repeat(41)
1582			+ [repeat('!', 35)]
1583			+ [repeat('x', 35)]->repeat(56)
1584  lines->setline(1)
1585  prop_type_add('someprop', {highlight: 'ErrorMsg'})
1586  prop_add(1, 27, {end_lnum: 1, end_col: 70, type: 'someprop'})
1587  prop_remove({type: 'someprop'}, 1)
1588  prop_add(35, 22, {end_lnum: 43, end_col: 43, type: 'someprop'})
1589  prop_remove({type: 'someprop'}, 35, 43)
1590  assert_equal([], prop_list(42))
1591
1592  bwipe!
1593  prop_type_delete('someprop')
1594enddef
1595
1596" This was calling ml_delete_int() and try to change text properties.
1597def Test_prop_add_delete_line()
1598  new
1599  var a = 10
1600  var b = 20
1601  repeat([''], a)->append('$')
1602  prop_type_add('Test', {highlight: 'ErrorMsg'})
1603  for lnum in range(1, a)
1604    for col in range(1, b)
1605      prop_add(1, 1, {end_lnum: lnum, end_col: col, type: 'Test'})
1606    endfor
1607  endfor
1608
1609  # check deleting lines is OK
1610  :5del
1611  :1del
1612  :$del
1613
1614  prop_type_delete('Test')
1615  bwipe!
1616enddef
1617
1618func Test_prop_in_linebreak()
1619  CheckRunVimInTerminal
1620
1621  let lines =<< trim END
1622    set breakindent linebreak breakat+=]
1623    call printf('%s]%s', repeat('x', 50), repeat('x', 70))->setline(1)
1624    call prop_type_add('test', #{highlight: 'ErrorMsg'})
1625    call prop_add(1, 51, #{length: 1, type: 'test'})
1626  END
1627  call writefile(lines, 'XscriptPropLinebreak')
1628  let buf = RunVimInTerminal('-S XscriptPropLinebreak', #{rows: 10})
1629  call TermWait(buf)
1630  call VerifyScreenDump(buf, 'Test_prop_linebreak', {})
1631
1632  call StopVimInTerminal(buf)
1633  call delete('XscriptPropLinebreak')
1634endfunc
1635
1636func Test_prop_after_tab()
1637  CheckRunVimInTerminal
1638
1639  let lines =<< trim END
1640    set breakindent linebreak breakat+=]
1641    call setline(1, "\t[xxx]")
1642    call prop_type_add('test', #{highlight: 'ErrorMsg'})
1643    call prop_add(1, 2, #{length: 1, type: 'test'})
1644  END
1645  call writefile(lines, 'XscriptPropAfterTab')
1646  let buf = RunVimInTerminal('-S XscriptPropAfterTab', #{rows: 10})
1647  call TermWait(buf)
1648  call VerifyScreenDump(buf, 'Test_prop_after_tab', {})
1649
1650  call StopVimInTerminal(buf)
1651  call delete('XscriptPropAfterTab')
1652endfunc
1653
1654func Test_prop_after_linebreak()
1655  CheckRunVimInTerminal
1656
1657  let lines =<< trim END
1658      set linebreak wrap
1659      call printf('%s+(%s)', 'x'->repeat(&columns / 2), 'x'->repeat(&columns / 2))->setline(1)
1660      call prop_type_add('test', #{highlight: 'ErrorMsg'})
1661      call prop_add(1, (&columns / 2) + 2, #{length: 1, type: 'test'})
1662  END
1663  call writefile(lines, 'XscriptPropAfterLinebreak')
1664  let buf = RunVimInTerminal('-S XscriptPropAfterLinebreak', #{rows: 10})
1665  call TermWait(buf)
1666  call VerifyScreenDump(buf, 'Test_prop_after_linebreak', {})
1667
1668  call StopVimInTerminal(buf)
1669  call delete('XscriptPropAfterLinebreak')
1670endfunc
1671
1672" Buffer number of 0 should be ignored, as if the parameter wasn't passed.
1673def Test_prop_bufnr_zero()
1674  new
1675  try
1676    var bufnr = bufnr('')
1677    setline(1, 'hello')
1678    prop_type_add('bufnr-global', {highlight: 'ErrorMsg'})
1679    prop_type_add('bufnr-buffer', {highlight: 'StatusLine', bufnr: bufnr})
1680
1681    prop_add(1, 1, {type: 'bufnr-global', length: 1})
1682    prop_add(1, 2, {type: 'bufnr-buffer', length: 1})
1683
1684    var list = prop_list(1)
1685    assert_equal([
1686       {id: 0, col: 1, type_bufnr: 0,         end: 1, type: 'bufnr-global', length: 1, start: 1},
1687       {id: 0, col: 2, type_bufnr: bufnr, end: 1, type: 'bufnr-buffer', length: 1, start: 1},
1688    ], list)
1689
1690    assert_equal(
1691      {highlight: 'ErrorMsg', end_incl: 0, start_incl: 0, priority: 0, combine: 1},
1692      prop_type_get('bufnr-global', {bufnr: list[0].type_bufnr}))
1693
1694    assert_equal(
1695      {highlight: 'StatusLine', end_incl: 0, start_incl: 0, priority: 0, bufnr: bufnr, combine: 1},
1696      prop_type_get('bufnr-buffer', {bufnr: list[1].type_bufnr}))
1697  finally
1698    bwipe!
1699    prop_type_delete('bufnr-global')
1700  endtry
1701enddef
1702
1703" Tests for the prop_list() function
1704func Test_prop_list()
1705  let lines =<< trim END
1706    new
1707    call AddPropTypes()
1708    call setline(1, repeat([repeat('a', 60)], 10))
1709    call prop_add(1, 4, {'type': 'one', 'id': 5, 'end_col': 6})
1710    call prop_add(1, 5, {'type': 'two', 'id': 10, 'end_col': 7})
1711    call prop_add(3, 12, {'type': 'one', 'id': 20, 'end_col': 14})
1712    call prop_add(3, 13, {'type': 'two', 'id': 10, 'end_col': 15})
1713    call prop_add(5, 20, {'type': 'one', 'id': 10, 'end_col': 22})
1714    call prop_add(5, 21, {'type': 'two', 'id': 20, 'end_col': 23})
1715    call assert_equal([
1716          \ {'id': 5, 'col': 4, 'type_bufnr': 0, 'end': 1,
1717          \  'type': 'one', 'length': 2, 'start': 1},
1718          \ {'id': 10, 'col': 5, 'type_bufnr': 0, 'end': 1,
1719          \  'type': 'two', 'length': 2, 'start': 1}], prop_list(1))
1720    #" text properties between a few lines
1721    call assert_equal([
1722          \ {'lnum': 3, 'id': 20, 'col': 12, 'type_bufnr': 0, 'end': 1,
1723          \  'type': 'one', 'length': 2, 'start': 1},
1724          \ {'lnum': 3, 'id': 10, 'col': 13, 'type_bufnr': 0, 'end': 1,
1725          \  'type': 'two', 'length': 2, 'start': 1},
1726          \ {'lnum': 5, 'id': 10, 'col': 20, 'type_bufnr': 0, 'end': 1,
1727          \  'type': 'one', 'length': 2, 'start': 1},
1728          \ {'lnum': 5, 'id': 20, 'col': 21, 'type_bufnr': 0, 'end': 1,
1729          \  'type': 'two', 'length': 2, 'start': 1}],
1730          \ prop_list(2, {'end_lnum': 5}))
1731    #" text properties across all the lines
1732    call assert_equal([
1733          \ {'lnum': 1, 'id': 5, 'col': 4, 'type_bufnr': 0, 'end': 1,
1734          \  'type': 'one', 'length': 2, 'start': 1},
1735          \ {'lnum': 3, 'id': 20, 'col': 12, 'type_bufnr': 0, 'end': 1,
1736          \  'type': 'one', 'length': 2, 'start': 1},
1737          \ {'lnum': 5, 'id': 10, 'col': 20, 'type_bufnr': 0, 'end': 1,
1738          \  'type': 'one', 'length': 2, 'start': 1}],
1739          \ prop_list(1, {'types': ['one'], 'end_lnum': -1}))
1740    #" text properties with the specified identifier
1741    call assert_equal([
1742          \ {'lnum': 3, 'id': 20, 'col': 12, 'type_bufnr': 0, 'end': 1,
1743          \  'type': 'one', 'length': 2, 'start': 1},
1744          \ {'lnum': 5, 'id': 20, 'col': 21, 'type_bufnr': 0, 'end': 1,
1745          \  'type': 'two', 'length': 2, 'start': 1}],
1746          \ prop_list(1, {'ids': [20], 'end_lnum': 10}))
1747    #" text properties of the specified type and id
1748    call assert_equal([
1749          \ {'lnum': 1, 'id': 10, 'col': 5, 'type_bufnr': 0, 'end': 1,
1750          \  'type': 'two', 'length': 2, 'start': 1},
1751          \ {'lnum': 3, 'id': 10, 'col': 13, 'type_bufnr': 0, 'end': 1,
1752          \  'type': 'two', 'length': 2, 'start': 1}],
1753          \ prop_list(1, {'types': ['two'], 'ids': [10], 'end_lnum': 20}))
1754    call assert_equal([], prop_list(1, {'ids': [40, 50], 'end_lnum': 10}))
1755    call assert_equal([], prop_list(6, {'end_lnum': 10}))
1756    call assert_equal([], prop_list(2, {'end_lnum': 2}))
1757    #" error cases
1758    call assert_fails("echo prop_list(1, {'end_lnum': -20})", 'E16:')
1759    call assert_fails("echo prop_list(4, {'end_lnum': 2})", 'E16:')
1760    call assert_fails("echo prop_list(1, {'end_lnum': '$'})", 'E889:')
1761    call assert_fails("echo prop_list(1, {'types': ['blue'], 'end_lnum': 10})",
1762          \ 'E971:')
1763    call assert_fails("echo prop_list(1, {'types': ['one', 'blue'],
1764          \ 'end_lnum': 10})", 'E971:')
1765    call assert_fails("echo prop_list(1, {'types': ['one', 10],
1766          \ 'end_lnum': 10})", 'E928:')
1767    call assert_fails("echo prop_list(1, {'types': ['']})", 'E971:')
1768    call assert_equal([], prop_list(2, {'types': []}))
1769    call assert_equal([], prop_list(2, {'types': test_null_list()}))
1770    call assert_fails("call prop_list(1, {'types': {}})", 'E714:')
1771    call assert_fails("call prop_list(1, {'types': 'one'})", 'E714:')
1772    call assert_equal([], prop_list(2, {'types': ['one'],
1773          \ 'ids': test_null_list()}))
1774    call assert_equal([], prop_list(2, {'types': ['one'], 'ids': []}))
1775    call assert_fails("call prop_list(1, {'types': ['one'], 'ids': {}})",
1776          \ 'E714:')
1777    call assert_fails("call prop_list(1, {'types': ['one'], 'ids': 10})",
1778          \ 'E714:')
1779    call assert_fails("call prop_list(1, {'types': ['one'], 'ids': [[]]})",
1780          \ 'E745:')
1781    call assert_fails("call prop_list(1, {'types': ['one'], 'ids': [10, []]})",
1782          \ 'E745:')
1783
1784    #" get text properties from a non-current buffer
1785    wincmd w
1786    call assert_equal([
1787          \ {'lnum': 1, 'id': 5, 'col': 4, 'type_bufnr': 0, 'end': 1,
1788          \ 'type': 'one', 'length': 2, 'start': 1},
1789          \ {'lnum': 1, 'id': 10, 'col': 5, 'type_bufnr': 0, 'end': 1,
1790          \ 'type': 'two', 'length': 2, 'start': 1},
1791          \ {'lnum': 3, 'id': 20, 'col': 12, 'type_bufnr': 0, 'end': 1,
1792          \ 'type': 'one', 'length': 2, 'start': 1},
1793          \ {'lnum': 3, 'id': 10, 'col': 13, 'type_bufnr': 0, 'end': 1,
1794          \ 'type': 'two', 'length': 2, 'start': 1}],
1795          \ prop_list(1, {'bufnr': winbufnr(1), 'end_lnum': 4}))
1796    wincmd w
1797
1798    #" get text properties after clearing all the properties
1799    call prop_clear(1, line('$'))
1800    call assert_equal([], prop_list(1, {'end_lnum': 10}))
1801
1802    call prop_add(2, 4, {'type': 'one', 'id': 5, 'end_col': 6})
1803    call prop_add(2, 4, {'type': 'two', 'id': 10, 'end_col': 6})
1804    call prop_add(2, 4, {'type': 'three', 'id': 15, 'end_col': 6})
1805    #" get text properties with a list of types
1806    call assert_equal([
1807          \ {'id': 10, 'col': 4, 'type_bufnr': 0, 'end': 1,
1808          \  'type': 'two', 'length': 2, 'start': 1},
1809          \ {'id': 5, 'col': 4, 'type_bufnr': 0, 'end': 1,
1810          \  'type': 'one', 'length': 2, 'start': 1}],
1811          \ prop_list(2, {'types': ['one', 'two']}))
1812    call assert_equal([
1813          \ {'id': 15, 'col': 4, 'type_bufnr': 0, 'end': 1,
1814          \  'type': 'three', 'length': 2, 'start': 1},
1815          \ {'id': 5, 'col': 4, 'type_bufnr': 0, 'end': 1,
1816          \  'type': 'one', 'length': 2, 'start': 1}],
1817          \ prop_list(2, {'types': ['one', 'three']}))
1818    #" get text properties with a list of identifiers
1819    call assert_equal([
1820          \ {'id': 10, 'col': 4, 'type_bufnr': 0, 'end': 1,
1821          \  'type': 'two', 'length': 2, 'start': 1},
1822          \ {'id': 5, 'col': 4, 'type_bufnr': 0, 'end': 1,
1823          \  'type': 'one', 'length': 2, 'start': 1}],
1824          \ prop_list(2, {'ids': [5, 10, 20]}))
1825    call prop_clear(1, line('$'))
1826    call assert_equal([], prop_list(2, {'types': ['one', 'two']}))
1827    call assert_equal([], prop_list(2, {'ids': [5, 10, 20]}))
1828
1829    #" get text properties from a hidden buffer
1830    edit! Xaaa
1831    call setline(1, repeat([repeat('b', 60)], 10))
1832    call prop_add(1, 4, {'type': 'one', 'id': 5, 'end_col': 6})
1833    call prop_add(4, 8, {'type': 'two', 'id': 10, 'end_col': 10})
1834    VAR bnr = bufnr()
1835    hide edit Xbbb
1836    call assert_equal([
1837          \ {'lnum': 1, 'id': 5, 'col': 4, 'type_bufnr': 0, 'end': 1,
1838          \  'type': 'one', 'length': 2, 'start': 1},
1839          \ {'lnum': 4, 'id': 10, 'col': 8, 'type_bufnr': 0, 'end': 1,
1840          \  'type': 'two', 'length': 2, 'start': 1}],
1841          \ prop_list(1, {'bufnr': bnr,
1842          \ 'types': ['one', 'two'], 'ids': [5, 10], 'end_lnum': -1}))
1843    #" get text properties from an unloaded buffer
1844    bunload! Xaaa
1845    call assert_equal([], prop_list(1, {'bufnr': bnr, 'end_lnum': -1}))
1846
1847    call DeletePropTypes()
1848    :%bw!
1849  END
1850  call CheckLegacyAndVim9Success(lines)
1851endfunc
1852
1853" vim: shiftwidth=2 sts=2 expandtab
1854