diff --git a/libraries/docs/apidoc/pom.xml b/libraries/docs/apidoc/pom.xml index 77db2cd1123..fe237a8717f 100644 --- a/libraries/docs/apidoc/pom.xml +++ b/libraries/docs/apidoc/pom.xml @@ -50,7 +50,8 @@ junit org - + https://github.com/JetBrains/kotlin/tree/master + ${project-root} diff --git a/libraries/tools/kdoc-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/doc/KDocMojo.java b/libraries/tools/kdoc-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/doc/KDocMojo.java index 16b7bc6c3be..287009860d2 100644 --- a/libraries/tools/kdoc-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/doc/KDocMojo.java +++ b/libraries/tools/kdoc-maven-plugin/src/main/java/org/jetbrains/kotlin/maven/doc/KDocMojo.java @@ -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 { diff --git a/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/KDocConfig.kt b/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/KDocConfig.kt index 442d1bc3d16..f968d1b860d 100644 --- a/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/KDocConfig.kt +++ b/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/KDocConfig.kt @@ -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 */ diff --git a/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/model/KotlinModel.kt b/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/model/KotlinModel.kt index fce3310a7c2..85be8b91aa2 100644 --- a/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/model/KotlinModel.kt +++ b/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/model/KotlinModel.kt @@ -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): Unit { val allNamespaces = HashSet() @@ -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 { @@ -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() public val classMap: SortedMap = TreeMap() @@ -838,8 +927,7 @@ class KClass(val pkg: KPackage, val descriptor: ClassDescriptor, var since: String = "", var authors: List = arrayList(), var baseClasses: List = arrayList(), - var nestedClasses: List = arrayList(), - var sourceLine: Int = 2): KClassOrPackage(pkg.model, descriptor), Comparable { + var nestedClasses: List = arrayList()): KClassOrPackage(pkg.model, descriptor), Comparable { 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 = arrayList(), var typeParameters: List = arrayList(), var exceptions: List = arrayList(), - var annotations: List = arrayList(), - var sourceLine: Int = 2): KAnnotated(owner.model, descriptor), Comparable { + var annotations: List = arrayList()): KAnnotated(owner.model, descriptor), Comparable { public val parameterTypeText: String = parameters.map{ it.aType.name }.makeString(", ") diff --git a/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/templates/KDocTemplate.kt b/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/templates/KDocTemplate.kt index 193ac17a269..e71a7d2cbf2 100644 --- a/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/templates/KDocTemplate.kt +++ b/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/templates/KDocTemplate.kt @@ -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 { diff --git a/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/templates/PackageTemplateSupport.kt b/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/templates/PackageTemplateSupport.kt index 142d80f4c77..b6fb43cf8c3 100644 --- a/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/templates/PackageTemplateSupport.kt +++ b/libraries/tools/kdoc/src/main/kotlin/org/jetbrains/kotlin/doc/templates/PackageTemplateSupport.kt @@ -207,7 +207,7 @@ abstract class PackageTemplateSupport(open val pkg: KPackage) : KDocTemplate() { } */ println("""""") - print("""${property.name}: """) + print("""${property.name}: """) print(link(property.returnType)) //printParameters(property) println("""""")
${property.name}: """) + print("""${property.name}: """) print(link(property.returnType)) //printParameters(property) println("""""")
${property.name}: """) print(link(property.returnType)) //printParameters(property) println("""