提交 43261a71 编写于 作者: J James Strachan

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

上级 37fcffc3
......@@ -50,7 +50,8 @@
<ignorePackage>junit</ignorePackage>
<ignorePackage>org</ignorePackage>
</ignorePackages>
<sourceRootHref>https://github.com/JetBrains/kotlin/tree/master</sourceRootHref>
<projectRootDir>${project-root}</projectRootDir>
</configuration>
<executions>
......
......@@ -118,6 +118,20 @@ public class KDocMojo extends KotlinCompileMojoBase {
*/
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
*
......@@ -150,10 +164,14 @@ public class KDocMojo extends KotlinCompileMojoBase {
docConfig.setTitle(title);
docConfig.setVersion(version);
docConfig.setWarnNoComments(warnNoComments);
docConfig.setSourceRootHref(sourceRootHref);
docConfig.setProjectRootDir(projectRootDir);
getLog().info("API docs output to: " + docConfig.getDocOutputDir());
getLog().info("classpath: " + classpath);
getLog().info("title: " + title);
getLog().info("sources: " + sources);
getLog().info("sourceRootHref: " + sourceRootHref);
getLog().info("projectRootDir: " + projectRootDir);
getLog().info("API docs ignore packages: " + ignorePackages);
}
else {
......
......@@ -41,6 +41,16 @@ class KDocConfig() {
*/
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
*/
......
......@@ -39,6 +39,10 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiDirectory
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
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 */
fun load(sources: List<JetFile?>): Unit {
val allNamespaces = HashSet<NamespaceDescriptor>()
......@@ -344,12 +367,30 @@ class KModel(var context: BindingContext, val config: KDocConfig) {
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? {
val psiElement = getPsiElement(descriptor)
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? {
return try {
BindingContextUtils.descriptorToDeclaration(context, descriptor)
......@@ -446,6 +487,17 @@ class KModel(var context: BindingContext, val config: KDocConfig) {
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
*/
......@@ -704,6 +756,39 @@ abstract class KAnnotated(val model: KModel, val declarationDescriptor: Declarat
val file = model.fileFor(declarationDescriptor)
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> {
......@@ -772,6 +857,10 @@ class KPackage(model: KModel, val descriptor: NamespaceDescriptor,
return if (answer.length == 0) "" else answer + "/"
}
// TODO generates java.lang.NoSuchMethodError: kotlin.util.namespace.hashMap(Ljet/TypeInfo;Ljet/TypeInfo;)Ljava/util/HashMap;
//val classes = sortedMap<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 authors: List<String> = arrayList<String>(),
var baseClasses: List<KType> = arrayList<KType>(),
var nestedClasses: List<KClass> = arrayList<KClass>(),
var sourceLine: Int = 2): KClassOrPackage(pkg.model, descriptor), Comparable<KClass> {
var nestedClasses: List<KClass> = arrayList<KClass>()): KClassOrPackage(pkg.model, descriptor), Comparable<KClass> {
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 typeParameters: List<KTypeParameter> = arrayList<KTypeParameter>(),
var exceptions: List<KClass> = arrayList<KClass>(),
var annotations: List<KAnnotation> = arrayList<KAnnotation>(),
var sourceLine: Int = 2): KAnnotated(owner.model, descriptor), Comparable<KFunction> {
var annotations: List<KAnnotation> = arrayList<KAnnotation>()): KAnnotated(owner.model, descriptor), Comparable<KFunction> {
public val parameterTypeText: String = parameters.map{ it.aType.name }.makeString(", ")
......
......@@ -54,30 +54,61 @@ abstract class KDocTemplate() : TextTemplate() {
}
open fun sourceHref(klass: KClass): String {
val pkg = klass.pkg
return if (pkg.local) {
"${pkg.nameAsRelativePath}src-html/${klass.nameAsPath}.html#line.${klass.sourceLine}"
if (klass.isLinkToSourceRepo()) {
return klass.sourceLink()
} 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 {
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)
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 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 {
......
......@@ -207,7 +207,7 @@ abstract class PackageTemplateSupport(open val pkg: KPackage) : KDocTemplate() {
}
*/
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))
//printParameters(property)
println("""</CODE>""")
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册