mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Move all documentation links processing to separate class
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
package com.jetbrains.python.documentation;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.intellij.codeInsight.documentation.DocumentationManagerProtocol;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.xml.CommonXmlStrings;
|
||||
@@ -51,6 +50,9 @@ class DocumentationBuilderKit {
|
||||
@NotNull
|
||||
static final Function<String, String> WRAP_IN_ITALIC = s -> "<i>" + s + "</i>";
|
||||
|
||||
@NotNull
|
||||
static final Function<String, String> WRAP_IN_CODE = s -> "<code>" + s + "</code>";
|
||||
|
||||
private DocumentationBuilderKit() {
|
||||
}
|
||||
|
||||
@@ -101,19 +103,4 @@ class DocumentationBuilderKit {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class LinkWrapper implements Function<Iterable<String>, Iterable<String>> {
|
||||
private final String myLink;
|
||||
|
||||
LinkWrapper(String link) {
|
||||
myLink = link;
|
||||
}
|
||||
|
||||
public Iterable<String> apply(Iterable<String> contents) {
|
||||
return new ChainIterable<String>()
|
||||
.addItem("<a href=\"").addItem(DocumentationManagerProtocol.PSI_ELEMENT_PROTOCOL).addItem(myLink).addItem("\">")
|
||||
.add(contents).addItem("</a>")
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,10 @@ import com.jetbrains.python.psi.resolve.PyResolveContext;
|
||||
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
|
||||
import com.jetbrains.python.psi.resolve.QualifiedResolveResult;
|
||||
import com.jetbrains.python.psi.resolve.RootVisitor;
|
||||
import com.jetbrains.python.psi.types.*;
|
||||
import com.jetbrains.python.psi.types.PyClassType;
|
||||
import com.jetbrains.python.psi.types.PyDynamicallyEvaluatedType;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import com.jetbrains.python.pyi.PyiUtil;
|
||||
import com.jetbrains.python.toolbox.ChainIterable;
|
||||
import com.jetbrains.python.toolbox.Maybe;
|
||||
@@ -384,10 +387,8 @@ public class PyDocumentationBuilder {
|
||||
if (inheritedDoc.length() > 1) {
|
||||
myEpilog.addItem(BR).addItem(BR);
|
||||
final String ancestorName = ancestor.getName();
|
||||
final String marker =
|
||||
(pyClass == ancestor) ? PythonDocumentationProvider.LINK_TYPE_CLASS : PythonDocumentationProvider.LINK_TYPE_PARENT;
|
||||
final String ancestorLink =
|
||||
$().addWith(new LinkWrapper(marker + ancestorName), $(ancestorName)).toString();
|
||||
pyClass == ancestor ? PyDocumentationLink.toContainingClass(ancestorName) : PyDocumentationLink.toAncestorOfContainingClass(ancestorName);
|
||||
if (isFromClass) {
|
||||
myEpilog.addItem(PyBundle.message("QDOC.copied.from.class.$0", ancestorLink));
|
||||
}
|
||||
@@ -487,13 +488,7 @@ public class PyDocumentationBuilder {
|
||||
final String description = typeAndDescr.second;
|
||||
|
||||
if (type != null) {
|
||||
final PyType pyType = PyTypeParser.getTypeByName(parameter, type, context);
|
||||
if (pyType instanceof PyClassType) {
|
||||
myBody.addItem(": ").addWith(new LinkWrapper(PythonDocumentationProvider.LINK_TYPE_PARAM), $(pyType.getName()));
|
||||
}
|
||||
else {
|
||||
myBody.addItem(": ").addItem(type);
|
||||
}
|
||||
myBody.addItem(": ").addItem(PyDocumentationLink.toParameterPossibleClass(type, parameter, context));
|
||||
}
|
||||
|
||||
if (description != null) {
|
||||
@@ -523,8 +518,11 @@ public class PyDocumentationBuilder {
|
||||
assert cls != null;
|
||||
final String type = PyUtil.isInstanceAttribute((PyExpression)myElement) ? "Instance attribute " : "Class attribute ";
|
||||
myProlog
|
||||
.addItem(type).addWith(TagBold, $().addWith(TagCode, $(((PyTargetExpression)myElement).getName())))
|
||||
.addItem(" of class ").addWith(PythonDocumentationProvider.LinkMyClass, $().addWith(TagCode, $(cls.getName()))).addItem(BR);
|
||||
.addItem(type)
|
||||
.addWith(TagBold, $().addWith(TagCode, $(((PyTargetExpression)myElement).getName())))
|
||||
.addItem(" of class ")
|
||||
.addItem(PyDocumentationLink.toContainingClass(WRAP_IN_CODE.apply(cls.getName())))
|
||||
.addItem(BR);
|
||||
|
||||
final String docString = PyPsiUtils.strValue(getEffectiveDocStringExpression((PyTargetExpression)myElement));
|
||||
if (docString != null) {
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.jetbrains.python.documentation
|
||||
|
||||
import com.intellij.codeInsight.documentation.DocumentationManagerProtocol
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.jetbrains.python.psi.PyClass
|
||||
import com.jetbrains.python.psi.PyFunction
|
||||
import com.jetbrains.python.psi.PyNamedParameter
|
||||
import com.jetbrains.python.psi.types.PyClassType
|
||||
import com.jetbrains.python.psi.types.PyTypeParser
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext
|
||||
|
||||
object PyDocumentationLink {
|
||||
|
||||
private const val LINK_TYPE_CLASS = "#class#"
|
||||
private const val LINK_TYPE_PARENT = "#parent#"
|
||||
private const val LINK_TYPE_PARAM = "#param#"
|
||||
private const val LINK_TYPE_TYPENAME = "#typename#"
|
||||
|
||||
@JvmStatic
|
||||
fun toContainingClass(content: String?): String {
|
||||
return "<a href=\"${DocumentationManagerProtocol.PSI_ELEMENT_PROTOCOL}$LINK_TYPE_CLASS\">$content</a>"
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun toAncestorOfContainingClass(ancestor: String?): String {
|
||||
return "<a href=\"${DocumentationManagerProtocol.PSI_ELEMENT_PROTOCOL}$LINK_TYPE_PARENT$ancestor\">$ancestor</a>"
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun toParameterPossibleClass(type: String, anchor: PsiElement, context: TypeEvalContext): String {
|
||||
val pyType = PyTypeParser.getTypeByName(anchor, type, context)
|
||||
return when (pyType) {
|
||||
is PyClassType -> "<a href=\"${DocumentationManagerProtocol.PSI_ELEMENT_PROTOCOL}$LINK_TYPE_PARAM\">$type</a>"
|
||||
else -> type
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun toPossibleClass(type: String, anchor: PsiElement, context: TypeEvalContext): String {
|
||||
val pyType = PyTypeParser.getTypeByName(anchor, type, context)
|
||||
return when (pyType) {
|
||||
is PyClassType -> "<a href=\"${DocumentationManagerProtocol.PSI_ELEMENT_PROTOCOL}$LINK_TYPE_TYPENAME$type\">$type</a>"
|
||||
else -> type
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun elementForLink(link: String, element: PsiElement, context: TypeEvalContext): PsiElement? {
|
||||
return if (link == LINK_TYPE_CLASS) {
|
||||
containingClass(element)
|
||||
}
|
||||
else if (link == LINK_TYPE_PARAM) {
|
||||
parameterPossibleClass(element, context)
|
||||
}
|
||||
else if (link.startsWith(LINK_TYPE_PARENT)) {
|
||||
ancestorOfContainingClass(element, link.substring(LINK_TYPE_PARENT.length), context)
|
||||
}
|
||||
else if (link.startsWith(LINK_TYPE_TYPENAME)) {
|
||||
possibleClass(link.substring(LINK_TYPE_TYPENAME.length), element, context)
|
||||
}
|
||||
else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
private fun containingClass(element: PsiElement): PyClass? {
|
||||
return when (element) {
|
||||
is PyClass -> element
|
||||
is PyFunction -> element.containingClass
|
||||
else -> PsiTreeUtil.getParentOfType(element, PyClass::class.java)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
private fun ancestorOfContainingClass(element: PsiElement, ancestor: String, context: TypeEvalContext): PyClass? {
|
||||
val ancestors = containingClass(element)?.getAncestorClasses(context)
|
||||
return ancestors?.find { it.name == ancestor }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
private fun parameterPossibleClass(parameter: PsiElement, context: TypeEvalContext): PyClass? {
|
||||
if (parameter is PyNamedParameter) {
|
||||
val type = context.getType(parameter)
|
||||
if (type is PyClassType) {
|
||||
return type.pyClass
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
private fun possibleClass(type: String, anchor: PsiElement, context: TypeEvalContext): PyClass? {
|
||||
val pyType = PyTypeParser.getTypeByName(anchor, type, context)
|
||||
return when (pyType) {
|
||||
is PyClassType -> pyType.pyClass
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.jetbrains.python.documentation.DocumentationBuilderKit.$;
|
||||
import static com.jetbrains.python.documentation.DocumentationBuilderKit.combUp;
|
||||
|
||||
/**
|
||||
@@ -421,13 +420,8 @@ public class PyTypeModelBuilder {
|
||||
|
||||
@Override
|
||||
protected void addType(String name) {
|
||||
final PyType type = PyTypeParser.getTypeByName(myAnchor, name);
|
||||
if (type instanceof PyClassType) {
|
||||
myBody.addWith(new DocumentationBuilderKit.LinkWrapper(PythonDocumentationProvider.LINK_TYPE_TYPENAME + name), $(name));
|
||||
}
|
||||
else {
|
||||
add(name);
|
||||
}
|
||||
final TypeEvalContext context = TypeEvalContext.userInitiated(myAnchor.getProject(), myAnchor.getContainingFile());
|
||||
myBody.addItem(PyDocumentationLink.toPossibleClass(name, myAnchor, context));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,15 +44,12 @@ import com.jetbrains.python.documentation.docstrings.DocStringUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
|
||||
import com.jetbrains.python.psi.types.PyClassType;
|
||||
import com.jetbrains.python.psi.types.PyType;
|
||||
import com.jetbrains.python.psi.types.PyTypeParser;
|
||||
import com.jetbrains.python.psi.types.TypeEvalContext;
|
||||
import com.jetbrains.python.toolbox.ChainIterable;
|
||||
import org.apache.commons.httpclient.HttpClient;
|
||||
import org.apache.commons.httpclient.methods.HeadMethod;
|
||||
import org.apache.commons.httpclient.params.HttpConnectionManagerParams;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jsoup.Jsoup;
|
||||
@@ -75,11 +72,6 @@ import static com.jetbrains.python.documentation.DocumentationBuilderKit.*;
|
||||
*/
|
||||
public class PythonDocumentationProvider extends AbstractDocumentationProvider implements ExternalDocumentationProvider {
|
||||
|
||||
@NonNls static final String LINK_TYPE_CLASS = "#class#";
|
||||
@NonNls static final String LINK_TYPE_PARENT = "#parent#";
|
||||
@NonNls static final String LINK_TYPE_PARAM = "#param#";
|
||||
@NonNls static final String LINK_TYPE_TYPENAME = "#typename#";
|
||||
|
||||
// provides ctrl+hover info
|
||||
@Override
|
||||
@Nullable
|
||||
@@ -282,7 +274,7 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
|
||||
final String name = cls.getName();
|
||||
cat.addItem("class ");
|
||||
if (allowHtml && linkOwnName) {
|
||||
cat.addWith(LinkMyClass, $(name));
|
||||
cat.addItem(PyDocumentationLink.toContainingClass(name));
|
||||
}
|
||||
else {
|
||||
cat.addWith(nameWrapper, $(name));
|
||||
@@ -303,7 +295,7 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
|
||||
isNotFirst = true;
|
||||
}
|
||||
if (allowHtml) {
|
||||
cat.addWith(new LinkWrapper(LINK_TYPE_PARENT + parentName), $(parentName));
|
||||
cat.addItem(PyDocumentationLink.toAncestorOfContainingClass(parentName));
|
||||
}
|
||||
else {
|
||||
cat.addItem(parentName);
|
||||
@@ -344,30 +336,9 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
|
||||
|
||||
@Override
|
||||
public PsiElement getDocumentationElementForLink(PsiManager psiManager, @NotNull String link, @NotNull PsiElement context) {
|
||||
if (link.equals(LINK_TYPE_CLASS)) {
|
||||
return inferContainingClassOf(context);
|
||||
}
|
||||
else if (link.equals(LINK_TYPE_PARAM)) {
|
||||
return inferClassOfParameter(context);
|
||||
}
|
||||
else if (link.startsWith(LINK_TYPE_PARENT)) {
|
||||
final PyClass cls = inferContainingClassOf(context);
|
||||
if (cls != null) {
|
||||
final String desiredName = link.substring(LINK_TYPE_PARENT.length());
|
||||
for (PyClass parent : cls.getAncestorClasses(null)) {
|
||||
final String parentName = parent.getName();
|
||||
if (parentName != null && parentName.equals(desiredName)) return parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (link.startsWith(LINK_TYPE_TYPENAME)) {
|
||||
final String typeName = link.substring(LINK_TYPE_TYPENAME.length());
|
||||
final PyType type = PyTypeParser.getTypeByName(context, typeName);
|
||||
if (type instanceof PyClassType) {
|
||||
return ((PyClassType)type).getPyClass();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return PyDocumentationLink.elementForLink(link,
|
||||
context,
|
||||
TypeEvalContext.userInitiated(context.getProject(), context.getContainingFile()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -585,30 +556,4 @@ public class PythonDocumentationProvider extends AbstractDocumentationProvider i
|
||||
}
|
||||
return super.getCustomDocumentationElement(editor, file, contextElement);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PyClass inferContainingClassOf(PsiElement context) {
|
||||
if (context instanceof PyClass) return (PyClass)context;
|
||||
if (context instanceof PyFunction) {
|
||||
return ((PyFunction)context).getContainingClass();
|
||||
}
|
||||
else {
|
||||
return PsiTreeUtil.getParentOfType(context, PyClass.class);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PyClass inferClassOfParameter(@NotNull PsiElement context) {
|
||||
if (context instanceof PyNamedParameter) {
|
||||
final PyType type = TypeEvalContext.userInitiated(context.getProject(), context.getContainingFile()).getType(
|
||||
(PyNamedParameter)context);
|
||||
if (type instanceof PyClassType) {
|
||||
return ((PyClassType)type).getPyClass();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static final LinkWrapper LinkMyClass = new LinkWrapper(LINK_TYPE_CLASS);
|
||||
// link item to containing class
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
<html><body><code><small>class <a href="psi_element://#class#">Foo</a>(<a href="psi_element://#parent#object">object</a>)</small><br><br>def <b>__init__</b>(self)</code><br><br><i>Documentation is missing.</i> The following is copied from class <code><a href="psi_element://#class#Foo">Foo</a></code>.<br><br><code>Doc of Foo.</code></body></html>
|
||||
<html><body><code><small>class <a href="psi_element://#class#">Foo</a>(<a href="psi_element://#parent#object">object</a>)</small><br><br>def <b>__init__</b>(self)</code><br><br><i>Documentation is missing.</i> The following is copied from class <code><a href="psi_element://#class#">Foo</a></code>.<br><br><code>Doc of Foo.</code></body></html>
|
||||
@@ -1 +1 @@
|
||||
<html><body><code>def <b>f</b>(p1: T1, p2: T2, p3: T3)<br>Inferred type: (p1: TypeVar('T1', <a href="psi_element://#typename#int">int</a>), p2: TypeVar('T2', <a href="psi_element://#typename#int">int</a>, <a href="psi_element://#typename#str">str</a>), p3: TypeVar('T3', List[<a href="psi_element://#typename#bool">bool</a>])) -> None<br></code></body></html>
|
||||
<html><body><code>def <b>f</b>(p1: T1, p2: T2, p3: T3)<br>Inferred type: (p1: TypeVar('T1', <a href="psi_element://#typename#int">int</a>), p2: TypeVar('T2', <a href="psi_element://#typename#int">int</a>, <a href="psi_element://#typename#str">str</a>), p3: TypeVar('T3', <a href="psi_element://#typename#List">List</a>[<a href="psi_element://#typename#bool">bool</a>])) -> None<br></code></body></html>
|
||||
Reference in New Issue
Block a user