1" Tests for search_stats, when "S" is not in 'shortmess'
2
3source screendump.vim
4source check.vim
5
6func Test_search_stat()
7  new
8  set shortmess-=S
9  " Append 50 lines with text to search for, "foobar" appears 20 times
10  call append(0, repeat(['foobar', 'foo', 'fooooobar', 'foba', 'foobar'], 10))
11  call nvim_win_set_cursor(0, [1, 0])
12
13  " searchcount() returns an empty dictionary when previous pattern was not set
14  call assert_equal({}, searchcount(#{pattern: ''}))
15  " but setting @/ should also work (even 'n' nor 'N' was executed)
16  " recompute the count when the last position is different.
17  call assert_equal(
18    \ #{current: 1, exact_match: 1, total: 40, incomplete: 0, maxcount: 99},
19    \ searchcount(#{pattern: 'foo'}))
20  call assert_equal(
21    \ #{current: 0, exact_match: 0, total: 10, incomplete: 0, maxcount: 99},
22    \ searchcount(#{pattern: 'fooooobar'}))
23  call assert_equal(
24    \ #{current: 0, exact_match: 0, total: 10, incomplete: 0, maxcount: 99},
25    \ searchcount(#{pattern: 'fooooobar', pos: [2, 1, 0]}))
26  call assert_equal(
27    \ #{current: 1, exact_match: 1, total: 10, incomplete: 0, maxcount: 99},
28    \ searchcount(#{pattern: 'fooooobar', pos: [3, 1, 0]}))
29  " on last char of match
30  call assert_equal(
31    \ #{current: 1, exact_match: 1, total: 10, incomplete: 0, maxcount: 99},
32    \ searchcount(#{pattern: 'fooooobar', pos: [3, 9, 0]}))
33  " on char after match
34  call assert_equal(
35    \ #{current: 1, exact_match: 0, total: 10, incomplete: 0, maxcount: 99},
36    \ searchcount(#{pattern: 'fooooobar', pos: [3, 10, 0]}))
37  call assert_equal(
38    \ #{current: 1, exact_match: 0, total: 10, incomplete: 0, maxcount: 99},
39    \ searchcount(#{pattern: 'fooooobar', pos: [4, 1, 0]}))
40  call assert_equal(
41    \ #{current: 1, exact_match: 0, total: 2, incomplete: 2, maxcount: 1},
42    \ searchcount(#{pattern: 'fooooobar', pos: [4, 1, 0], maxcount: 1}))
43  call assert_equal(
44    \ #{current: 0, exact_match: 0, total: 2, incomplete: 2, maxcount: 1},
45    \ searchcount(#{pattern: 'fooooobar', maxcount: 1}))
46
47  " match at second line
48  call cursor(1, 1)
49  let messages_before = execute('messages')
50  let @/ = 'fo*\(bar\?\)\?'
51  let g:a = execute(':unsilent :norm! n')
52  let stat = '\[2/50\]'
53  let pat = escape(@/, '()*?'). '\s\+'
54  call assert_match(pat .. stat, g:a)
55  call assert_equal(
56    \ #{current: 2, exact_match: 1, total: 50, incomplete: 0, maxcount: 99},
57    \ searchcount(#{recompute: 0}))
58  " didn't get added to message history
59  call assert_equal(messages_before, execute('messages'))
60
61  " Match at last line
62  call cursor(line('$')-2, 1)
63  let g:a = execute(':unsilent :norm! n')
64  let stat = '\[50/50\]'
65  call assert_match(pat .. stat, g:a)
66  call assert_equal(
67    \ #{current: 50, exact_match: 1, total: 50, incomplete: 0, maxcount: 99},
68    \ searchcount(#{recompute: 0}))
69
70  " No search stat
71  set shortmess+=S
72  call cursor(1, 1)
73  let stat = '\[2/50\]'
74  let g:a = execute(':unsilent :norm! n')
75  call assert_notmatch(pat .. stat, g:a)
76  call writefile(getline(1, '$'), 'sample.txt')
77  " n does not update search stat
78  call assert_equal(
79    \ #{current: 50, exact_match: 1, total: 50, incomplete: 0, maxcount: 99},
80    \ searchcount(#{recompute: 0}))
81  call assert_equal(
82    \ #{current: 2, exact_match: 1, total: 50, incomplete: 0, maxcount: 99},
83    \ searchcount(#{recompute: v:true}))
84  set shortmess-=S
85
86  " Many matches
87  call cursor(line('$')-2, 1)
88  let @/ = '.'
89  let pat = escape(@/, '()*?'). '\s\+'
90  let g:a = execute(':unsilent :norm! n')
91  let stat = '\[>99/>99\]'
92  call assert_match(pat .. stat, g:a)
93  call assert_equal(
94    \ #{current: 100, exact_match: 0, total: 100, incomplete: 2, maxcount: 99},
95    \ searchcount(#{recompute: 0}))
96  call assert_equal(
97    \ #{current: 272, exact_match: 1, total: 280, incomplete: 0, maxcount: 0},
98    \ searchcount(#{recompute: v:true, maxcount: 0, timeout: 200}))
99  call assert_equal(
100    \ #{current: 1, exact_match: 1, total: 280, incomplete: 0, maxcount: 0},
101    \ searchcount(#{recompute: 1, maxcount: 0, pos: [1, 1, 0], timeout: 200}))
102  call cursor(line('$'), 1)
103  let g:a = execute(':unsilent :norm! n')
104  let stat = 'W \[1/>99\]'
105  call assert_match(pat .. stat, g:a)
106  call assert_equal(
107    \ #{current: 1, exact_match: 1, total: 100, incomplete: 2, maxcount: 99},
108    \ searchcount(#{recompute: 0}))
109  call assert_equal(
110    \ #{current: 1, exact_match: 1, total: 280, incomplete: 0, maxcount: 0},
111    \ searchcount(#{recompute: 1, maxcount: 0, timeout: 200}))
112  call assert_equal(
113    \ #{current: 271, exact_match: 1, total: 280, incomplete: 0, maxcount: 0},
114    \ searchcount(#{recompute: 1, maxcount: 0, pos: [line('$')-2, 1, 0], timeout: 200}))
115
116  " Many matches
117  call cursor(1, 1)
118  let g:a = execute(':unsilent :norm! n')
119  let stat = '\[2/>99\]'
120  call assert_match(pat .. stat, g:a)
121  call cursor(1, 1)
122  let g:a = execute(':unsilent :norm! N')
123  let stat = 'W \[>99/>99\]'
124  call assert_match(pat .. stat, g:a)
125
126  " right-left
127  if exists("+rightleft")
128    set rl
129    call cursor(1,1)
130    let @/ = 'foobar'
131    let pat = 'raboof/\s\+'
132    let g:a = execute(':unsilent :norm! n')
133    let stat = '\[20/2\]'
134    call assert_match(pat .. stat, g:a)
135    set norl
136  endif
137
138  " right-left bottom
139  if exists("+rightleft")
140    set rl
141    call cursor('$',1)
142    let pat = 'raboof?\s\+'
143    let g:a = execute(':unsilent :norm! N')
144    let stat = '\[20/20\]'
145    call assert_match(pat .. stat, g:a)
146    set norl
147  endif
148
149  " right-left back at top
150  if exists("+rightleft")
151    set rl
152    call cursor('$',1)
153    let pat = 'raboof/\s\+'
154    let g:a = execute(':unsilent :norm! n')
155    let stat = 'W \[20/1\]'
156    call assert_match(pat .. stat, g:a)
157    call assert_match('search hit BOTTOM, continuing at TOP', g:a)
158    set norl
159  endif
160
161  " normal, back at bottom
162  call cursor(1,1)
163  let @/ = 'foobar'
164  let pat = '?foobar\s\+'
165  let g:a = execute(':unsilent :norm! N')
166  let stat = 'W \[20/20\]'
167  call assert_match(pat .. stat, g:a)
168  call assert_match('search hit TOP, continuing at BOTTOM', g:a)
169  call assert_match('W \[20/20\]', Screenline(&lines))
170
171  " normal, no match
172  call cursor(1,1)
173  let @/ = 'zzzzzz'
174  let g:a = ''
175  try
176    let g:a = execute(':unsilent :norm! n')
177  catch /^Vim\%((\a\+)\)\=:E486/
178    let stat = ''
179    " error message is not redir'ed to g:a, it is empty
180    call assert_true(empty(g:a))
181  catch
182    call assert_false(1)
183  endtry
184
185  " with count
186  call cursor(1, 1)
187  let @/ = 'fo*\(bar\?\)\?'
188  let g:a = execute(':unsilent :norm! 2n')
189  let stat = '\[3/50\]'
190  let pat = escape(@/, '()*?'). '\s\+'
191  call assert_match(pat .. stat, g:a)
192  let g:a = execute(':unsilent :norm! 2n')
193  let stat = '\[5/50\]'
194  call assert_match(pat .. stat, g:a)
195
196  " with offset
197  call cursor(1, 1)
198  call feedkeys("/fo*\\(bar\\?\\)\\?/+1\<cr>", 'tx')
199  let g:a = execute(':unsilent :norm! n')
200  let stat = '\[5/50\]'
201  let pat = escape(@/ .. '/+1', '()*?'). '\s\+'
202  call assert_match(pat .. stat, g:a)
203
204  " normal, n comes from a mapping
205  "     Need to move over more than 64 lines to trigger char_avail(.
206  nnoremap n nzv
207  call cursor(1,1)
208  call append(50, repeat(['foobar', 'foo', 'fooooobar', 'foba', 'foobar'], 10))
209  call setline(2, 'find this')
210  call setline(70, 'find this')
211  let @/ = 'find this'
212  let pat = '/find this\s\+'
213  let g:a = execute(':unsilent :norm n')
214  " g:a will contain several lines
215  let g:b = split(g:a, "\n")[-1]
216  let stat = '\[1/2\]'
217  call assert_match(pat .. stat, g:b)
218  unmap n
219
220  " normal, but silent
221  call cursor(1,1)
222  let @/ = 'find this'
223  let pat = '/find this\s\+'
224  let g:a = execute(':norm! n')
225  let stat = '\[1/2\]'
226  call assert_notmatch(pat .. stat, g:a)
227
228  " normal, n comes from a silent mapping
229  " First test a normal mapping, then a silent mapping
230  call cursor(1,1)
231  nnoremap n n
232  let @/ = 'find this'
233  let pat = '/find this\s\+'
234  let g:a = execute(':unsilent :norm n')
235  let g:b = split(g:a, "\n")[-1]
236  let stat = '\[1/2\]'
237  call assert_match(pat .. stat, g:b)
238  nnoremap <silent> n n
239  call cursor(1,1)
240  let g:a = execute(':unsilent :norm n')
241  let g:b = split(g:a, "\n")[-1]
242  let stat = '\[1/2\]'
243  call assert_notmatch(pat .. stat, g:b)
244  call assert_match(stat, g:b)
245  " Test that the message is not truncated
246  " it would insert '...' into the output.
247  call assert_match('^\s\+' .. stat, g:b)
248  unmap n
249
250  " Time out
251  %delete _
252  call append(0, repeat(['foobar', 'foo', 'fooooobar', 'foba', 'foobar'], 100000))
253  call cursor(1, 1)
254  call assert_equal(1, searchcount(#{pattern: 'foo', maxcount: 0, timeout: 1}).incomplete)
255
256  " Clean up
257  set shortmess+=S
258  " close the window
259  bwipe!
260endfunc
261
262func Test_searchcount_fails()
263  call assert_fails('echo searchcount("boo!")', 'E715:')
264endfunc
265
266func Test_search_stat_foldopen()
267  CheckScreendump
268
269  let lines =<< trim END
270    set shortmess-=S
271    setl foldenable foldmethod=indent foldopen-=search
272    call append(0, ['if', "\tfoo", "\tfoo", 'endif'])
273    let @/ = 'foo'
274    call cursor(1,1)
275    norm n
276  END
277  call writefile(lines, 'Xsearchstat1')
278
279  let buf = RunVimInTerminal('-S Xsearchstat1', #{rows: 10})
280  call TermWait(buf)
281  call VerifyScreenDump(buf, 'Test_searchstat_3', {})
282
283  call term_sendkeys(buf, "n")
284  call TermWait(buf)
285  call VerifyScreenDump(buf, 'Test_searchstat_3', {})
286
287  call term_sendkeys(buf, "n")
288  call TermWait(buf)
289  call VerifyScreenDump(buf, 'Test_searchstat_3', {})
290
291  call StopVimInTerminal(buf)
292  call delete('Xsearchstat1')
293endfunc
294
295func! Test_search_stat_screendump()
296  CheckScreendump
297
298  let lines =<< trim END
299    set shortmess-=S
300    " Append 50 lines with text to search for, "foobar" appears 20 times
301    call append(0, repeat(['foobar', 'foo', 'fooooobar', 'foba', 'foobar'], 20))
302    call setline(2, 'find this')
303    call setline(70, 'find this')
304    nnoremap n n
305    let @/ = 'find this'
306    call cursor(1,1)
307    norm n
308  END
309  call writefile(lines, 'Xsearchstat')
310  let buf = RunVimInTerminal('-S Xsearchstat', #{rows: 10})
311  call term_wait(buf)
312  call VerifyScreenDump(buf, 'Test_searchstat_1', {})
313
314  call term_sendkeys(buf, ":nnoremap <silent> n n\<cr>")
315  call term_sendkeys(buf, "gg0n")
316  call term_wait(buf)
317  call VerifyScreenDump(buf, 'Test_searchstat_2', {})
318
319  call StopVimInTerminal(buf)
320  call delete('Xsearchstat')
321endfunc
322
323func Test_searchcount_in_statusline()
324  CheckScreendump
325
326  let lines =<< trim END
327    set shortmess-=S
328    call append(0, 'this is something')
329    function TestSearchCount() abort
330      let search_count = searchcount()
331      if !empty(search_count)
332        return '[' . search_count.current . '/' . search_count.total . ']'
333      else
334        return ''
335      endif
336    endfunction
337    set hlsearch
338    set laststatus=2 statusline+=%{TestSearchCount()}
339  END
340  call writefile(lines, 'Xsearchstatusline')
341  let buf = RunVimInTerminal('-S Xsearchstatusline', #{rows: 10})
342  call TermWait(buf)
343  call term_sendkeys(buf, "/something")
344  call VerifyScreenDump(buf, 'Test_searchstat_4', {})
345
346  call term_sendkeys(buf, "\<Esc>")
347  call StopVimInTerminal(buf)
348  call delete('Xsearchstatusline')
349endfunc
350