Skip to content

Commit

Permalink
Created extension point for SourceFileProvider so that the Hyperlinki…
Browse files Browse the repository at this point in the history
…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
dotta committed Aug 15, 2012
1 parent ad6095c commit 8562d0d
Show file tree
Hide file tree
Showing 10 changed files with 227 additions and 36 deletions.
1 change: 1 addition & 0 deletions org.scala-ide.sdt.core/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 8 additions & 0 deletions org.scala-ide.sdt.core/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down
106 changes: 106 additions & 0 deletions org.scala-ide.sdt.core/schema/sourceFileProviders.exsd
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 12 additions & 11 deletions org.scala-ide.sdt.core/src/scala/tools/eclipse/LocateSymbol.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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]
Expand All @@ -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
Expand All @@ -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()
Expand All @@ -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) {
Expand All @@ -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)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
*
Expand All @@ -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 _ =>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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]
}
Original file line number Diff line number Diff line change
@@ -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 8562d0d

Please sign in to comment.