diff --git a/org.eclipse.search/META-INF/MANIFEST.MF b/org.eclipse.search/META-INF/MANIFEST.MF
index 4249dc0a2..4259ddac5 100644
--- a/org.eclipse.search/META-INF/MANIFEST.MF
+++ b/org.eclipse.search/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.search; singleton:=true
-Bundle-Version: 3.14.200.qualifier
+Bundle-Version: 3.15.100.qualifier
Bundle-Activator: org.eclipse.search.internal.ui.SearchPlugin
Bundle-ActivationPolicy: lazy
Bundle-Vendor: %providerName
diff --git a/org.eclipse.search/new search/org/eclipse/search/ui/IResearchQuery.java b/org.eclipse.search/new search/org/eclipse/search/ui/IResearchQuery.java
new file mode 100644
index 000000000..fdeee5318
--- /dev/null
+++ b/org.eclipse.search/new search/org/eclipse/search/ui/IResearchQuery.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2022 Joerg Kubitz 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
+ *
+ * Contributors:
+ * Joerg Kubitz - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.search.ui;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+
+/**
+ * Represents a particular search query that can be rerun in background with an
+ * updated search text
+ *
+ * Clients may implement this interface.
+ *
+ *
+ * @since 3.15
+ */
+public interface IResearchQuery extends ISearchQuery {
+ /**
+ * @return the text the user was searching for
+ */
+ String getSearchString();
+
+ /**
+ * Sets the search text for the next run
+ *
+ * @param s
+ * the text the user is searching for
+ * @see org.eclipse.search.ui.ISearchQuery#run(IProgressMonitor)
+ */
+ void setSearchString(String s);
+
+ @Override
+ public default boolean canRerun() {
+ return true;
+ }
+
+ @Override
+ public default boolean canRunInBackground() {
+ return true;
+ }
+
+}
diff --git a/org.eclipse.search/new search/org/eclipse/search/ui/NewSearchUI.java b/org.eclipse.search/new search/org/eclipse/search/ui/NewSearchUI.java
index 2431df106..540e8d5bd 100644
--- a/org.eclipse.search/new search/org/eclipse/search/ui/NewSearchUI.java
+++ b/org.eclipse.search/new search/org/eclipse/search/ui/NewSearchUI.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * Copyright (c) 2000, 2022 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -13,6 +13,8 @@
*******************************************************************************/
package org.eclipse.search.ui;
+import java.util.Objects;
+
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.dialogs.ErrorDialog;
@@ -243,9 +245,12 @@ public static boolean isQueryRunning(ISearchQuery query) {
/**
* Sends a 'cancel' command to the given query running in background.
* The call has no effect if the query is not running, not in background or is not cancelable.
+ *
+ * The query may still running.
*
* @param query
* the query
+ * @see #waitFinished
* @since 3.1
*/
public static void cancelQuery(ISearchQuery query) {
@@ -255,6 +260,19 @@ public static void cancelQuery(ISearchQuery query) {
InternalSearchUI.getInstance().cancelSearch(query);
}
+ /**
+ * waits till the query is finished
+ *
+ * @param query
+ * the query
+ * @see #cancelQuery
+ * @since 3.15
+ */
+ public static void waitFinished(ISearchQuery query) {
+ Objects.requireNonNull(query, "query must not be null"); //$NON-NLS-1$
+ InternalSearchUI.getInstance().waitFinished(query);
+ }
+
/**
* Removes the given search query.
*
diff --git a/org.eclipse.search/new search/org/eclipse/search2/internal/ui/InternalSearchUI.java b/org.eclipse.search/new search/org/eclipse/search2/internal/ui/InternalSearchUI.java
index 78c080055..52d4ddc9b 100644
--- a/org.eclipse.search/new search/org/eclipse/search2/internal/ui/InternalSearchUI.java
+++ b/org.eclipse.search/new search/org/eclipse/search2/internal/ui/InternalSearchUI.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * Copyright (c) 2000, 2022 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -15,8 +15,9 @@
package org.eclipse.search2.internal.ui;
import java.lang.reflect.InvocationTargetException;
-import java.util.HashMap;
import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
@@ -39,6 +40,7 @@
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.ISearchResult;
import org.eclipse.search.ui.ISearchResultViewPart;
+import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.search2.internal.ui.text.PositionTracker;
@@ -48,31 +50,28 @@ public class InternalSearchUI {
private static InternalSearchUI fgInstance;
// contains all running jobs
- private HashMap fSearchJobs;
+ private final Map fSearchJobs;
- private QueryManager fSearchResultsManager;
- private PositionTracker fPositionTracker;
+ private final QueryManager fSearchResultsManager;
+ private final PositionTracker fPositionTracker;
- private SearchViewManager fSearchViewManager;
+ private final SearchViewManager fSearchViewManager;
public static final Object FAMILY_SEARCH = new Object();
private static class SearchJobRecord {
- public ISearchQuery query;
- public Job job;
- public boolean isRunning;
+ public final ISearchQuery query;
+ public volatile Job job;
public SearchJobRecord(ISearchQuery job) {
this.query= job;
- this.isRunning= false;
this.job= null;
}
}
-
private class InternalSearchJob extends Job {
- private SearchJobRecord fSearchJobRecord;
+ private final SearchJobRecord fSearchJobRecord;
public InternalSearchJob(SearchJobRecord sjr) {
super(sjr.query.getLabel());
@@ -102,6 +101,7 @@ protected IStatus run(IProgressMonitor monitor) {
fSearchJobRecord.job= null;
return status;
}
+
@Override
public boolean belongsTo(Object family) {
return family == InternalSearchUI.FAMILY_SEARCH;
@@ -110,12 +110,10 @@ public boolean belongsTo(Object family) {
}
private void searchJobStarted(SearchJobRecord record) {
- record.isRunning= true;
getSearchManager().queryStarting(record.query);
}
private void searchJobFinished(SearchJobRecord record) {
- record.isRunning= false;
fSearchJobs.remove(record.query);
getSearchManager().queryFinished(record.query);
}
@@ -125,7 +123,7 @@ private void searchJobFinished(SearchJobRecord record) {
*/
public InternalSearchUI() {
fgInstance= this;
- fSearchJobs= new HashMap<>();
+ fSearchJobs= new ConcurrentHashMap<>();
fSearchResultsManager= new QueryManager();
fPositionTracker= new PositionTracker();
@@ -137,7 +135,7 @@ public InternalSearchUI() {
/**
* @return returns the shared instance.
*/
- public static InternalSearchUI getInstance() {
+ public static synchronized InternalSearchUI getInstance() {
if (fgInstance ==null)
fgInstance= new InternalSearchUI();
return fgInstance;
@@ -158,8 +156,13 @@ private IWorkbenchSiteProgressService getProgressService() {
}
public boolean runSearchInBackground(ISearchQuery query, ISearchResultViewPart view) {
- if (isQueryRunning(query))
+ SearchJobRecord sjr = new SearchJobRecord(query);
+ // do not rerun same query in parallel (avoid MT issues):
+ NewSearchUI.waitFinished(query); // blocks UI :-(
+ SearchJobRecord queryRunning = fSearchJobs.putIfAbsent(query, sjr);
+ if (queryRunning != null) {
return false;
+ }
// prepare view
if (view == null) {
@@ -170,8 +173,6 @@ public boolean runSearchInBackground(ISearchQuery query, ISearchResultViewPart v
addQuery(query);
- SearchJobRecord sjr= new SearchJobRecord(query);
- fSearchJobs.put(query, sjr);
Job job= new InternalSearchJob(sjr);
job.setPriority(Job.BUILD);
@@ -188,12 +189,13 @@ public boolean runSearchInBackground(ISearchQuery query, ISearchResultViewPart v
}
public boolean isQueryRunning(ISearchQuery query) {
- SearchJobRecord sjr= fSearchJobs.get(query);
- return sjr != null && sjr.isRunning;
+ return fSearchJobs.containsKey(query);
}
public IStatus runSearchInForeground(IRunnableContext context, final ISearchQuery query, ISearchResultViewPart view) {
- if (isQueryRunning(query)) {
+ SearchJobRecord sjr = new SearchJobRecord(query);
+ SearchJobRecord queryRunning = fSearchJobs.putIfAbsent(query, sjr);
+ if (queryRunning != null) {
return Status.CANCEL_STATUS;
}
@@ -206,9 +208,6 @@ public IStatus runSearchInForeground(IRunnableContext context, final ISearchQuer
addQuery(query);
- SearchJobRecord sjr= new SearchJobRecord(query);
- fSearchJobs.put(query, sjr);
-
if (context == null)
context= new ProgressMonitorDialog(null);
@@ -264,13 +263,30 @@ private void doShutdown() {
}
- public void cancelSearch(ISearchQuery job) {
- SearchJobRecord rec= fSearchJobs.get(job);
- if (rec != null && rec.job != null)
- rec.job.cancel();
+ public void cancelSearch(ISearchQuery query) {
+ SearchJobRecord rec = fSearchJobs.get(query);
+ Job job = rec == null ? null : rec.job;
+ if (job != null) {
+ job.cancel();
+ // note that the job may still be running for some time
+ // which can cause multithreading issues.
+ }
}
-
+ public boolean waitFinished(ISearchQuery query) {
+ SearchJobRecord rec = fSearchJobs.get(query);
+ Job job = rec == null ? null : rec.job;
+ if (job != null) {
+ try {
+ job.cancel();
+ job.join();
+ return true;
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ return false;
+ }
public QueryManager getSearchManager() {
return fSearchResultsManager;
@@ -348,5 +364,4 @@ private void showSearchResult(SearchView searchView, ISearchResult result) {
searchView.showSearchResult(result);
}
-
}
diff --git a/org.eclipse.search/new search/org/eclipse/search2/internal/ui/SearchView.java b/org.eclipse.search/new search/org/eclipse/search2/internal/ui/SearchView.java
index 1294c9423..7c385ba9e 100644
--- a/org.eclipse.search/new search/org/eclipse/search2/internal/ui/SearchView.java
+++ b/org.eclipse.search/new search/org/eclipse/search2/internal/ui/SearchView.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * Copyright (c) 2000, 2022 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -18,12 +18,19 @@
package org.eclipse.search2.internal.ui;
import java.text.MessageFormat;
+import java.time.Duration;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
+import java.util.Objects;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyAdapter;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.ModifyEvent;
+import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
@@ -34,6 +41,7 @@
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
+import org.eclipse.swt.widgets.Text;
import org.eclipse.core.commands.operations.IUndoContext;
@@ -49,7 +57,13 @@
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.util.Throttler;
import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+
+import org.eclipse.jface.text.ITextSelection;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IMemento;
@@ -81,6 +95,7 @@
import org.eclipse.search.internal.ui.SearchPlugin;
import org.eclipse.search.ui.IContextMenuConstants;
import org.eclipse.search.ui.IQueryListener;
+import org.eclipse.search.ui.IResearchQuery;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.ISearchResult;
import org.eclipse.search.ui.ISearchResultPage;
@@ -112,6 +127,7 @@ public class SearchView extends PageBookView implements ISearchResultViewPart, I
private Composite fPageContent;
private Link fDescription;
+ private Text filterText;
private Composite fDescriptionComposite;
/**
@@ -429,6 +445,7 @@ private void internalShowSearchPage(ISearchResultPage page, ISearchResult search
}
updatePartName();
updateLabel();
+ updateFilter();
updateCancelAction();
updateHelpContextID(page);
@@ -477,18 +494,163 @@ public void updateLabel() {
fDescriptionComposite.setLayout(layout);
fDescriptionComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
- fDescription= new Link(fDescriptionComposite, SWT.NONE);
- GridData gridData= new GridData(SWT.FILL, SWT.CENTER, true, false);
- gridData.horizontalIndent= 5;
+ fDescription = new Link(fDescriptionComposite, SWT.NONE);
+ GridData gridData = new GridData(SWT.FILL, SWT.CENTER, true, false);
+ gridData.horizontalIndent = 5;
fDescription.setLayoutData(gridData);
- fDescription.setText(label);
- Label separator= new Label(fDescriptionComposite, SWT.SEPARATOR | SWT.HORIZONTAL);
+ Label separator = new Label(fDescriptionComposite, SWT.SEPARATOR | SWT.HORIZONTAL);
separator.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+
+ ISearchQuery query = fCurrentSearch.getQuery();
+ if (query instanceof IResearchQuery) {
+ filterText = new Text(fDescriptionComposite,
+ SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL | SWT.ICON_SEARCH);
+ filterText.addKeyListener(new KeyAdapter() {
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if (e.character == SWT.CR)
+ openSearch();
+ }
+ // optional
+ // filterText.setMessage(WorkbenchMessages.FilteredTree_FilterMessage);
+ });
+ filterText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
+ } else {
+ if (filterText != null) {
+ filterText.dispose();
+ filterText = null;
+ }
+ }
+
fPageContent.layout();
- } else {
- fDescription.setText(label);
}
+ fDescription.setText(label);
+ }
+ }
+ }
+
+ public void updateFilter() {
+ if (filterText != null && !filterText.isDisposed()) {
+ String searchString = null;
+ ISearchQuery query = fCurrentSearch.getQuery();
+ if (query instanceof IResearchQuery) {
+ searchString = ((IResearchQuery) query).getSearchString();
+ }
+ if (searchString != null) {
+ searchString = LegacyActionTools.escapeMnemonics(searchString);
+ }
+ filterText.setVisible(searchString != null);
+ if (searchString != null && !Objects.equals(filterText.getText(), searchString)) {
+ filterText.setText(searchString);
+ }
+ // after setting text to avoid initial modification fires event
+ ModifyListener inputListener = this::textChanged;
+ filterText.removeModifyListener(inputListener);
+ filterText.addModifyListener(inputListener);
+ }
+ }
+
+ private void openSearch() {
+ ISelectionProvider backup = getSite().getSelectionProvider();
+ try {
+ String searchText = filterText.getText();
+ getSite().setSelectionProvider(new SelectionProviderAdapter(searchText));
+ new OpenSearchDialogAction().run(); // uses selection
+ } finally {
+ getSite().setSelectionProvider(backup);
+ }
+ }
+
+ private Throttler researchThrottled = new Throttler(PlatformUI.getWorkbench().getDisplay(), Duration.ofMillis(50),
+ this::research);
+
+ private void research() {
+ String searchText = filterText.getText();
+ ISearchQuery query = fCurrentSearch.getQuery();
+ if (query instanceof IResearchQuery) {
+ ((IResearchQuery) query).setSearchString(searchText);
+ fSearchAgainAction.run();
+ }
+ }
+
+ private void textChanged(@SuppressWarnings("unused") ModifyEvent ignored) {
+ researchThrottled.throttledExec();
+ }
+
+ private class SelectionProviderAdapter implements ISelectionProvider, ISelectionChangedListener {
+ private final String fakeSelection;
+
+ private final class StringSelection implements ITextSelection {
+ private final String text;
+
+ public StringSelection(String text) {
+ this.text = text;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return text.isEmpty();
+ }
+
+ @Override
+ public int getOffset() {
+ return 0;
+ }
+
+ @Override
+ public int getLength() {
+ return text.length();
+ }
+
+ @Override
+ public int getStartLine() {
+ return 0;
+ }
+
+ @Override
+ public int getEndLine() {
+ return 0;
+ }
+
+ @Override
+ public String getText() {
+ return text;
+ }
+ }
+
+ private final ArrayList fListeners = new ArrayList<>();
+
+ public SelectionProviderAdapter(String searchText) {
+ this.fakeSelection = searchText;
+ }
+
+ @Override
+ public void addSelectionChangedListener(ISelectionChangedListener listener) {
+ fListeners.add(listener);
+ }
+
+ @Override
+ public ISelection getSelection() {
+ return new StringSelection(fakeSelection);
+ }
+
+ @Override
+ public void removeSelectionChangedListener(ISelectionChangedListener listener) {
+ fListeners.remove(listener);
+ }
+
+ @Override
+ public void setSelection(ISelection selection) {
+ // ignore
+ }
+
+ @Override
+ public void selectionChanged(SelectionChangedEvent event) {
+ // forward to my listeners
+ SelectionChangedEvent wrappedEvent = new SelectionChangedEvent(this, event.getSelection());
+ for (ISelectionChangedListener listener : fListeners) {
+ listener.selectionChanged(wrappedEvent);
}
}
}
diff --git a/org.eclipse.search/search/org/eclipse/search/internal/core/text/TextSearchVisitor.java b/org.eclipse.search/search/org/eclipse/search/internal/core/text/TextSearchVisitor.java
index 9a174e40c..3ee9ff3d7 100644
--- a/org.eclipse.search/search/org/eclipse/search/internal/core/text/TextSearchVisitor.java
+++ b/org.eclipse.search/search/org/eclipse/search/internal/core/text/TextSearchVisitor.java
@@ -333,38 +333,45 @@ public IStatus search(IFile[] files, IProgressMonitor monitor) {
long startTime= TRACING ? System.currentTimeMillis() : 0;
Job monitorUpdateJob= new Job(SearchMessages.TextSearchVisitor_progress_updating_job) {
- private int fLastNumberOfScannedFiles= 0;
@Override
public IStatus run(IProgressMonitor inner) {
- while (!inner.isCanceled()) {
- // Propagate user cancellation to the JobGroup.
- if (fProgressMonitor.isCanceled()) {
- jobGroup.cancel();
- break;
- }
+ int fLastNumberOfScannedFiles = 0;
+ try {
+ while (!inner.isCanceled()) {
+ // Propagate user cancellation to the JobGroup.
+ if (fProgressMonitor.isCanceled()) {
+ break;
+ }
- IFile file;
- int numberOfScannedFiles;
- synchronized (fLock) {
- file= fCurrentFile;
- numberOfScannedFiles= fNumberOfScannedFiles;
- }
- if (file != null) {
- String fileName= file.getName();
- Object[] args= { fileName, Integer.valueOf(numberOfScannedFiles), Integer.valueOf(fNumberOfFilesToScan)};
- fProgressMonitor.subTask(Messages.format(SearchMessages.TextSearchVisitor_scanning, args));
- int steps= numberOfScannedFiles - fLastNumberOfScannedFiles;
- fProgressMonitor.worked(steps);
- fLastNumberOfScannedFiles += steps;
- }
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- return Status.OK_STATUS;
+ IFile file;
+ int numberOfScannedFiles;
+ synchronized (fLock) {
+ file = fCurrentFile;
+ numberOfScannedFiles = fNumberOfScannedFiles;
+ }
+ if (numberOfScannedFiles == fNumberOfFilesToScan) {
+ return Status.OK_STATUS;
+ }
+ if (file != null) {
+ String fileName = file.getName();
+ Object[] args = { fileName, Integer.valueOf(numberOfScannedFiles),
+ Integer.valueOf(fNumberOfFilesToScan) };
+ fProgressMonitor.subTask(Messages.format(SearchMessages.TextSearchVisitor_scanning, args));
+ int steps = numberOfScannedFiles - fLastNumberOfScannedFiles;
+ fProgressMonitor.worked(steps);
+ fLastNumberOfScannedFiles += steps;
+ }
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ return Status.OK_STATUS;
+ }
}
+ return Status.OK_STATUS;
+ } finally {
+ jobGroup.cancel();
}
- return Status.OK_STATUS;
}
};
@@ -373,8 +380,6 @@ public IStatus run(IProgressMonitor inner) {
? SearchMessages.TextSearchVisitor_filesearch_task_label
: ""; //$NON-NLS-1$
fProgressMonitor.beginTask(taskName, fNumberOfFilesToScan);
- monitorUpdateJob.setSystem(true);
- monitorUpdateJob.schedule();
try {
fCollector.beginReporting();
Map documentsInEditors= PlatformUI.isWorkbenchRunning() ? evalNonFileBufferDocuments() : Collections.emptyMap();
@@ -384,13 +389,15 @@ public IStatus run(IProgressMonitor inner) {
Map> localFilesByLocation = new LinkedHashMap<>();
Map> remotFilesByLocation = new LinkedHashMap<>();
- for (IFile file : files) {
- IPath path = file.getLocation();
- String key = path == null ? file.getLocationURI().toString() : path.toString();
- Map> filesByLocation = (path != null) ? localFilesByLocation
- : remotFilesByLocation;
- filesByLocation.computeIfAbsent(key, k -> new ArrayList<>()).add(file);
+ if (!fProgressMonitor.isCanceled()) {
+ for (IFile file : files) {
+ IPath path = file.getLocation();
+ String key = path == null ? file.getLocationURI().toString() : path.toString();
+ Map> filesByLocation = (path != null) ? localFilesByLocation
+ : remotFilesByLocation;
+ filesByLocation.computeIfAbsent(key, k -> new ArrayList<>()).add(file);
+ }
}
localFilesByLocation.values().forEach(fileBatches::offer);
remotFilesByLocation.values().forEach(fileBatches::offer);
@@ -400,10 +407,22 @@ public IStatus run(IProgressMonitor inner) {
job.setJobGroup(jobGroup);
job.schedule();
}
+ monitorUpdateJob.setSystem(true);
+ monitorUpdateJob.schedule();
// The monitorUpdateJob is managing progress and cancellation,
// so it is ok to pass a null monitor into the job group.
- jobGroup.join(0, null);
+ try {
+ // avoid stale jobs
+ monitorUpdateJob.join(0, null);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ jobGroup.join(0, null); // XXX can deadlock, but using
+ // fProgressMonitor shows wrong
+ // progress. no guarantee
+ // monitorUpdateJob is running in
+ // another worker
if (fProgressMonitor.isCanceled())
throw new OperationCanceledException(SearchMessages.TextSearchVisitor_canceled);
@@ -412,7 +431,6 @@ public IStatus run(IProgressMonitor inner) {
} catch (InterruptedException e) {
throw new OperationCanceledException(SearchMessages.TextSearchVisitor_canceled);
} finally {
- monitorUpdateJob.cancel();
fileBatches.clear();
}
} finally {
diff --git a/org.eclipse.search/search/org/eclipse/search/internal/ui/SearchMessages.java b/org.eclipse.search/search/org/eclipse/search/internal/ui/SearchMessages.java
index e29d18ae7..6e320ed67 100644
--- a/org.eclipse.search/search/org/eclipse/search/internal/ui/SearchMessages.java
+++ b/org.eclipse.search/search/org/eclipse/search/internal/ui/SearchMessages.java
@@ -160,6 +160,10 @@ private SearchMessages() {
public static String FileSearchQuery_singularLabel;
public static String FileSearchQuery_singularLabel_fileNameSearch;
public static String FileSearchQuery_pluralPattern_fileNameSearch;
+ public static String FileResearchQuery_pluralPattern;
+ public static String FileResearchQuery_singularLabel;
+ public static String FileResearchQuery_singularLabel_fileNameSearch;
+ public static String FileResearchQuery_pluralPattern_fileNameSearch;
public static String OpenSearchDialogAction_label;
public static String OpenSearchDialogAction_tooltip;
public static String FileTypeEditor_typeDelimiter;
@@ -220,4 +224,6 @@ private SearchMessages() {
public static String TextSearchEngineRegistry_defaulttextsearch_label;
public static String FileSearchQuery_singularPatternWithFileExt;
public static String FileSearchQuery_pluralPatternWithFileExt;
+ public static String FileResearchQuery_singularPatternWithFileExt;
+ public static String FileResearchQuery_pluralPatternWithFileExt;
}
diff --git a/org.eclipse.search/search/org/eclipse/search/internal/ui/SearchMessages.properties b/org.eclipse.search/search/org/eclipse/search/internal/ui/SearchMessages.properties
index a7c3ed874..4ec17b3a9 100644
--- a/org.eclipse.search/search/org/eclipse/search/internal/ui/SearchMessages.properties
+++ b/org.eclipse.search/search/org/eclipse/search/internal/ui/SearchMessages.properties
@@ -152,6 +152,13 @@ FileSearchQuery_singularPatternWithFileExt= ''{0}'' - 1 match in {1} ({2})
FileSearchQuery_singularLabel_fileNameSearch=1 file name matching ''{0}'' in {1}
FileSearchQuery_pluralPattern_fileNameSearch={1} file names matching ''{0}'' in {2}
+FileResearchQuery_pluralPattern= {1} matches in {2}
+FileResearchQuery_singularLabel= 1 match in {1}
+FileResearchQuery_pluralPatternWithFileExt= {1} matches in {2} ({3})
+FileResearchQuery_singularPatternWithFileExt= 1 match in {1} ({2})
+FileResearchQuery_singularLabel_fileNameSearch=1 file name matching ''{0}'' in {1}
+FileResearchQuery_pluralPattern_fileNameSearch={1} file names matching ''{0}'' in {2}
+
OpenSearchDialogAction_label= Search
OpenSearchDialogAction_tooltip= Search
diff --git a/org.eclipse.search/search/org/eclipse/search/internal/ui/text/FileSearchQuery.java b/org.eclipse.search/search/org/eclipse/search/internal/ui/text/FileSearchQuery.java
index a3fe73456..46b01e46f 100644
--- a/org.eclipse.search/search/org/eclipse/search/internal/ui/text/FileSearchQuery.java
+++ b/org.eclipse.search/search/org/eclipse/search/internal/ui/text/FileSearchQuery.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2015 IBM Corporation and others.
+ * Copyright (c) 2000, 2022 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -38,6 +38,7 @@
import org.eclipse.search.internal.core.text.PatternConstructor;
import org.eclipse.search.internal.ui.Messages;
import org.eclipse.search.internal.ui.SearchMessages;
+import org.eclipse.search.ui.IResearchQuery;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.ISearchResult;
import org.eclipse.search.ui.text.AbstractTextSearchResult;
@@ -45,7 +46,7 @@
import org.eclipse.search.ui.text.Match;
-public class FileSearchQuery implements ISearchQuery {
+public class FileSearchQuery implements ISearchQuery, IResearchQuery {
private final static class TextSearchResultCollector extends TextSearchRequestor {
@@ -195,7 +196,7 @@ private void flushMatches() {
}
private final FileTextSearchScope fScope;
- private final String fSearchText;
+ private volatile String fSearchText;
private final boolean fIsRegEx;
private final boolean fIsCaseSensitive;
private final boolean fIsWholeWord;
@@ -220,11 +221,6 @@ public FileTextSearchScope getSearchScope() {
return fScope;
}
- @Override
- public boolean canRunInBackground() {
- return true;
- }
-
@Override
public IStatus run(final IProgressMonitor monitor) {
AbstractTextSearchResult textResult= (AbstractTextSearchResult) getSearchResult();
@@ -251,43 +247,55 @@ private boolean isScopeAllFileTypes() {
@Override
public String getLabel() {
- Pattern searchPattern = getSearchPattern();
- return searchPattern.pattern().isEmpty() ? SearchMessages.FileSearchQuery_label
+ return isFileNameSearch() ? SearchMessages.FileSearchQuery_label
: Messages.format(SearchMessages.TextSearchVisitor_textsearch_task_label, getSearchString());
}
+ @Override
public String getSearchString() {
return fSearchText;
}
+ @Override
+ public void setSearchString(String s) {
+ this.fSearchText = s;
+ }
+
public String getResultLabel(int nMatches) {
String searchString= getSearchString();
+ boolean research = true; /// TODO?
if (!searchString.isEmpty()) {
// text search
if (isScopeAllFileTypes()) {
// search all file extensions
if (nMatches == 1) {
Object[] args= { searchString, fScope.getDescription() };
- return Messages.format(SearchMessages.FileSearchQuery_singularLabel, args);
+ return Messages.format(research ? SearchMessages.FileResearchQuery_singularLabel
+ : SearchMessages.FileSearchQuery_singularLabel, args);
}
Object[] args= { searchString, Integer.valueOf(nMatches), fScope.getDescription() };
- return Messages.format(SearchMessages.FileSearchQuery_pluralPattern, args);
+ return Messages.format(research ? SearchMessages.FileResearchQuery_pluralPattern
+ : SearchMessages.FileSearchQuery_pluralPattern, args);
}
// search selected file extensions
if (nMatches == 1) {
Object[] args= { searchString, fScope.getDescription(), fScope.getFilterDescription() };
- return Messages.format(SearchMessages.FileSearchQuery_singularPatternWithFileExt, args);
+ return Messages.format(research ? SearchMessages.FileResearchQuery_singularPatternWithFileExt
+ : SearchMessages.FileSearchQuery_singularPatternWithFileExt, args);
}
Object[] args= { searchString, Integer.valueOf(nMatches), fScope.getDescription(), fScope.getFilterDescription() };
- return Messages.format(SearchMessages.FileSearchQuery_pluralPatternWithFileExt, args);
+ return Messages.format(research ? SearchMessages.FileResearchQuery_pluralPatternWithFileExt
+ : SearchMessages.FileSearchQuery_pluralPatternWithFileExt, args);
}
// file search
if (nMatches == 1) {
Object[] args= { fScope.getFilterDescription(), fScope.getDescription() };
- return Messages.format(SearchMessages.FileSearchQuery_singularLabel_fileNameSearch, args);
+ return Messages.format(research ? SearchMessages.FileResearchQuery_singularLabel_fileNameSearch
+ : SearchMessages.FileSearchQuery_singularLabel_fileNameSearch, args);
}
Object[] args= { fScope.getFilterDescription(), Integer.valueOf(nMatches), fScope.getDescription() };
- return Messages.format(SearchMessages.FileSearchQuery_pluralPattern_fileNameSearch, args);
+ return Messages.format(research ? SearchMessages.FileResearchQuery_pluralPattern_fileNameSearch
+ : SearchMessages.FileSearchQuery_pluralPattern_fileNameSearch, args);
}
/**
@@ -325,11 +333,6 @@ public boolean isWholeWord() {
return fIsWholeWord;
}
- @Override
- public boolean canRerun() {
- return true;
- }
-
@Override
public ISearchResult getSearchResult() {
if (fResult == null) {
diff --git a/org.eclipse.search/search/org/eclipse/search/internal/ui/text/TextSearchPage.java b/org.eclipse.search/search/org/eclipse/search/internal/ui/text/TextSearchPage.java
index 75bb63ea7..df90b0baf 100644
--- a/org.eclipse.search/search/org/eclipse/search/internal/ui/text/TextSearchPage.java
+++ b/org.eclipse.search/search/org/eclipse/search/internal/ui/text/TextSearchPage.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * Copyright (c) 2000, 2022 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
@@ -20,6 +20,7 @@
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -110,6 +111,7 @@ public class TextSearchPage extends DialogPage implements ISearchPage, IReplaceP
* @since 3.6
*/
private static final String STORE_EXTENSIONS= "EXTENSIONS"; //$NON-NLS-1$
+ private static WeakReference lastQuery;
private List fPreviousSearchPatterns= new ArrayList<>(HISTORY_SIZE);
@@ -274,7 +276,13 @@ private ISearchQuery newQuery() throws CoreException {
@Override
public boolean performAction() {
try {
- NewSearchUI.runQueryInBackground(newQuery());
+ ISearchQuery last = lastQuery == null ? null : lastQuery.get();
+ if (last != null) {
+ NewSearchUI.cancelQuery(last);
+ }
+ ISearchQuery newQuery = newQuery();
+ lastQuery=new WeakReference<>(newQuery);
+ NewSearchUI.runQueryInBackground(newQuery);
} catch (CoreException e) {
ErrorDialog.openError(getShell(), SearchMessages.TextSearchPage_replace_searchproblems_title, SearchMessages.TextSearchPage_replace_searchproblems_message, e.getStatus());
return false;