Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Created extension point for SourceFileProvider so that the Hyperlinki…

…ng logic can be easily used by other plugins (e.g., worksheet)

The extension mechanism is such that other plugins will be able to register new source file provider and benefit of the existing
declaration hyperlink detection logic.

The fix was motivated by https://github.com/dragos/scala-worksheet/issues/23, and hopefully it will be useful for others.

Fix #1001208
  • Loading branch information...
commit 8562d0de48421dab4227cf0ccf62c31a42b79308 1 parent ad6095c
@dotta dotta authored
View
1  org.scala-ide.sdt.core/META-INF/MANIFEST.MF
@@ -103,6 +103,7 @@ Export-Package:
scala.tools.eclipse.semantichighlighting.classifier,
scala.tools.eclipse.semantichighlighting.implicits,
scala.tools.eclipse.semicolon,
+ scala.tools.eclipse.sourcefileprovider,
scala.tools.eclipse.templates,
scala.tools.eclipse.ui,
scala.tools.eclipse.ui.actions,
View
8 org.scala-ide.sdt.core/plugin.xml
@@ -2,6 +2,7 @@
<?eclipse version="3.2"?>
<plugin>
<extension-point id="reconciliationParticipants" name="Reconcilation Participants" schema="schema/reconciliationParticipants.exsd"/>
+ <extension-point id="sourcefileprovider" name="Source file Providers" schema="schema/sourceFileProviders.exsd"/>
<extension
point="org.eclipse.ui.propertyPages">
<page
@@ -871,6 +872,13 @@
sequence="M2+M3+D S" />
</extension>
+ <extension point="org.scala-ide.sdt.core.sourcefileprovider">
+ <provider
+ class="scala.tools.eclipse.javaelements.ScalaSourceFileProvider"
+ file_extension="scala">
+ </provider>
+ </extension>
+
<extension point="org.scala-ide.sdt.aspects.cuprovider">
<provider
class="scala.tools.eclipse.javaelements.ScalaCompilationUnitProvider"
View
106 org.scala-ide.sdt.core/schema/sourceFileProviders.exsd
@@ -0,0 +1,106 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<!-- Schema file written by PDE -->
+<schema targetNamespace="org.scala-ide.sdt.core" xmlns="http://www.w3.org/2001/XMLSchema">
+<annotation>
+ <appInfo>
+ <meta.schema plugin="org.scala-ide.sdt.core" id="sourcefileprovider" name="SourceFile Provider"/>
+ </appInfo>
+ <documentation>
+ A provider for obtaining a compilation unit from a path.
+ </documentation>
+ </annotation>
+
+ <element name="extension">
+ <annotation>
+ <appInfo>
+ <meta.element />
+ </appInfo>
+ </annotation>
+ <complexType>
+ <sequence minOccurs="1" maxOccurs="unbounded">
+ <element ref="provider"/>
+ </sequence>
+ <attribute name="point" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="id" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="name" type="string">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ <appInfo>
+ <meta.attribute translatable="true"/>
+ </appInfo>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <element name="provider">
+ <complexType>
+ <attribute name="class" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ <attribute name="file_extension" type="string" use="required">
+ <annotation>
+ <documentation>
+
+ </documentation>
+ </annotation>
+ </attribute>
+ </complexType>
+ </element>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="since"/>
+ </appInfo>
+ <documentation>
+ 2.1
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="examples"/>
+ </appInfo>
+ <documentation>
+ [Enter extension point usage example here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="apiinfo"/>
+ </appInfo>
+ <documentation>
+ [Enter API information here.]
+ </documentation>
+ </annotation>
+
+ <annotation>
+ <appInfo>
+ <meta.section type="implementation"/>
+ </appInfo>
+ <documentation>
+ [Enter information about supplied implementation of this extension point.]
+ </documentation>
+ </annotation>
+
+
+</schema>
View
1  org.scala-ide.sdt.core/src/scala/tools/eclipse/InteractiveCompilationUnit.scala
@@ -2,7 +2,6 @@ package scala.tools.eclipse
import org.eclipse.core.resources.IFile
import org.eclipse.jdt.core.compiler.IProblem
-
import scala.tools.nsc.util.BatchSourceFile
import scala.tools.nsc.util.SourceFile
import scala.tools.nsc.interactive.Response
View
23 org.scala-ide.sdt.core/src/scala/tools/eclipse/LocateSymbol.scala
@@ -15,15 +15,15 @@ import org.eclipse.ui.texteditor.ITextEditor
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit
import org.eclipse.jdt.ui.actions.SelectionDispatchAction
import org.eclipse.jdt.internal.ui.javaeditor.{EditorUtility, JavaElementHyperlink}
-
import tools.nsc.symtab.Flags._
import scala.tools.nsc.io.AbstractFile
-
-import javaelements.{ScalaSourceFile, ScalaClassFile, ScalaCompilationUnit}
+import javaelements.{ScalaSourceFile, ScalaClassFile}
+import org.eclipse.core.runtime.IPath
+import scala.tools.eclipse.sourcefileprovider.SourceFileProviderRegistry
trait LocateSymbol { self : ScalaPresentationCompiler =>
- def locate(sym : Symbol, scu : InteractiveCompilationUnit): Option[(ScalaCompilationUnit, Int)] = {
+ def locate(sym : Symbol, scu : InteractiveCompilationUnit): Option[(InteractiveCompilationUnit, Int)] = {
def find[T, V](arr : Array[T])(f : T => Option[V]) : Option[V] = {
for(e <- arr) {
f(e) match {
@@ -33,7 +33,7 @@ trait LocateSymbol { self : ScalaPresentationCompiler =>
}
None
}
- def findClassFile = {
+ def findClassFile(): Option[ScalaClassFile] = {
logger.debug("Looking for a classfile for " + sym.fullName)
val packName = sym.enclosingPackage.fullName
val project = scu.scalaProject.javaProject.asInstanceOf[JavaProject]
@@ -52,7 +52,7 @@ trait LocateSymbol { self : ScalaPresentationCompiler =>
}
}
- def findCompilationUnit() = {
+ def findCompilationUnit(): Option[IPath] = {
logger.info("Looking for a compilation unit for " + sym.fullName)
val project = scu.scalaProject.javaProject.asInstanceOf[JavaProject]
val nameLookup = new SearchableEnvironment(project, null: WorkingCopyOwner).nameLookup
@@ -62,7 +62,7 @@ trait LocateSymbol { self : ScalaPresentationCompiler =>
Option(nameLookup.findCompilationUnit(name)) map (_.getResource().getFullPath())
}
- def findSourceFile() =
+ def findSourceFile(): Option[IPath] =
if (sym.sourceFile ne null) {
val path = new Path(sym.sourceFile.path)
val root = ResourcesPlugin.getWorkspace().getRoot()
@@ -76,10 +76,10 @@ trait LocateSymbol { self : ScalaPresentationCompiler =>
val sourceFile = findSourceFile()
val target =
- if (sourceFile.isDefined)
- ScalaSourceFile.createFromPath(sourceFile.get.toString)
+ if(sourceFile.isDefined)
+ SourceFileProviderRegistry.getProvider(sourceFile.get).createFrom(sourceFile.get)
else
- findClassFile
+ findClassFile()
target flatMap { file =>
val pos = if (sym.pos eq NoPosition) {
@@ -92,7 +92,8 @@ trait LocateSymbol { self : ScalaPresentationCompiler =>
Some(sym.pos)
pos flatMap { p =>
- if (p eq NoPosition) None else Some(file, p.point)
+ if (p eq NoPosition) None
+ else Some(file, p.point)
}
}
}
View
22 org.scala-ide.sdt.core/src/scala/tools/eclipse/hyperlink/text/Hyperlink.scala
@@ -1,10 +1,12 @@
package scala.tools.eclipse.hyperlink.text
-import org.eclipse.jdt.internal.core.Openable
+import org.eclipse.core.resources.IFile
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility
-import org.eclipse.jface.text.hyperlink.IHyperlink
import org.eclipse.jface.text.IRegion
+import org.eclipse.jface.text.hyperlink.IHyperlink
import org.eclipse.ui.texteditor.ITextEditor
+import org.eclipse.jdt.internal.core.Openable
+import scala.tools.eclipse.InteractiveCompilationUnit
/** A creator of resolved hyperlinks.
*
@@ -13,17 +15,23 @@ import org.eclipse.ui.texteditor.ITextEditor
*/
object Hyperlink {
- type Factory = (Openable, Int, Int, String, IRegion) => IHyperlink
+ type Factory = (AnyRef, Int, Int, String, IRegion) => IHyperlink
- def withText(name: String)(file: Openable, pos: Int, len: Int, label: String, wordRegion: IRegion): IHyperlink =
- new ScalaHyperlink(file, pos, len, label, text = name, wordRegion)
+ def withText(name: String)(openableOrUnit: AnyRef, pos: Int, len: Int, label: String, wordRegion: IRegion): IHyperlink =
+ new ScalaHyperlink(openableOrUnit, pos, len, label, text = name, wordRegion)
- private class ScalaHyperlink(file: Openable, pos: Int, len: Int, label: String, text: String, wordRegion: IRegion) extends IHyperlink {
+ private class ScalaHyperlink(openableOrUnit: AnyRef, pos: Int, len: Int, label: String, text: String, wordRegion: IRegion) extends IHyperlink {
def getHyperlinkRegion = wordRegion
def getTypeLabel = label
def getHyperlinkText = text
def open = {
- EditorUtility.openInEditor(file, true) match {
+ /* This is a bad hack, but is currently needed to correctly navigate to sources attached to a binary file. */
+ val part = openableOrUnit match {
+ case editorInput: Openable => EditorUtility.openInEditor(editorInput, true)
+ case unit: InteractiveCompilationUnit => EditorUtility.openInEditor(unit.workspaceFile, true)
+ case _ => null
+ }
+ part match {
case editor: ITextEditor => editor.selectAndReveal(pos, len)
case _ =>
}
View
22 org.scala-ide.sdt.core/src/scala/tools/eclipse/javaelements/ScalaClassFile.scala
@@ -7,26 +7,18 @@ package scala.tools.eclipse.javaelements
import java.util.{ HashMap => JHashMap }
+import scala.tools.eclipse.ScalaImages
+import scala.tools.eclipse.contribution.weaving.jdt.IScalaClassFile
+import scala.tools.nsc.io.{AbstractFile, VirtualFile}
+
import org.eclipse.core.resources.IResource
+import org.eclipse.core.runtime.IProgressMonitor
import org.eclipse.core.runtime.IStatus
-import org.eclipse.jdt.core.{
- ICompilationUnit, IJavaElement, IPackageDeclaration, IProblemRequestor, IJavaModelStatusConstants, IType, JavaModelException,
- WorkingCopyOwner }
+import org.eclipse.jdt.core.{IJavaElement, IType, WorkingCopyOwner}
import org.eclipse.jdt.core.compiler.{ CharOperation, IProblem }
-import org.eclipse.jdt.internal.compiler.env
-import org.eclipse.jdt.internal.compiler.env.IBinaryType
-import org.eclipse.jdt.internal.core.{
- BasicCompilationUnit, BinaryType, ClassFile, DefaultWorkingCopyOwner, JavaModelStatus, JavaProject, JavaElement, PackageFragment }
+import org.eclipse.jdt.internal.core.{BinaryType, ClassFile, JavaModelStatus, PackageFragment}
import org.eclipse.jdt.internal.core.util.Util
-import org.eclipse.core.runtime.IProgressMonitor
-import org.eclipse.jdt.core.dom.CompilationUnit
-
-import scala.tools.nsc.io.{ AbstractFile, VirtualFile }
-
-import scala.tools.eclipse.ScalaImages
-import scala.tools.eclipse.contribution.weaving.jdt.IScalaClassFile
-
class ScalaClassFile(parent : PackageFragment, name : String, sourceFile : String)
extends ClassFile(parent, name) with ScalaCompilationUnit with IScalaClassFile {
override def getImageDescriptor = ScalaImages.SCALA_CLASS_FILE
View
15 org.scala-ide.sdt.core/src/scala/tools/eclipse/javaelements/ScalaSourceFile.scala
@@ -21,6 +21,15 @@ import org.eclipse.jdt.core.compiler.CharOperation
import scala.tools.nsc.interactive.Response
import scala.tools.eclipse.reconciliation.ReconciliationParticipantsExtensionPoint
import org.eclipse.jdt.core.JavaModelException
+import scala.tools.eclipse.InteractiveCompilationUnit
+import scala.tools.eclipse.sourcefileprovider.SourceFileProvider
+import org.eclipse.core.runtime.IPath
+
+
+class ScalaSourceFileProvider extends SourceFileProvider {
+ override def createFrom(path: IPath): Option[InteractiveCompilationUnit] =
+ ScalaSourceFile.createFromPath(path.toString)
+}
object ScalaSourceFile {
val handleFactory = new HandleFactory
@@ -28,11 +37,13 @@ object ScalaSourceFile {
def createFromPath(path : String) : Option[ScalaSourceFile] = {
if (!path.endsWith(".scala"))
None
- else
- handleFactory.createOpenable(path, null) match {
+ else {
+ val openable = handleFactory.createOpenable(path, null)
+ openable match {
case ssf : ScalaSourceFile => Some(ssf)
case _ => None
}
+ }
}
}
View
9 org.scala-ide.sdt.core/src/scala/tools/eclipse/sourcefileprovider/SourceFileProvider.scala
@@ -0,0 +1,9 @@
+package scala.tools.eclipse.sourcefileprovider
+
+import org.eclipse.core.runtime.IPath
+import scala.tools.eclipse.InteractiveCompilationUnit
+
+trait SourceFileProvider {
+ /** Create a compilation unit for the passed workspace `path`.*/
+ def createFrom(path: IPath): Option[InteractiveCompilationUnit]
+}
View
56 org.scala-ide.sdt.core/src/scala/tools/eclipse/sourcefileprovider/SourceFileProviderRegistry.scala
@@ -0,0 +1,56 @@
+package scala.tools.eclipse.sourcefileprovider
+
+import java.util.concurrent.{ConcurrentMap, ConcurrentHashMap}
+import org.eclipse.core.runtime.CoreException
+import org.eclipse.core.runtime.IConfigurationElement
+import org.eclipse.core.runtime.IExtension
+import org.eclipse.core.runtime.IExtensionPoint
+import org.eclipse.core.runtime.Platform
+import scala.tools.eclipse.logging.HasLogger
+import org.eclipse.core.runtime.IPath
+
+object SourceFileProviderRegistry extends HasLogger {
+ private val EXTENSION_POINT = "org.scala-ide.sdt.core.sourcefileprovider"
+
+ private object FileExtension {
+ def apply(path: IPath): FileExtension = new FileExtension(path.getFileExtension())
+ }
+
+ private case class FileExtension(extension: String)
+
+ // Note: The map has to be thread-safe, since it can potentially be accessed by different threads at the same time
+ private val registry: ConcurrentMap[FileExtension, SourceFileProvider] = new ConcurrentHashMap
+
+ registerProviders()
+
+ def getProvider(path: IPath): SourceFileProvider = getProvider(FileExtension(path))
+
+ private def getProvider(extension: FileExtension): SourceFileProvider = registry get extension
+
+ private def registerProviders() {
+ val extensionPoint = Platform.getExtensionRegistry().getExtensionPoint(EXTENSION_POINT)
+ if (extensionPoint != null) {
+ val extensions = extensionPoint.getExtensions()
+ for {
+ extension <- extensions
+ config <- extension.getConfigurationElements
+ if config.isValid
+ } try {
+ val provider = config.createExecutableExtension("class").asInstanceOf[SourceFileProvider]
+ registerProvider(config.getAttribute("file_extension"), provider)
+ } catch {
+ case e: CoreException =>
+ eclipseLog.error("Failed to register source file provider for extension point: " + extension, e)
+ }
+ }
+ }
+
+ private def registerProvider(fileExtension: String, provider: SourceFileProvider): Unit = {
+ val extension = FileExtension(fileExtension)
+ if(registry containsKey extension) eclipseLog.warn("Source file provider for file extension `%s` already exists. Registration of `%s` will hence be ignored.".format(fileExtension, provider))
+ else registry put (extension, provider)
+ }
+
+ // Note: we may need to implement the `IRegistryEventListener` if we want to support plugins that are started on the fly. This can be easily done
+ // via `Platform.getExtensionRegistry().addListener(...)`
+}
Please sign in to comment.
Something went wrong with that request. Please try again.