PY-29717 Add proper links on methods

This commit is contained in:
Mikhail Golubev
2018-06-09 15:59:37 +03:00
parent 1bc66f1853
commit bfbf0a083a
4 changed files with 42 additions and 13 deletions
@@ -425,7 +425,7 @@ public class PyDocumentationBuilder {
: null;
if (ancestorLink != null) {
final ChainIterable<String> link = mySectionsMap.get(PyBundle.message("QDOC.documentation.is.copied.from"));
link.addWith(TagCode, isFromClass ? $(ancestorLink) : $(ancestorLink).addItem("." + methodName));
link.addWith(TagCode, isFromClass ? $(ancestorLink) : $(PyDocumentationLink.toFunction(inherited)));
}
myContent.add(formatDocString(pyFunction, inheritedDoc));
return;
@@ -18,9 +18,11 @@ package com.jetbrains.python.documentation
import com.intellij.codeInsight.documentation.DocumentationManagerProtocol
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.QualifiedName
import com.jetbrains.python.psi.PyClass
import com.jetbrains.python.psi.PyFunction
import com.jetbrains.python.psi.PyNamedParameter
import com.jetbrains.python.psi.PyPsiFacade
import com.jetbrains.python.psi.types.PyClassType
import com.jetbrains.python.psi.types.PyTypeParser
import com.jetbrains.python.psi.types.TypeEvalContext
@@ -30,6 +32,7 @@ object PyDocumentationLink {
private const val LINK_TYPE_CLASS = "#class#"
private const val LINK_TYPE_PARAM = "#param#"
private const val LINK_TYPE_TYPENAME = "#typename#"
private const val LINK_TYPE_FUNC = "#func#"
@JvmStatic
fun toContainingClass(content: String?): String {
@@ -57,20 +60,46 @@ object PyDocumentationLink {
}
}
@JvmStatic
fun toFunction(func: PyFunction): String = toFunction(func.qualifiedName ?: func.name.orEmpty(), func)
@JvmStatic
fun toFunction(content: String, func: PyFunction): String {
val qualifiedName = func.qualifiedName
return when {
qualifiedName != null -> "<a href=\"${DocumentationManagerProtocol.PSI_ELEMENT_PROTOCOL}$LINK_TYPE_FUNC$qualifiedName\">$content</a>"
else -> content
}
}
@JvmStatic
fun elementForLink(link: String, element: PsiElement, context: TypeEvalContext): PsiElement? {
return if (link == LINK_TYPE_CLASS) {
containingClass(element)
return when {
link == LINK_TYPE_CLASS -> containingClass(element)
link == LINK_TYPE_PARAM -> parameterPossibleClass(element, context)
link.startsWith(LINK_TYPE_TYPENAME) -> possibleClass(link.substring(LINK_TYPE_TYPENAME.length), element, context)
link.startsWith(LINK_TYPE_FUNC) -> possibleFunction(link.substring(LINK_TYPE_FUNC.length), element)
else -> null
}
else if (link == LINK_TYPE_PARAM) {
parameterPossibleClass(element, context)
}
else if (link.startsWith(LINK_TYPE_TYPENAME)) {
possibleClass(link.substring(LINK_TYPE_TYPENAME.length), element, context)
}
else {
null
}
@JvmStatic
private fun possibleFunction(qualifiedName: String, element: PsiElement): PyFunction? {
// TODO a better, more general way to resolve qualified names of function
val facade = PyPsiFacade.getInstance(element.project)
val qName = QualifiedName.fromDottedString(qualifiedName)
val resolveContext = facade.createResolveContextFromFoothold(element).copyWithMembers()
val topLevel = facade.resolveQualifiedName(qName, resolveContext).filterIsInstance<PyFunction>().firstOrNull()
if (topLevel != null) {
return topLevel
}
return facade.resolveQualifiedName(qName.removeLastComponent(), resolveContext)
.asSequence()
.filterIsInstance<PyClass>()
.map { it.findMethodByName(qName.lastComponent, false, null) }
.firstOrNull()
}
@JvmStatic
@@ -1 +1 @@
<html><body><div class='definition'><pre><small>class <a href="psi_element://#class#">Foo</a>(<a href="psi_element://#typename#ClassUndocumentedEmptyConstructor.Moo">Moo</a>)</small><br><br>def&nbsp;<b>__init__</b>(self:&nbsp;<a href="psi_element://#typename#Foo">Foo</a>)&nbsp;-&gt;&nbsp;None</pre></div><div class='content'>Doc&nbsp;of&nbsp;Moo()</div><table class='sections'><tr><td valign='top' class='section'><p>Documentation is copied from:</td><td valign='top'><code><a href="psi_element://#typename#ClassUndocumentedEmptyConstructor.Moo">Moo</a>.__init__</code></td></table></body></html>
<html><body><div class='definition'><pre><small>class <a href="psi_element://#class#">Foo</a>(<a href="psi_element://#typename#ClassUndocumentedEmptyConstructor.Moo">Moo</a>)</small><br><br>def&nbsp;<b>__init__</b>(self:&nbsp;<a href="psi_element://#typename#Foo">Foo</a>)&nbsp;-&gt;&nbsp;None</pre></div><div class='content'>Doc&nbsp;of&nbsp;Moo()</div><table class='sections'><tr><td valign='top' class='section'><p>Documentation is copied from:</td><td valign='top'><code><a href="psi_element://#func#ClassUndocumentedEmptyConstructor.Moo.__init__">ClassUndocumentedEmptyConstructor.Moo.__init__</a></code></td></table></body></html>
@@ -1 +1 @@
<html><body><div class='definition'><pre><small>class <a href="psi_element://#class#">B</a>(<a href="psi_element://#typename#InheritedMethod.A">A</a>)</small><br><br>def&nbsp;<b>foo</b>(self:&nbsp;<a href="psi_element://#typename#B">B</a>)&nbsp;-&gt;&nbsp;None</pre></div><div class='content'>Doc&nbsp;from&nbsp;A.foo.</div><table class='sections'><tr><td valign='top' class='section'><p>Documentation is copied from:</td><td valign='top'><code><a href="psi_element://#typename#InheritedMethod.A">A</a>.foo</code></td></table></body></html>
<html><body><div class='definition'><pre><small>class <a href="psi_element://#class#">B</a>(<a href="psi_element://#typename#InheritedMethod.A">A</a>)</small><br><br>def&nbsp;<b>foo</b>(self:&nbsp;<a href="psi_element://#typename#B">B</a>)&nbsp;-&gt;&nbsp;None</pre></div><div class='content'>Doc&nbsp;from&nbsp;A.foo.</div><table class='sections'><tr><td valign='top' class='section'><p>Documentation is copied from:</td><td valign='top'><code><a href="psi_element://#func#InheritedMethod.A.foo">InheritedMethod.A.foo</a></code></td></table></body></html>