blob: afdc104d7c58c5bb13a5c3c6f38d8029528256e6 [file] [log] [blame]
Yegappan Lakshmanan5e877ba2022-03-25 21:19:26 +00001" Tests for the substitute (:s) command
Bram Moolenaarcd055da2016-09-02 19:50:48 +02002
Bram Moolenaar9f6277b2020-02-11 22:04:02 +01003source shared.vim
Bram Moolenaar7a2217b2021-06-06 12:33:49 +02004source check.vim
Bram Moolenaara04f4572022-09-13 13:45:26 +01005source screendump.vim
Bram Moolenaar9f6277b2020-02-11 22:04:02 +01006
Christian Brabandt26c11c52023-11-22 21:26:41 +01007" NOTE: This needs to be the first test to be
8" run in the file, since it depends on
9" that the previous substitution atom
10" was not yet set.
11"
12" recursive call of :s and sub-replace special
13" (did cause heap-use-after free in < v9.0.2121)
14func Test_aaaa_substitute_expr_recursive_special()
15 func R()
16 " FIXME: leaving out the 'n' flag leaks memory, why?
17 %s/./\='.'/gn
18 endfunc
19 new Xfoobar_UAF
20 put ='abcdef'
21 let bufnr = bufnr('%')
22 try
23 silent! :s/./~\=R()/0
24 "call assert_fails(':s/./~\=R()/0', 'E939:')
25 let @/='.'
26 ~g
27 catch /^Vim\%((\a\+)\)\=:E565:/
28 endtry
29 delfunc R
30 exe bufnr .. "bw!"
31endfunc
32
Bram Moolenaar1e115362019-01-09 23:01:02 +010033func Test_multiline_subst()
Bram Moolenaarcd055da2016-09-02 19:50:48 +020034 enew!
35 call append(0, ["1 aa",
36 \ "bb",
37 \ "cc",
38 \ "2 dd",
39 \ "ee",
40 \ "3 ef",
41 \ "gh",
42 \ "4 ij",
43 \ "5 a8",
44 \ "8b c9",
45 \ "9d",
46 \ "6 e7",
47 \ "77f",
48 \ "xxxxx"])
49
50 1
51 " test if replacing a line break works with a back reference
52 /^1/,/^2/s/\n\(.\)/ \1/
53 " test if inserting a line break works with a back reference
54 /^3/,/^4/s/\(.\)$/\r\1/
55 " test if replacing a line break with another line break works
56 /^5/,/^6/s/\(\_d\{3}\)/x\1x/
57 call assert_equal('1 aa bb cc 2 dd ee', getline(1))
58 call assert_equal('3 e', getline(2))
59 call assert_equal('f', getline(3))
60 call assert_equal('g', getline(4))
61 call assert_equal('h', getline(5))
62 call assert_equal('4 i', getline(6))
63 call assert_equal('j', getline(7))
64 call assert_equal('5 ax8', getline(8))
65 call assert_equal('8xb cx9', getline(9))
66 call assert_equal('9xd', getline(10))
67 call assert_equal('6 ex7', getline(11))
68 call assert_equal('7x7f', getline(12))
69 call assert_equal('xxxxx', getline(13))
70 enew!
Bram Moolenaar1e115362019-01-09 23:01:02 +010071endfunc
Bram Moolenaar8c50d502017-02-17 18:28:24 +010072
Bram Moolenaar1e115362019-01-09 23:01:02 +010073func Test_substitute_variants()
Bram Moolenaar8c50d502017-02-17 18:28:24 +010074 " Validate that all the 2-/3-letter variants which embed the flags into the
75 " command name actually work.
76 enew!
77 let ln = 'Testing string'
78 let variants = [
79 \ { 'cmd': ':s/Test/test/c', 'exp': 'testing string', 'prompt': 'y' },
80 \ { 'cmd': ':s/foo/bar/ce', 'exp': ln },
81 \ { 'cmd': ':s/t/r/cg', 'exp': 'Tesring srring', 'prompt': 'a' },
82 \ { 'cmd': ':s/t/r/ci', 'exp': 'resting string', 'prompt': 'y' },
83 \ { 'cmd': ':s/t/r/cI', 'exp': 'Tesring string', 'prompt': 'y' },
Bram Moolenaarea3db912020-02-02 15:32:13 +010084 \ { 'cmd': ':s/t/r/c', 'exp': 'Testing string', 'prompt': 'n' },
Bram Moolenaar8c50d502017-02-17 18:28:24 +010085 \ { 'cmd': ':s/t/r/cn', 'exp': ln },
86 \ { 'cmd': ':s/t/r/cp', 'exp': 'Tesring string', 'prompt': 'y' },
87 \ { 'cmd': ':s/t/r/cl', 'exp': 'Tesring string', 'prompt': 'y' },
88 \ { 'cmd': ':s/t/r/gc', 'exp': 'Tesring srring', 'prompt': 'a' },
Bram Moolenaarea3db912020-02-02 15:32:13 +010089 \ { 'cmd': ':s/i/I/gc', 'exp': 'TestIng string', 'prompt': 'l' },
Bram Moolenaar8c50d502017-02-17 18:28:24 +010090 \ { 'cmd': ':s/foo/bar/ge', 'exp': ln },
91 \ { 'cmd': ':s/t/r/g', 'exp': 'Tesring srring' },
92 \ { 'cmd': ':s/t/r/gi', 'exp': 'resring srring' },
93 \ { 'cmd': ':s/t/r/gI', 'exp': 'Tesring srring' },
94 \ { 'cmd': ':s/t/r/gn', 'exp': ln },
95 \ { 'cmd': ':s/t/r/gp', 'exp': 'Tesring srring' },
96 \ { 'cmd': ':s/t/r/gl', 'exp': 'Tesring srring' },
97 \ { 'cmd': ':s//r/gr', 'exp': 'Testr strr' },
98 \ { 'cmd': ':s/t/r/ic', 'exp': 'resting string', 'prompt': 'y' },
99 \ { 'cmd': ':s/foo/bar/ie', 'exp': ln },
100 \ { 'cmd': ':s/t/r/i', 'exp': 'resting string' },
101 \ { 'cmd': ':s/t/r/iI', 'exp': 'Tesring string' },
102 \ { 'cmd': ':s/t/r/in', 'exp': ln },
103 \ { 'cmd': ':s/t/r/ip', 'exp': 'resting string' },
104 \ { 'cmd': ':s//r/ir', 'exp': 'Testr string' },
105 \ { 'cmd': ':s/t/r/Ic', 'exp': 'Tesring string', 'prompt': 'y' },
106 \ { 'cmd': ':s/foo/bar/Ie', 'exp': ln },
107 \ { 'cmd': ':s/t/r/Ig', 'exp': 'Tesring srring' },
108 \ { 'cmd': ':s/t/r/Ii', 'exp': 'resting string' },
109 \ { 'cmd': ':s/t/r/I', 'exp': 'Tesring string' },
110 \ { 'cmd': ':s/t/r/Ip', 'exp': 'Tesring string' },
111 \ { 'cmd': ':s/t/r/Il', 'exp': 'Tesring string' },
112 \ { 'cmd': ':s//r/Ir', 'exp': 'Testr string' },
113 \ { 'cmd': ':s//r/rc', 'exp': 'Testr string', 'prompt': 'y' },
114 \ { 'cmd': ':s//r/rg', 'exp': 'Testr strr' },
115 \ { 'cmd': ':s//r/ri', 'exp': 'Testr string' },
116 \ { 'cmd': ':s//r/rI', 'exp': 'Testr string' },
117 \ { 'cmd': ':s//r/rn', 'exp': 'Testing string' },
118 \ { 'cmd': ':s//r/rp', 'exp': 'Testr string' },
119 \ { 'cmd': ':s//r/rl', 'exp': 'Testr string' },
120 \ { 'cmd': ':s//r/r', 'exp': 'Testr string' },
Bram Moolenaarea3db912020-02-02 15:32:13 +0100121 \ { 'cmd': ':s/i/I/gc', 'exp': 'Testing string', 'prompt': 'q' },
Bram Moolenaar8c50d502017-02-17 18:28:24 +0100122 \]
123
124 for var in variants
125 for run in [1, 2]
126 let cmd = var.cmd
127 if run == 2 && cmd =~ "/.*/.*/."
128 " Change :s/from/to/{flags} to :s{flags}
129 let cmd = substitute(cmd, '/.*/', '', '')
130 endif
131 call setline(1, [ln])
132 let msg = printf('using "%s"', cmd)
133 let @/='ing'
134 let v:errmsg = ''
135 call feedkeys(cmd . "\<CR>" . get(var, 'prompt', ''), 'ntx')
136 " No error should exist (matters for testing e flag)
137 call assert_equal('', v:errmsg, msg)
138 call assert_equal(var.exp, getline('.'), msg)
139 endfor
140 endfor
Bram Moolenaar1e115362019-01-09 23:01:02 +0100141endfunc
Bram Moolenaarba748c82017-02-26 14:00:07 +0100142
Bram Moolenaar94747162019-02-10 21:55:26 +0100143" Test the l, p, # flags.
144func Test_substitute_flags_lp()
145 new
146 call setline(1, "abc\tdef\<C-h>ghi")
147
148 let a = execute('s/a/a/p')
149 call assert_equal("\nabc def^Hghi", a)
150
151 let a = execute('s/a/a/l')
152 call assert_equal("\nabc^Idef^Hghi$", a)
153
154 let a = execute('s/a/a/#')
155 call assert_equal("\n 1 abc def^Hghi", a)
156
157 let a = execute('s/a/a/p#')
158 call assert_equal("\n 1 abc def^Hghi", a)
159
160 let a = execute('s/a/a/l#')
161 call assert_equal("\n 1 abc^Idef^Hghi$", a)
162
163 let a = execute('s/a/a/')
164 call assert_equal("", a)
165
166 bwipe!
167endfunc
168
Bram Moolenaarba748c82017-02-26 14:00:07 +0100169func Test_substitute_repeat()
170 " This caused an invalid memory access.
Bram Moolenaarb18b4962022-09-02 21:55:50 +0100171 split Xsubfile
Bram Moolenaarba748c82017-02-26 14:00:07 +0100172 s/^/x
173 call feedkeys("Qsc\<CR>y", 'tx')
174 bwipe!
175endfunc
zeertzjq789679c2024-05-23 17:41:26 +0200176
zeertzjq30741372024-05-24 07:37:36 +0200177" Test :s with ? as delimiter.
178func Test_substitute_question_delimiter()
zeertzjq789679c2024-05-23 17:41:26 +0200179 new
180 call setline(1, '??:??')
181 %s?\?\??!!?g
182 call assert_equal('!!:!!', getline(1))
183 bwipe!
184endfunc
185
Bram Moolenaard77aa4d2019-02-10 22:50:14 +0100186" Test %s/\n// which is implemented as a special case to use a
187" more efficient join rather than doing a regular substitution.
188func Test_substitute_join()
189 new
190
191 call setline(1, ["foo\tbar", "bar\<C-H>foo"])
192 let a = execute('%s/\n//')
193 call assert_equal("", a)
194 call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
195 call assert_equal('\n', histget("search", -1))
196
197 call setline(1, ["foo\tbar", "bar\<C-H>foo"])
198 let a = execute('%s/\n//g')
199 call assert_equal("", a)
200 call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
201 call assert_equal('\n', histget("search", -1))
202
203 call setline(1, ["foo\tbar", "bar\<C-H>foo"])
204 let a = execute('%s/\n//p')
205 call assert_equal("\nfoo barbar^Hfoo", a)
206 call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
207 call assert_equal('\n', histget("search", -1))
208
209 call setline(1, ["foo\tbar", "bar\<C-H>foo"])
210 let a = execute('%s/\n//l')
211 call assert_equal("\nfoo^Ibarbar^Hfoo$", a)
212 call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
213 call assert_equal('\n', histget("search", -1))
214
215 call setline(1, ["foo\tbar", "bar\<C-H>foo"])
216 let a = execute('%s/\n//#')
217 call assert_equal("\n 1 foo barbar^Hfoo", a)
218 call assert_equal(["foo\tbarbar\<C-H>foo"], getline(1, '$'))
219 call assert_equal('\n', histget("search", -1))
220
Bram Moolenaarea3db912020-02-02 15:32:13 +0100221 call setline(1, ['foo', 'bar', 'baz', 'qux'])
222 call execute('1,2s/\n//')
223 call assert_equal(['foobarbaz', 'qux'], getline(1, '$'))
224
Bram Moolenaard77aa4d2019-02-10 22:50:14 +0100225 bwipe!
226endfunc
227
228func Test_substitute_count()
229 new
230 call setline(1, ['foo foo', 'foo foo', 'foo foo', 'foo foo', 'foo foo'])
231 2
232
233 s/foo/bar/3
234 call assert_equal(['foo foo', 'bar foo', 'bar foo', 'bar foo', 'foo foo'],
235 \ getline(1, '$'))
236
237 call assert_fails('s/foo/bar/0', 'E939:')
238
Bram Moolenaarea3db912020-02-02 15:32:13 +0100239 call setline(1, ['foo foo', 'foo foo', 'foo foo', 'foo foo', 'foo foo'])
240 2,4s/foo/bar/ 10
241 call assert_equal(['foo foo', 'foo foo', 'foo foo', 'bar foo', 'bar foo'],
242 \ getline(1, '$'))
243
Christian Brabandtac637872023-11-14 20:45:48 +0100244 call assert_fails('s/./b/2147483647', 'E1510:')
Bram Moolenaard77aa4d2019-02-10 22:50:14 +0100245 bwipe!
246endfunc
247
248" Test substitute 'n' flag (report number of matches, do not substitute).
249func Test_substitute_flag_n()
250 new
251 let lines = ['foo foo', 'foo foo', 'foo foo', 'foo foo', 'foo foo']
252 call setline(1, lines)
253
254 call assert_equal("\n3 matches on 3 lines", execute('2,4s/foo/bar/n'))
255 call assert_equal("\n6 matches on 3 lines", execute('2,4s/foo/bar/gn'))
256
257 " c flag (confirm) should be ignored when using n flag.
258 call assert_equal("\n3 matches on 3 lines", execute('2,4s/foo/bar/nc'))
259
260 " No substitution should have been done.
261 call assert_equal(lines, getline(1, '$'))
262
Bram Moolenaarea3db912020-02-02 15:32:13 +0100263 %delete _
264 call setline(1, ['A', 'Bar', 'Baz'])
265 call assert_equal("\n1 match on 1 line", execute('s/\nB\@=//gn'))
266
Bram Moolenaard77aa4d2019-02-10 22:50:14 +0100267 bwipe!
268endfunc
269
270func Test_substitute_errors()
271 new
272 call setline(1, 'foobar')
273
274 call assert_fails('s/FOO/bar/', 'E486:')
275 call assert_fails('s/foo/bar/@', 'E488:')
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200276 call assert_fails('s/\(/bar/', 'E54:')
Bram Moolenaar5d98dc22020-01-29 21:57:34 +0100277 call assert_fails('s afooabara', 'E146:')
278 call assert_fails('s\\a', 'E10:')
Bram Moolenaard77aa4d2019-02-10 22:50:14 +0100279
280 setl nomodifiable
281 call assert_fails('s/foo/bar/', 'E21:')
282
Bram Moolenaar0e05de42020-03-25 22:23:46 +0100283 call assert_fails("let s=substitute([], 'a', 'A', 'g')", 'E730:')
284 call assert_fails("let s=substitute('abcda', [], 'A', 'g')", 'E730:')
285 call assert_fails("let s=substitute('abcda', 'a', [], 'g')", 'E730:')
286 call assert_fails("let s=substitute('abcda', 'a', 'A', [])", 'E730:')
Bram Moolenaar531be472020-09-23 22:38:05 +0200287 call assert_fails("let s=substitute('abc', '\\%(', 'A', 'g')", 'E53:')
Bram Moolenaar0e05de42020-03-25 22:23:46 +0100288
Bram Moolenaard77aa4d2019-02-10 22:50:14 +0100289 bwipe!
290endfunc
291
Bram Moolenaar1a333bc2017-08-30 20:21:58 +0200292" Test for *sub-replace-special* and *sub-replace-expression* on substitute().
293func Test_sub_replace_1()
294 " Run the tests with 'magic' on
295 set magic
296 set cpo&
297 call assert_equal('AA', substitute('A', 'A', '&&', ''))
298 call assert_equal('&', substitute('B', 'B', '\&', ''))
299 call assert_equal('C123456789987654321', substitute('C123456789', 'C\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)', '\0\9\8\7\6\5\4\3\2\1', ''))
300 call assert_equal('d', substitute('D', 'D', 'd', ''))
301 call assert_equal('~', substitute('E', 'E', '~', ''))
302 call assert_equal('~', substitute('F', 'F', '\~', ''))
303 call assert_equal('Gg', substitute('G', 'G', '\ugg', ''))
304 call assert_equal('Hh', substitute('H', 'H', '\Uh\Eh', ''))
305 call assert_equal('iI', substitute('I', 'I', '\lII', ''))
306 call assert_equal('jJ', substitute('J', 'J', '\LJ\EJ', ''))
307 call assert_equal('Kk', substitute('K', 'K', '\Uk\ek', ''))
308 call assert_equal("l\<C-V>\<C-M>l",
309 \ substitute('lLl', 'L', "\<C-V>\<C-M>", ''))
310 call assert_equal("m\<C-M>m", substitute('mMm', 'M', '\r', ''))
311 call assert_equal("n\<C-V>\<C-M>n",
312 \ substitute('nNn', 'N', "\\\<C-V>\<C-M>", ''))
313 call assert_equal("o\no", substitute('oOo', 'O', '\n', ''))
314 call assert_equal("p\<C-H>p", substitute('pPp', 'P', '\b', ''))
315 call assert_equal("q\tq", substitute('qQq', 'Q', '\t', ''))
316 call assert_equal('r\r', substitute('rRr', 'R', '\\', ''))
317 call assert_equal('scs', substitute('sSs', 'S', '\c', ''))
318 call assert_equal("u\nu", substitute('uUu', 'U', "\n", ''))
319 call assert_equal("v\<C-H>v", substitute('vVv', 'V', "\b", ''))
320 call assert_equal("w\\w", substitute('wWw', 'W', "\\", ''))
321 call assert_equal("x\<C-M>x", substitute('xXx', 'X', "\r", ''))
322 call assert_equal("YyyY", substitute('Y', 'Y', '\L\uyYy\l\EY', ''))
323 call assert_equal("zZZz", substitute('Z', 'Z', '\U\lZzZ\u\Ez', ''))
Bram Moolenaar004a6782020-04-11 17:09:31 +0200324 " \v or \V after $
325 call assert_equal('abxx', substitute('abcd', 'xy$\v|cd$', 'xx', ''))
326 call assert_equal('abxx', substitute('abcd', 'xy$\V\|cd\$', 'xx', ''))
Bram Moolenaar1a333bc2017-08-30 20:21:58 +0200327endfunc
328
329func Test_sub_replace_2()
330 " Run the tests with 'magic' off
331 set nomagic
332 set cpo&
333 call assert_equal('AA', substitute('A', 'A', '&&', ''))
334 call assert_equal('&', substitute('B', 'B', '\&', ''))
335 call assert_equal('C123456789987654321', substitute('C123456789', 'C\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)', '\0\9\8\7\6\5\4\3\2\1', ''))
336 call assert_equal('d', substitute('D', 'D', 'd', ''))
337 call assert_equal('~', substitute('E', 'E', '~', ''))
338 call assert_equal('~', substitute('F', 'F', '\~', ''))
339 call assert_equal('Gg', substitute('G', 'G', '\ugg', ''))
340 call assert_equal('Hh', substitute('H', 'H', '\Uh\Eh', ''))
341 call assert_equal('iI', substitute('I', 'I', '\lII', ''))
342 call assert_equal('jJ', substitute('J', 'J', '\LJ\EJ', ''))
343 call assert_equal('Kk', substitute('K', 'K', '\Uk\ek', ''))
344 call assert_equal("l\<C-V>\<C-M>l",
345 \ substitute('lLl', 'L', "\<C-V>\<C-M>", ''))
346 call assert_equal("m\<C-M>m", substitute('mMm', 'M', '\r', ''))
347 call assert_equal("n\<C-V>\<C-M>n",
348 \ substitute('nNn', 'N', "\\\<C-V>\<C-M>", ''))
349 call assert_equal("o\no", substitute('oOo', 'O', '\n', ''))
350 call assert_equal("p\<C-H>p", substitute('pPp', 'P', '\b', ''))
351 call assert_equal("q\tq", substitute('qQq', 'Q', '\t', ''))
352 call assert_equal('r\r', substitute('rRr', 'R', '\\', ''))
353 call assert_equal('scs', substitute('sSs', 'S', '\c', ''))
354 call assert_equal("t\<C-M>t", substitute('tTt', 'T', "\r", ''))
355 call assert_equal("u\nu", substitute('uUu', 'U', "\n", ''))
356 call assert_equal("v\<C-H>v", substitute('vVv', 'V', "\b", ''))
357 call assert_equal('w\w', substitute('wWw', 'W', "\\", ''))
358 call assert_equal('XxxX', substitute('X', 'X', '\L\uxXx\l\EX', ''))
359 call assert_equal('yYYy', substitute('Y', 'Y', '\U\lYyY\u\Ey', ''))
360endfunc
361
362func Test_sub_replace_3()
363 set magic&
364 set cpo&
365 call assert_equal('a\a', substitute('aAa', 'A', '\="\\"', ''))
366 call assert_equal('b\\b', substitute('bBb', 'B', '\="\\\\"', ''))
367 call assert_equal("c\rc", substitute('cCc', 'C', "\\=\"\r\"", ''))
368 call assert_equal("d\\\rd", substitute('dDd', 'D', "\\=\"\\\\\r\"", ''))
369 call assert_equal("e\\\\\re", substitute('eEe', 'E', "\\=\"\\\\\\\\\r\"", ''))
370 call assert_equal('f\rf', substitute('fFf', 'F', '\="\\r"', ''))
371 call assert_equal('j\nj', substitute('jJj', 'J', '\="\\n"', ''))
372 call assert_equal("k\<C-M>k", substitute('kKk', 'K', '\="\r"', ''))
373 call assert_equal("l\nl", substitute('lLl', 'L', '\="\n"', ''))
374endfunc
375
376" Test for submatch() on substitute().
377func Test_sub_replace_4()
378 set magic&
379 set cpo&
380 call assert_equal('a\a', substitute('aAa', 'A',
381 \ '\=substitute(submatch(0), ".", "\\", "")', ''))
382 call assert_equal('b\b', substitute('bBb', 'B',
383 \ '\=substitute(submatch(0), ".", "\\\\", "")', ''))
384 call assert_equal("c\<C-V>\<C-M>c", substitute('cCc', 'C', '\=substitute(submatch(0), ".", "\<C-V>\<C-M>", "")', ''))
385 call assert_equal("d\<C-V>\<C-M>d", substitute('dDd', 'D', '\=substitute(submatch(0), ".", "\\\<C-V>\<C-M>", "")', ''))
386 call assert_equal("e\\\<C-V>\<C-M>e", substitute('eEe', 'E', '\=substitute(submatch(0), ".", "\\\\\<C-V>\<C-M>", "")', ''))
387 call assert_equal("f\<C-M>f", substitute('fFf', 'F', '\=substitute(submatch(0), ".", "\\r", "")', ''))
388 call assert_equal("j\nj", substitute('jJj', 'J', '\=substitute(submatch(0), ".", "\\n", "")', ''))
389 call assert_equal("k\rk", substitute('kKk', 'K', '\=substitute(submatch(0), ".", "\r", "")', ''))
390 call assert_equal("l\nl", substitute('lLl', 'L', '\=substitute(submatch(0), ".", "\n", "")', ''))
391endfunc
392
393func Test_sub_replace_5()
394 set magic&
395 set cpo&
396 call assert_equal('A123456789987654321', substitute('A123456789',
397 \ 'A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
398 \ '\=submatch(0) . submatch(9) . submatch(8) . ' .
399 \ 'submatch(7) . submatch(6) . submatch(5) . ' .
400 \ 'submatch(4) . submatch(3) . submatch(2) . submatch(1)',
401 \ ''))
402 call assert_equal("[['A123456789'], ['9'], ['8'], ['7'], ['6'], " .
403 \ "['5'], ['4'], ['3'], ['2'], ['1']]",
404 \ substitute('A123456789',
405 \ 'A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
406 \ '\=string([submatch(0, 1), submatch(9, 1), ' .
Bram Moolenaarf6ed61e2019-09-07 19:05:09 +0200407 \ 'submatch(8, 1), 7->submatch(1), submatch(6, 1), ' .
Bram Moolenaar1a333bc2017-08-30 20:21:58 +0200408 \ 'submatch(5, 1), submatch(4, 1), submatch(3, 1), ' .
409 \ 'submatch(2, 1), submatch(1, 1)])',
410 \ ''))
411endfunc
412
413func Test_sub_replace_6()
414 set magic&
415 set cpo+=/
416 call assert_equal('a', substitute('A', 'A', 'a', ''))
417 call assert_equal('%', substitute('B', 'B', '%', ''))
418 set cpo-=/
419 call assert_equal('c', substitute('C', 'C', 'c', ''))
420 call assert_equal('%', substitute('D', 'D', '%', ''))
421endfunc
422
423func Test_sub_replace_7()
424 set magic&
425 set cpo&
426 call assert_equal('AA', substitute('AA', 'A.', '\=submatch(0)', ''))
427 call assert_equal("B\nB", substitute("B\nB", 'B.', '\=submatch(0)', ''))
428 call assert_equal("['B\n']B", substitute("B\nB", 'B.', '\=string(submatch(0, 1))', ''))
429 call assert_equal('-abab', substitute('-bb', '\zeb', 'a', 'g'))
430 call assert_equal('c-cbcbc', substitute('-bb', '\ze', 'c', 'g'))
431endfunc
432
433" Test for *:s%* on :substitute.
434func Test_sub_replace_8()
435 new
436 set magic&
437 set cpo&
438 $put =',,X'
439 s/\(^\|,\)\ze\(,\|X\)/\1N/g
440 call assert_equal('N,,NX', getline("$"))
441 $put =',,Y'
442 let cmd = ':s/\(^\|,\)\ze\(,\|Y\)/\1N/gc'
443 call feedkeys(cmd . "\<CR>a", "xt")
444 call assert_equal('N,,NY', getline("$"))
445 :$put =',,Z'
446 let cmd = ':s/\(^\|,\)\ze\(,\|Z\)/\1N/gc'
447 call feedkeys(cmd . "\<CR>yy", "xt")
448 call assert_equal('N,,NZ', getline("$"))
449 enew! | close
450endfunc
451
452func Test_sub_replace_9()
453 new
454 set magic&
455 set cpo&
456 $put ='xxx'
457 call feedkeys(":s/x/X/gc\<CR>yyq", "xt")
458 call assert_equal('XXx', getline("$"))
459 enew! | close
460endfunc
461
462func Test_sub_replace_10()
463 set magic&
464 set cpo&
465 call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g'))
466 call assert_equal('aaa', substitute('123', '\zs.', 'a', 'g'))
467 call assert_equal('1a2a3a', substitute('123', '.\zs', 'a', 'g'))
468 call assert_equal('a1a2a3a', substitute('123', '\ze', 'a', 'g'))
469 call assert_equal('a1a2a3', substitute('123', '\ze.', 'a', 'g'))
470 call assert_equal('aaa', substitute('123', '.\ze', 'a', 'g'))
471 call assert_equal('aa2a3a', substitute('123', '1\|\ze', 'a', 'g'))
472 call assert_equal('1aaa', substitute('123', '1\zs\|[23]', 'a', 'g'))
473endfunc
Bram Moolenaar15993ce2017-10-26 20:21:44 +0200474
Bram Moolenaarb0745b22019-11-09 22:28:11 +0100475func SubReplacer(text, submatches)
476 return a:text .. a:submatches[0] .. a:text
477endfunc
zeertzjq48db5da2022-09-16 12:10:03 +0100478func SubReplacerVar(text, ...)
479 return a:text .. a:1[0] .. a:text
480endfunc
zeertzjqabd58d82022-09-16 16:06:32 +0100481def SubReplacerVar9(text: string, ...args: list<list<string>>): string
482 return text .. args[0][0] .. text
483enddef
Bram Moolenaar4c054e92019-11-10 00:13:50 +0100484func SubReplacer20(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, submatches)
485 return a:t3 .. a:submatches[0] .. a:t11
486endfunc
Bram Moolenaarb0745b22019-11-09 22:28:11 +0100487
488func Test_substitute_partial()
zeertzjq48db5da2022-09-16 12:10:03 +0100489 call assert_equal('1foo2foo3', substitute('123', '2', function('SubReplacer', ['foo']), 'g'))
490 call assert_equal('1foo2foo3', substitute('123', '2', function('SubReplacerVar', ['foo']), 'g'))
zeertzjqabd58d82022-09-16 16:06:32 +0100491 call assert_equal('1foo2foo3', substitute('123', '2', function('SubReplacerVar9', ['foo']), 'g'))
Bram Moolenaar4c054e92019-11-10 00:13:50 +0100492
zeertzjq48db5da2022-09-16 12:10:03 +0100493 " 19 arguments plus one is just OK
494 let Replacer = function('SubReplacer20', repeat(['foo'], 19))
495 call assert_equal('1foo2foo3', substitute('123', '2', Replacer, 'g'))
Bram Moolenaar4c054e92019-11-10 00:13:50 +0100496
zeertzjq48db5da2022-09-16 12:10:03 +0100497 " 20 arguments plus one is too many
498 let Replacer = function('SubReplacer20', repeat(['foo'], 20))
499 call assert_fails("call substitute('123', '2', Replacer, 'g')", 'E118:')
Bram Moolenaarb0745b22019-11-09 22:28:11 +0100500endfunc
501
Bram Moolenaar7a2217b2021-06-06 12:33:49 +0200502func Test_substitute_float()
Bram Moolenaar7a2217b2021-06-06 12:33:49 +0200503 call assert_equal('number 1.23', substitute('number ', '$', { -> 1.23 }, ''))
504 vim9 assert_equal('number 1.23', substitute('number ', '$', () => 1.23, ''))
505endfunc
506
Bram Moolenaar15993ce2017-10-26 20:21:44 +0200507" Tests for *sub-replace-special* and *sub-replace-expression* on :substitute.
508
509" Execute a list of :substitute command tests
510func Run_SubCmd_Tests(tests)
511 enew!
512 for t in a:tests
513 let start = line('.') + 1
514 let end = start + len(t[2]) - 1
515 exe "normal o" . t[0]
516 call cursor(start, 1)
517 exe t[1]
518 call assert_equal(t[2], getline(start, end), t[1])
519 endfor
520 enew!
521endfunc
522
523func Test_sub_cmd_1()
524 set magic
525 set cpo&
526
527 " List entry format: [input, cmd, output]
528 let tests = [['A', 's/A/&&/', ['AA']],
529 \ ['B', 's/B/\&/', ['&']],
530 \ ['C123456789', 's/C\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\0\9\8\7\6\5\4\3\2\1/', ['C123456789987654321']],
531 \ ['D', 's/D/d/', ['d']],
532 \ ['E', 's/E/~/', ['d']],
533 \ ['F', 's/F/\~/', ['~']],
534 \ ['G', 's/G/\ugg/', ['Gg']],
535 \ ['H', 's/H/\Uh\Eh/', ['Hh']],
536 \ ['I', 's/I/\lII/', ['iI']],
537 \ ['J', 's/J/\LJ\EJ/', ['jJ']],
538 \ ['K', 's/K/\Uk\ek/', ['Kk']],
539 \ ['lLl', "s/L/\<C-V>\<C-M>/", ["l\<C-V>", 'l']],
540 \ ['mMm', 's/M/\r/', ['m', 'm']],
541 \ ['nNn', "s/N/\\\<C-V>\<C-M>/", ["n\<C-V>", 'n']],
542 \ ['oOo', 's/O/\n/', ["o\no"]],
543 \ ['pPp', 's/P/\b/', ["p\<C-H>p"]],
544 \ ['qQq', 's/Q/\t/', ["q\tq"]],
545 \ ['rRr', 's/R/\\/', ['r\r']],
546 \ ['sSs', 's/S/\c/', ['scs']],
547 \ ['tTt', "s/T/\<C-V>\<C-J>/", ["t\<C-V>\<C-J>t"]],
548 \ ['U', 's/U/\L\uuUu\l\EU/', ['UuuU']],
zeertzjq3269efd2022-06-12 11:13:05 +0100549 \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']],
550 \ ['\', 's/\\/\\\\/', ['\\']]
Bram Moolenaar15993ce2017-10-26 20:21:44 +0200551 \ ]
552 call Run_SubCmd_Tests(tests)
553endfunc
554
555func Test_sub_cmd_2()
556 set nomagic
557 set cpo&
558
559 " List entry format: [input, cmd, output]
560 let tests = [['A', 's/A/&&/', ['&&']],
561 \ ['B', 's/B/\&/', ['B']],
562 \ ['C123456789', 's/\mC\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\0\9\8\7\6\5\4\3\2\1/', ['C123456789987654321']],
563 \ ['D', 's/D/d/', ['d']],
564 \ ['E', 's/E/~/', ['~']],
565 \ ['F', 's/F/\~/', ['~']],
566 \ ['G', 's/G/\ugg/', ['Gg']],
567 \ ['H', 's/H/\Uh\Eh/', ['Hh']],
568 \ ['I', 's/I/\lII/', ['iI']],
569 \ ['J', 's/J/\LJ\EJ/', ['jJ']],
570 \ ['K', 's/K/\Uk\ek/', ['Kk']],
571 \ ['lLl', "s/L/\<C-V>\<C-M>/", ["l\<C-V>", 'l']],
572 \ ['mMm', 's/M/\r/', ['m', 'm']],
573 \ ['nNn', "s/N/\\\<C-V>\<C-M>/", ["n\<C-V>", 'n']],
574 \ ['oOo', 's/O/\n/', ["o\no"]],
575 \ ['pPp', 's/P/\b/', ["p\<C-H>p"]],
576 \ ['qQq', 's/Q/\t/', ["q\tq"]],
577 \ ['rRr', 's/R/\\/', ['r\r']],
578 \ ['sSs', 's/S/\c/', ['scs']],
579 \ ['tTt', "s/T/\<C-V>\<C-J>/", ["t\<C-V>\<C-J>t"]],
580 \ ['U', 's/U/\L\uuUu\l\EU/', ['UuuU']],
zeertzjq3269efd2022-06-12 11:13:05 +0100581 \ ['V', 's/V/\U\lVvV\u\Ev/', ['vVVv']],
582 \ ['\', 's/\\/\\\\/', ['\\']]
Bram Moolenaar15993ce2017-10-26 20:21:44 +0200583 \ ]
584 call Run_SubCmd_Tests(tests)
585endfunc
586
587func Test_sub_cmd_3()
588 set nomagic
589 set cpo&
590
591 " List entry format: [input, cmd, output]
592 let tests = [['aAa', "s/A/\\='\\'/", ['a\a']],
593 \ ['bBb', "s/B/\\='\\\\'/", ['b\\b']],
594 \ ['cCc', "s/C/\\='\<C-V>\<C-M>'/", ["c\<C-V>", 'c']],
595 \ ['dDd', "s/D/\\='\\\<C-V>\<C-M>'/", ["d\\\<C-V>", 'd']],
596 \ ['eEe', "s/E/\\='\\\\\<C-V>\<C-M>'/", ["e\\\\\<C-V>", 'e']],
597 \ ['fFf', "s/F/\\='\r'/", ['f', 'f']],
598 \ ['gGg', "s/G/\\='\<C-V>\<C-J>'/", ["g\<C-V>", 'g']],
599 \ ['hHh', "s/H/\\='\\\<C-V>\<C-J>'/", ["h\\\<C-V>", 'h']],
600 \ ['iIi', "s/I/\\='\\\\\<C-V>\<C-J>'/", ["i\\\\\<C-V>", 'i']],
601 \ ['jJj', "s/J/\\='\n'/", ['j', 'j']],
602 \ ['kKk', 's/K/\="\r"/', ['k', 'k']],
603 \ ['lLl', 's/L/\="\n"/', ['l', 'l']]
604 \ ]
605 call Run_SubCmd_Tests(tests)
606endfunc
607
Bram Moolenaarf1699962019-08-31 17:48:19 +0200608" Test for submatch() on :substitute.
Bram Moolenaar15993ce2017-10-26 20:21:44 +0200609func Test_sub_cmd_4()
610 set magic&
611 set cpo&
612
613 " List entry format: [input, cmd, output]
614 let tests = [ ['aAa', "s/A/\\=substitute(submatch(0), '.', '\\', '')/",
Bram Moolenaar1e115362019-01-09 23:01:02 +0100615 \ ['a\a']],
Bram Moolenaar15993ce2017-10-26 20:21:44 +0200616 \ ['bBb', "s/B/\\=substitute(submatch(0), '.', '\\', '')/",
Bram Moolenaar1e115362019-01-09 23:01:02 +0100617 \ ['b\b']],
Bram Moolenaar15993ce2017-10-26 20:21:44 +0200618 \ ['cCc', "s/C/\\=substitute(submatch(0), '.', '\<C-V>\<C-M>', '')/",
619 \ ["c\<C-V>", 'c']],
620 \ ['dDd', "s/D/\\=substitute(submatch(0), '.', '\\\<C-V>\<C-M>', '')/",
621 \ ["d\<C-V>", 'd']],
622 \ ['eEe', "s/E/\\=substitute(submatch(0), '.', '\\\\\<C-V>\<C-M>', '')/",
623 \ ["e\\\<C-V>", 'e']],
624 \ ['fFf', "s/F/\\=substitute(submatch(0), '.', '\\r', '')/",
625 \ ['f', 'f']],
626 \ ['gGg', 's/G/\=substitute(submatch(0), ".", "\<C-V>\<C-J>", "")/',
627 \ ["g\<C-V>", 'g']],
628 \ ['hHh', 's/H/\=substitute(submatch(0), ".", "\\\<C-V>\<C-J>", "")/',
629 \ ["h\<C-V>", 'h']],
630 \ ['iIi', 's/I/\=substitute(submatch(0), ".", "\\\\\<C-V>\<C-J>", "")/',
631 \ ["i\\\<C-V>", 'i']],
632 \ ['jJj', "s/J/\\=substitute(submatch(0), '.', '\\n', '')/",
633 \ ['j', 'j']],
634 \ ['kKk', "s/K/\\=substitute(submatch(0), '.', '\\r', '')/",
635 \ ['k', 'k']],
636 \ ['lLl', "s/L/\\=substitute(submatch(0), '.', '\\n', '')/",
637 \ ['l', 'l']],
638 \ ]
639 call Run_SubCmd_Tests(tests)
640endfunc
641
642func Test_sub_cmd_5()
643 set magic&
644 set cpo&
645
646 " List entry format: [input, cmd, output]
647 let tests = [ ['A123456789', 's/A\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\=submatch(0) . submatch(9) . submatch(8) . submatch(7) . submatch(6) . submatch(5) . submatch(4) . submatch(3) . submatch(2) . submatch(1)/', ['A123456789987654321']],
648 \ ['B123456789', 's/B\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)/\=string([submatch(0, 1), submatch(9, 1), submatch(8, 1), submatch(7, 1), submatch(6, 1), submatch(5, 1), submatch(4, 1), submatch(3, 1), submatch(2, 1), submatch(1, 1)])/', ["[['B123456789'], ['9'], ['8'], ['7'], ['6'], ['5'], ['4'], ['3'], ['2'], ['1']]"]],
649 \ ]
650 call Run_SubCmd_Tests(tests)
651endfunc
652
653" Test for *:s%* on :substitute.
654func Test_sub_cmd_6()
655 set magic&
656 set cpo+=/
657
658 " List entry format: [input, cmd, output]
659 let tests = [ ['A', 's/A/a/', ['a']],
660 \ ['B', 's/B/%/', ['a']],
661 \ ]
662 call Run_SubCmd_Tests(tests)
663
664 set cpo-=/
665 let tests = [ ['C', 's/C/c/', ['c']],
666 \ ['D', 's/D/%/', ['%']],
667 \ ]
668 call Run_SubCmd_Tests(tests)
669
670 set cpo&
671endfunc
672
673" Test for :s replacing \n with line break.
674func Test_sub_cmd_7()
675 set magic&
676 set cpo&
677
678 " List entry format: [input, cmd, output]
679 let tests = [ ["A\<C-V>\<C-M>A", 's/A./\=submatch(0)/', ['A', 'A']],
680 \ ["B\<C-V>\<C-J>B", 's/B./\=submatch(0)/', ['B', 'B']],
681 \ ["C\<C-V>\<C-J>C", 's/C./\=strtrans(string(submatch(0, 1)))/', [strtrans("['C\<C-J>']C")]],
682 \ ["D\<C-V>\<C-J>\nD", 's/D.\nD/\=strtrans(string(submatch(0, 1)))/', [strtrans("['D\<C-J>', 'D']")]],
683 \ ["E\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>\n\<C-V>\<C-J>E", 's/E\_.\{-}E/\=strtrans(string(submatch(0, 1)))/', [strtrans("['E\<C-J>', '\<C-J>', '\<C-J>', '\<C-J>', '\<C-J>E']")]],
684 \ ]
685 call Run_SubCmd_Tests(tests)
686
687 exe "normal oQ\nQ\<Esc>k"
Bram Moolenaare2e40752020-09-04 21:18:46 +0200688 call assert_fails('s/Q[^\n]Q/\=submatch(0)."foobar"/', 'E486:')
Bram Moolenaar15993ce2017-10-26 20:21:44 +0200689 enew!
690endfunc
691
692func TitleString()
693 let check = 'foo' =~ 'bar'
694 return ""
695endfunc
696
697func Test_sub_cmd_8()
698 set titlestring=%{TitleString()}
699
700 enew!
701 call append(0, ['', 'test_one', 'test_two'])
702 call cursor(1,1)
703 /^test_one/s/.*/\="foo\nbar"/
704 call assert_equal('foo', getline(2))
705 call assert_equal('bar', getline(3))
706 call feedkeys(':/^test_two/s/.*/\="foo\nbar"/c', "t")
707 call feedkeys("\<CR>y", "xt")
708 call assert_equal('foo', getline(4))
709 call assert_equal('bar', getline(5))
710
711 enew!
712 set titlestring&
713endfunc
Bram Moolenaar0e97b942019-03-27 22:53:53 +0100714
Bram Moolenaar80341bc2019-05-20 20:34:51 +0200715func Test_sub_cmd_9()
716 new
717 let input = ['1 aaa', '2 aaa', '3 aaa']
718 call setline(1, input)
719 func Foo()
720 return submatch(0)
721 endfunc
722 %s/aaa/\=Foo()/gn
723 call assert_equal(input, getline(1, '$'))
724 call assert_equal(1, &modifiable)
725
726 delfunc Foo
727 bw!
728endfunc
729
Bram Moolenaara04f4572022-09-13 13:45:26 +0100730func Test_sub_highlight_zero_match()
731 CheckRunVimInTerminal
732
733 let lines =<< trim END
734 call setline(1, ['one', 'two', 'three'])
735 END
736 call writefile(lines, 'XscriptSubHighlight', 'D')
737 let buf = RunVimInTerminal('-S XscriptSubHighlight', #{rows: 8, cols: 60})
738 call term_sendkeys(buf, ":%s/^/ /c\<CR>")
739 call VerifyScreenDump(buf, 'Test_sub_highlight_zer_match_1', {})
740
741 call term_sendkeys(buf, "\<Esc>")
742 call StopVimInTerminal(buf)
743endfunc
744
Bram Moolenaar0e97b942019-03-27 22:53:53 +0100745func Test_nocatch_sub_failure_handling()
Bram Moolenaar94722c52023-01-28 19:19:03 +0000746 " normal error results in all replacements
Bram Moolenaar80341bc2019-05-20 20:34:51 +0200747 func Foo()
Bram Moolenaar0e97b942019-03-27 22:53:53 +0100748 foobar
749 endfunc
750 new
751 call setline(1, ['1 aaa', '2 aaa', '3 aaa'])
zeertzjq3269efd2022-06-12 11:13:05 +0100752 " need silent! to avoid a delay when entering Insert mode
753 silent! %s/aaa/\=Foo()/g
Bram Moolenaar0e97b942019-03-27 22:53:53 +0100754 call assert_equal(['1 0', '2 0', '3 0'], getline(1, 3))
755
zeertzjq3269efd2022-06-12 11:13:05 +0100756 " Throw without try-catch causes abort after the first line.
Bram Moolenaar0e97b942019-03-27 22:53:53 +0100757 " We cannot test this, since it would stop executing the test script.
758
759 " try/catch does not result in any changes
760 func! Foo()
761 throw 'error'
762 endfunc
763 call setline(1, ['1 aaa', '2 aaa', '3 aaa'])
764 let error_caught = 0
765 try
766 %s/aaa/\=Foo()/g
767 catch
768 let error_caught = 1
769 endtry
770 call assert_equal(1, error_caught)
771 call assert_equal(['1 aaa', '2 aaa', '3 aaa'], getline(1, 3))
772
Bram Moolenaar6349e942019-05-18 13:41:22 +0200773 " Same, but using "n" flag so that "sandbox" gets set
774 call setline(1, ['1 aaa', '2 aaa', '3 aaa'])
775 let error_caught = 0
776 try
777 %s/aaa/\=Foo()/gn
778 catch
779 let error_caught = 1
780 endtry
781 call assert_equal(1, error_caught)
782 call assert_equal(['1 aaa', '2 aaa', '3 aaa'], getline(1, 3))
783
Bram Moolenaar80341bc2019-05-20 20:34:51 +0200784 delfunc Foo
Bram Moolenaar0e97b942019-03-27 22:53:53 +0100785 bwipe!
786endfunc
Bram Moolenaarc6b37db2019-04-27 18:00:34 +0200787
788" Test ":s/pat/sub/" with different ~s in sub.
789func Test_replace_with_tilde()
790 new
791 " Set the last replace string to empty
792 s/^$//
793 call append(0, ['- Bug in "vPPPP" on this text:'])
794 normal gg
795 s/u/~u~/
796 call assert_equal('- Bug in "vPPPP" on this text:', getline(1))
797 s/i/~u~/
798 call assert_equal('- Bug uuun "vPPPP" on this text:', getline(1))
799 s/o/~~~/
800 call assert_equal('- Bug uuun "vPPPP" uuuuuuuuun this text:', getline(1))
801 close!
802endfunc
803
804func Test_replace_keeppatterns()
805 new
806 a
807foobar
808
809substitute foo asdf
810
811one two
812.
813
814 normal gg
815 /^substitute
816 s/foo/bar/
817 call assert_equal('foo', @/)
818 call assert_equal('substitute bar asdf', getline('.'))
819
820 /^substitute
821 keeppatterns s/asdf/xyz/
822 call assert_equal('^substitute', @/)
823 call assert_equal('substitute bar xyz', getline('.'))
824
825 exe "normal /bar /e\<CR>"
826 call assert_equal(15, col('.'))
827 normal -
828 keeppatterns /xyz
829 call assert_equal('bar ', @/)
830 call assert_equal('substitute bar xyz', getline('.'))
831 exe "normal 0dn"
832 call assert_equal('xyz', getline('.'))
833
834 close!
835endfunc
Bram Moolenaarbb265962019-10-31 04:38:36 +0100836
837func Test_sub_beyond_end()
838 new
839 call setline(1, '#')
840 let @/ = '^#\n\zs'
841 s///e
842 call assert_equal('#', getline(1))
843 bwipe!
844endfunc
Bram Moolenaar5d98dc22020-01-29 21:57:34 +0100845
Bram Moolenaarea3db912020-02-02 15:32:13 +0100846" Test for repeating last substitution using :~ and :&r
847func Test_repeat_last_sub()
848 new
849 call setline(1, ['blue green yellow orange white'])
850 s/blue/red/
851 let @/ = 'yellow'
852 ~
853 let @/ = 'white'
854 :&r
855 let @/ = 'green'
856 s//gray
857 call assert_equal('red gray red orange red', getline(1))
858 close!
859endfunc
860
861" Test for Vi compatible substitution:
862" \/{string}/, \?{string}? and \&{string}&
863func Test_sub_vi_compatibility()
864 new
865 call setline(1, ['blue green yellow orange blue'])
866 let @/ = 'orange'
867 s\/white/
868 let @/ = 'blue'
869 s\?amber?
870 let @/ = 'white'
871 s\&green&
872 call assert_equal('amber green yellow white green', getline(1))
873 close!
Bram Moolenaar9fb7b422022-03-05 21:13:26 +0000874
875 call assert_fails('vim9cmd s\/white/', 'E1270:')
876 call assert_fails('vim9cmd s\?white?', 'E1270:')
877 call assert_fails('vim9cmd s\&white&', 'E1270:')
Bram Moolenaarea3db912020-02-02 15:32:13 +0100878endfunc
879
880" Test for substitute with the new text longer than the original text
881func Test_sub_expand_text()
882 new
883 call setline(1, 'abcabcabcabcabcabcabcabc')
884 s/b/\=repeat('B', 10)/g
885 call assert_equal(repeat('aBBBBBBBBBBc', 8), getline(1))
886 close!
887endfunc
888
Bram Moolenaar07ada5f2020-02-05 20:38:22 +0100889" Test for command failures when the last substitute pattern is not set.
890func Test_sub_with_no_last_pat()
Bram Moolenaar9f6277b2020-02-11 22:04:02 +0100891 let lines =<< trim [SCRIPT]
892 call assert_fails('~', 'E33:')
Bram Moolenaar9b7bf9e2020-07-11 22:14:59 +0200893 call assert_fails('s//abc/g', 'E35:')
894 call assert_fails('s\/bar', 'E35:')
895 call assert_fails('s\&bar&', 'E33:')
Bram Moolenaar9f6277b2020-02-11 22:04:02 +0100896 call writefile(v:errors, 'Xresult')
897 qall!
898 [SCRIPT]
Bram Moolenaar56564962022-10-10 22:39:42 +0100899 call writefile(lines, 'Xscript', 'D')
Bram Moolenaar9f6277b2020-02-11 22:04:02 +0100900 if RunVim([], [], '--clean -S Xscript')
901 call assert_equal([], readfile('Xresult'))
902 endif
Bram Moolenaar07ada5f2020-02-05 20:38:22 +0100903
Bram Moolenaar9f6277b2020-02-11 22:04:02 +0100904 let lines =<< trim [SCRIPT]
905 set cpo+=/
906 call assert_fails('s/abc/%/', 'E33:')
907 call writefile(v:errors, 'Xresult')
908 qall!
909 [SCRIPT]
910 call writefile(lines, 'Xscript')
911 if RunVim([], [], '--clean -S Xscript')
912 call assert_equal([], readfile('Xresult'))
913 endif
914
Bram Moolenaar9f6277b2020-02-11 22:04:02 +0100915 call delete('Xresult')
Bram Moolenaar07ada5f2020-02-05 20:38:22 +0100916endfunc
917
Bram Moolenaarca68ae12020-03-30 19:32:53 +0200918func Test_substitute()
919 call assert_equal('a1a2a3a', substitute('123', '\zs', 'a', 'g'))
Bram Moolenaar004a6782020-04-11 17:09:31 +0200920 " Substitute with special keys
921 call assert_equal("a\<End>c", substitute('abc', "a.c", "a\<End>c", ''))
922endfunc
923
924func Test_substitute_expr()
925 let g:val = 'XXX'
926 call assert_equal('XXX', substitute('yyy', 'y*', '\=g:val', ''))
927 call assert_equal('XXX', substitute('yyy', 'y*', {-> g:val}, ''))
928 call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
929 \ '\=nr2char("0x" . submatch(1))', 'g'))
930 call assert_equal("-\u1b \uf2-", substitute("-%1b %f2-", '%\(\x\x\)',
931 \ {-> nr2char("0x" . submatch(1))}, 'g'))
932
933 call assert_equal('231', substitute('123', '\(.\)\(.\)\(.\)',
934 \ {-> submatch(2) . submatch(3) . submatch(1)}, ''))
935
936 func Recurse()
937 return substitute('yyy', 'y\(.\)y', {-> submatch(1)}, '')
938 endfunc
939 " recursive call works
940 call assert_equal('-y-x-', substitute('xxx', 'x\(.\)x', {-> '-' . Recurse() . '-' . submatch(1) . '-'}, ''))
941
942 call assert_fails("let s=submatch([])", 'E745:')
943 call assert_fails("let s=submatch(2, [])", 'E745:')
944endfunc
945
946func Test_invalid_submatch()
947 " This was causing invalid memory access in Vim-7.4.2232 and older
948 call assert_fails("call substitute('x', '.', {-> submatch(10)}, '')", 'E935:')
949 call assert_fails('eval submatch(-1)', 'E935:')
950 call assert_equal('', submatch(0))
951 call assert_equal('', submatch(1))
952 call assert_equal([], submatch(0, 1))
953 call assert_equal([], submatch(1, 1))
954endfunc
955
Bram Moolenaar8a0dcf42020-09-06 15:14:45 +0200956func Test_submatch_list_concatenate()
957 let pat = 'A\(.\)'
958 let Rep = {-> string([submatch(0, 1)] + [[submatch(1)]])}
959 call substitute('A1', pat, Rep, '')->assert_equal("[['A1'], ['1']]")
960endfunc
961
Bram Moolenaar004a6782020-04-11 17:09:31 +0200962func Test_substitute_expr_arg()
963 call assert_equal('123456789-123456789=', substitute('123456789',
964 \ '\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)\(.\)',
965 \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
966
967 call assert_equal('123456-123456=789', substitute('123456789',
968 \ '\(.\)\(.\)\(.\)\(a*\)\(n*\)\(.\)\(.\)\(.\)\(x*\)',
969 \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
970
971 call assert_equal('123456789-123456789x=', substitute('123456789',
972 \ '\(.\)\(.\)\(.*\)',
973 \ {m -> m[0] . '-' . m[1] . m[2] . m[3] . 'x' . m[4] . m[5] . m[6] . m[7] . m[8] . m[9] . '='}, ''))
974
975 call assert_fails("call substitute('xxx', '.', {m -> string(add(m, 'x'))}, '')", 'E742:')
976 call assert_fails("call substitute('xxx', '.', {m -> string(insert(m, 'x'))}, '')", 'E742:')
977 call assert_fails("call substitute('xxx', '.', {m -> string(extend(m, ['x']))}, '')", 'E742:')
978 call assert_fails("call substitute('xxx', '.', {m -> string(remove(m, 1))}, '')", 'E742:')
979endfunc
980
981" Test for using a function to supply the substitute string
982func Test_substitute_using_func()
983 func Xfunc()
984 return '1234'
985 endfunc
986 call assert_equal('a1234f', substitute('abcdef', 'b..e',
987 \ function("Xfunc"), ''))
988 delfunc Xfunc
989endfunc
990
991" Test for using submatch() with a multiline match
992func Test_substitute_multiline_submatch()
993 new
994 call setline(1, ['line1', 'line2', 'line3', 'line4'])
995 %s/^line1\(\_.\+\)line4$/\=submatch(1)/
996 call assert_equal(['', 'line2', 'line3', ''], getline(1, '$'))
997 close!
Bram Moolenaarca68ae12020-03-30 19:32:53 +0200998endfunc
999
Bram Moolenaardf365142021-05-03 20:01:45 +02001000func Test_substitute_skipped_range()
1001 new
1002 if 0
1003 /1/5/2/2/\n
1004 endif
1005 call assert_equal([0, 1, 1, 0, 1], getcurpos())
1006 bwipe!
1007endfunc
1008
Dominique Pellebfb2bb12021-08-14 21:11:51 +02001009" Test using the 'gdefault' option (when on, flag 'g' is default on).
1010func Test_substitute_gdefault()
1011 new
1012
1013 " First check without 'gdefault'
1014 call setline(1, 'foo bar foo')
1015 s/foo/FOO/
1016 call assert_equal('FOO bar foo', getline(1))
1017 call setline(1, 'foo bar foo')
1018 s/foo/FOO/g
1019 call assert_equal('FOO bar FOO', getline(1))
1020 call setline(1, 'foo bar foo')
1021 s/foo/FOO/gg
1022 call assert_equal('FOO bar foo', getline(1))
1023
1024 " Then check with 'gdefault'
1025 set gdefault
1026 call setline(1, 'foo bar foo')
1027 s/foo/FOO/
1028 call assert_equal('FOO bar FOO', getline(1))
1029 call setline(1, 'foo bar foo')
1030 s/foo/FOO/g
1031 call assert_equal('FOO bar foo', getline(1))
1032 call setline(1, 'foo bar foo')
1033 s/foo/FOO/gg
1034 call assert_equal('FOO bar FOO', getline(1))
1035
1036 " Setting 'compatible' should reset 'gdefault'
1037 call assert_equal(1, &gdefault)
1038 set compatible
1039 call assert_equal(0, &gdefault)
1040 set nocompatible
1041 call assert_equal(0, &gdefault)
1042
1043 bw!
1044endfunc
1045
Bram Moolenaar37f47952022-01-29 14:21:51 +00001046" This was using "old_sub" after it was freed.
1047func Test_using_old_sub()
1048 set compatible maxfuncdepth=10
1049 new
1050 call setline(1, 'some text.')
1051 func Repl()
1052 ~
1053 s/
1054 endfunc
Bram Moolenaar44ddf192022-06-21 22:15:25 +01001055 silent! s/\%')/\=Repl()
Bram Moolenaar37f47952022-01-29 14:21:51 +00001056
1057 delfunc Repl
1058 bwipe!
1059 set nocompatible
1060endfunc
1061
Bram Moolenaare2bd8602022-05-18 13:11:57 +01001062" This was switching windows in between computing the length and using it.
1063func Test_sub_change_window()
1064 silent! lfile
1065 sil! norm o0000000000000000000000000000000000000000000000000000
1066 func Repl()
1067 lopen
1068 endfunc
1069 silent! s/\%')/\=Repl()
1070 bwipe!
1071 bwipe!
1072 delfunc Repl
1073endfunc
1074
Bram Moolenaar338f1fc2022-05-26 15:56:23 +01001075" This was undoign a change in between computing the length and using it.
1076func Do_Test_sub_undo_change()
1077 new
1078 norm o0000000000000000000000000000000000000000000000000000
1079 silent! s/\%')/\=Repl()
1080 bwipe!
1081endfunc
1082
1083func Test_sub_undo_change()
1084 func Repl()
1085 silent! norm g-
1086 endfunc
1087 call Do_Test_sub_undo_change()
1088
1089 func! Repl()
1090 silent earlier
1091 endfunc
1092 call Do_Test_sub_undo_change()
1093
1094 delfunc Repl
1095endfunc
1096
Bram Moolenaar71223e22022-05-30 15:23:09 +01001097" This was opening a command line window from the expression
1098func Test_sub_open_cmdline_win()
1099 " the error only happens in a very specific setup, run a new Vim instance to
1100 " get a clean starting point.
1101 let lines =<< trim [SCRIPT]
Bram Moolenaarbe990422022-05-30 16:01:42 +01001102 set vb t_vb=
Bram Moolenaar71223e22022-05-30 15:23:09 +01001103 norm o0000000000000000000000000000000000000000000000000000
1104 func Replace()
1105 norm q/
1106 endfunc
1107 s/\%')/\=Replace()
1108 redir >Xresult
1109 messages
1110 redir END
1111 qall!
1112 [SCRIPT]
Bram Moolenaar56564962022-10-10 22:39:42 +01001113 call writefile(lines, 'Xscript', 'D')
Bram Moolenaar71223e22022-05-30 15:23:09 +01001114 if RunVim([], [], '-u NONE -S Xscript')
Bram Moolenaarbe990422022-05-30 16:01:42 +01001115 call assert_match('E565: Not allowed to change text or change window',
1116 \ readfile('Xresult')->join('XX'))
Bram Moolenaar71223e22022-05-30 15:23:09 +01001117 endif
1118
Bram Moolenaar71223e22022-05-30 15:23:09 +01001119 call delete('Xresult')
1120endfunc
1121
Bram Moolenaard6211a52022-06-18 19:48:14 +01001122" This was editing a script file from the expression
1123func Test_sub_edit_scriptfile()
1124 new
1125 norm o0000000000000000000000000000000000000000000000000000
1126 func EditScript()
Bram Moolenaarb18b4962022-09-02 21:55:50 +01001127 silent! scr! Xsedfile
Bram Moolenaard6211a52022-06-18 19:48:14 +01001128 endfunc
1129 s/\%')/\=EditScript()
1130
1131 delfunc EditScript
1132 bwipe!
1133endfunc
1134
Bram Moolenaarcc762a42022-11-25 13:03:31 +00001135" This was editing another file from the expression.
1136func Test_sub_expr_goto_other_file()
1137 call writefile([''], 'Xfileone', 'D')
1138 enew!
1139 call setline(1, ['a', 'b', 'c', 'd',
1140 \ 'Xfileone zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz'])
1141
1142 func g:SplitGotoFile()
1143 exe "sil! norm 0\<C-W>gf"
1144 return ''
1145 endfunc
1146
1147 $
1148 s/\%')/\=g:SplitGotoFile()
1149
1150 delfunc g:SplitGotoFile
1151 bwipe!
1152endfunc
1153
Bram Moolenaar3ac1d972023-01-04 17:17:54 +00001154func Test_recursive_expr_substitute()
1155 " this was reading invalid memory
1156 let lines =<< trim END
1157 func Repl(g, n)
1158 s
1159 r%:s000
1160 endfunc
1161 next 0
1162 let caught = 0
1163 s/\%')/\=Repl(0, 0)
1164 qall!
1165 END
1166 call writefile(lines, 'XexprSubst', 'D')
1167 call RunVim([], [], '--clean -S XexprSubst')
1168endfunc
1169
Yegappan Lakshmanan5e877ba2022-03-25 21:19:26 +00001170" Test for the 2-letter and 3-letter :substitute commands
1171func Test_substitute_short_cmd()
1172 new
1173 call setline(1, ['one', 'one one one'])
1174 s/one/two
1175 call cursor(2, 1)
1176
1177 " :sc
1178 call feedkeys(":sc\<CR>y", 'xt')
1179 call assert_equal('two one one', getline(2))
1180
1181 " :scg
1182 call setline(2, 'one one one')
1183 call feedkeys(":scg\<CR>nyq", 'xt')
1184 call assert_equal('one two one', getline(2))
1185
1186 " :sci
1187 call setline(2, 'ONE One onE')
1188 call feedkeys(":sci\<CR>y", 'xt')
1189 call assert_equal('two One onE', getline(2))
1190
1191 " :scI
1192 set ignorecase
1193 call setline(2, 'ONE One one')
1194 call feedkeys(":scI\<CR>y", 'xt')
1195 call assert_equal('ONE One two', getline(2))
1196 set ignorecase&
1197
1198 " :scn
1199 call setline(2, 'one one one')
1200 let t = execute('scn')->split("\n")
1201 call assert_equal(['1 match on 1 line'], t)
1202 call assert_equal('one one one', getline(2))
1203
1204 " :scp
1205 call setline(2, "\tone one one")
1206 redir => output
1207 call feedkeys(":scp\<CR>y", 'xt')
1208 redir END
1209 call assert_equal(' two one one', output->split("\n")[-1])
1210 call assert_equal("\ttwo one one", getline(2))
1211
1212 " :scl
1213 call setline(2, "\tone one one")
1214 redir => output
1215 call feedkeys(":scl\<CR>y", 'xt')
1216 redir END
1217 call assert_equal("^Itwo one one$", output->split("\n")[-1])
1218 call assert_equal("\ttwo one one", getline(2))
1219
1220 " :sgc
1221 call setline(2, 'one one one one one')
1222 call feedkeys(":sgc\<CR>nyyq", 'xt')
1223 call assert_equal('one two two one one', getline(2))
1224
1225 " :sg
1226 call setline(2, 'one one one')
1227 sg
1228 call assert_equal('two two two', getline(2))
1229
1230 " :sgi
1231 call setline(2, 'ONE One onE')
1232 sgi
1233 call assert_equal('two two two', getline(2))
1234
1235 " :sgI
1236 set ignorecase
1237 call setline(2, 'ONE One one')
1238 sgI
1239 call assert_equal('ONE One two', getline(2))
1240 set ignorecase&
1241
1242 " :sgn
1243 call setline(2, 'one one one')
1244 let t = execute('sgn')->split("\n")
1245 call assert_equal(['3 matches on 1 line'], t)
1246 call assert_equal('one one one', getline(2))
1247
1248 " :sgp
1249 call setline(2, "\tone one one")
1250 redir => output
1251 sgp
1252 redir END
1253 call assert_equal(' two two two', output->split("\n")[-1])
1254 call assert_equal("\ttwo two two", getline(2))
1255
1256 " :sgl
1257 call setline(2, "\tone one one")
1258 redir => output
1259 sgl
1260 redir END
1261 call assert_equal("^Itwo two two$", output->split("\n")[-1])
1262 call assert_equal("\ttwo two two", getline(2))
1263
1264 " :sgr
1265 call setline(2, "one one one")
1266 call cursor(2, 1)
1267 s/abc/xyz/e
1268 let @/ = 'one'
1269 sgr
1270 call assert_equal('xyz xyz xyz', getline(2))
1271
1272 " :sic
1273 call cursor(1, 1)
1274 s/one/two/e
1275 call setline(2, "ONE One one")
1276 call cursor(2, 1)
1277 call feedkeys(":sic\<CR>y", 'xt')
1278 call assert_equal('two One one', getline(2))
1279
1280 " :si
1281 call setline(2, "ONE One one")
1282 si
1283 call assert_equal('two One one', getline(2))
1284
1285 " :siI
1286 call setline(2, "ONE One one")
1287 siI
1288 call assert_equal('ONE One two', getline(2))
1289
1290 " :sin
1291 call setline(2, 'ONE One onE')
1292 let t = execute('sin')->split("\n")
1293 call assert_equal(['1 match on 1 line'], t)
1294 call assert_equal('ONE One onE', getline(2))
1295
1296 " :sip
1297 call setline(2, "\tONE One onE")
1298 redir => output
1299 sip
1300 redir END
1301 call assert_equal(' two One onE', output->split("\n")[-1])
1302 call assert_equal("\ttwo One onE", getline(2))
1303
1304 " :sir
1305 call setline(2, "ONE One onE")
1306 call cursor(2, 1)
1307 s/abc/xyz/e
1308 let @/ = 'one'
1309 sir
1310 call assert_equal('xyz One onE', getline(2))
1311
1312 " :sIc
1313 call cursor(1, 1)
1314 s/one/two/e
1315 call setline(2, "ONE One one")
1316 call cursor(2, 1)
1317 call feedkeys(":sIc\<CR>y", 'xt')
1318 call assert_equal('ONE One two', getline(2))
1319
1320 " :sIg
1321 call setline(2, "ONE one onE one")
1322 sIg
1323 call assert_equal('ONE two onE two', getline(2))
1324
1325 " :sIi
1326 call setline(2, "ONE One one")
1327 sIi
1328 call assert_equal('two One one', getline(2))
1329
1330 " :sI
1331 call setline(2, "ONE One one")
1332 sI
1333 call assert_equal('ONE One two', getline(2))
1334
1335 " :sIn
1336 call setline(2, 'ONE One one')
1337 let t = execute('sIn')->split("\n")
1338 call assert_equal(['1 match on 1 line'], t)
1339 call assert_equal('ONE One one', getline(2))
1340
1341 " :sIp
1342 call setline(2, "\tONE One one")
1343 redir => output
1344 sIp
1345 redir END
1346 call assert_equal(' ONE One two', output->split("\n")[-1])
1347 call assert_equal("\tONE One two", getline(2))
1348
1349 " :sIl
1350 call setline(2, "\tONE onE one")
1351 redir => output
1352 sIl
1353 redir END
1354 call assert_equal("^IONE onE two$", output->split("\n")[-1])
1355 call assert_equal("\tONE onE two", getline(2))
1356
1357 " :sIr
1358 call setline(2, "ONE one onE")
1359 call cursor(2, 1)
1360 s/abc/xyz/e
1361 let @/ = 'one'
1362 sIr
1363 call assert_equal('ONE xyz onE', getline(2))
1364
1365 " :src
1366 call setline(2, "ONE one one")
1367 call cursor(2, 1)
1368 s/abc/xyz/e
1369 let @/ = 'one'
1370 call feedkeys(":src\<CR>y", 'xt')
1371 call assert_equal('ONE xyz one', getline(2))
1372
1373 " :srg
1374 call setline(2, "one one one")
1375 call cursor(2, 1)
1376 s/abc/xyz/e
1377 let @/ = 'one'
1378 srg
1379 call assert_equal('xyz xyz xyz', getline(2))
1380
1381 " :sri
1382 call setline(2, "ONE one onE")
1383 call cursor(2, 1)
1384 s/abc/xyz/e
1385 let @/ = 'one'
1386 sri
1387 call assert_equal('xyz one onE', getline(2))
1388
1389 " :srI
1390 call setline(2, "ONE one onE")
1391 call cursor(2, 1)
1392 s/abc/xyz/e
1393 let @/ = 'one'
1394 srI
1395 call assert_equal('ONE xyz onE', getline(2))
1396
1397 " :srn
1398 call setline(2, "ONE one onE")
1399 call cursor(2, 1)
1400 s/abc/xyz/e
1401 let @/ = 'one'
1402 let t = execute('srn')->split("\n")
1403 call assert_equal(['1 match on 1 line'], t)
1404 call assert_equal('ONE one onE', getline(2))
1405
1406 " :srp
1407 call setline(2, "\tONE one onE")
1408 call cursor(2, 1)
1409 s/abc/xyz/e
1410 let @/ = 'one'
1411 redir => output
1412 srp
1413 redir END
1414 call assert_equal(' ONE xyz onE', output->split("\n")[-1])
1415 call assert_equal("\tONE xyz onE", getline(2))
1416
1417 " :srl
1418 call setline(2, "\tONE one onE")
1419 call cursor(2, 1)
1420 s/abc/xyz/e
1421 let @/ = 'one'
1422 redir => output
1423 srl
1424 redir END
1425 call assert_equal("^IONE xyz onE$", output->split("\n")[-1])
1426 call assert_equal("\tONE xyz onE", getline(2))
1427
1428 " :sr
1429 call setline(2, "ONE one onE")
1430 call cursor(2, 1)
1431 s/abc/xyz/e
1432 let @/ = 'one'
1433 sr
1434 call assert_equal('ONE xyz onE', getline(2))
1435
1436 " :sce
1437 s/abc/xyz/e
1438 call assert_fails("sc", 'E486:')
1439 sce
1440 " :sge
1441 call assert_fails("sg", 'E486:')
1442 sge
1443 " :sie
1444 call assert_fails("si", 'E486:')
1445 sie
1446 " :sIe
1447 call assert_fails("sI", 'E486:')
1448 sIe
1449
1450 bw!
1451endfunc
Bram Moolenaar37f47952022-01-29 14:21:51 +00001452
Bram Moolenaarab9a2d82023-05-09 21:15:30 +01001453" Check handling expanding "~" resulting in extremely long text.
Bram Moolenaar916d6dd2023-05-09 21:45:47 +01001454" FIXME: disabled, it takes too long to run on CI
Bram Moolenaara4467c42023-05-09 22:07:11 +01001455"func Test_substitute_tilde_too_long()
1456" enew!
1457"
1458" s/.*/ixxx
1459" s//~~~~~~~~~AAAAAAA@(
1460"
1461" " Either fails with "out of memory" or "text too long".
1462" " This can take a long time.
1463" call assert_fails('sil! norm &&&&&&&&&', ['E1240:\|E342:'])
1464"
1465" bwipe!
1466"endfunc
Bram Moolenaarab9a2d82023-05-09 21:15:30 +01001467
Bram Moolenaar44ddf192022-06-21 22:15:25 +01001468" This should be done last to reveal a memory leak when vim_regsub_both() is
1469" called to evaluate an expression but it is not used in a second call.
1470func Test_z_substitute_expr_leak()
1471 func SubExpr()
1472 ~n
1473 endfunc
1474 silent! s/\%')/\=SubExpr()
1475 delfunc SubExpr
1476endfunc
1477
Christian Brabandt18d27092023-09-06 19:53:36 +02001478func Test_substitute_expr_switch_win()
1479 func R()
1480 wincmd x
1481 return 'XXXX'
1482 endfunc
1483 new Xfoobar
1484 let bufnr = bufnr('%')
Christian Brabandt26c11c52023-11-22 21:26:41 +01001485 put ='abcdef'
Christian Brabandt18d27092023-09-06 19:53:36 +02001486 silent! s/\%')/\=R()
Christian Brabandtee17b6f2023-09-09 11:23:50 +02001487 call assert_fails(':%s/./\=R()/g', 'E565:')
Christian Brabandt18d27092023-09-06 19:53:36 +02001488 delfunc R
1489 exe bufnr .. "bw!"
1490endfunc
1491
Christian Brabandt26c11c52023-11-22 21:26:41 +01001492" recursive call of :s using test-replace special
1493func Test_substitute_expr_recursive()
1494 func Q()
1495 %s/./\='foobar'/gn
1496 return "foobar"
1497 endfunc
1498 func R()
1499 %s/./\=Q()/g
1500 endfunc
1501 new Xfoobar_UAF
1502 let bufnr = bufnr('%')
1503 put ='abcdef'
1504 silent! s/./\=R()/g
1505 call assert_fails(':%s/./\=R()/g', 'E565:')
1506 delfunc R
1507 delfunc Q
1508 exe bufnr .. "bw!"
1509endfunc
1510
Yegappan Lakshmanan4776e642024-05-19 09:06:50 +02001511" Test for changing 'cpo' in a substitute expression
1512func Test_substitute_expr_cpo()
1513 func XSubExpr()
1514 set cpo=
1515 return 'x'
1516 endfunc
1517
1518 let save_cpo = &cpo
1519 call assert_equal('xxx', substitute('abc', '.', '\=XSubExpr()', 'g'))
1520 call assert_equal(save_cpo, &cpo)
1521
1522 delfunc XSubExpr
1523endfunc
1524
Bram Moolenaar5d98dc22020-01-29 21:57:34 +01001525" vim: shiftwidth=2 sts=2 expandtab