Skip to content

Commit

Permalink
Search while you type... #78
Browse files Browse the repository at this point in the history
Adds a search filter to search result View after first search.
Press ENTER to get into the fine grained search Dialog.

Providers have to implement IResearchQuery

+ some multithreading hardening of the search which pop up under that
heavy load.
  • Loading branch information
EcljpseB0T committed Aug 25, 2022
1 parent c407b4a commit 3334d01
Show file tree
Hide file tree
Showing 10 changed files with 389 additions and 100 deletions.
2 changes: 1 addition & 1 deletion org.eclipse.search/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
* <p>
* Clients may implement this interface.
* </p>
*
* @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;
}

}
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand All @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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;
Expand All @@ -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;

Expand All @@ -48,31 +50,28 @@ public class InternalSearchUI {
private static InternalSearchUI fgInstance;

// contains all running jobs
private HashMap<ISearchQuery, SearchJobRecord> fSearchJobs;
private final Map<ISearchQuery, SearchJobRecord> 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());
Expand Down Expand Up @@ -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;
Expand All @@ -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);
}
Expand All @@ -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();

Expand All @@ -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;
Expand All @@ -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) {
Expand All @@ -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);
Expand All @@ -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;
}

Expand All @@ -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);

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -348,5 +364,4 @@ private void showSearchResult(SearchView searchView, ISearchResult result) {
searchView.showSearchResult(result);
}


}
Loading

0 comments on commit 3334d01

Please sign in to comment.