Skip to content

Commit b448d7e

Browse files
committed
Abort failing FindReplaceDocumentAdapter operations consistently #2657
The implementation of FindReplaceDocumentAdapter#findReplace(), used by its API methods #find() and #replace(), currently leaves an inconsistent state when failing because of an invalid input or state. This inconsistent state affects the last executed operation type, which the adapter stores in order to identify whether a replace operation is preceded by an according find operation. In case the #findReplace() method is called with an invalid position or to execute a replace without a preceding find, the method aborts (throwing an exception) without storing the operation to be executed as the last executed operation, i.e., it leaves the adapter as if that method was never called. In case the method is called in regular expression mode and the expression used as find or replace input is invalid, the operation aborts (throwing an exception) but still stores the operation to be executed as the last executed operation, i.e., it leaves the adapter as if that method was called successfully. This behavior is unexpected as is handles invalid inputs inconsistently. This also becomes visible in the existing consumers, such as FindReplaceTarget#replaceSelection() used by the FindReplaceDialog and FindReplaceOvleray. They assume that in case an exception occurs while trying to perform a replace operation, a subsequent replace should succeed without an additional find operation being required. This assumption does currently not hold. This change corrects the behavior of the FindReplaceDocumentAdapter#findReplace() method to always leave the adapter in an unmodified state when the method fails because of being called with invalid input or in an inconsistent state. In addition, regular expressions with an unfinished character escape at the end now lead to a proper exception. Fixes ##2657
1 parent 688f837 commit b448d7e

File tree

3 files changed

+53
-8
lines changed

3 files changed

+53
-8
lines changed

bundles/org.eclipse.text/src/org/eclipse/jface/text/FindReplaceDocumentAdapter.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,6 @@ private IRegion findReplace(final FindReplaceOperationCode operationCode, int st
188188
}
189189
}
190190

