@@ -246,32 +246,108 @@ def test_replace_backwards(self):
246246 equal (text .get ('1.2' , '1.5' ), 'was' )
247247
248248 def test_replace_all (self ):
249+ # The default mode, forward with wrap around, replaces every
250+ # match, both below and above the current position.
251+ equal = self .assertEqual
249252 text = self .text
250253 pv = self .engine .patvar
251254 rv = self .dialog .replvar
252255 replace_all = self .dialog .replace_all
253256
254- text .insert ('insert' , '\n ' )
255- text .insert ('insert' , text .get ('1.0' , 'end' )* 100 )
256- pv .set ('is' )
257- rv .set ('was' )
257+ text .delete ('1.0' , 'end' )
258+ text .insert ('1.0' , 'a\n a\n a\n ' )
259+ text .mark_set ('insert' , '2.1' )
260+ pv .set ('a' )
261+ rv .set ('b' )
258262 replace_all ()
259- self . assertNotIn ( 'is' , text .get ('1.0' , 'end' ))
263+ equal ( text .get ('1.0' , '3. end' ), 'b \n b \n b' ) # Wrapped around.
260264
265+ # An empty regular expression is reported as an error.
261266 self .engine .revar .set (True )
262267 pv .set ('' )
263268 replace_all ()
264269 self .assertIn ('error' , showerror .title )
265270 self .assertIn ('Empty' , showerror .message )
266271
272+ # An invalid replacement expression is reported as an error,
273+ # and nothing is replaced.
274+ text .delete ('1.0' , 'end' )
275+ text .insert ('1.0' , 'asT' )
267276 pv .set ('[s][T]' )
268277 rv .set ('\\ ' )
269278 replace_all ()
279+ self .assertIn ('error' , showerror .title )
280+ self .assertIn ('Invalid Replace Expression' , showerror .message )
281+ equal (text .get ('1.0' , '1.end' ), 'asT' )
270282
283+ # A pattern that is not present replaces nothing.
271284 self .engine .revar .set (False )
285+ text .delete ('1.0' , 'end' )
286+ text .insert ('1.0' , 'unchanged' )
272287 pv .set ('text which is not present' )
273288 rv .set ('foobar' )
274289 replace_all ()
290+ equal (text .get ('1.0' , '1.end' ), 'unchanged' )
291+
292+ def test_replace_all_backwards_no_wrap (self ):
293+ # gh-71956: 'up' without wrap replaces all matches from the start
294+ # of the text down to the current position, not just one up.
295+ equal = self .assertEqual
296+ text = self .text
297+ pv = self .engine .patvar
298+ rv = self .dialog .replvar
299+ replace_all = self .dialog .replace_all
300+ self .engine .backvar .set (True )
301+ self .engine .wrapvar .set (False )
302+
303+ text .delete ('1.0' , 'end' )
304+ text .insert ('1.0' , 'a\n a\n a\n ' )
305+ text .mark_set ('insert' , '2.1' )
306+ pv .set ('a' )
307+ rv .set ('b' )
308+ replace_all ()
309+ equal (text .get ('1.0' , '1.end' ), 'b' ) # Above the cursor.
310+ equal (text .get ('2.0' , '2.end' ), 'b' ) # At the cursor.
311+ equal (text .get ('3.0' , '3.end' ), 'a' ) # Below the cursor, untouched.
312+
313+ def test_replace_all_forwards_no_wrap (self ):
314+ # 'down' without wrap replaces all matches from the current
315+ # position to the end of the text, and none before it.
316+ equal = self .assertEqual
317+ text = self .text
318+ pv = self .engine .patvar
319+ rv = self .dialog .replvar
320+ replace_all = self .dialog .replace_all
321+ self .engine .backvar .set (False )
322+ self .engine .wrapvar .set (False )
323+
324+ text .delete ('1.0' , 'end' )
325+ text .insert ('1.0' , 'a\n a\n a\n ' )
326+ text .mark_set ('insert' , '2.1' )
327+ pv .set ('a' )
328+ rv .set ('b' )
329+ replace_all ()
330+ equal (text .get ('1.0' , '1.end' ), 'a' ) # Before the cursor, untouched.
331+ equal (text .get ('2.0' , '2.end' ), 'a' ) # Before the cursor, untouched.
332+ equal (text .get ('3.0' , '3.end' ), 'b' ) # After the cursor.
333+
334+ def test_replace_all_backwards_wrap (self ):
335+ # With wrap around, an 'up' search also replaces every match.
336+ equal = self .assertEqual
337+ text = self .text
338+ pv = self .engine .patvar
339+ rv = self .dialog .replvar
340+ replace_all = self .dialog .replace_all
341+ self .engine .backvar .set (True )
342+ self .engine .wrapvar .set (True )
343+
344+ text .delete ('1.0' , 'end' )
345+ text .insert ('1.0' , 'a\n a\n a\n ' )
346+ text .mark_set ('insert' , '2.1' )
347+ pv .set ('a' )
348+ rv .set ('b' )
349+ replace_all ()
350+ equal (text .get ('1.0' , '3.end' ), 'b\n b\n b' )
275351
276352 def test_default_command (self ):
277353 text = self .text
0 commit comments