diff --git a/org.eclipse.search/META-INF/MANIFEST.MF b/org.eclipse.search/META-INF/MANIFEST.MF index 4249dc0a2cf..0e5a24e25d8 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.14.300.qualifier Bundle-Activator: org.eclipse.search.internal.ui.SearchPlugin Bundle-ActivationPolicy: lazy Bundle-Vendor: %providerName 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 9a174e40cde..97677d07e8b 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 @@ -181,7 +181,7 @@ protected IStatus run(IProgressMonitor inner) { SubMonitor subMonitor = SubMonitor.convert(inner, fileBatches.size() / jobCount); // approximate this.fileCharSequenceProvider= new FileCharSequenceProvider(); List sameFiles; - while (((sameFiles = fileBatches.poll()) != null) && !fFatalError) { + while (((sameFiles = fileBatches.poll()) != null) && !fFatalError && !fProgressMonitor.isCanceled()) { IStatus status = processFile(sameFiles, subMonitor.split(1)); // Only accumulate interesting status if (!status.isOK()) @@ -190,6 +190,9 @@ protected IStatus run(IProgressMonitor inner) { // Stop processing and return the status for the completed jobs. } fileCharSequenceProvider= null; + synchronized (fLock) { + fLock.notify(); + } return multiStatus; } @@ -291,17 +294,16 @@ public Map getDocumentsInEditors() { private final TextSearchRequestor fCollector; private final Pattern fSearchPattern; - private IProgressMonitor fProgressMonitor; + private volatile IProgressMonitor fProgressMonitor; - private int fNumberOfFilesToScan; private int fNumberOfScannedFiles; // Protected by fLock private IFile fCurrentFile; // Protected by fLock - private Object fLock= new Object(); + private final Object fLock = new Object(); private final MultiStatus fStatus; private volatile boolean fFatalError; // If true, terminates the search. - private boolean fIsLightweightAutoRefresh; + private volatile boolean fIsLightweightAutoRefresh; public TextSearchVisitor(TextSearchRequestor collector, Pattern searchPattern) { fCollector= collector; @@ -317,102 +319,101 @@ public IStatus search(IFile[] files, IProgressMonitor monitor) { if (files.length == 0) { return fStatus; } - fProgressMonitor= monitor == null ? new NullProgressMonitor() : monitor; - fNumberOfScannedFiles= 0; - fNumberOfFilesToScan= files.length; - fCurrentFile= null; - + fProgressMonitor = monitor == null ? new NullProgressMonitor() : monitor; + synchronized (fLock) { + fNumberOfScannedFiles = 0; + fCurrentFile = null; + } int threadsNeeded = Math.min(files.length, NUMBER_OF_LOGICAL_THREADS); // All but 1 threads should search. 1 thread does the UI updates: int jobCount = fCollector.canRunInParallel() && threadsNeeded > 1 ? threadsNeeded - 1 : 1; - - // Seed count over 1 can cause endless waits, see bug 543629 comment 2 - // TODO use seed = jobCount after the bug 543660 in JobGroup is fixed - final int seed = 1; - final JobGroup jobGroup = new TextSearchJobGroup("Text Search", jobCount, seed); //$NON-NLS-1$ 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; - } - - 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; - } - } - return Status.OK_STATUS; - } - }; - try { String taskName= fSearchPattern.pattern().isEmpty() ? SearchMessages.TextSearchVisitor_filesearch_task_label : ""; //$NON-NLS-1$ - fProgressMonitor.beginTask(taskName, fNumberOfFilesToScan); - monitorUpdateJob.setSystem(true); - monitorUpdateJob.schedule(); try { fCollector.beginReporting(); + if (fProgressMonitor.isCanceled()) { + throw new OperationCanceledException(SearchMessages.TextSearchVisitor_canceled); + } + Map documentsInEditors= PlatformUI.isWorkbenchRunning() ? evalNonFileBufferDocuments() : Collections.emptyMap(); // group files with same content together: - Map> localFilesByLocation = new LinkedHashMap<>(); - Map> remotFilesByLocation = new LinkedHashMap<>(); + Map> remoteFilesByLocation = 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; + : remoteFilesByLocation; filesByLocation.computeIfAbsent(key, k -> new ArrayList<>()).add(file); } localFilesByLocation.values().forEach(fileBatches::offer); - remotFilesByLocation.values().forEach(fileBatches::offer); + remoteFilesByLocation.values().forEach(fileBatches::offer); + int numberOfFilesToScan = fileBatches.size(); + fProgressMonitor.beginTask(taskName, numberOfFilesToScan); + + // Seed count over 1 can cause endless waits, see bug 543629 + // comment 2 + // TODO use seed = jobCount after the bug 543660 in JobGroup is + // fixed + final int seed = 1; + final JobGroup jobGroup = new TextSearchJobGroup("Text Search", jobCount, seed); //$NON-NLS-1$ for (int i = 0; i < jobCount; i++) { Job job = new TextSearchJob(documentsInEditors, jobCount); job.setJobGroup(jobGroup); job.schedule(); } - - // The monitorUpdateJob is managing progress and cancellation, - // so it is ok to pass a null monitor into the job group. + // update progress until finished or canceled: + int numberOfScannedFiles = 0; + int lastNumberOfScannedFiles = 0; + while (!fProgressMonitor.isCanceled() && !jobGroup.getActiveJobs().isEmpty() + && numberOfScannedFiles != numberOfFilesToScan) { + IFile file; + synchronized (fLock) { + try { + // time only relevant on how often progress is + // updated, but cancel is notified immediately: + fLock.wait(100); + } catch (InterruptedException e) { + fProgressMonitor.setCanceled(true); + break; + } + file = fCurrentFile; + numberOfScannedFiles = fNumberOfScannedFiles; + } + if (file != null) { + String fileName = file.getName(); + Object[] args = { fileName, Integer.valueOf(numberOfScannedFiles), + Integer.valueOf(numberOfFilesToScan) }; + fProgressMonitor.subTask(Messages.format(SearchMessages.TextSearchVisitor_scanning, args)); + int steps = numberOfScannedFiles - lastNumberOfScannedFiles; + fProgressMonitor.worked(steps); + lastNumberOfScannedFiles += steps; + } + } + if (fProgressMonitor.isCanceled()) { + jobGroup.cancel(); + } + // no need to pass progressMonitor (which would show wrong + // progress) but null because jobGroup was already finished / + // canceled anyway: jobGroup.join(0, null); - if (fProgressMonitor.isCanceled()) + if (fProgressMonitor.isCanceled()) { throw new OperationCanceledException(SearchMessages.TextSearchVisitor_canceled); + } fStatus.addAll(jobGroup.getResult()); return fStatus; } catch (InterruptedException e) { throw new OperationCanceledException(SearchMessages.TextSearchVisitor_canceled); } finally { - monitorUpdateJob.cancel(); fileBatches.clear(); } } finally { @@ -501,6 +502,7 @@ private boolean hasBinaryContent(CharSequence seq, IFile file) throws CoreExcept } } } catch (IndexOutOfBoundsException e) { + // ignored } catch (FileCharSequenceException ex) { if (ex.getCause() instanceof CharConversionException) return true; 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 e29d18ae7c6..ab1bd61b56a 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 @@ -134,7 +134,6 @@ private SearchMessages() { public static String TextSearchPage_searchBinary_label; public static String TextSearchVisitor_filesearch_task_label; public static String TextSearchVisitor_patterntoocomplex0; - public static String TextSearchVisitor_progress_updating_job; public static String TextSearchVisitor_scanning; public static String TextSearchVisitor_error; public static String TextSearchVisitor_canceled; 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 a7c3ed87476..6486299000e 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 @@ -106,9 +106,8 @@ TextSearchVisitor_error= File ''{1}'' has been skipped, problem while reading: ( TextSearchVisitor_canceled= Operation Canceled TextSearchVisitor_unsupportedcharset=File ''{1}'' has been skipped: Unsupported encoding ''{0}''. TextSearchVisitor_patterntoocomplex0=Search pattern is too complex. Search canceled. -TextSearchVisitor_progress_updating_job=Search progress polling TextSearchVisitor_filesearch_task_label=Searching for files... -TextSearchVisitor_textsearch_task_label=Searching for pattern ''{0}''... +TextSearchVisitor_textsearch_task_label=Searching ''{0}'' TextSearchVisitor_illegalcharset=File ''{1}'' has been skipped: Illegal encoding ''{0}''. SortDropDownAction_label= S&ort By