Browse files

Merge pull request #182 from scala-ide/feature/composite-hyperlink-de…

…tector

New extension point for Source File Providers
  • Loading branch information...
2 parents 1ca4d56 + 8562d0d commit 2bbd7356ecde8dcdd154868e19c3987e48096306 @dotta dotta committed Aug 15, 2012
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
5 org.scala-ide.sdt.core/src/scala/tools/eclipse/ScalaSourceViewerConfiguration.scala
@@ -35,7 +35,7 @@ import scala.tools.eclipse.lexical._
import scala.tools.eclipse.formatter.ScalaFormattingStrategy
import scala.tools.eclipse.ui.AutoCloseBracketStrategy
import scala.tools.eclipse.properties.syntaxcolouring.ScalaSyntaxClasses
-import scala.tools.eclipse.hyperlink.text.detector.HyperlinksDetector
+import scala.tools.eclipse.hyperlink.text.detector.{CompositeHyperlinkDetector, DeclarationHyperlinkDetector, ImplicitHyperlinkDetector}
import scalariform.ScalaVersions
class ScalaSourceViewerConfiguration(store: IPreferenceStore, scalaPreferenceStore: IPreferenceStore, editor: ITextEditor)
@@ -88,7 +88,8 @@ class ScalaSourceViewerConfiguration(store: IPreferenceStore, scalaPreferenceSto
}
override def getHyperlinkDetectors(sv: ISourceViewer): Array[IHyperlinkDetector] = {
- val detector = HyperlinksDetector()
+ val strategies = List(DeclarationHyperlinkDetector(), ImplicitHyperlinkDetector())
+ val detector = new CompositeHyperlinkDetector(strategies)
if (editor != null) detector.setContext(editor)
Array(detector)
}
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
5 ...-ide.sdt.core/src/scala/tools/eclipse/hyperlink/text/detector/BaseHyperlinkDetector.scala
@@ -35,5 +35,8 @@ abstract class BaseHyperlinkDetector extends AbstractHyperlinkDetector {
}
}
- protected[detector] def runDetectionStrategy(scu: InteractiveCompilationUnit, textEditor: ITextEditor, currentSelection: IRegion): List[IHyperlink]
+ private[detector] def friendRunDetectionStrategy(scu: InteractiveCompilationUnit, textEditor: ITextEditor, currentSelection: IRegion): List[IHyperlink] =
+ runDetectionStrategy(scu, textEditor, currentSelection)
+
+ protected def runDetectionStrategy(scu: InteractiveCompilationUnit, textEditor: ITextEditor, currentSelection: IRegion): List[IHyperlink]
}
View
11 ...nk/text/detector/HyperlinksDetector.scala → ...detector/CompositeHyperlinkDetector.scala
@@ -7,14 +7,7 @@ import org.eclipse.ui.texteditor.ITextEditor
import scala.tools.eclipse.InteractiveCompilationUnit
-private class HyperlinksDetector extends BaseHyperlinkDetector {
-
- private val strategies: List[BaseHyperlinkDetector] = List(DeclarationHyperlinkDetector(), ImplicitHyperlinkDetector())
-
+class CompositeHyperlinkDetector(strategies: List[BaseHyperlinkDetector]) extends BaseHyperlinkDetector {
override protected[detector] def runDetectionStrategy(scu: InteractiveCompilationUnit, textEditor: ITextEditor, currentSelection: IRegion): List[IHyperlink] =
- strategies flatMap { _.runDetectionStrategy(scu, textEditor, currentSelection) }
-}
-
-object HyperlinksDetector {
- def apply(): AbstractHyperlinkDetector = new HyperlinksDetector
+ strategies flatMap { _.friendRunDetectionStrategy(scu, textEditor, currentSelection) }
}
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,18 +21,29 @@ 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
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 ...-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(...)`
+}

0 comments on commit 2bbd735

Please sign in to comment.