191-
// Set state
192-
fFindReplaceState= operationCode;
193-
194191
if (operationCode == REPLACE || operationCode == REPLACE_FIND_NEXT) {
195192
if (regExSearch) {
196193
Pattern pattern= fFindReplaceMatcher.pattern();
@@ -199,7 +196,10 @@ private IRegion findReplace(final FindReplaceOperationCode operationCode, int st
199196
replaceText= interpretReplaceEscapes(replaceText, prevMatch);
200197
Matcher replaceTextMatcher= pattern.matcher(prevMatch);
201198
replaceText= replaceTextMatcher.replaceFirst(replaceText);
202-
} catch (IndexOutOfBoundsException ex) {
199+
} catch (IndexOutOfBoundsException | IllegalArgumentException ex) {
200+
// These exceptions are thrown by Matcher#replaceFirst(), capturing information about
201+
// invalid regular expression patterns, such as unfinished character escape sequences
202+
// at the end of the pattern
203203
throw new PatternSyntaxException(ex.getLocalizedMessage(), replaceText, -1);
204204
}
205205
}
@@ -214,12 +214,13 @@ private IRegion findReplace(final FindReplaceOperationCode operationCode, int st
214214
}
215215

216216
fDocument.replace(offset, length, replaceText);
217-
217+
fFindReplaceState= operationCode;
218+
218219
if (operationCode == REPLACE) {
219220
return new Region(offset, replaceText.length());
220221
}
221222
}
222-
223+
223224
if (operationCode != REPLACE) {
224225
try {
225226
if (forwardSearch) {
@@ -230,8 +231,11 @@ private IRegion findReplace(final FindReplaceOperationCode operationCode, int st
230231
else
231232
found= fFindReplaceMatcher.find();
232233

233-
if (operationCode == REPLACE_FIND_NEXT)
234+
if (operationCode == REPLACE_FIND_NEXT) {
234235
fFindReplaceState= FIND_NEXT;
236+
} else {
237+
fFindReplaceState= operationCode;
238+
}
235239

236240
if (found && !fFindReplaceMatcher.group().isEmpty())
237241
return new Region(fFindReplaceMatcher.start(), fFindReplaceMatcher.group().length());
@@ -247,6 +251,7 @@ private IRegion findReplace(final FindReplaceOperationCode operationCode, int st
247251
found= fFindReplaceMatcher.find(index + 1);
248252
}
249253
fFindReplaceMatchOffset= index;
254+
fFindReplaceState= operationCode;
250255
if (index > -1) {
251256
// must set matcher to correct position
252257
fFindReplaceMatcher.find(index);

tests/org.eclipse.text.tests/src/org/eclipse/text/tests/FindReplaceDocumentAdapterTest.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@
1515
*******************************************************************************/
1616
package org.eclipse.text.tests;
1717

18-
import static org.junit.Assert.*;
18+
import static org.junit.Assert.assertEquals;
19+
import static org.junit.Assert.assertNotNull;
20+
import static org.junit.Assert.assertNull;
21+
import static org.junit.Assert.assertThrows;
22+
import static org.junit.Assert.assertTrue;
23+
import static org.junit.Assert.fail;
1924

2025
import java.util.Arrays;
2126
import java.util.Locale;
@@ -290,6 +295,18 @@ public void testRegexReplace3() throws Exception {
290295
assertEquals("f0", fDocument.get());
291296
}
292297

298+
@Test
299+
public void testRegexReplace_invalidRegex() throws Exception {
300+
FindReplaceDocumentAdapter findReplaceDocumentAdapter = new FindReplaceDocumentAdapter(fDocument);
301+
302+
fDocument.set("foo");
303+
assertThrows(PatternSyntaxException.class, () -> regexReplace("foo", "foo\\", findReplaceDocumentAdapter));
304+
assertEquals("foo", fDocument.get());
305+
306+
findReplaceDocumentAdapter.replace("foo" + System.lineSeparator(), true);
307+
assertEquals("foo" + System.lineSeparator(), fDocument.get());
308+
}
309+
293310
/*
294311
* @since 3.4
295312
*/

tests/org.eclipse.ui.workbench.texteditor.tests/src/org/eclipse/ui/internal/findandreplace/FindReplaceLogicTest.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,29 @@ public void testPerformReplaceAndFindRegEx_incrementalActive() {
438438
executeReplaceAndFindRegExTest(textViewer, findReplaceLogic);
439439
}
440440

441+
@Test
442+
public void testPerformReplaceAndFindRegEx_withInvalidEscapeInReplace() {
443+
TextViewer textViewer= setupTextViewer("Hello");
444+
IFindReplaceLogic findReplaceLogic= setupFindReplaceLogicObject(textViewer);
445+
findReplaceLogic.activate(SearchOptions.FORWARD);
446+
findReplaceLogic.activate(SearchOptions.REGEX);
447+
448+
setFindAndReplaceString(findReplaceLogic, "Hello", "Hello\\");
449+
boolean status= findReplaceLogic.performReplaceAndFind();
450+
assertFalse(status);
451+
assertThat(textViewer.getDocument().get(), equalTo("Hello"));
452+
assertThat(findReplaceLogic.getTarget().getSelectionText(), equalTo("Hello"));
453+
assertThat(findReplaceLogic.getStatus(), instanceOf(InvalidRegExStatus.class));
454+
455+
setFindAndReplaceString(findReplaceLogic, "Hello", "Hello" + System.lineSeparator());
456+
457+
status= findReplaceLogic.performReplaceAndFind();
458+
assertTrue(status);
459+
assertThat(textViewer.getDocument().get(), equalTo("Hello" + System.lineSeparator()));
460+
assertThat(findReplaceLogic.getTarget().getSelectionText(), equalTo("Hello" + System.lineSeparator()));
461+
expectStatusIsCode(findReplaceLogic, FindStatus.StatusCode.NO_MATCH);
462+
}
463+
441464
private void executeReplaceAndFindRegExTest(TextViewer textViewer, IFindReplaceLogic findReplaceLogic) {
442465
setFindAndReplaceString(findReplaceLogic, "<(\\w*)>", " ");
443466

0 commit comments

Comments
 (0)