mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-64074 Add Quick Documentation for type parameters and type alias statements
Merge-request: IJ-MR-119974 Merged-by: Daniil Kalinin <Daniil.Kalinin@jetbrains.com> GitOrigin-RevId: fa0c57b3005b31d892a394b3a5f595ac10135a71
This commit is contained in:
committed by
intellij-monorepo-bot
parent
583bea7f1a
commit
c79da2708c
@@ -0,0 +1,38 @@
|
||||
<div class="section" id="the-type-statement">
|
||||
<span id="type"></span><h2>The <a class="reference internal" href="#type"><tt class="xref std std-keyword docutils literal"><span class="pre">type</span></tt></a> statement</h2>
|
||||
<pre id="index-47">
|
||||
<strong id="grammar-token-python-grammar-type_stmt"><span id="grammar-token-type-stmt"></span>type_stmt</strong> ::= 'type' <a class="reference internal" href="lexical_analysis.html#grammar-token-python-grammar-identifier"><code class="xref docutils literal notranslate"><span class="pre">identifier</span></code></a> [<a class="reference internal" href="compound_stmts.html#grammar-token-python-grammar-type_params"><code class="xref docutils literal notranslate"><span class="pre">type_params</span></code></a>] "=" <a class="reference internal" href="expressions.html#grammar-token-python-grammar-expression"><code class="xref docutils literal notranslate"><span class="pre">expression</span></code></a>
|
||||
</pre>
|
||||
<p>The <code class="xref std std-keyword docutils literal notranslate"><span class="pre">type</span></code> statement declares a type alias, which is an instance
|
||||
of <a class="reference internal" href="../library/typing.html#typing.TypeAliasType" title="typing.TypeAliasType"><code class="xref py py-class docutils literal notranslate"><span class="pre">typing.TypeAliasType</span></code></a>.</p>
|
||||
<p>For example, the following statement creates a type alias:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="nb">type</span> <span class="n">Point</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">float</span><span class="p">,</span> <span class="nb">float</span><span class="p">]</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This code is roughly equivalent to:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">annotation</span><span class="o">-</span><span class="k">def</span> <span class="nf">VALUE_OF_Point</span><span class="p">():</span>
|
||||
<span class="k">return</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">float</span><span class="p">,</span> <span class="nb">float</span><span class="p">]</span>
|
||||
<span class="n">Point</span> <span class="o">=</span> <span class="n">typing</span><span class="o">.</span><span class="n">TypeAliasType</span><span class="p">(</span><span class="s2">"Point"</span><span class="p">,</span> <span class="n">VALUE_OF_Point</span><span class="p">())</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">annotation-def</span></code> indicates an <a class="reference internal" href="executionmodel.html#annotation-scopes"><span class="std std-ref">annotation scope</span></a>, which behaves
|
||||
mostly like a function, but with several small differences.</p>
|
||||
<p>The value of the
|
||||
type alias is evaluated in the annotation scope. It is not evaluated when the
|
||||
type alias is created, but only when the value is accessed through the type alias’s
|
||||
<code class="xref py py-attr docutils literal notranslate"><span class="pre">__value__</span></code> attribute (see <a class="reference internal" href="executionmodel.html#lazy-evaluation"><span class="std std-ref">Lazy evaluation</span></a>).
|
||||
This allows the type alias to refer to names that are not yet defined.</p>
|
||||
<p>Type aliases may be made generic by adding a <a class="reference internal" href="compound_stmts.html#type-params"><span class="std std-ref">type parameter list</span></a>
|
||||
after the name. See <a class="reference internal" href="compound_stmts.html#generic-type-aliases"><span class="std std-ref">Generic type aliases</span></a> for more.</p>
|
||||
<p><code class="xref std std-keyword docutils literal notranslate"><span class="pre">type</span></code> is a <a class="reference internal" href="lexical_analysis.html#soft-keywords"><span class="std std-ref">soft keyword</span></a>.</p>
|
||||
<div class="versionadded">
|
||||
<p><span class="versionmodified added">New in version 3.12.</span></p>
|
||||
</div>
|
||||
<div class="admonition seealso">
|
||||
<p class="admonition-title">See also</p>
|
||||
<dl class="simple">
|
||||
<dt><span class="target" id="index-48"></span><a class="pep reference external" href="https://peps.python.org/pep-0695/"><strong>PEP 695</strong></a> - Type Parameter Syntax</dt><dd><p>Introduced the <code class="xref std std-keyword docutils literal notranslate"><span class="pre">type</span></code> statement and syntax for
|
||||
generic classes and functions.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
@@ -189,6 +189,10 @@ QDOC.variable.name=Variable "{0}"
|
||||
QDOC.parameter.of.function.name=Parameter "{0}" of function "{1}"
|
||||
QDOC.parameter.of.method.name=Parameter "{0}" of method "{1}"
|
||||
QDOC.parameter.name=Parameter "{0}"
|
||||
QDOC.type.parameter.name.of.link=Type parameter {0} of {1}
|
||||
QDOC.type.parameter.name=Type parameter {0}
|
||||
QDOC.type.parameter.kind=kind:
|
||||
QDOC.type.alias.statement.name.of.link=Type alias statement {0} of {1}
|
||||
|
||||
### Formatter
|
||||
formatter.panel.dict.alignment.do.not.align=Do not align
|
||||
|
||||
+56
@@ -17,6 +17,8 @@ import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.FactoryMap;
|
||||
import com.jetbrains.python.*;
|
||||
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
|
||||
import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil;
|
||||
import com.jetbrains.python.documentation.docstrings.DocStringUtil;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.PyBuiltinCache;
|
||||
@@ -85,6 +87,12 @@ public class PyDocumentationBuilder {
|
||||
else if (elementDefinition instanceof PyNamedParameter) {
|
||||
buildFromParameter((PyNamedParameter)elementDefinition);
|
||||
}
|
||||
else if (elementDefinition instanceof PyTypeParameter typeParameter) {
|
||||
buildFromTypeParameter(typeParameter);
|
||||
}
|
||||
else if (elementDefinition instanceof PyTypeAliasStatement typeAliasStatement) {
|
||||
buildFromTypeAliasStatement(typeAliasStatement);
|
||||
}
|
||||
|
||||
final ASTNode node = elementDefinition.getNode();
|
||||
if (node != null) {
|
||||
@@ -230,6 +238,44 @@ public class PyDocumentationBuilder {
|
||||
myBody.append(PythonDocumentationProvider.describeParameter(parameter, myContext));
|
||||
}
|
||||
|
||||
private void buildFromTypeParameter(@NotNull PyTypeParameter typeParameter) {
|
||||
ScopeOwner scopeOwner = ScopeUtil.getScopeOwner(typeParameter);
|
||||
HtmlChunk link = null;
|
||||
String typeParamName = typeParameter.getName();
|
||||
if (scopeOwner instanceof PyFunction pyFunction) {
|
||||
link = getLinkToFunction(pyFunction, true);
|
||||
}
|
||||
else if (scopeOwner instanceof PyClass pyClass) {
|
||||
link = getLinkToClass(pyClass, true);
|
||||
}
|
||||
else if (scopeOwner instanceof PyTypeAliasStatement typeAliasStatement) {
|
||||
link = getLinkToTypeAliasStatement(typeAliasStatement);
|
||||
}
|
||||
if (link != null && typeParamName != null) {
|
||||
myBody.appendRaw(PyPsiBundle.message("QDOC.type.parameter.name.of.link", HtmlChunk.text(typeParamName).bold(), link)).br();
|
||||
myBody.append(PythonDocumentationProvider.describeTypeParameter(typeParameter, true, myContext));
|
||||
}
|
||||
}
|
||||
|
||||
private void buildFromTypeAliasStatement(@NotNull PyTypeAliasStatement typeAliasStatement) {
|
||||
ScopeOwner scopeOwner = ScopeUtil.getScopeOwner(typeAliasStatement);
|
||||
HtmlChunk link = null;
|
||||
String typeParamName = typeAliasStatement.getName();
|
||||
if (scopeOwner instanceof PyFunction pyFunction) {
|
||||
link = getLinkToFunction(pyFunction, true);
|
||||
}
|
||||
else if (scopeOwner instanceof PyClass pyClass) {
|
||||
link = getLinkToClass(pyClass, true);
|
||||
}
|
||||
else if (scopeOwner instanceof PyFile pyFile) {
|
||||
link = getLinkToModule(pyFile);
|
||||
}
|
||||
if (link != null && typeParamName != null) {
|
||||
myBody.appendRaw(PyPsiBundle.message("QDOC.type.alias.statement.name.of.link", HtmlChunk.text(typeParamName).bold(), link)).br();
|
||||
myBody.append(PythonDocumentationProvider.describeTypeAlias(typeAliasStatement, myContext));
|
||||
}
|
||||
}
|
||||
|
||||
private @NotNull HtmlChunk runFormatterService(@NotNull @Nls String description) {
|
||||
final DocstringFormatterRequest output =
|
||||
PyStructuredDocstringFormatter.formatDocstring(myElement, new DocstringFormatterRequest(description, Collections.emptyList()),
|
||||
@@ -742,6 +788,16 @@ public class PyDocumentationBuilder {
|
||||
return HtmlChunk.raw(linkText);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static HtmlChunk getLinkToTypeAliasStatement(@NotNull PyTypeAliasStatement typeAliasStatement) {
|
||||
final String linkText = typeAliasStatement.getQualifiedName();
|
||||
final PsiFile file = typeAliasStatement.getContainingFile();
|
||||
if (linkText == null || typeAliasStatement.getName() == null || file == null) {
|
||||
return null;
|
||||
}
|
||||
return PyDocumentationLink.toTypeAliasStatement(linkText, typeAliasStatement);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static PyStringLiteralExpression getEffectiveDocStringExpression(@NotNull PyDocStringOwner owner) {
|
||||
final PyStringLiteralExpression expression = owner.getDocStringExpression();
|
||||
|
||||
@@ -74,6 +74,15 @@ object PyDocumentationLink {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun toTypeAliasStatement(@NlsSafe content: String, typeAliasStatement: PyTypeAliasStatement) : HtmlChunk {
|
||||
val qualifiedName = typeAliasStatement.qualifiedName
|
||||
return when {
|
||||
qualifiedName != null -> HtmlChunk.link("${DocumentationManagerProtocol.PSI_ELEMENT_PROTOCOL}$LINK_TYPE_FUNC$qualifiedName", content)
|
||||
else -> HtmlChunk.text(content)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun toModule(@NlsSafe content: String, qualifiedName: String): HtmlChunk {
|
||||
return HtmlChunk.link("${DocumentationManagerProtocol.PSI_ELEMENT_PROTOCOL}$LINK_TYPE_MODULE$qualifiedName", content)
|
||||
|
||||
+49
@@ -92,6 +92,12 @@ public class PythonDocumentationProvider implements DocumentationProvider {
|
||||
else if (element instanceof PyExpression) {
|
||||
return describeExpression((PyExpression)element, referenceElement, context);
|
||||
}
|
||||
else if (element instanceof PyTypeParameter typeParameter) {
|
||||
return PyPsiBundle.message("QDOC.type.parameter.name", describeTypeParameter(typeParameter, true, context));
|
||||
}
|
||||
else if (element instanceof PyTypeAliasStatement typeAliasStatement) {
|
||||
return describeTypeAlias(typeAliasStatement, context).toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -135,6 +141,49 @@ public class PythonDocumentationProvider implements DocumentationProvider {
|
||||
return result.toFragment();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static HtmlChunk describeTypeParameter(@NotNull PyTypeParameter typeParameter, boolean showKind, @NotNull TypeEvalContext context) {
|
||||
HtmlBuilder result = new HtmlBuilder();
|
||||
result.append(styledSpan(StringUtil.notNullize(typeParameter.getName()), PyHighlighter.PY_TYPE_PARAMETER));
|
||||
PyExpression boundExpression = typeParameter.getBoundExpression();
|
||||
if (boundExpression != null && typeParameter.getBoundExpressionText() != null) {
|
||||
result.append(styledSpan(": ", PyHighlighter.PY_OPERATION_SIGN));
|
||||
result.append(highlightExpressionText(typeParameter.getBoundExpressionText(), typeParameter.getBoundExpression()));
|
||||
}
|
||||
if (showKind) {
|
||||
result
|
||||
.append(", ")
|
||||
.append(PyPsiBundle.message("QDOC.type.parameter.kind"))
|
||||
.append(" ")
|
||||
.append(styledSpan(formatTypeWithLinks(context.getType(typeParameter), typeParameter, typeParameter, context), PyHighlighter.PY_ANNOTATION));
|
||||
}
|
||||
return result.toFragment();
|
||||
}
|
||||
|
||||
|
||||
@NotNull
|
||||
static HtmlChunk describeTypeAlias(@NotNull PyTypeAliasStatement typeAliasStatement, @NotNull TypeEvalContext context) {
|
||||
HtmlBuilder result = new HtmlBuilder();
|
||||
result.append(styledSpan("type ", PyHighlighter.PY_KEYWORD)); //NON-NLS
|
||||
result.append(styledSpan(StringUtil.notNullize(typeAliasStatement.getName()), DefaultLanguageHighlighterColors.IDENTIFIER));
|
||||
if (typeAliasStatement.getTypeParameterList() != null) {
|
||||
List<PyTypeParameter> typeParameters = typeAliasStatement.getTypeParameterList().getTypeParameters();
|
||||
result.append(styledSpan("[", PyHighlighter.PY_BRACKETS));
|
||||
result.append(StreamEx
|
||||
.of(typeParameters)
|
||||
.map(typeParameter -> describeTypeParameter(typeParameter, false, context))
|
||||
.collect(HtmlChunk.toFragment(styledSpan(", ", PyHighlighter.PY_COMMA))));
|
||||
result.append(styledSpan("]", PyHighlighter.PY_BRACKETS));
|
||||
}
|
||||
PyExpression typeExpression = typeAliasStatement.getTypeExpression();
|
||||
if (typeExpression != null) {
|
||||
result.append(styledSpan(" = ", PyHighlighter.PY_OPERATION_SIGN));
|
||||
result.append(styledSpan(formatTypeWithLinks(context.getType(typeExpression),
|
||||
typeExpression, typeAliasStatement, context), PyHighlighter.PY_ANNOTATION));
|
||||
}
|
||||
return result.toFragment();
|
||||
}
|
||||
|
||||
@NlsSafe
|
||||
@NotNull
|
||||
private static String describeFunctionWithTypes(@NotNull PyFunction function,
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<html><body><div class="definition"><pre>Type alias statement <b>my_dict</b> of <a href="psi_element://#module#TypeAliasStatement">TypeAliasStatement</a><br/><span style="color:#000080;font-weight:bold;">type </span><span style="color:#000000;">my_dict</span><span style="">[</span><span style="color:#000000;">T</span><span style="">, </span><span style="color:#000000;">U</span><span style="">]</span><span style=""> = </span><span style="color:#000000;">Type<span style="">[</span><span style="color:#000080;"><a href="psi_element://#typename#dict">dict</a></span><span style="">[</span>T<span style="">, </span>U<span style="">]</span><span style="">]</span></span></pre></div></body></html>
|
||||
@@ -0,0 +1,3 @@
|
||||
type my_dict[T, U] = dict[T, U]
|
||||
|
||||
x: my_d<the_ref>ict
|
||||
@@ -0,0 +1,38 @@
|
||||
<html><body><div class="content"><div class="section" id="the-type-statement">
|
||||
<span id="type"></span><h2>The <a class="reference internal" href="#type"><tt class="xref std std-keyword docutils literal"><span class="pre">type</span></tt></a> statement</h2>
|
||||
<pre id="index-47">
|
||||
<strong id="grammar-token-python-grammar-type_stmt"><span id="grammar-token-type-stmt"></span>type_stmt</strong> ::= 'type' <a class="reference internal" href="lexical_analysis.html#grammar-token-python-grammar-identifier"><code class="xref docutils literal notranslate"><span class="pre">identifier</span></code></a> [<a class="reference internal" href="compound_stmts.html#grammar-token-python-grammar-type_params"><code class="xref docutils literal notranslate"><span class="pre">type_params</span></code></a>] "=" <a class="reference internal" href="expressions.html#grammar-token-python-grammar-expression"><code class="xref docutils literal notranslate"><span class="pre">expression</span></code></a>
|
||||
</pre>
|
||||
<p>The <code class="xref std std-keyword docutils literal notranslate"><span class="pre">type</span></code> statement declares a type alias, which is an instance
|
||||
of <a class="reference internal" href="../library/typing.html#typing.TypeAliasType" title="typing.TypeAliasType"><code class="xref py py-class docutils literal notranslate"><span class="pre">typing.TypeAliasType</span></code></a>.</p>
|
||||
<p>For example, the following statement creates a type alias:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="nb">type</span> <span class="n">Point</span> <span class="o">=</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">float</span><span class="p">,</span> <span class="nb">float</span><span class="p">]</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>This code is roughly equivalent to:</p>
|
||||
<div class="highlight-python3 notranslate"><div class="highlight"><pre><span></span><span class="n">annotation</span><span class="o">-</span><span class="k">def</span> <span class="nf">VALUE_OF_Point</span><span class="p">():</span>
|
||||
<span class="k">return</span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">float</span><span class="p">,</span> <span class="nb">float</span><span class="p">]</span>
|
||||
<span class="n">Point</span> <span class="o">=</span> <span class="n">typing</span><span class="o">.</span><span class="n">TypeAliasType</span><span class="p">(</span><span class="s2">"Point"</span><span class="p">,</span> <span class="n">VALUE_OF_Point</span><span class="p">())</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p><code class="docutils literal notranslate"><span class="pre">annotation-def</span></code> indicates an <a class="reference internal" href="executionmodel.html#annotation-scopes"><span class="std std-ref">annotation scope</span></a>, which behaves
|
||||
mostly like a function, but with several small differences.</p>
|
||||
<p>The value of the
|
||||
type alias is evaluated in the annotation scope. It is not evaluated when the
|
||||
type alias is created, but only when the value is accessed through the type alias’s
|
||||
<code class="xref py py-attr docutils literal notranslate"><span class="pre">__value__</span></code> attribute (see <a class="reference internal" href="executionmodel.html#lazy-evaluation"><span class="std std-ref">Lazy evaluation</span></a>).
|
||||
This allows the type alias to refer to names that are not yet defined.</p>
|
||||
<p>Type aliases may be made generic by adding a <a class="reference internal" href="compound_stmts.html#type-params"><span class="std std-ref">type parameter list</span></a>
|
||||
after the name. See <a class="reference internal" href="compound_stmts.html#generic-type-aliases"><span class="std std-ref">Generic type aliases</span></a> for more.</p>
|
||||
<p><code class="xref std std-keyword docutils literal notranslate"><span class="pre">type</span></code> is a <a class="reference internal" href="lexical_analysis.html#soft-keywords"><span class="std std-ref">soft keyword</span></a>.</p>
|
||||
<div class="versionadded">
|
||||
<p><span class="versionmodified added">New in version 3.12.</span></p>
|
||||
</div>
|
||||
<div class="admonition seealso">
|
||||
<p class="admonition-title">See also</p>
|
||||
<dl class="simple">
|
||||
<dt><span class="target" id="index-48"></span><a class="pep reference external" href="https://peps.python.org/pep-0695/"><strong>PEP 695</strong></a> - Type Parameter Syntax</dt><dd><p>Introduced the <code class="xref std std-keyword docutils literal notranslate"><span class="pre">type</span></code> statement and syntax for
|
||||
generic classes and functions.</p>
|
||||
</dd>
|
||||
</dl>
|
||||
</div></div></body></html>
|
||||
@@ -0,0 +1 @@
|
||||
ty<the_ref>pe my_type[T: str] = list[T]
|
||||
@@ -0,0 +1 @@
|
||||
<html><body><div class="definition"><pre>Type parameter <b>T</b> of <a href="psi_element://#func#TypeParameter.foo">TypeParameter.foo</a><br/><span style="color:#000000;">T</span><span style="">: </span><span style="">(str, bytes)</span>, kind: <span style="color:#000000;">TypeVar</span></pre></div></body></html>
|
||||
@@ -0,0 +1,2 @@
|
||||
def foo[T: (str, bytes)](x: T<the_ref>) -> T:
|
||||
return x
|
||||
@@ -856,6 +856,21 @@ public class Py3QuickDocTest extends LightMarkedTestCase {
|
||||
checkHTMLOnly();
|
||||
}
|
||||
|
||||
// PY-64074
|
||||
public void testTypeParameter() {
|
||||
checkHTMLOnly();
|
||||
}
|
||||
|
||||
// PY-64074
|
||||
public void testTypeKeyword() {
|
||||
checkHTMLOnly();
|
||||
}
|
||||
|
||||
// PY-64074
|
||||
public void testTypeAliasStatement() {
|
||||
checkHTMLOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return super.getTestDataPath() + "/quickdoc/";
|
||||
|
||||
Reference in New Issue
Block a user