In progress: [ Issue 44 ] Add ability to hyperlink to imported files
https://code.google.com/p/protobuf-dt/issues/detail?id=44

diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufUiModule.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufUiModule.java
index 0ada389..a006703 100644
--- a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufUiModule.java
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/ProtobufUiModule.java
@@ -10,6 +10,7 @@
 
 import static com.google.inject.name.Names.named;
 
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
 import org.eclipse.ui.plugin.AbstractUIPlugin;
 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
 import org.eclipse.xtext.ui.editor.IXtextEditorCallback;
@@ -18,6 +19,7 @@
 
 import com.google.eclipse.protobuf.scoping.IFileUriResolver;
 import com.google.eclipse.protobuf.ui.builder.AutoAddNatureEditorCallback;
+import com.google.eclipse.protobuf.ui.editor.ProtobufHyperlinkDetector;
 import com.google.eclipse.protobuf.ui.outline.LinkWithEditor;
 import com.google.eclipse.protobuf.ui.outline.ProtobufOutlinePage;
 import com.google.eclipse.protobuf.ui.preferences.compiler.CompilerPreferenceStoreInitializer;
@@ -40,6 +42,10 @@
     return ProtobufOutlinePage.class;
   }
 
+  @Override public Class<? extends IHyperlinkDetector> bindIHyperlinkDetector() {
+    return ProtobufHyperlinkDetector.class;
+  }
+
   @Override public Class<? extends IXtextEditorCallback> bindIXtextEditorCallback() {
     return AutoAddNatureEditorCallback.class;
   }
@@ -58,12 +64,12 @@
   public void configurePathsPreferencesInitializer(Binder binder) {
     configurePreferenceInitializer(binder, "pathsPreferences", PathsPreferenceStoreInitializer.class);
   }
-  
+
   private void configurePreferenceInitializer(Binder binder, String name,
       Class<? extends IPreferenceStoreInitializer> initializerType) {
     binder.bind(IPreferenceStoreInitializer.class).annotatedWith(named(name)).to(initializerType);
   }
-  
+
   public void configureFileUriResolver(Binder binder) {
     binder.bind(IFileUriResolver.class).to(FileUriResolver.class);
   }
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/ImportHyperlink.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/ImportHyperlink.java
new file mode 100644
index 0000000..78f284e
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/ImportHyperlink.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package com.google.eclipse.protobuf.ui.editor;
+
+import org.eclipse.emf.common.util.URI;
+import org.eclipse.jface.text.IRegion;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+
+/**
+ * A hyperlink for imported .proto files.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+class ImportHyperlink implements IHyperlink {
+
+  private final URI importUri;
+  private final String uriText;
+  private final IRegion region;
+
+  ImportHyperlink(URI importUri, String uriText, IRegion region) {
+    this.importUri = importUri;
+    this.uriText = uriText;
+    this.region = region;
+  }
+
+  public IRegion getHyperlinkRegion() {
+    return region;
+  }
+
+  public String getTypeLabel() {
+    return "type";
+  }
+
+  public String getHyperlinkText() {
+    return "text";
+  }
+
+  public void open() {
+    String scheme = importUri.scheme();
+    if ("platform".equals(scheme)) openFromWorkspace();
+    if ("file".equals(scheme)) openFromFileSystem();
+  }
+
+  private void openFromWorkspace() {
+    System.out.println("open from Workspace");
+  }
+
+  private void openFromFileSystem() {
+    System.out.println("open from file system");
+  }
+}
diff --git a/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/ProtobufHyperlinkDetector.java b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/ProtobufHyperlinkDetector.java
new file mode 100644
index 0000000..2d29674
--- /dev/null
+++ b/com.google.eclipse.protobuf.ui/src/com/google/eclipse/protobuf/ui/editor/ProtobufHyperlinkDetector.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2011 Google Inc.
+ *
+ * All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse
+ * Public License v1.0 which accompanies this distribution, and is available at
+ *
+ * http://www.eclipse.org/legal/epl-v10.html
+ */
+package com.google.eclipse.protobuf.ui.editor;
+
+import static org.eclipse.emf.common.util.URI.createURI;
+
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.jface.text.*;
+import org.eclipse.jface.text.hyperlink.IHyperlink;
+import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
+import org.eclipse.xtext.CrossReference;
+import org.eclipse.xtext.resource.EObjectAtOffsetHelper;
+import org.eclipse.xtext.resource.XtextResource;
+import org.eclipse.xtext.ui.editor.hyperlinking.DefaultHyperlinkDetector;
+import org.eclipse.xtext.ui.editor.model.IXtextDocument;
+import org.eclipse.xtext.util.concurrent.IUnitOfWork;
+
+import com.google.eclipse.protobuf.protobuf.Import;
+import com.google.inject.Inject;
+
+/**
+ * Represents an implementation of interface <code>{@link IHyperlinkDetector}</code> to find and convert
+ * {@link CrossReference elements}, at a given location, to {@code IHyperlink}.
+ *
+ * @author alruiz@google.com (Alex Ruiz)
+ */
+public class ProtobufHyperlinkDetector extends DefaultHyperlinkDetector {
+
+  @Inject private EObjectAtOffsetHelper eObjectAtOffsetHelper;
+
+  @Override public IHyperlink[] detectHyperlinks(ITextViewer textViewer, final IRegion region,
+      final boolean canShowMultipleHyperlinks) {
+    IXtextDocument document = (IXtextDocument)textViewer.getDocument();
+    IHyperlink[] importHyperlinks = importHyperlinks(document, region);
+    if (importHyperlinks != null) return importHyperlinks;
+    return document.readOnly(new IUnitOfWork<IHyperlink[], XtextResource>() {
+      public IHyperlink[] exec(XtextResource resource) {
+        return getHelper().createHyperlinksByOffset(resource, region.getOffset(), canShowMultipleHyperlinks);
+      }
+    });
+  }
+
+  private IHyperlink[] importHyperlinks(final IXtextDocument document, final IRegion region) {
+    return document.readOnly(new IUnitOfWork<IHyperlink[], XtextResource>() {
+      public IHyperlink[] exec(XtextResource resource) {
+        EObject resolved = eObjectAtOffsetHelper.resolveElementAt(resource, region.getOffset());
+        if (!(resolved instanceof Import)) return null;
+        Import anImport = (Import) resolved;
+        try {
+          int lineNumber = document.getLineOfOffset(region.getOffset());
+          int lineLength = document.getLineLength(lineNumber);
+          document.get(region.getOffset(), lineLength - region.getOffset());
+        } catch (BadLocationException e) {
+        }
+        String importUri = anImport.getImportURI();
+        IHyperlink hyperlink = new ImportHyperlink(createURI(importUri), importUri, region);
+        return new IHyperlink[] { hyperlink };
+      }
+    });
+  }
+}