diff options
8 files changed, 164 insertions, 34 deletions
diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTarget.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTarget.java index dc2939b8127..00042b61a4c 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTarget.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTarget.java @@ -20,31 +20,29 @@ import org.eclipse.swt.graphics.Point; /** * Defines the target for finding and replacing strings. * <p> - * The two main methods are <code>findAndSelect</code> and - * <code>replaceSelection</code>. The target does not provide any way to - * modify the content other than replacing the selection. + * The two main methods are <code>findAndSelect</code> and <code>replaceSelection</code>. The target + * does not provide any way to modify the content other than replacing the selection. * <p> * - * In order to provide backward compatibility for clients of - * <code>IFindReplaceTarget</code>, extension interfaces are used as a means - * of evolution. The following extension interfaces exist: + * In order to provide backward compatibility for clients of <code>IFindReplaceTarget</code>, + * extension interfaces are used as a means of evolution. The following extension interfaces exist: * <ul> - * <li>{@link org.eclipse.jface.text.IFindReplaceTargetExtension} since version - * 2.0 introducing the notion of find/replace session and of a find/replace - * scope. In additions, in allows clients to replace all occurrences of a given - * find query.</li> - * <li>{@link org.eclipse.jface.text.IFindReplaceTargetExtension3} since - * version 3.0 allowing clients to specify search queries as regular - * expressions.</li> + * <li>{@link org.eclipse.jface.text.IFindReplaceTargetExtension} since version 2.0 introducing the + * notion of find/replace session and of a find/replace scope. In additions, in allows clients to + * replace all occurrences of a given find query.</li> + * <li>{@link org.eclipse.jface.text.IFindReplaceTargetExtension3} since version 3.0 allowing + * clients to specify search queries as regular expressions.</li> + * <li>{@link org.eclipse.jface.text.IFindReplaceTargetExtension4} since version 3.19 allowing + * clients to select multiple text ranges in the target.</li> * </ul> * <p> * Clients of a <code>IFindReplaceTarget</code> that also implements the - * <code>IFindReplaceTargetExtension</code> have to indicate the start of a find/replace - * session before using the target and to indicate the end of the session when the - * target is no longer used. + * <code>IFindReplaceTargetExtension</code> have to indicate the start of a find/replace session + * before using the target and to indicate the end of the session when the target is no longer used. * * @see org.eclipse.jface.text.IFindReplaceTargetExtension * @see org.eclipse.jface.text.IFindReplaceTargetExtension3 + * @see org.eclipse.jface.text.IFindReplaceTargetExtension4 */ public interface IFindReplaceTarget { diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension.java index fdce26b6ae5..ace456b7d68 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension.java @@ -76,6 +76,7 @@ public interface IFindReplaceTargetExtension { * * @param offset the offset of the selection * @param length the length of the selection + * @see IFindReplaceTargetExtension4#setSelection(IRegion[]) */ void setSelection(int offset, int length); diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension4.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension4.java new file mode 100644 index 00000000000..ed88eb53dd9 --- /dev/null +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/IFindReplaceTargetExtension4.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2021 Red Hat Inc. and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.jface.text; + +/** + * Extension interface for {@link org.eclipse.jface.text.IFindReplaceTarget} providing methods to + * select multiple text ranges. + * + * @since 3.19 + */ +public interface IFindReplaceTargetExtension4 { + + void setSelection(IRegion[] ranges); +} diff --git a/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewer.java b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewer.java index d8867419338..4582be94166 100644 --- a/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewer.java +++ b/org.eclipse.jface.text/src/org/eclipse/jface/text/TextViewer.java @@ -765,7 +765,7 @@ public class TextViewer extends Viewer implements /** * This viewer's find/replace target. */ - class FindReplaceTarget implements IFindReplaceTarget, IFindReplaceTargetExtension, IFindReplaceTargetExtension3 { + class FindReplaceTarget implements IFindReplaceTarget, IFindReplaceTargetExtension, IFindReplaceTargetExtension3, IFindReplaceTargetExtension4 { /** The range for this target. */ private FindReplaceRange fRange; @@ -908,6 +908,11 @@ public class TextViewer extends Viewer implements } @Override + public void setSelection(IRegion[] regions) { + TextViewer.this.setSelectedRanges(regions); + } + + @Override public void setScope(IRegion scope) { if (fRange != null) fRange.uninstall(); diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorMessages.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorMessages.java index 7fae625d9a3..9715d5c0e64 100644 --- a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorMessages.java +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorMessages.java @@ -117,11 +117,14 @@ final class EditorMessages extends NLS { public static String FindReplace_ReplaceFindButton_label; public static String FindReplace_ReplaceSelectionButton_label; public static String FindReplace_ReplaceAllButton_label; + public static String FindReplace_SelectAllButton_label; public static String FindReplace_CloseButton_label; public static String FindReplace_Status_noMatch_label; public static String FindReplace_Status_noMatchWithValue_label; public static String FindReplace_Status_replacement_label; public static String FindReplace_Status_replacements_label; + public static String FindReplace_Status_selection_label; + public static String FindReplace_Status_selections_label; public static String FindReplace_Status_wrapped_label; public static String FindNext_Status_noMatch_label; public static String AbstractDocumentProvider_ok; diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorMessages.properties b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorMessages.properties index 82c0a41278b..8ef0eb868c5 100644 --- a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorMessages.properties +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/EditorMessages.properties @@ -114,11 +114,14 @@ FindReplace_FindNextButton_label=Fi&nd FindReplace_ReplaceFindButton_label=Replace/Fin&d FindReplace_ReplaceSelectionButton_label=&Replace FindReplace_ReplaceAllButton_label=Replace &All +FindReplace_SelectAllButton_label=&Select All FindReplace_CloseButton_label=Close FindReplace_Status_noMatch_label=String not found FindReplace_Status_noMatchWithValue_label=String ''{0}'' not found FindReplace_Status_replacement_label=1 match replaced FindReplace_Status_replacements_label={0} matches replaced +FindReplace_Status_selection_label=1 match selected +FindReplace_Status_selections_label={0} matches selected FindReplace_Status_wrapped_label=Wrapped search FindNext_Status_noMatch_label=String ''{0}'' not found diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java index db816066155..525f5c7dfe9 100644 --- a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceDialog.java @@ -64,6 +64,7 @@ import org.eclipse.jface.text.FindReplaceDocumentAdapterContentProposalProvider; import org.eclipse.jface.text.IFindReplaceTarget; import org.eclipse.jface.text.IFindReplaceTargetExtension; import org.eclipse.jface.text.IFindReplaceTargetExtension3; +import org.eclipse.jface.text.IFindReplaceTargetExtension4; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.TextUtilities; @@ -83,6 +84,8 @@ import org.eclipse.ui.internal.texteditor.SWTUtil; */ class FindReplaceDialog extends Dialog { + private static final int CLOSE_BUTTON_ID = 101; + /** * Updates the find replace dialog on activation changes. */ @@ -196,7 +199,7 @@ class FindReplaceDialog extends Dialog { */ private Button fIsRegExCheckBox; - private Button fReplaceSelectionButton, fReplaceFindButton, fFindNextButton, fReplaceAllButton; + private Button fReplaceSelectionButton, fReplaceFindButton, fFindNextButton, fReplaceAllButton, fSelectAllButton; private Combo fFindField, fReplaceField; /** @@ -340,6 +343,18 @@ class FindReplaceDialog extends Dialog { }); setGridData(fFindNextButton, SWT.FILL, true, SWT.FILL, false); + fSelectAllButton = makeButton(panel, EditorMessages.FindReplace_SelectAllButton_label, 106, false, + new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + performSelectAll(); + updateFindAndReplaceHistory(); + } + }); + setGridData(fSelectAllButton, SWT.FILL, true, SWT.FILL, false); + + new Label(panel, SWT.NONE); // filler + fReplaceFindButton= makeButton(panel, EditorMessages.FindReplace_ReplaceFindButton_label, 103, false, new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { @@ -822,7 +837,7 @@ class FindReplaceDialog extends Dialog { setGridData(fStatusLabel, SWT.FILL, true, SWT.CENTER, false); String label= EditorMessages.FindReplace_CloseButton_label; - Button closeButton= createButton(panel, 101, label, false); + Button closeButton = createButton(panel, CLOSE_BUTTON_ID, label, false); setGridData(closeButton, SWT.RIGHT, false, SWT.BOTTOM, false); return panel; @@ -1360,7 +1375,8 @@ class FindReplaceDialog extends Dialog { public int numberOfOccurrences; @Override public void run() { - numberOfOccurrences= replaceAll(findString, replaceString == null ? "" : replaceString, isForwardSearch(), isCaseSensitiveSearch(), isWholeWordSearch(), isRegExSearchAvailableAndChecked()); //$NON-NLS-1$ + numberOfOccurrences = replaceAll(findString, replaceString == null ? "" : replaceString, //$NON-NLS-1$ + isCaseSensitiveSearch(), isWholeWordSearch(), isRegExSearchAvailableAndChecked()); } } @@ -1392,8 +1408,59 @@ class FindReplaceDialog extends Dialog { } /** + * Replaces all occurrences of the user's findString with the replace string. + * Indicate to the user the number of replacements that occur. + */ + private void performSelectAll() { + + int selectCount = 0; + final String findString = getFindString(); + + if (findString != null && !findString.isEmpty()) { + + class SelectAllRunnable implements Runnable { + public int numberOfOccurrences; + + @Override + public void run() { + numberOfOccurrences = selectAll(findString, isCaseSensitiveSearch(), isWholeWordSearch(), + isRegExSearchAvailableAndChecked()); + } + } + + try { + SelectAllRunnable runnable = new SelectAllRunnable(); + BusyIndicator.showWhile(fActiveShell.getDisplay(), runnable); + selectCount = runnable.numberOfOccurrences; + + if (selectCount != 0) { + if (selectCount == 1) { // not plural + statusMessage(EditorMessages.FindReplace_Status_selection_label); + } else { + String msg = EditorMessages.FindReplace_Status_selections_label; + msg = NLSUtility.format(msg, String.valueOf(selectCount)); + statusMessage(msg); + } + } else { + String msg = NLSUtility.format(EditorMessages.FindReplace_Status_noMatchWithValue_label, + findString); + statusMessage(false, EditorMessages.FindReplace_Status_noMatch_label, msg); + } + } catch (PatternSyntaxException ex) { + statusError(ex.getLocalizedMessage()); + } catch (IllegalStateException ex) { + // we don't keep state in this dialog + } + } + writeSelection(); + updateButtonState(); + } + + /** * Validates the state of the find/replace target. - * @return <code>true</code> if target can be changed, <code>false</code> otherwise + * + * @return <code>true</code> if target can be changed, <code>false</code> + * otherwise * @since 2.1 */ private boolean validateTargetState() { @@ -1493,7 +1560,6 @@ class FindReplaceDialog extends Dialog { * * @param findString the string to search for * @param replaceString the replacement string - * @param forwardSearch the search direction * @param caseSensitive should the search be case sensitive * @param wholeWord does the search string represent a complete word * @param regExSearch if <code>true</code> findString represents a regular expression @@ -1501,13 +1567,13 @@ class FindReplaceDialog extends Dialog { * * @since 3.0 */ - private int replaceAll(String findString, String replaceString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord, boolean regExSearch) { + private int replaceAll(String findString, String replaceString, boolean caseSensitive, boolean wholeWord, + boolean regExSearch) { int replaceCount= 0; int findReplacePosition= 0; findReplacePosition= 0; - forwardSearch= true; if (!validateTargetState()) return replaceCount; @@ -1518,18 +1584,11 @@ class FindReplaceDialog extends Dialog { try { int index= 0; while (index != -1) { - index= findAndSelect(findReplacePosition, findString, forwardSearch, caseSensitive, wholeWord, regExSearch); + index = findAndSelect(findReplacePosition, findString, true, caseSensitive, wholeWord, regExSearch); if (index != -1) { // substring not contained from current position Point selection= replaceSelection(replaceString, regExSearch); replaceCount++; - - if (forwardSearch) - findReplacePosition= selection.x + selection.y; - else { - findReplacePosition= selection.x - 1; - if (findReplacePosition == -1) - break; - } + findReplacePosition = selection.x + selection.y; } } } finally { @@ -1540,6 +1599,32 @@ class FindReplaceDialog extends Dialog { return replaceCount; } + private int selectAll(String findString, boolean caseSensitive, boolean wholeWord, boolean regExSearch) { + + int replaceCount = 0; + int position = 0; + + if (!validateTargetState()) + return replaceCount; + + List<Region> selectedRegions = new ArrayList<>(); + int index = 0; + do { + index = findAndSelect(position, findString, true, caseSensitive, wholeWord, regExSearch); + if (index != -1) { // substring not contained from current position + Point selection = fTarget.getSelection(); + selectedRegions.add(new Region(selection.x, selection.y)); + replaceCount++; + position = selection.x + selection.y; + } + } while (index != -1); + if (fTarget instanceof IFindReplaceTargetExtension4) { + ((IFindReplaceTargetExtension4) fTarget).setSelection(selectedRegions.toArray(IRegion[]::new)); + } + + return replaceCount; + } + // ------- UI creation --------------------------------------- /** @@ -1752,6 +1837,10 @@ class FindReplaceDialog extends Dialog { updateButtonState(); } + if (okToUse(fSelectAllButton)) { + fSelectAllButton.setEnabled(fTarget instanceof IFindReplaceTargetExtension4); + } + setContentAssistsEnablement(isRegExSearchAvailableAndChecked()); } diff --git a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceTarget.java b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceTarget.java index 420a8fb0528..54d0b49492e 100644 --- a/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceTarget.java +++ b/org.eclipse.ui.workbench.texteditor/src/org/eclipse/ui/texteditor/FindReplaceTarget.java @@ -19,6 +19,7 @@ import org.eclipse.swt.graphics.Point; import org.eclipse.jface.text.IFindReplaceTarget; import org.eclipse.jface.text.IFindReplaceTargetExtension; import org.eclipse.jface.text.IFindReplaceTargetExtension3; +import org.eclipse.jface.text.IFindReplaceTargetExtension4; import org.eclipse.jface.text.IRegion; @@ -26,7 +27,8 @@ import org.eclipse.jface.text.IRegion; * Internal find/replace target wrapping the editor's source viewer. * @since 2.1 */ -class FindReplaceTarget implements IFindReplaceTarget, IFindReplaceTargetExtension, IFindReplaceTargetExtension2, IFindReplaceTargetExtension3 { +class FindReplaceTarget implements IFindReplaceTarget, IFindReplaceTargetExtension, IFindReplaceTargetExtension2, + IFindReplaceTargetExtension3, IFindReplaceTargetExtension4 { /** The editor */ private AbstractTextEditor fEditor; @@ -171,6 +173,13 @@ class FindReplaceTarget implements IFindReplaceTarget, IFindReplaceTargetExtensi } @Override + public void setSelection(IRegion[] regions) { + if (fTarget instanceof IFindReplaceTargetExtension4) { + ((IFindReplaceTargetExtension4) fTarget).setSelection(regions); + } + } + + @Override public void setScopeHighlightColor(Color color) { if (getExtension() != null) getExtension().setScopeHighlightColor(color); |