Wednesday, June 11, 2014

Adding hyperlink detectors to editors

Ever tried clicking on a method name in the java editor while holding the ctrl key? Sure you have. This hyperlink functionality is extensible and in this post we will see how to do that.

Source code for this tutorial is available on googlecode as a single zip archive, as a Team Project Set or you can checkout the SVN projects directly.

Step 1: Creating the extension

Our target will be to create a simple hyperlink whenever we detect the word "preferences" in a text editor. Upon a click the preferences dialog should pop up.

Start with a new Plug-in Project, open the Manifest Editor and switch to the Extensions tab. Now add an org.eclipse.ui.workbench.texteditor.hyperlinkDetectors extension. Provide a unique id and a nice name. The name will be visible in the preferences under General/Editors/Text Editors/Hyperlinking.

The targetId points to the type of editor we would like to create our links in. As we want to use it in all text editors use org.eclipse.ui.DefaultTextEditor here.


Step 2: Class implementation

Create a new class PreferencesHyperlinkEditor extending from AbstractHyperlinkDetector. Therefore you need to define a dependency to the org.eclipse.jface.text plug-in.
package com.codeandme.hyperlinkdetector;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.hyperlink.AbstractHyperlinkDetector;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;

public class PreferencesHyperlinkDetector extends AbstractHyperlinkDetector implements IHyperlinkDetector {

 private static final String PREFERENCES = "preferences";

 @Override
 public IHyperlink[] detectHyperlinks(ITextViewer textViewer, IRegion region, boolean canShowMultipleHyperlinks) {

  IDocument document = textViewer.getDocument();
  int offset = region.getOffset();

  // extract relevant characters
  IRegion lineRegion;
  String candidate;
  try {
   lineRegion = document.getLineInformationOfOffset(offset);
   candidate = document.get(lineRegion.getOffset(), lineRegion.getLength());
  } catch (BadLocationException ex) {
   return null;
  }

  // look for keyword
  int index = candidate.indexOf(PREFERENCES);
  if (index != -1) {

   // detect region containing keyword
   IRegion targetRegion = new Region(lineRegion.getOffset() + index, PREFERENCES.length());
   if ((targetRegion.getOffset() <= offset) && ((targetRegion.getOffset() + targetRegion.getLength()) > offset))
    // create link
    return new IHyperlink[] { new PreferencesHyperlink(targetRegion) };
  }

  return null;
 }
}
The sample implementation just extracts some text and calculates offsets within the text file. It will fail if you type"preferences" more than once per line, but as a proof of concept this should be sufficient.

We also need to provide an implementation of IHyperlink. The most important method there is open(), which is called upon the click event.
package com.codeandme.hyperlinkdetector;

import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.dialogs.PreferencesUtil;

public class PreferencesHyperlink implements IHyperlink {

 private final IRegion fUrlRegion;

 public PreferencesHyperlink(IRegion urlRegion) {
  fUrlRegion = urlRegion;
 }

 @Override
 public IRegion getHyperlinkRegion() {
  return fUrlRegion;
 }

 @Override
 public String getTypeLabel() {
  return null;
 }

 @Override
 public String getHyperlinkText() {
  return null;
 }

 @Override
 public void open() {
  PreferencesUtil.createPreferenceDialogOn(Display.getDefault().getActiveShell(), null, null, null).open();
 }
}
To test your implementation open a new text file, enter some sample text and hover over the word "preferences" while holding the ctrl key.

3 comments:

  1. Another very useful tutorial.Thank you very much!

    ReplyDelete
  2. Wonderful - thank you so much, made my life much easier - particularly just giving out the zipped project - everyone should do that.

    ReplyDelete
  3. I tried and implemented the same but the control never reaches to my PreferencesHyperlinkDetector class

    ReplyDelete