allow kdoc to link to the online source code in a source code repo (such as github)

This commit is contained in:
James Strachan
2012-04-13 15:07:37 +01:00
parent 37fcffc39d
commit 43261a7172
6 changed files with 173 additions and 26 deletions
+2 -1
View File
@@ -50,7 +50,8 @@
<ignorePackage>junit</ignorePackage> <ignorePackage>junit</ignorePackage>
<ignorePackage>org</ignorePackage> <ignorePackage>org</ignorePackage>
</ignorePackages> </ignorePackages>
<sourceRootHref>https://github.com/JetBrains/kotlin/tree/master</sourceRootHref>
<projectRootDir>${project-root}</projectRootDir>
</configuration> </configuration>
<executions> <executions>
@@ -118,6 +118,20 @@ public class KDocMojo extends KotlinCompileMojoBase {
*/ */
private String version; private String version;
/**
* The HTTP link to source code
*
* @parameter expression="${sourceRootHref}"
*/
private String sourceRootHref;
/**
* The root project directory used to deduce relative file names when linking to source code
*
* @parameter expression="${projectRootDir}" default-value="${project.basedir}"
*/
private String projectRootDir;
/** /**
* Whether warnings should be generated if no comments could be found for classes, functions and properties being documented * Whether warnings should be generated if no comments could be found for classes, functions and properties being documented
* *
@@ -150,10 +164,14 @@ public class KDocMojo extends KotlinCompileMojoBase {
docConfig.setTitle(title); docConfig.setTitle(title);
docConfig.setVersion(version); docConfig.setVersion(version);
docConfig.setWarnNoComments(warnNoComments); docConfig.setWarnNoComments(warnNoComments);
docConfig.setSourceRootHref(sourceRootHref);
docConfig.setProjectRootDir(projectRootDir);
getLog().info("API docs output to: " + docConfig.getDocOutputDir()); getLog().info("API docs output to: " + docConfig.getDocOutputDir());
getLog().info("classpath: " + classpath); getLog().info("classpath: " + classpath);
getLog().info("title: " + title); getLog().info("title: " + title);
getLog().info("sources: " + sources); getLog().info("sources: " + sources);
getLog().info("sourceRootHref: " + sourceRootHref);
getLog().info("projectRootDir: " + projectRootDir);
getLog().info("API docs ignore packages: " + ignorePackages); getLog().info("API docs ignore packages: " + ignorePackages);
} }
else { else {
@@ -41,6 +41,16 @@ class KDocConfig() {
*/ */
public var warnNoComments: Boolean = true public var warnNoComments: Boolean = true
/**
* Returns the HTTP URL of the root directory of source code that we should link to
*/
public var sourceRootHref: String? = null
/**
* The root project directory used to deduce relative file names when linking to source code
*/
public var projectRootDir: String? = null
/** /**
* Returns true if protected functions and properties should be documented * Returns true if protected functions and properties should be documented
*/ */
@@ -39,6 +39,10 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.PsiDirectory import com.intellij.psi.PsiDirectory
import org.jetbrains.jet.lang.descriptors.Visibilities import org.jetbrains.jet.lang.descriptors.Visibilities
import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils
import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils.LineAndColumn
import com.intellij.psi.PsiFileSystemItem
import java.io.File
/** /**
@@ -190,6 +194,25 @@ class KModel(var context: BindingContext, val config: KDocConfig) {
public val version: String public val version: String
get() = config.version get() = config.version
private var _projectRootDir: String? = null
/**
* Returns the root project directory for calculating relative source links
*/
fun projectRootDir(): String {
if (_projectRootDir == null) {
val rootDir = config.projectRootDir
_projectRootDir = if (rootDir == null) {
warning("KDocConfig does not have a projectRootDir defined so we cannot generate relative source Hrefs")
""
} else {
File(rootDir).getCanonicalPath() ?: ""
}
}
return _projectRootDir ?: ""
}
/** Loads the model from the given set of source files */ /** Loads the model from the given set of source files */
fun load(sources: List<JetFile?>): Unit { fun load(sources: List<JetFile?>): Unit {
val allNamespaces = HashSet<NamespaceDescriptor>() val allNamespaces = HashSet<NamespaceDescriptor>()
@@ -344,12 +367,30 @@ class KModel(var context: BindingContext, val config: KDocConfig) {
return null return null
} }
fun locationFor(descriptor: DeclarationDescriptor): LineAndColumn? {
val psiElement = getPsiElement(descriptor)
if (psiElement != null) {
val document = psiElement.getContainingFile()?.getViewProvider()?.getDocument()
if (document != null) {
val offset = psiElement.getTextOffset()
return DiagnosticUtils.offsetToLineAndColumn(document, offset)
}
}
return null
}
fun fileFor(descriptor: DeclarationDescriptor): String? { fun fileFor(descriptor: DeclarationDescriptor): String? {
val psiElement = getPsiElement(descriptor) val psiElement = getPsiElement(descriptor)
return psiElement?.getContainingFile()?.getName() return psiElement?.getContainingFile()?.getName()
} }
fun filePath(descriptor: DeclarationDescriptor): String? {
val psiElement = getPsiElement(descriptor)
val file = psiElement?.getContainingFile()
return filePath(file)
}
protected fun getPsiElement(descriptor: DeclarationDescriptor): PsiElement? { protected fun getPsiElement(descriptor: DeclarationDescriptor): PsiElement? {
return try { return try {
BindingContextUtils.descriptorToDeclaration(context, descriptor) BindingContextUtils.descriptorToDeclaration(context, descriptor)
@@ -446,6 +487,17 @@ class KModel(var context: BindingContext, val config: KDocConfig) {
return null return null
} }
protected fun filePath(file: PsiFileSystemItem?): String? {
if (file != null) {
var dir = file.getParent()
if (dir != null) {
val parentName = filePath(dir) ?: ""
return parentName + "/" + file.getName()
}
}
return null
}
/** /**
* Extracts the block of code within { .. } tokens or returning null if it can't be found * Extracts the block of code within { .. } tokens or returning null if it can't be found
*/ */
@@ -704,6 +756,39 @@ abstract class KAnnotated(val model: KModel, val declarationDescriptor: Declarat
val file = model.fileFor(declarationDescriptor) val file = model.fileFor(declarationDescriptor)
return model.wikiConvert(wikiDescription, TemplateLinkRenderer(this, template), file) return model.wikiConvert(wikiDescription, TemplateLinkRenderer(this, template), file)
} }
fun isLinkToSourceRepo(): Boolean {
return model.config.sourceRootHref != null
}
fun sourceLink(): String {
val file = model.filePath(declarationDescriptor)
if (file != null) {
// lets remove the root project directory
val rootDir = model.projectRootDir()
val canonicalFile = File(file).getCanonicalPath() ?: ""
val relativeFile = if (rootDir != null && canonicalFile.startsWith(rootDir))
canonicalFile.substring(rootDir.length()) else canonicalFile
return sourceLinkFor(relativeFile!!)
}
return ""
}
protected fun sourceLinkFor(filePath: String, lineLinkText: String = "#L"): String {
val root = model.config.sourceRootHref!!
val cleanRoot = root.trimTrailing("/")
val cleanPath = filePath.trimLeading("/")
return "$cleanRoot/$cleanPath$lineLinkText$sourceLine"
}
fun location(): LineAndColumn? = model.locationFor(declarationDescriptor)
val sourceLine: Int
get() {
val loc = location()
return if (loc != null) loc.getLine() else 1
}
} }
abstract class KNamed(val name: String, model: KModel, declarationDescriptor: DeclarationDescriptor): KAnnotated(model, declarationDescriptor), Comparable<KNamed> { abstract class KNamed(val name: String, model: KModel, declarationDescriptor: DeclarationDescriptor): KAnnotated(model, declarationDescriptor), Comparable<KNamed> {
@@ -772,6 +857,10 @@ class KPackage(model: KModel, val descriptor: NamespaceDescriptor,
return if (answer.length == 0) "" else answer + "/" return if (answer.length == 0) "" else answer + "/"
} }
// TODO generates java.lang.NoSuchMethodError: kotlin.util.namespace.hashMap(Ljet/TypeInfo;Ljet/TypeInfo;)Ljava/util/HashMap; // TODO generates java.lang.NoSuchMethodError: kotlin.util.namespace.hashMap(Ljet/TypeInfo;Ljet/TypeInfo;)Ljava/util/HashMap;
//val classes = sortedMap<String,KClass>() //val classes = sortedMap<String,KClass>()
public val classMap: SortedMap<String, KClass> = TreeMap<String, KClass>() public val classMap: SortedMap<String, KClass> = TreeMap<String, KClass>()
@@ -838,8 +927,7 @@ class KClass(val pkg: KPackage, val descriptor: ClassDescriptor,
var since: String = "", var since: String = "",
var authors: List<String> = arrayList<String>(), var authors: List<String> = arrayList<String>(),
var baseClasses: List<KType> = arrayList<KType>(), var baseClasses: List<KType> = arrayList<KType>(),
var nestedClasses: List<KClass> = arrayList<KClass>(), var nestedClasses: List<KClass> = arrayList<KClass>()): KClassOrPackage(pkg.model, descriptor), Comparable<KClass> {
var sourceLine: Int = 2): KClassOrPackage(pkg.model, descriptor), Comparable<KClass> {
public override fun compareTo(other: KClass): Int = name.compareTo(other.name) public override fun compareTo(other: KClass): Int = name.compareTo(other.name)
@@ -921,8 +1009,7 @@ class KFunction(val descriptor: CallableDescriptor, val owner: KClassOrPackage,
var modifiers: List<String> = arrayList<String>(), var modifiers: List<String> = arrayList<String>(),
var typeParameters: List<KTypeParameter> = arrayList<KTypeParameter>(), var typeParameters: List<KTypeParameter> = arrayList<KTypeParameter>(),
var exceptions: List<KClass> = arrayList<KClass>(), var exceptions: List<KClass> = arrayList<KClass>(),
var annotations: List<KAnnotation> = arrayList<KAnnotation>(), var annotations: List<KAnnotation> = arrayList<KAnnotation>()): KAnnotated(owner.model, descriptor), Comparable<KFunction> {
var sourceLine: Int = 2): KAnnotated(owner.model, descriptor), Comparable<KFunction> {
public val parameterTypeText: String = parameters.map{ it.aType.name }.makeString(", ") public val parameterTypeText: String = parameters.map{ it.aType.name }.makeString(", ")
@@ -54,30 +54,61 @@ abstract class KDocTemplate() : TextTemplate() {
} }
open fun sourceHref(klass: KClass): String { open fun sourceHref(klass: KClass): String {
val pkg = klass.pkg if (klass.isLinkToSourceRepo()) {
return if (pkg.local) { return klass.sourceLink()
"${pkg.nameAsRelativePath}src-html/${klass.nameAsPath}.html#line.${klass.sourceLine}"
} else { } else {
href(klass) val pkg = klass.pkg
return if (pkg.local) {
"${pkg.nameAsRelativePath}src-html/${klass.nameAsPath}.html#line.${klass.sourceLine}"
} else {
href(klass)
}
} }
} }
open fun sourceHref(f: KFunction): String { open fun sourceHref(f: KFunction): String {
val owner = f.owner if (f.isLinkToSourceRepo()) {
return if (owner is KClass) { return f.sourceLink()
val pkg = owner.pkg } else {
if (pkg.local) { val owner = f.owner
"${rootHref(pkg)}src-html/${owner.simpleName}.html#line.${f.sourceLine}" return if (owner is KClass) {
} else { val pkg = owner.pkg
href(f) if (pkg.local) {
} "${rootHref(pkg)}src-html/${owner.simpleName}.html#line.${f.sourceLine}"
} else if (owner is KPackage) { } else {
if (owner.local) { href(f)
// TODO how to find the function in a package??? }
"${rootHref(owner)}src-html/namespace.html#line.${f.sourceLine}" } else if (owner is KPackage) {
} else { if (owner.local) {
href(owner) // TODO how to find the function in a package???
} "${rootHref(owner)}src-html/namespace.html#line.${f.sourceLine}"
} else href(f) } else {
href(owner)
}
} else href(f)
}
}
open fun sourceHref(f: KProperty): String {
if (f.isLinkToSourceRepo()) {
return f.sourceLink()
} else {
val owner = f.owner
return if (owner is KClass) {
val pkg = owner.pkg
if (pkg.local) {
"${rootHref(pkg)}src-html/${owner.simpleName}.html#line.${f.sourceLine}"
} else {
href(f)
}
} else if (owner is KPackage) {
if (owner.local) {
// TODO how to find the function in a package???
"${rootHref(owner)}src-html/namespace.html#line.${f.sourceLine}"
} else {
href(owner)
}
} else href(f)
}
} }
open fun link(c: KClass, fullName: Boolean = false): String { open fun link(c: KClass, fullName: Boolean = false): String {
@@ -207,7 +207,7 @@ abstract class PackageTemplateSupport(open val pkg: KPackage) : KDocTemplate() {
} }
*/ */
println("""</CODE></FONT></TD>""") println("""</CODE></FONT></TD>""")
print("""<TD><CODE><B><A HREF="${href(property)}">${property.name}</A></B>: """) print("""<TD><CODE><B><A HREF="${sourceHref(property)}">${property.name}</A></B>: """)
print(link(property.returnType)) print(link(property.returnType))
//printParameters(property) //printParameters(property)
println("""</CODE>""") println("""</CODE>""")