Eclipse Dev: Custom Search Page

This is for the archive and due to lack of useful tutorials on writing custom search pages in your own Eclipse plugin. It turned out that the default text search is good enough for my purposes, so I’ll stick it here as a future reference.

To start, your Eclipse plugin needs to add org.eclipse.search as a dependency, and, if you don’t have it already, org.eclipse.jface.text, too. With that, the Require-Bundle part of your MANIFEST.MF should look something like this:

Require-Bundle: org.eclipse.ui,
org.eclipse.core.runtime,
org.eclipse.ui.views,
org.eclipse.core.resources,
org.eclipse.ui.ide,
org.eclipse.ui.workbench.texteditor,
org.eclipse.text,
org.eclipse.ui.editors,
org.eclipse.search,
org.eclipse.jface.text

Next, we’ll specify the extension for our custom search page, using the plugin.xml editor or just pasting and modifying this snippet:

<extension point="org.eclipse.search.searchPages">
  <page
    class="com.acm.your.product.CustomSearchPage"
    id="yourplugin.page1"
    label="Your Custom Search, rename this">
  </page>
</extension>

And the actual implementation, providing a textfield as input, initialized with whatever text selection is currently active (thats where the org.eclipse.jface.text dependency comes from), and doing a text search across the full workspace:

package com.acm.your.product;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.search.ui.ISearchPage;
import org.eclipse.search.ui.ISearchPageContainer;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.search.ui.text.TextSearchQueryProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

public class CustomSearchPage extends DialogPage implements ISearchPage {

	private String selected;
	private Text idText;
	
	public IdSearchPage() {
		super();
	}
	
	public IdSearchPage(String title) {
		super(title);
	}

	public boolean performAction() {
		if (idText.getText().length() == 0)
			return false;
		try {
			NewSearchUI.runQueryInBackground(TextSearchQueryProvider.getPreferred().createQuery(idText.getText()));
		} catch (IllegalArgumentException e) {
			throw new RuntimeException(e);
		} catch (CoreException e) {
			throw new RuntimeException(e);
		}
        return true;
	}

	public void setContainer(ISearchPageContainer container) {
		if (container.getSelection() instanceof TextSelection) {
			selected = ((TextSelection) container.getSelection()).getText();
		}
	}

	public void createControl(Composite parent) {
		GridLayout layout = new GridLayout(1, false);
		layout.horizontalSpacing = 5;
		layout.verticalSpacing = 5;
		parent.setLayout(layout);
		new Label(parent, 0).setText("Containing text:");
		idText = new Text(parent, SWT.BORDER);
		idText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		if (selected != null) {
			idText.setText(selected);
			idText.setSelection(0, selected.length());
		}
		setControl(parent);
	}
	
	@Override
	public void setVisible(boolean visible) {
		super.setVisible(visible);
		idText.setFocus();
	}
	
}

You can also restrict the files to search by creating a list first, and pass that to the search provider:

package com.acm.your.product;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceProxy;
import org.eclipse.core.resources.IResourceProxyVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.DialogPage;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.search.ui.ISearchPage;
import org.eclipse.search.ui.ISearchPageContainer;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.search.ui.text.TextSearchQueryProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;

public class CustomSearchPage2 extends DialogPage implements ISearchPage {

	private String selected;
	private Text idText;
	
	public IdSearchPage() {
		super();
	}
	
	public IdSearchPage(String title) {
		super(title);
	}

	public boolean performAction() {
		if (idText.getText().length() == 0)
			return false;
		try {
			final List files = new ArrayList();
			ResourcesPlugin.getWorkspace().getRoot().accept(new IResourceProxyVisitor() {
				public boolean visit(IResourceProxy proxy) throws CoreException {
					if (proxy.getType() == IResource.FILE) {
						IFile file = (IFile) proxy.requestResource();
			            if (file.getLocation().getFileExtension() != null && file.getLocation().getFileExtension().matches("xml|html|js|css")) {
			            	files.add(file);
			            }
			         }
			         return true;
				}
			}, IResource.DEPTH_INFINITE);
			NewSearchUI.runQueryInBackground(TextSearchQueryProvider.getPreferred().createQuery(idText.getText(), files.toArray(new IResource[0])));
		} catch (IllegalArgumentException e) {
			throw new RuntimeException(e);
		} catch (CoreException e) {
			throw new RuntimeException(e);
		}
        return true;
	}

	public void setContainer(ISearchPageContainer container) {
		if (container.getSelection() instanceof TextSelection) {
			selected = ((TextSelection) container.getSelection()).getText();
		}
	}

	public void createControl(Composite parent) {
		GridLayout layout = new GridLayout(1, false);
		layout.horizontalSpacing = 5;
		layout.verticalSpacing = 5;
		parent.setLayout(layout);
		new Label(parent, 0).setText("Containing text:");
		idText = new Text(parent, SWT.BORDER);
		idText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		if (selected != null) {
			idText.setText(selected);
			idText.setSelection(0, selected.length());
		}
		setControl(parent);
	}
	
	@Override
	public void setVisible(boolean visible) {
		super.setVisible(visible);
		idText.setFocus();
	}
	
}

This in itself doesn’t yet provide a lot of value, but if you actually need to build a custom search, its a good start.

Roughly based on this sort-of tutorial on the Eclipse wiki.

-Jörn

No more comments.
  1. Jorn
    Great tutorial, thanks a lot for publishing this. I was searching for hours for any explanation on hoe to add my search. Finally found one.
    Small question: I have implemented a plug-in with multi-page editor – one tab is text source and another is TableViewer. Now, I want to be able to search the things in table and mark the cell with text found in some different color. What should I override to do that? Thanks in advance

  2. Can’t really help you with that. Maybe check the Eclipse Glance source code: http://marketplace.eclipse.org/content/eclipse-glance

  3. A really great tutorial, thank you 🙂