PY-61639 Extracted PythonEnterHandler

GitOrigin-RevId: 8fd0c063c917e07c3f1bf98533c3de867f770ee9
This commit is contained in:
Petr Golubev
2024-01-24 16:22:01 +01:00
committed by intellij-monorepo-bot
parent 1a73bd2d21
commit 25b8ebcf83
74 changed files with 535 additions and 372 deletions

2
.idea/modules.xml generated
View File

@@ -1149,6 +1149,8 @@
<module fileurl="file://$PROJECT_DIR$/python/intellij.python.pydev.iml" filepath="$PROJECT_DIR$/python/intellij.python.pydev.iml" />
<module fileurl="file://$PROJECT_DIR$/python/python-rest/intellij.python.reStructuredText.iml" filepath="$PROJECT_DIR$/python/python-rest/intellij.python.reStructuredText.iml" />
<module fileurl="file://$PROJECT_DIR$/python/python-sdk/intellij.python.sdk.iml" filepath="$PROJECT_DIR$/python/python-sdk/intellij.python.sdk.iml" />
<module fileurl="file://$PROJECT_DIR$/python/python-syntax/intellij.python.syntax.iml" filepath="$PROJECT_DIR$/python/python-syntax/intellij.python.syntax.iml" />
<module fileurl="file://$PROJECT_DIR$/python/python-syntax-core/intellij.python.syntax.core.iml" filepath="$PROJECT_DIR$/python/python-syntax-core/intellij.python.syntax.core.iml" />
<module fileurl="file://$PROJECT_DIR$/python/python-terminal/intellij.python.terminal.iml" filepath="$PROJECT_DIR$/python/python-terminal/intellij.python.terminal.iml" />
<module fileurl="file://$PROJECT_DIR$/python/tools/intellij.python.tools.iml" filepath="$PROJECT_DIR$/python/tools/intellij.python.tools.iml" />
<module fileurl="file://$PROJECT_DIR$/RegExpSupport/intellij.regexp.iml" filepath="$PROJECT_DIR$/RegExpSupport/intellij.regexp.iml" />

View File

@@ -31,7 +31,9 @@ object PythonCommunityPluginModules {
"intellij.commandInterface",
"intellij.python.sdk",
"intellij.python.featuresTrainer",
"intellij.jupyter.core"
"intellij.jupyter.core",
"intellij.python.syntax",
"intellij.python.syntax.core"
)
const val pythonCommunityName: String = "python-ce"

View File

@@ -147,5 +147,6 @@
<orderEntry type="module" module-name="intellij.platform.util.coroutines" />
<orderEntry type="module" module-name="intellij.commandInterface" scope="RUNTIME" />
<orderEntry type="module" module-name="intellij.python.community.impl.poetry" scope="RUNTIME" />
<orderEntry type="module" module-name="intellij.python.syntax" />
</component>
</module>

View File

@@ -19,5 +19,6 @@
<orderEntry type="module" module-name="intellij.platform.concurrency" />
<orderEntry type="module" module-name="intellij.platform.ide.util.io" />
<orderEntry type="module" module-name="intellij.platform.debugger.impl" />
<orderEntry type="module" module-name="intellij.python.syntax.core" />
</component>
</module>

View File

@@ -90,6 +90,14 @@ public interface PyAstNamedParameter extends PyAstParameter, PsiNamedElement, Ps
return ParamHelperCore.getDefaultValueText(getDefaultValue());
}
/**
* @param includeDefaultValue if true, include the default value after an "=".
* @return canonical representation of parameter.
* Includes asterisks for *param and **param, and name.
*/
@NotNull
String getRepr(boolean includeDefaultValue);
@Override
@NotNull
default PyAstNamedParameter getAsNamed() {

View File

@@ -1,6 +1,7 @@
package com.jetbrains.python.ast.docstring;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.ast.PyAstDocStringOwner;
import com.jetbrains.python.ast.PyAstElement;
import com.jetbrains.python.ast.PyAstExpressionStatement;
@@ -35,4 +36,20 @@ public final class DocStringUtilCore {
}
return null;
}
/**
* Returns containing docstring expression of class definition, function definition or module.
* Useful to test whether particular PSI element is or belongs to such docstring.
*/
@Nullable
public static PyAstStringLiteralExpression getParentDefinitionDocString(@NotNull PsiElement element) {
final PyAstDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(element, PyAstDocStringOwner.class);
if (docStringOwner != null) {
final PyAstStringLiteralExpression docString = docStringOwner.getDocStringExpression();
if (PsiTreeUtil.isAncestor(docString, element, false)) {
return docString;
}
}
return null;
}
}

View File

@@ -12,6 +12,7 @@ import com.intellij.psi.util.QualifiedName;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ObjectUtils;
import com.jetbrains.python.ast.*;
import com.jetbrains.python.psi.PyElementType;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -47,6 +48,20 @@ public final class PyPsiUtilsCore {
return PsiTreeUtil.skipWhitespacesAndCommentsForward(start);
}
/**
* Returns first child psi element with specified element type or {@code null} if no such element exists.
* Semantically it's the same as {@code getChildByFilter(element, TokenSet.create(type), 0)}.
*
* @param element tree parent node
* @param type element type expected
* @return child element described
*/
@Nullable
public static PsiElement getFirstChildOfType(@NotNull final PsiElement element, @NotNull PyElementType type) {
final ASTNode child = element.getNode().findChildByType(type);
return child != null ? child.getPsi() : null;
}
/**
* Returns child element in the psi tree
*

View File

@@ -1,7 +1,11 @@
package com.jetbrains.python.ast.impl;
import com.intellij.openapi.editor.Document;
import com.intellij.psi.PsiDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.ObjectUtils;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.ast.*;
@@ -76,6 +80,41 @@ public final class PyUtilCore {
return unfoldParentheses(targets, new ArrayList<>(targets.length), false, true);
}
/**
* Retrieves the document from {@link PsiDocumentManager} using the anchor PSI element and, if it's not null,
* passes it to the consumer function.
* <p>
* The document is first released from pending PSI operations and then committed after the function has been applied
* in a {@code try/finally} block, so that subsequent operations on PSI could be performed.
*
* @see PsiDocumentManager#doPostponedOperationsAndUnblockDocument(Document)
* @see PsiDocumentManager#commitDocument(Document)
* @see #updateDocumentUnblockedAndCommitted(PsiElement, Function)
*/
public static void updateDocumentUnblockedAndCommitted(@NotNull PsiElement anchor, @NotNull Consumer<? super Document> consumer) {
updateDocumentUnblockedAndCommitted(anchor, document -> {
consumer.consume(document);
return null;
});
}
@Nullable
public static <T> T updateDocumentUnblockedAndCommitted(@NotNull PsiElement anchor, @NotNull Function<? super Document, ? extends T> func) {
final PsiDocumentManager manager = PsiDocumentManager.getInstance(anchor.getProject());
// manager.getDocument(anchor.getContainingFile()) doesn't work with intention preview
final Document document = anchor.getContainingFile().getViewProvider().getDocument();
if (document != null) {
manager.doPostponedOperationsAndUnblockDocument(document);
try {
return func.fun(document);
}
finally {
manager.commitDocument(document);
}
}
return null;
}
@Nullable
public static PyAstLoopStatement getCorrespondingLoop(@NotNull PsiElement breakOrContinue) {
return breakOrContinue instanceof PyAstContinueStatement || breakOrContinue instanceof PyAstBreakStatement

View File

@@ -16,5 +16,6 @@
<orderEntry type="module" module-name="intellij.platform.analysis" />
<orderEntry type="module" module-name="intellij.python.parser" exported="" />
<orderEntry type="module" module-name="intellij.python.ast" exported="" />
<orderEntry type="module" module-name="intellij.python.syntax.core" />
</component>
</module>

View File

@@ -42,6 +42,7 @@ public interface PyNamedParameter extends PyAstNamedParameter, PyParameter, PsiN
* @return canonical representation of parameter.
* Includes asterisks for *param and **param, and name.
*/
@Override
@NotNull
default String getRepr(boolean includeDefaultValue) {
return getRepr(includeDefaultValue, null);

View File

@@ -218,8 +218,7 @@ public final class PyPsiUtils {
*/
@Nullable
public static PsiElement getFirstChildOfType(@NotNull final PsiElement element, @NotNull PyElementType type) {
final ASTNode child = element.getNode().findChildByType(type);
return child != null ? child.getPsi() : null;
return PyPsiUtilsCore.getFirstChildOfType(element, type);
}
/**

View File

@@ -37,5 +37,6 @@
<orderEntry type="module" module-name="intellij.platform.workspace.jps" />
<orderEntry type="module" module-name="intellij.platform.backend.workspace" />
<orderEntry type="module" module-name="intellij.python.parser" />
<orderEntry type="module" module-name="intellij.python.syntax.core" exported="" />
</component>
</module>

View File

@@ -1,4 +1,6 @@
<idea-plugin>
<idea-plugin xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="/META-INF/PythonSyntaxCore.xml" xpointer="xpointer(/idea-plugin/*)"/>
<extensions defaultExtensionNs="com.intellij">
<readWriteAccessDetector implementation="com.jetbrains.python.findUsages.PyReadWriteAccessDetector"/>
@@ -56,8 +58,6 @@
<filePropertyPusher implementation="com.jetbrains.python.psi.impl.PythonLanguageLevelPusher"/>
<applicationService serviceImplementation="com.jetbrains.python.codeInsight.PyCodeInsightSettings"/>
<weigher key="completion" implementationClass="com.jetbrains.python.codeInsight.completion.PythonCompletionWeigher" order="first"/>
<referencesSearch implementation="com.jetbrains.python.psi.search.PyInitReferenceSearchExecutor"/>
@@ -76,8 +76,6 @@
<preFormatProcessor implementation="com.jetbrains.python.formatter.PyPreFormatProcessor"/>
<postFormatProcessor implementation="com.jetbrains.python.formatter.PyTrailingBlankLinesPostFormatProcessor"/>
<postFormatProcessor implementation="com.jetbrains.python.formatter.PyFromImportPostFormatProcessor"/>
<lang.whiteSpaceFormattingStrategy language="Python"
implementationClass="com.jetbrains.python.formatter.PyWhiteSpaceFormattingStrategy"/>
<lang.lineWrapStrategy language="Python" implementationClass="com.jetbrains.python.formatter.PyLineWrapPositionStrategy"/>
<lang.commenter language="Python" implementationClass="com.jetbrains.python.PythonCommenter"/>
@@ -96,9 +94,6 @@
<applicationService serviceImplementation="com.jetbrains.python.documentation.PythonDocumentationMap"/>
<moduleService serviceImplementation="com.jetbrains.python.documentation.PyDocumentationSettings$ModuleService"/>
<applicationService serviceImplementation="com.jetbrains.python.documentation.PyDocumentationSettings$AppService"/>
<applicationService serviceInterface="com.jetbrains.python.codeInsight.imports.ImportLocationHelper"
serviceImplementation="com.jetbrains.python.codeInsight.imports.PyImportLocationHelper"/>

View File

@@ -194,11 +194,6 @@ 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
formatter.panel.dict.alignment.align.on.colon=Align on colon
formatter.panel.dict.alignment.align.on.value=Align on value
ignore.overridden.functions=Ignore overridden functions
### Intentions ###

View File

@@ -14,7 +14,7 @@ import com.jetbrains.python.PyPsiBundle;
import com.jetbrains.python.PythonUiService;
import com.jetbrains.python.documentation.PyDocumentationSettings;
import com.jetbrains.python.documentation.docstrings.DocStringFormat;
import com.jetbrains.python.documentation.docstrings.DocStringUtil;
import com.jetbrains.python.documentation.docstrings.DocStringParser;
import com.jetbrains.python.documentation.docstrings.PyDocstringGenerator;
import com.jetbrains.python.documentation.doctest.PyDocstringFile;
import com.jetbrains.python.inspections.quickfix.DocstringQuickFix;
@@ -96,7 +96,7 @@ public final class PyGenerateDocstringIntention extends PyBaseIntentionAction {
* @return false if no structured docstring format was specified initially and user didn't select any, true otherwise
*/
public static boolean ensureNotPlainDocstringFormat(@NotNull PsiElement anchor) {
final Module module = DocStringUtil.getModuleForElement(anchor);
final Module module = DocStringParser.getModuleForElement(anchor);
if (module == null) {
return false;
}

View File

@@ -117,7 +117,7 @@ public final class SpecifyTypeInDocstringIntention extends TypeIntention {
public static void startTemplate(PyDocstringGenerator generator) {
Preconditions.checkNotNull(generator.getDocStringOwner(), "For this action docstring owner must be supplied");
final PyStringLiteralExpression docStringExpression = generator.getDocStringExpression();
final PyStringLiteralExpression docStringExpression = (PyStringLiteralExpression)generator.getDocStringExpression();
assert docStringExpression != null;
final TemplateBuilder builder = TemplateBuilderFactory.getInstance().createTemplateBuilder(docStringExpression);

View File

@@ -19,6 +19,7 @@ import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.ProjectScope;
import com.intellij.util.ArrayUtilRt;
import com.jetbrains.python.PyPsiBundle;
import com.jetbrains.python.ast.PyAstFunction;
import com.jetbrains.python.psi.PyClass;
import com.jetbrains.python.psi.PyFunction;
import org.jetbrains.annotations.NotNull;
@@ -127,8 +128,8 @@ public final class PySignatureCacheManagerImpl extends PySignatureCacheManager {
@Override
@Nullable
public String findParameterType(@NotNull PyFunction function, @NotNull String name) {
final PySignature signature = findSignature(function);
public String findParameterType(@NotNull PyAstFunction function, @NotNull String name) {
final PySignature signature = findSignature((PyFunction)function);
if (signature != null) {
return signature.getArgTypeQualifiedName(name);
}
@@ -137,10 +138,10 @@ public final class PySignatureCacheManagerImpl extends PySignatureCacheManager {
@Override
@Nullable
public PySignature findSignature(@NotNull PyFunction function) {
VirtualFile file = getFile(function);
public PySignature findSignature(@NotNull PyAstFunction function) {
VirtualFile file = getFile((PyFunction)function);
if (file != null) {
return readSignatureAttributeFromFile(file, getFunctionName(function));
return readSignatureAttributeFromFile(file, getFunctionName((PyFunction)function));
}
else {
return null;

View File

@@ -11,15 +11,17 @@ import com.intellij.psi.PsiManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.Function;
import com.jetbrains.python.documentation.docstrings.DocStringFormat;
import com.jetbrains.python.documentation.docstrings.DocStringUtil;
import com.jetbrains.python.documentation.docstrings.DocStringParser;
import com.jetbrains.python.psi.PyDocStringOwner;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyStringLiteralExpression;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
final class PyDocumentationSettingsDetector
@ApiStatus.Internal
public final class PyDocumentationSettingsDetector
implements Function<Pair<Module, Collection<VirtualFile>>, PyDocumentationSettings.ServiceState> {
private static final Logger LOG = Logger.getInstance(PyDocumentationSettingsDetector.class);
@@ -56,7 +58,7 @@ final class PyDocumentationSettingsDetector
for (PyDocStringOwner owner : children) {
final PyStringLiteralExpression docStringExpression = owner.getDocStringExpression();
if (docStringExpression != null) {
final DocStringFormat guessed = DocStringUtil.guessDocStringFormat(docStringExpression.getStringValue());
final DocStringFormat guessed = DocStringParser.guessDocStringFormat(docStringExpression.getStringValue());
if (guessed != PyDocumentationSettings.DEFAULT_DOC_STRING_FORMAT) {
return guessed;
}

View File

@@ -13,6 +13,7 @@ import com.intellij.openapi.progress.ProgressManager;
import com.intellij.psi.PsiElement;
import com.jetbrains.python.PythonRuntimeService;
import com.jetbrains.python.documentation.docstrings.DocStringFormat;
import com.jetbrains.python.documentation.docstrings.DocStringParser;
import com.jetbrains.python.documentation.docstrings.DocStringUtil;
import com.jetbrains.python.psi.PyIndentUtil;
import org.jetbrains.annotations.NotNull;
@@ -33,12 +34,12 @@ public final class PyStructuredDocstringFormatter {
public static PyDocumentationBuilder.DocstringFormatterRequest formatDocstring(@NotNull PsiElement element,
@NotNull PyDocumentationBuilder.DocstringFormatterRequest docstringFormatterRequest,
@NotNull List<String> flags) {
Module module = DocStringUtil.getModuleForElement(element);
Module module = DocStringParser.getModuleForElement(element);
if (module == null) return new PyDocumentationBuilder.DocstringFormatterRequest();
final String docstring = docstringFormatterRequest.getBody();
final String preparedDocstring = PyIndentUtil.removeCommonIndent(docstring, true).trim();
final DocStringFormat format = DocStringUtil.guessDocStringFormat(preparedDocstring, element);
final DocStringFormat format = DocStringParser.guessDocStringFormat(preparedDocstring, element);
if (format == DocStringFormat.PLAIN) {
return null;

View File

@@ -43,7 +43,7 @@ public final class DocStringSectionHeaderCompletionContributor extends Completio
final PsiElement stringNode = parameters.getOriginalPosition();
assert stringNode != null;
final int offset = parameters.getOffset();
final DocStringFormat format = DocStringUtil.getConfiguredDocStringFormat(file);
final DocStringFormat format = DocStringParser.getConfiguredDocStringFormat(file);
if (!(format == DocStringFormat.GOOGLE || format == DocStringFormat.NUMPY)) {
return;
}

View File

@@ -41,7 +41,7 @@ public final class DocStringTagCompletionContributor extends CompletionContribut
@NotNull ProcessingContext context,
@NotNull CompletionResultSet result) {
final PsiFile file = parameters.getOriginalFile();
DocStringFormat format = DocStringUtil.getConfiguredDocStringFormat(file);
DocStringFormat format = DocStringParser.getConfiguredDocStringFormat(file);
if (format == DocStringFormat.EPYTEXT || format == DocStringFormat.REST) {
int offset = parameters.getOffset();
final String text = file.getText();

View File

@@ -2,24 +2,14 @@
package com.jetbrains.python.documentation.docstrings;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ObjectUtils;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
import com.jetbrains.python.documentation.PyDocumentationSettings;
import com.jetbrains.python.ast.docstring.DocStringUtilCore;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import com.jetbrains.python.toolbox.Substring;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -52,11 +42,11 @@ public final class DocStringUtil {
* @param anchor PSI element that will be used to retrieve docstring format from the containing file or the project module
* @return structured docstring for one of supported formats or instance of {@link PlainDocString} if none was recognized.
* @see DocStringFormat#ALL_NAMES_BUT_PLAIN
* @see #guessDocStringFormat(String, PsiElement)
* @see DocStringParser#guessDocStringFormat(String, PsiElement)
*/
@NotNull
public static StructuredDocString parse(@NotNull String text, @Nullable PsiElement anchor) {
final DocStringFormat format = guessDocStringFormat(text, anchor);
final DocStringFormat format = DocStringParser.guessDocStringFormat(text, anchor);
return parseDocStringContent(format, text);
}
@@ -69,7 +59,7 @@ public final class DocStringUtil {
*/
@NotNull
public static StructuredDocString parseDocString(@NotNull PyStringLiteralExpression stringLiteral) {
return parseDocString(guessDocStringFormat(stringLiteral.getStringValue(), stringLiteral), stringLiteral);
return parseDocString(DocStringParser.guessDocStringFormat(stringLiteral.getStringValue(), stringLiteral), stringLiteral);
}
@NotNull
@@ -80,15 +70,7 @@ public final class DocStringUtil {
@NotNull
public static StructuredDocString parseDocString(@NotNull DocStringFormat format, @NotNull ASTNode node) {
//Preconditions.checkArgument(node.getElementType() == PyTokenTypes.DOCSTRING);
return parseDocString(format, node.getText());
}
/**
* @param stringText docstring text with possible string prefix and quotes
*/
@NotNull
public static StructuredDocString parseDocString(@NotNull DocStringFormat format, @NotNull String stringText) {
return parseDocString(format, stripPrefixAndQuotes(stringText));
return DocStringParser.parseDocString(format, node.getText());
}
/**
@@ -97,120 +79,7 @@ public final class DocStringUtil {
*/
@NotNull
public static StructuredDocString parseDocStringContent(@NotNull DocStringFormat format, @NotNull String stringContent) {
return parseDocString(format, new Substring(stringContent));
}
@NotNull
public static StructuredDocString parseDocString(@NotNull DocStringFormat format, @NotNull Substring content) {
return switch (format) {
case REST -> new SphinxDocString(content);
case EPYTEXT -> new EpydocString(content);
case GOOGLE -> new GoogleCodeStyleDocString(content);
case NUMPY -> new NumpyDocString(content);
case PLAIN -> new PlainDocString(content);
};
}
@NotNull
private static Substring stripPrefixAndQuotes(@NotNull String text) {
final TextRange contentRange = PyStringLiteralUtil.getContentRange(text);
return new Substring(text, contentRange.getStartOffset(), contentRange.getEndOffset());
}
/**
* @return docstring format inferred heuristically solely from its content. For more reliable result use anchored version
* {@link #guessDocStringFormat(String, PsiElement)} of this method.
* @see #guessDocStringFormat(String, PsiElement)
*/
@NotNull
public static DocStringFormat guessDocStringFormat(@NotNull String text) {
if (isLikeEpydocDocString(text)) {
return DocStringFormat.EPYTEXT;
}
if (isLikeSphinxDocString(text)) {
return DocStringFormat.REST;
}
if (isLikeNumpyDocstring(text)) {
return DocStringFormat.NUMPY;
}
if (isLikeGoogleDocString(text)) {
return DocStringFormat.GOOGLE;
}
return DocStringFormat.PLAIN;
}
/**
* @param text docstring text <em>with both quotes and string prefix stripped</em>
* @param anchor PSI element that will be used to retrieve docstring format from the containing file or the project module
* @return docstring inferred heuristically and if unsuccessful fallback to configured format retrieved from anchor PSI element
* @see #getConfiguredDocStringFormat(PsiElement)
*/
@NotNull
public static DocStringFormat guessDocStringFormat(@NotNull String text, @Nullable PsiElement anchor) {
final DocStringFormat guessed = guessDocStringFormat(text);
return guessed == DocStringFormat.PLAIN && anchor != null ? getConfiguredDocStringFormatOrPlain(anchor) : guessed;
}
/**
* @param anchor PSI element that will be used to retrieve docstring format from the containing file or the project module
* @return docstring format configured for file or module containing given anchor PSI element
* @see PyDocumentationSettings#getFormatForFile(PsiFile)
*/
@Nullable
public static DocStringFormat getConfiguredDocStringFormat(@NotNull PsiElement anchor) {
final Module module = getModuleForElement(anchor);
if (module == null) {
return null;
}
final PyDocumentationSettings settings = PyDocumentationSettings.getInstance(module);
return settings.getFormatForFile(anchor.getContainingFile());
}
@NotNull
public static DocStringFormat getConfiguredDocStringFormatOrPlain(@NotNull PsiElement anchor) {
return ObjectUtils.chooseNotNull(getConfiguredDocStringFormat(anchor), DocStringFormat.PLAIN);
}
public static boolean isLikeSphinxDocString(@NotNull String text) {
return text.contains(":param ") ||
text.contains(":key ") || text.contains(":keyword ") ||
text.contains(":return:") || text.contains(":returns:") ||
text.contains(":raise ") || text.contains(":raises ") || text.contains(":except ") || text.contains(":exception ") ||
text.contains(":rtype") || text.contains(":type") ||
text.contains(":var") || text.contains(":ivar") || text.contains(":cvar");
}
public static boolean isLikeEpydocDocString(@NotNull String text) {
return text.contains("@param ") ||
text.contains("@kwarg ") || text.contains("@keyword ") || text.contains("@kwparam ") ||
text.contains("@raise ") || text.contains("@raises ") || text.contains("@except ") || text.contains("@exception ") ||
text.contains("@return:") ||
text.contains("@rtype") || text.contains("@type") ||
text.contains("@var") || text.contains("@ivar") || text.contains("@cvar");
}
public static boolean isLikeGoogleDocString(@NotNull String text) {
for (@NonNls String title : StringUtil.findMatches(text, GoogleCodeStyleDocString.SECTION_HEADER, 1)) {
if (SectionBasedDocString.isValidSectionTitle(title)) {
return true;
}
}
return false;
}
public static boolean isLikeNumpyDocstring(@NotNull String text) {
final String[] lines = StringUtil.splitByLines(text, false);
for (int i = 0; i < lines.length; i++) {
final String line = lines[i];
if (NumpyDocString.SECTION_HEADER.matcher(line).matches() && i > 0) {
@NonNls final String lineBefore = lines[i - 1];
if (SectionBasedDocString.SECTION_NAMES.contains(StringUtil.toLowerCase(lineBefore.trim()))) {
return true;
}
}
}
return false;
return DocStringParser.parseDocString(format, new Substring(stringContent));
}
/**
@@ -236,14 +105,7 @@ public final class DocStringUtil {
*/
@Nullable
public static PyStringLiteralExpression getParentDefinitionDocString(@NotNull PsiElement element) {
final PyDocStringOwner docStringOwner = PsiTreeUtil.getParentOfType(element, PyDocStringOwner.class);
if (docStringOwner != null) {
final PyStringLiteralExpression docString = docStringOwner.getDocStringExpression();
if (PsiTreeUtil.isAncestor(docString, element, false)) {
return docString;
}
}
return null;
return (PyStringLiteralExpression)DocStringUtilCore.getParentDefinitionDocString(element);
}
public static boolean isDocStringExpression(@NotNull PyExpression expression) {
@@ -290,16 +152,4 @@ public final class DocStringUtil {
}
return false;
}
// Might return {@code null} in some rare cases when PSI element doesn't have an associated module.
// For instance, an empty IDEA project with a Python scratch file.
@Nullable
public static Module getModuleForElement(@NotNull PsiElement element) {
final Module module = ModuleUtilCore.findModuleForPsiElement(element.getContainingFile());
if (module != null) {
return module;
}
return ArrayUtil.getFirstElement(ModuleManager.getInstance(element.getProject()).getModules());
}
}

View File

@@ -1,55 +0,0 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.editor;
import com.intellij.psi.PsiComment;
import com.jetbrains.python.psi.*;
public final class PyEditorHandlerConfig {
private PyEditorHandlerConfig() {
}
public static final Class[] IMPLICIT_WRAP_CLASSES = new Class[] {
PyListLiteralExpression.class,
PySetLiteralExpression.class,
PyDictLiteralExpression.class,
PyDictLiteralExpression.class,
PyParenthesizedExpression.class,
PyArgumentList.class,
PyParameterList.class,
PyGroupPattern.class,
PySequencePattern.class,
PyMappingPattern.class,
PyPatternArgumentList.class,
PyTypeParameterList.class,
};
public static final Class[] WRAPPABLE_CLASSES = new Class[]{
PsiComment.class,
PyParenthesizedExpression.class,
PyListCompExpression.class,
PyDictCompExpression.class,
PySetCompExpression.class,
PyDictLiteralExpression.class,
PySetLiteralExpression.class,
PyListLiteralExpression.class,
PyArgumentList.class,
PyParameterList.class,
PyDecoratorList.class,
PySliceExpression.class,
PySubscriptionExpression.class,
PyGeneratorExpression.class,
PyGroupPattern.class,
PyMappingPattern.class,
PyPatternArgumentList.class,
PyTypeParameterList.class,
};
static final Class[] CLASSES_TO_PARENTHESISE_ON_ENTER = new Class[]{
PyBinaryExpression.class,
PyCallExpression.class,
PyFromImportStatement.class,
PyTupleExpression.class,
PyWithStatement.class,
PySequencePattern.class,
PyReferenceExpression.class,
};
}

View File

@@ -11,6 +11,7 @@ import com.intellij.psi.SmartPsiElementPointer;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.jetbrains.python.PyPsiBundle;
import com.jetbrains.python.ast.PyAstStringLiteralExpression;
import com.jetbrains.python.documentation.docstrings.PyDocstringGenerator;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
@@ -93,7 +94,7 @@ public class DocstringQuickFix extends PsiUpdateModCommandQuickFix {
.forDocStringOwner(docStringOwner)
.withInferredParameters(false)
.addFirstEmptyLine();
final PyStringLiteralExpression updated = docstringGenerator.buildAndInsert().getDocStringExpression();
final PyAstStringLiteralExpression updated = docstringGenerator.buildAndInsert().getDocStringExpression();
if (updated != null && editor != null) {
final int offset = updated.getTextOffset();
editor.getCaretModel().moveToOffset(offset);

View File

@@ -727,27 +727,12 @@ public final class PyUtil {
* @see #updateDocumentUnblockedAndCommitted(PsiElement, Function)
*/
public static void updateDocumentUnblockedAndCommitted(@NotNull PsiElement anchor, @NotNull Consumer<? super Document> consumer) {
updateDocumentUnblockedAndCommitted(anchor, document -> {
consumer.consume(document);
return null;
});
PyUtilCore.updateDocumentUnblockedAndCommitted(anchor, consumer);
}
@Nullable
public static <T> T updateDocumentUnblockedAndCommitted(@NotNull PsiElement anchor, @NotNull Function<? super Document, ? extends T> func) {
final PsiDocumentManager manager = PsiDocumentManager.getInstance(anchor.getProject());
// manager.getDocument(anchor.getContainingFile()) doesn't work with intention preview
final Document document = anchor.getContainingFile().getViewProvider().getDocument();
if (document != null) {
manager.doPostponedOperationsAndUnblockDocument(document);
try {
return func.fun(document);
}
finally {
manager.commitDocument(document);
}
}
return null;
return PyUtilCore.updateDocumentUnblockedAndCommitted(anchor, func);
}
@Nullable

View File

@@ -19,7 +19,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.util.ArrayUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.documentation.docstrings.DocStringUtil;
import com.jetbrains.python.documentation.docstrings.DocStringParser;
import com.jetbrains.python.documentation.docstrings.PyDocstringGenerator;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.NotNull;
@@ -78,7 +78,7 @@ public class PyFunctionBuilder {
*/
public PyFunctionBuilder(@NotNull String name, @NotNull PsiElement settingsAnchor) {
myName = name;
myDocStringGenerator = PyDocstringGenerator.create(DocStringUtil.getConfiguredDocStringFormatOrPlain(settingsAnchor),
myDocStringGenerator = PyDocstringGenerator.create(DocStringParser.getConfiguredDocStringFormatOrPlain(settingsAnchor),
PyIndentUtil.getIndentFromSettings(settingsAnchor.getContainingFile()),
settingsAnchor);
mySettingAnchor = settingsAnchor;

View File

@@ -64,7 +64,7 @@ public final class DocStringAnnotator extends PyAnnotator {
private void annotateDocStringStmt(final PyStringLiteralExpression stmt) {
if (stmt != null) {
final DocStringFormat format = DocStringUtil.getConfiguredDocStringFormat(stmt);
final DocStringFormat format = DocStringParser.getConfiguredDocStringFormat(stmt);
final String[] tags;
if (format == DocStringFormat.EPYTEXT) {
tags = EpydocString.ALL_TAGS;

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="intellij.platform.editor" />
<orderEntry type="module" module-name="intellij.platform.core" />
<orderEntry type="module" module-name="intellij.python.parser" />
<orderEntry type="module" module-name="intellij.platform.projectModel" />
<orderEntry type="module" module-name="intellij.python.ast" />
<orderEntry type="module" module-name="intellij.platform.codeStyle.impl" />
<orderEntry type="module" module-name="intellij.platform.codeStyle" />
<orderEntry type="library" name="fastutil-min" level="project" />
<orderEntry type="module" module-name="intellij.platform.core.impl" />
<orderEntry type="library" name="Guava" level="project" />
<orderEntry type="module" module-name="intellij.platform.projectModel.impl" />
<orderEntry type="module" module-name="intellij.platform.analysis" />
</component>
</module>

View File

@@ -0,0 +1,12 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<applicationService serviceImplementation="com.jetbrains.python.codeInsight.PyCodeInsightSettings"/>
<lang.whiteSpaceFormattingStrategy language="Python"
implementationClass="com.jetbrains.python.formatter.PyWhiteSpaceFormattingStrategy"/>
<moduleService serviceImplementation="com.jetbrains.python.documentation.PyDocumentationSettings$ModuleService"/>
<applicationService serviceImplementation="com.jetbrains.python.documentation.PyDocumentationSettings$AppService"/>
<applicationService serviceInterface="com.jetbrains.python.PythonCodeStyleService"
serviceImplementation="com.jetbrains.python.PythonCodeStyleServiceImpl"/>
</extensions>
</idea-plugin>

View File

@@ -0,0 +1,4 @@
### Formatter
formatter.panel.dict.alignment.do.not.align=Do not align
formatter.panel.dict.alignment.align.on.colon=Align on colon
formatter.panel.dict.alignment.align.on.value=Align on value

View File

@@ -0,0 +1,26 @@
package com.jetbrains.python;
import com.intellij.DynamicBundle;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.PropertyKey;
import java.util.function.Supplier;
public final class PySyntaxCoreBundle extends DynamicBundle {
@NonNls public static final String BUNDLE = "messages.PySyntaxCoreBundle";
public static final PySyntaxCoreBundle INSTANCE = new PySyntaxCoreBundle();
private PySyntaxCoreBundle() { super(BUNDLE); }
@NotNull
public static @Nls String message(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
return INSTANCE.getMessage(key, params);
}
@NotNull
public static Supplier<@Nls String> messagePointer(@NotNull @PropertyKey(resourceBundle = BUNDLE) String key, Object @NotNull ... params) {
return INSTANCE.getLazyMessage(key, params);
}
}

View File

@@ -3,7 +3,7 @@ package com.jetbrains.python.debugger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.jetbrains.python.psi.PyFunction;
import com.jetbrains.python.ast.PyAstFunction;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -38,10 +38,10 @@ public abstract class PySignatureCacheManager {
public abstract void recordSignature(@NotNull PySignature signature);
@Nullable
public abstract String findParameterType(@NotNull PyFunction function, @NotNull String name);
public abstract String findParameterType(@NotNull PyAstFunction function, @NotNull String name);
@Nullable
public abstract PySignature findSignature(@NotNull PyFunction function);
public abstract PySignature findSignature(@NotNull PyAstFunction function);
public abstract boolean clearCache();
}

View File

@@ -9,14 +9,13 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiFile;
import com.intellij.util.xmlb.annotations.OptionTag;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareModuleConfiguratorImpl;
import com.jetbrains.python.ast.PyAstFile;
import com.jetbrains.python.ast.PyAstTargetExpression;
import com.jetbrains.python.ast.impl.PyPsiUtilsCore;
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareService;
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareServiceClasses;
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareServiceModuleConfigurator;
import com.jetbrains.python.documentation.docstrings.DocStringFormat;
import com.jetbrains.python.psi.PyFile;
import com.jetbrains.python.psi.PyTargetExpression;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -29,11 +28,11 @@ public abstract class PyDocumentationSettings
PyDocumentationSettings.AppService,
PyDocumentationSettings.ModuleService> {
private static final PyDefaultProjectAwareServiceClasses<ServiceState, PyDocumentationSettings, AppService, ModuleService>
@ApiStatus.Internal
public static final PyDefaultProjectAwareServiceClasses<ServiceState, PyDocumentationSettings, AppService, ModuleService>
SERVICE_CLASSES = new PyDefaultProjectAwareServiceClasses<>(AppService.class, ModuleService.class);
final static DocStringFormat DEFAULT_DOC_STRING_FORMAT = DocStringFormat.REST;
private static final PyDocumentationSettingsDetector DETECTOR = new PyDocumentationSettingsDetector();
protected PyDocumentationSettings() {
super(new ServiceState());
@@ -43,11 +42,6 @@ public abstract class PyDocumentationSettings
return SERVICE_CLASSES.getService(module);
}
@NotNull
public static PyDefaultProjectAwareServiceModuleConfigurator getConfigurator() {
return new PyDefaultProjectAwareModuleConfiguratorImpl<>(SERVICE_CLASSES, DETECTOR);
}
public final boolean isNumpyFormat(PsiFile file) {
return isFormat(file, DocStringFormat.NUMPY);
@@ -58,7 +52,7 @@ public abstract class PyDocumentationSettings
}
private boolean isFormat(@Nullable PsiFile file, @NotNull DocStringFormat format) {
return file instanceof PyFile ? getFormatForFile(file) == format : getState().getFormat() == format;
return file instanceof PyAstFile ? getFormatForFile(file) == format : getState().getFormat() == format;
}
@NotNull
@@ -69,10 +63,10 @@ public abstract class PyDocumentationSettings
@Nullable
public static DocStringFormat getFormatFromDocformatAttribute(@NotNull PsiFile file) {
if (file instanceof PyFile) {
final PyTargetExpression expr = ((PyFile)file).findTopLevelAttribute(PyNames.DOCFORMAT);
if (file instanceof PyAstFile) {
final PyAstTargetExpression expr = ((PyAstFile)file).findTopLevelAttribute(PyNames.DOCFORMAT);
if (expr != null) {
final String docformat = PyPsiUtils.strValue(expr.findAssignedValue());
final String docformat = PyPsiUtilsCore.strValue(expr.findAssignedValue());
if (docformat != null) {
final List<String> words = StringUtil.split(docformat, " ");
if (words.size() > 0) {

View File

@@ -0,0 +1,153 @@
package com.jetbrains.python.documentation.docstrings;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ObjectUtils;
import com.jetbrains.python.documentation.PyDocumentationSettings;
import com.jetbrains.python.psi.PyStringLiteralUtil;
import com.jetbrains.python.psi.StructuredDocString;
import com.jetbrains.python.toolbox.Substring;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public final class DocStringParser {
/**
* @param stringText docstring text with possible string prefix and quotes
*/
@NotNull
public static StructuredDocString parseDocString(@NotNull DocStringFormat format, @NotNull String stringText) {
return parseDocString(format, stripPrefixAndQuotes(stringText));
}
@NotNull
public static StructuredDocString parseDocString(@NotNull DocStringFormat format, @NotNull Substring content) {
return switch (format) {
case REST -> new SphinxDocString(content);
case EPYTEXT -> new EpydocString(content);
case GOOGLE -> new GoogleCodeStyleDocString(content);
case NUMPY -> new NumpyDocString(content);
case PLAIN -> new PlainDocString(content);
};
}
@NotNull
private static Substring stripPrefixAndQuotes(@NotNull String text) {
final TextRange contentRange = PyStringLiteralUtil.getContentRange(text);
return new Substring(text, contentRange.getStartOffset(), contentRange.getEndOffset());
}
/**
* @return docstring format inferred heuristically solely from its content. For more reliable result use anchored version
* {@link #guessDocStringFormat(String, PsiElement)} of this method.
* @see #guessDocStringFormat(String, PsiElement)
*/
@NotNull
public static DocStringFormat guessDocStringFormat(@NotNull String text) {
if (isLikeEpydocDocString(text)) {
return DocStringFormat.EPYTEXT;
}
if (isLikeSphinxDocString(text)) {
return DocStringFormat.REST;
}
if (isLikeNumpyDocstring(text)) {
return DocStringFormat.NUMPY;
}
if (isLikeGoogleDocString(text)) {
return DocStringFormat.GOOGLE;
}
return DocStringFormat.PLAIN;
}
/**
* @param text docstring text <em>with both quotes and string prefix stripped</em>
* @param anchor PSI element that will be used to retrieve docstring format from the containing file or the project module
* @return docstring inferred heuristically and if unsuccessful fallback to configured format retrieved from anchor PSI element
* @see #getConfiguredDocStringFormat(PsiElement)
*/
@NotNull
public static DocStringFormat guessDocStringFormat(@NotNull String text, @Nullable PsiElement anchor) {
final DocStringFormat guessed = guessDocStringFormat(text);
return guessed == DocStringFormat.PLAIN && anchor != null ? getConfiguredDocStringFormatOrPlain(anchor) : guessed;
}
/**
* @param anchor PSI element that will be used to retrieve docstring format from the containing file or the project module
* @return docstring format configured for file or module containing given anchor PSI element
* @see PyDocumentationSettings#getFormatForFile(PsiFile)
*/
@Nullable
public static DocStringFormat getConfiguredDocStringFormat(@NotNull PsiElement anchor) {
final Module module = getModuleForElement(anchor);
if (module == null) {
return null;
}
final PyDocumentationSettings settings = PyDocumentationSettings.getInstance(module);
return settings.getFormatForFile(anchor.getContainingFile());
}
@NotNull
public static DocStringFormat getConfiguredDocStringFormatOrPlain(@NotNull PsiElement anchor) {
return ObjectUtils.chooseNotNull(getConfiguredDocStringFormat(anchor), DocStringFormat.PLAIN);
}
public static boolean isLikeSphinxDocString(@NotNull String text) {
return text.contains(":param ") ||
text.contains(":key ") || text.contains(":keyword ") ||
text.contains(":return:") || text.contains(":returns:") ||
text.contains(":raise ") || text.contains(":raises ") || text.contains(":except ") || text.contains(":exception ") ||
text.contains(":rtype") || text.contains(":type") ||
text.contains(":var") || text.contains(":ivar") || text.contains(":cvar");
}
public static boolean isLikeEpydocDocString(@NotNull String text) {
return text.contains("@param ") ||
text.contains("@kwarg ") || text.contains("@keyword ") || text.contains("@kwparam ") ||
text.contains("@raise ") || text.contains("@raises ") || text.contains("@except ") || text.contains("@exception ") ||
text.contains("@return:") ||
text.contains("@rtype") || text.contains("@type") ||
text.contains("@var") || text.contains("@ivar") || text.contains("@cvar");
}
public static boolean isLikeGoogleDocString(@NotNull String text) {
for (@NonNls String title : StringUtil.findMatches(text, GoogleCodeStyleDocString.SECTION_HEADER, 1)) {
if (SectionBasedDocString.isValidSectionTitle(title)) {
return true;
}
}
return false;
}
public static boolean isLikeNumpyDocstring(@NotNull String text) {
final String[] lines = StringUtil.splitByLines(text, false);
for (int i = 0; i < lines.length; i++) {
final String line = lines[i];
if (NumpyDocString.SECTION_HEADER.matcher(line).matches() && i > 0) {
@NonNls final String lineBefore = lines[i - 1];
if (SectionBasedDocString.SECTION_NAMES.contains(StringUtil.toLowerCase(lineBefore.trim()))) {
return true;
}
}
}
return false;
}
// Might return {@code null} in some rare cases when PSI element doesn't have an associated module.
// For instance, an empty IDEA project with a Python scratch file.
@Nullable
public static Module getModuleForElement(@NotNull PsiElement element) {
final Module module = ModuleUtilCore.findModuleForPsiElement(element.getContainingFile());
if (module != null) {
return module;
}
return ArrayUtil.getFirstElement(ModuleManager.getInstance(element.getProject()).getModules());
}
}

View File

@@ -24,8 +24,11 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.ast.*;
import com.jetbrains.python.ast.impl.PyUtilCore;
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
import com.jetbrains.python.debugger.PySignature;
import com.jetbrains.python.debugger.PySignatureCacheManager;
@@ -43,7 +46,7 @@ public final class PyDocstringGenerator {
private final List<DocstringParam> myRemovedParams = new ArrayList<>();
private final String myDocStringText;
// Updated after buildAndInsert()
@Nullable private final PyDocStringOwner myDocStringOwner;
@Nullable private final PyAstDocStringOwner myDocStringOwner;
private final String myDocStringIndent;
private final DocStringFormat myDocStringFormat;
private final PsiElement mySettingsAnchor;
@@ -54,7 +57,7 @@ public final class PyDocstringGenerator {
private boolean myParametersPrepared = false;
private String myQuotes = TRIPLE_DOUBLE_QUOTES;
private PyDocstringGenerator(@Nullable PyDocStringOwner docStringOwner,
private PyDocstringGenerator(@Nullable PyAstDocStringOwner docStringOwner,
@Nullable String docStringText,
@NotNull DocStringFormat format,
@NotNull String indentation,
@@ -68,13 +71,13 @@ public final class PyDocstringGenerator {
}
@NotNull
public static PyDocstringGenerator forDocStringOwner(@NotNull PyDocStringOwner owner) {
public static PyDocstringGenerator forDocStringOwner(@NotNull PyAstDocStringOwner owner) {
String indentation = "";
if (owner instanceof PyStatementListContainer) {
indentation = PyIndentUtil.getElementIndent(((PyStatementListContainer)owner).getStatementList());
if (owner instanceof PyAstStatementListContainer) {
indentation = PyIndentUtil.getElementIndent(((PyAstStatementListContainer)owner).getStatementList());
}
final String docStringText = owner.getDocStringExpression() == null ? null : owner.getDocStringExpression().getText();
return new PyDocstringGenerator(owner, docStringText, DocStringUtil.getConfiguredDocStringFormatOrPlain(owner), indentation, owner);
return new PyDocstringGenerator(owner, docStringText, DocStringParser.getConfiguredDocStringFormatOrPlain(owner), indentation, owner);
}
/**
@@ -88,10 +91,10 @@ public final class PyDocstringGenerator {
}
@NotNull
public static PyDocstringGenerator update(@NotNull PyStringLiteralExpression docString) {
return new PyDocstringGenerator(PsiTreeUtil.getParentOfType(docString, PyDocStringOwner.class),
public static PyDocstringGenerator update(@NotNull PyAstStringLiteralExpression docString) {
return new PyDocstringGenerator(PsiTreeUtil.getParentOfType(docString, PyAstDocStringOwner.class),
docString.getText(),
DocStringUtil.getConfiguredDocStringFormatOrPlain(docString),
DocStringParser.getConfiguredDocStringFormatOrPlain(docString),
PyIndentUtil.getElementIndent(docString),
docString);
}
@@ -114,7 +117,7 @@ public final class PyDocstringGenerator {
}
@NotNull
public PyDocstringGenerator withParam(@NotNull PyNamedParameter param) {
public PyDocstringGenerator withParam(@NotNull PyAstNamedParameter param) {
return withParam(getPreferredParameterName(param));
}
@@ -125,7 +128,7 @@ public final class PyDocstringGenerator {
}
@NotNull
public PyDocstringGenerator withParamTypedByName(@NotNull PyNamedParameter name, @Nullable String type) {
public PyDocstringGenerator withParamTypedByName(@NotNull PyAstNamedParameter name, @Nullable String type) {
return withParamTypedByName(getPreferredParameterName(name), type);
}
@@ -174,8 +177,8 @@ public final class PyDocstringGenerator {
*/
@NotNull
public PyDocstringGenerator withInferredParameters(boolean addReturn) {
if (myDocStringOwner instanceof PyFunction) {
for (PyParameter param : ((PyFunction)myDocStringOwner).getParameterList().getParameters()) {
if (myDocStringOwner instanceof PyAstFunction) {
for (PyAstParameter param : ((PyAstFunction)myDocStringOwner).getParameterList().getParameters()) {
if (param.getAsNamed() == null) {
continue;
}
@@ -184,12 +187,12 @@ public final class PyDocstringGenerator {
if (StringUtil.isEmpty(paramName) || param.isSelf() || docString != null && docString.getParameters().contains(paramName)) {
continue;
}
withParam((PyNamedParameter)param);
withParam((PyAstNamedParameter)param);
}
final RaiseVisitor visitor = new RaiseVisitor();
final PyStatementList statementList = ((PyFunction)myDocStringOwner).getStatementList();
final PyAstStatementList statementList = ((PyAstFunction)myDocStringOwner).getStatementList();
statementList.accept(visitor);
if (!PyUtil.isInitOrNewMethod(myDocStringOwner) && (visitor.myHasReturn || addReturn)) {
if (!PyUtilCore.isInitOrNewMethod(myDocStringOwner) && (visitor.myHasReturn || addReturn)) {
// will add :return: placeholder in Sphinx/Epydoc docstrings
withReturnValue(null);
}
@@ -233,8 +236,8 @@ public final class PyDocstringGenerator {
// Sanitize parameters
PySignature signature = null;
if (myDocStringOwner instanceof PyFunction && myUseTypesFromDebuggerSignature) {
signature = PySignatureCacheManager.getInstance(myDocStringOwner.getProject()).findSignature((PyFunction)myDocStringOwner);
if (myDocStringOwner instanceof PyAstFunction && myUseTypesFromDebuggerSignature) {
signature = PySignatureCacheManager.getInstance(myDocStringOwner.getProject()).findSignature((PyAstFunction)myDocStringOwner);
}
final DocStringFormat format = myDocStringFormat;
final ArrayList<DocstringParam> filtered = new ArrayList<>();
@@ -288,18 +291,18 @@ public final class PyDocstringGenerator {
}
@Nullable
public PyStringLiteralExpression getDocStringExpression() {
public PyAstStringLiteralExpression getDocStringExpression() {
Preconditions.checkNotNull(myDocStringOwner, "For this action docstring owner must be supplied");
return myDocStringOwner.getDocStringExpression();
}
@Nullable
private StructuredDocString getStructuredDocString() {
return myDocStringText == null ? null : DocStringUtil.parseDocString(myDocStringFormat, myDocStringText);
return myDocStringText == null ? null : DocStringParser.parseDocString(myDocStringFormat, myDocStringText);
}
@NotNull
public String getPreferredParameterName(@NotNull PyNamedParameter parameter) {
public String getPreferredParameterName(@NotNull PyAstNamedParameter parameter) {
if (getDocStringFormat() == DocStringFormat.GOOGLE) {
return parameter.getAsNamed().getRepr(false);
}
@@ -448,26 +451,26 @@ public final class PyDocstringGenerator {
}
@NotNull
public PyDocStringOwner buildAndInsert(@NotNull String replacementText) {
public PyAstDocStringOwner buildAndInsert(@NotNull String replacementText) {
Preconditions.checkNotNull(myDocStringOwner, "For this action docstring owner must be supplied");
final Project project = myDocStringOwner.getProject();
PyElementGenerator elementGenerator = PyElementGenerator.getInstance(project);
final PyExpressionStatement replacement = elementGenerator.createDocstring(replacementText);
PyAstElementGenerator elementGenerator = PyAstElementGenerator.getInstance(project);
final PyAstExpressionStatement replacement = elementGenerator.createDocstring(replacementText);
final PyStringLiteralExpression docStringExpression = getDocStringExpression();
final PyAstStringLiteralExpression docStringExpression = getDocStringExpression();
if (docStringExpression != null) {
docStringExpression.replace(replacement.getExpression());
}
else {
PyStatementListContainer container = PyUtil.as(myDocStringOwner, PyStatementListContainer.class);
PyAstStatementListContainer container = ObjectUtils.tryCast(myDocStringOwner, PyAstStatementListContainer.class);
if (container == null) {
throw new IllegalStateException("Should be a function or class");
}
final PyStatementList statements = container.getStatementList();
final PyAstStatementList statements = container.getStatementList();
final String indentation = PyIndentUtil.getElementIndent(statements);
PyUtil.updateDocumentUnblockedAndCommitted(myDocStringOwner, document -> {
PyUtilCore.updateDocumentUnblockedAndCommitted(myDocStringOwner, document -> {
final PsiElement beforeStatements = statements.getPrevSibling();
String replacementWithLineBreaks = "\n" + indentation + replacementText;
if (statements.getStatements().length > 0) {
@@ -490,7 +493,7 @@ public final class PyDocstringGenerator {
}
@NotNull
public PyDocStringOwner buildAndInsert() {
public PyAstDocStringOwner buildAndInsert() {
Preconditions.checkNotNull(myDocStringOwner, "For this action docstring owner must be supplied");
return buildAndInsert(buildDocString());
}
@@ -550,22 +553,22 @@ public final class PyDocstringGenerator {
}
}
private static class RaiseVisitor extends PyRecursiveElementVisitor {
private static class RaiseVisitor extends PyAstRecursiveElementVisitor {
private boolean myHasRaise = false;
private boolean myHasReturn = false;
@Nullable private PyExpression myRaiseTarget = null;
@Nullable private PyAstExpression myRaiseTarget = null;
@Override
public void visitPyRaiseStatement(@NotNull PyRaiseStatement node) {
public void visitPyRaiseStatement(@NotNull PyAstRaiseStatement node) {
myHasRaise = true;
final PyExpression[] expressions = node.getExpressions();
final PyAstExpression[] expressions = node.getExpressions();
if (expressions.length > 0) {
myRaiseTarget = expressions[0];
}
}
@Override
public void visitPyReturnStatement(@NotNull PyReturnStatement node) {
public void visitPyReturnStatement(@NotNull PyAstReturnStatement node) {
myHasReturn = true;
}
@@ -573,8 +576,8 @@ public final class PyDocstringGenerator {
public String getRaiseTargetText() {
if (myRaiseTarget != null) {
String raiseTarget = myRaiseTarget.getText();
if (myRaiseTarget instanceof PyCallExpression) {
final PyExpression callee = ((PyCallExpression)myRaiseTarget).getCallee();
if (myRaiseTarget instanceof PyAstCallExpression) {
final PyAstExpression callee = ((PyAstCallExpression)myRaiseTarget).getCallee();
if (callee != null) {
raiseTarget = callee.getText();
}
@@ -587,7 +590,7 @@ public final class PyDocstringGenerator {
}
@Nullable
public PyDocStringOwner getDocStringOwner() {
public PyAstDocStringOwner getDocStringOwner() {
return myDocStringOwner;
}

View File

@@ -0,0 +1,58 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.jetbrains.python.editor;
import com.intellij.psi.PsiComment;
import com.jetbrains.python.ast.*;
import com.jetbrains.python.psi.*;
import org.jetbrains.annotations.ApiStatus;
public final class PyEditorHandlerConfig {
private PyEditorHandlerConfig() {
}
public static final Class[] IMPLICIT_WRAP_CLASSES = new Class[] {
PyAstListLiteralExpression.class,
PyAstSetLiteralExpression.class,
PyAstDictLiteralExpression.class,
PyAstDictLiteralExpression.class,
PyAstParenthesizedExpression.class,
PyAstArgumentList.class,
PyAstParameterList.class,
PyAstGroupPattern.class,
PyAstSequencePattern.class,
PyAstMappingPattern.class,
PyAstPatternArgumentList.class,
PyAstTypeParameterList.class,
};
public static final Class[] WRAPPABLE_CLASSES = new Class[]{
PsiComment.class,
PyAstParenthesizedExpression.class,
PyAstListCompExpression.class,
PyAstDictCompExpression.class,
PyAstSetCompExpression.class,
PyAstDictLiteralExpression.class,
PyAstSetLiteralExpression.class,
PyAstListLiteralExpression.class,
PyAstArgumentList.class,
PyAstParameterList.class,
PyAstDecoratorList.class,
PyAstSliceExpression.class,
PyAstSubscriptionExpression.class,
PyAstGeneratorExpression.class,
PyAstGroupPattern.class,
PyAstMappingPattern.class,
PyAstPatternArgumentList.class,
PyAstTypeParameterList.class,
};
@ApiStatus.Internal
public static final Class[] CLASSES_TO_PARENTHESISE_ON_ENTER = new Class[]{
PyAstBinaryExpression.class,
PyAstCallExpression.class,
PyAstFromImportStatement.class,
PyAstTupleExpression.class,
PyAstWithStatement.class,
PyAstSequencePattern.class,
PyAstReferenceExpression.class,
};
}

View File

@@ -5,7 +5,7 @@ import com.intellij.formatting.WrapType;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CommonCodeStyleSettings;
import com.intellij.psi.codeStyle.CustomCodeStyleSettings;
import com.jetbrains.python.PyPsiBundle;
import com.jetbrains.python.PySyntaxCoreBundle;
import org.intellij.lang.annotations.MagicConstant;
import org.jetbrains.annotations.NotNull;
@@ -13,9 +13,9 @@ import org.jetbrains.annotations.NotNull;
public class PyCodeStyleSettings extends CustomCodeStyleSettings {
public enum DictAlignment {
NONE(PyPsiBundle.message("formatter.panel.dict.alignment.do.not.align")),
ON_VALUE(PyPsiBundle.message("formatter.panel.dict.alignment.align.on.value")),
ON_COLON(PyPsiBundle.message("formatter.panel.dict.alignment.align.on.colon"));
NONE(PySyntaxCoreBundle.message("formatter.panel.dict.alignment.do.not.align")),
ON_VALUE(PySyntaxCoreBundle.message("formatter.panel.dict.alignment.align.on.value")),
ON_COLON(PySyntaxCoreBundle.message("formatter.panel.dict.alignment.align.on.colon"));
String description;

View File

@@ -9,9 +9,9 @@ import com.intellij.psi.formatter.StaticSymbolWhiteSpaceDefinitionStrategy;
import com.intellij.psi.impl.source.tree.TreeUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.ast.*;
import com.jetbrains.python.ast.impl.PyPsiUtilsCore;
import com.jetbrains.python.editor.PyEditorHandlerConfig;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyPsiUtils;
import it.unimi.dsi.fastutil.ints.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntIterator;
@@ -146,7 +146,7 @@ public class PyWhiteSpaceFormattingStrategy extends StaticSymbolWhiteSpaceDefini
}
private static boolean needInsertBackslash(ASTNode nodeAtCaret, boolean autoWrapInProgress) {
if (PsiTreeUtil.getParentOfType(nodeAtCaret.getPsi(), PyFStringFragment.class) != null) {
if (PsiTreeUtil.getParentOfType(nodeAtCaret.getPsi(), PyAstFStringFragment.class) != null) {
return false;
}
@@ -198,7 +198,7 @@ public class PyWhiteSpaceFormattingStrategy extends StaticSymbolWhiteSpaceDefini
return false;
}
if (wrappableAfter == null) {
return !(wrappableBefore instanceof PyDecoratorList);
return !(wrappableBefore instanceof PyAstDecoratorList);
}
return wrappableBefore != wrappableAfter;
}
@@ -210,8 +210,8 @@ public class PyWhiteSpaceFormattingStrategy extends StaticSymbolWhiteSpaceDefini
: findAfterCaret(nodeAtCaret, PyEditorHandlerConfig.WRAPPABLE_CLASSES);
if (wrappable == null) {
PsiElement emptyTuple = before
? findBeforeCaret(nodeAtCaret, PyTupleExpression.class)
: findAfterCaret(nodeAtCaret, PyTupleExpression.class);
? findBeforeCaret(nodeAtCaret, PyAstTupleExpression.class)
: findAfterCaret(nodeAtCaret, PyAstTupleExpression.class);
if (emptyTuple != null && emptyTuple.getNode().getFirstChildNode().getElementType() == PyTokenTypes.LPAR) {
wrappable = emptyTuple;
}
@@ -221,12 +221,12 @@ public class PyWhiteSpaceFormattingStrategy extends StaticSymbolWhiteSpaceDefini
@Nullable
private static PsiElement findStatementBeforeCaret(ASTNode node) {
return findBeforeCaret(node, PyStatement.class, PyStatementPart.class);
return findBeforeCaret(node, PyAstStatement.class, PyAstStatementPart.class);
}
@Nullable
private static PsiElement findStatementAfterCaret(ASTNode node) {
return findAfterCaret(node, PyStatement.class, PyStatementPart.class);
return findAfterCaret(node, PyAstStatement.class, PyAstStatementPart.class);
}
private static PsiElement findBeforeCaret(ASTNode atCaret, Class<? extends PsiElement>... classes) {
@@ -256,7 +256,7 @@ public class PyWhiteSpaceFormattingStrategy extends StaticSymbolWhiteSpaceDefini
for (Class<? extends T> aClass : classes) {
if (aClass.isInstance(run)) return (T)run;
}
if (run instanceof PsiFile || run instanceof PyStatementList) break;
if (run instanceof PsiFile || run instanceof PyAstStatementList) break;
run = run.getParent();
}
@@ -264,7 +264,7 @@ public class PyWhiteSpaceFormattingStrategy extends StaticSymbolWhiteSpaceDefini
}
private static boolean inFromImportParentheses(PsiElement statement, int offset) {
if (!(statement instanceof PyFromImportStatement fromImportStatement)) {
if (!(statement instanceof PyAstFromImportStatement fromImportStatement)) {
return false;
}
PsiElement leftParen = fromImportStatement.getLeftParen();
@@ -275,24 +275,24 @@ public class PyWhiteSpaceFormattingStrategy extends StaticSymbolWhiteSpaceDefini
}
private static boolean inWithItemsParentheses(@NotNull PsiElement statement, int offset) {
if (!(statement instanceof PyWithStatement)) {
if (!(statement instanceof PyAstWithStatement)) {
return false;
}
final PsiElement leftParen = PyPsiUtils.getFirstChildOfType(statement, PyTokenTypes.LPAR);
final PsiElement leftParen = PyPsiUtilsCore.getFirstChildOfType(statement, PyTokenTypes.LPAR);
return leftParen != null && offset >= leftParen.getTextRange().getEndOffset();
}
private static boolean inCaseClauseParentheses(@NotNull PsiElement statement, int offset) {
if (!(statement instanceof PyCaseClause caseClause)) {
if (!(statement instanceof PyAstCaseClause caseClause)) {
return false;
}
final PyPattern pattern = caseClause.getPattern();
final PyAstPattern pattern = caseClause.getPattern();
if (pattern == null) {
return false;
}
final PsiElement leftParen = PyPsiUtils.getChildByFilter(pattern, PyTokenTypes.OPEN_BRACES, 0);
final PsiElement leftParen = PyPsiUtilsCore.getChildByFilter(pattern, PyTokenTypes.OPEN_BRACES, 0);
return leftParen != null && offset >= leftParen.getTextRange().getEndOffset();
}
}

View File

@@ -24,8 +24,10 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PythonCodeStyleService;
import com.jetbrains.python.ast.PyAstStatementList;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -33,8 +35,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collections;
import java.util.List;
import static com.jetbrains.python.psi.PyUtil.as;
/**
* Contains various methods for manipulation on indentation found in arbitrary text and individual lines:
* <ul>
@@ -89,7 +89,7 @@ public final class PyIndentUtil {
if (anchor instanceof PsiFile) {
return "";
}
final PyStatementList statementList = getAnchorStatementList(anchor);
final PyAstStatementList statementList = getAnchorStatementList(anchor);
if (statementList == null) {
return "";
}
@@ -105,9 +105,9 @@ public final class PyIndentUtil {
}
@NotNull
private static String getExpectedBlockIndent(@NotNull PyStatementList anchor) {
private static String getExpectedBlockIndent(@NotNull PyAstStatementList anchor) {
final String indentStep = getIndentFromSettings(anchor.getContainingFile());
final PyStatementList parentBlock = PsiTreeUtil.getParentOfType(anchor, PyStatementList.class, true);
final PyAstStatementList parentBlock = PsiTreeUtil.getParentOfType(anchor, PyAstStatementList.class, true);
if (parentBlock != null) {
return getElementIndent(parentBlock) + indentStep;
}
@@ -115,14 +115,14 @@ public final class PyIndentUtil {
}
@Nullable
private static PyStatementList getAnchorStatementList(@NotNull PsiElement element) {
PyStatementList statementList = null;
private static PyAstStatementList getAnchorStatementList(@NotNull PsiElement element) {
PyAstStatementList statementList = null;
// First whitespace right before the statement list (right after ":")
if (element instanceof PsiWhiteSpace) {
statementList = as(element.getNextSibling(), PyStatementList.class);
statementList = ObjectUtils.tryCast(element.getNextSibling(), PyAstStatementList.class);
}
if (statementList == null) {
statementList = PsiTreeUtil.getParentOfType(element, PyStatementList.class, false);
statementList = PsiTreeUtil.getParentOfType(element, PyAstStatementList.class, false);
}
return statementList;
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="intellij.platform.analysis" />
<orderEntry type="module" module-name="intellij.platform.core" />
<orderEntry type="module" module-name="intellij.python.parser" />
<orderEntry type="module" module-name="intellij.platform.editor" />
<orderEntry type="module" module-name="intellij.python.syntax.core" />
<orderEntry type="module" module-name="intellij.python.ast" />
<orderEntry type="module" module-name="intellij.platform.lang.impl" />
<orderEntry type="module" module-name="intellij.platform.core.ui" />
<orderEntry type="module" module-name="intellij.platform.codeStyle.impl" />
<orderEntry type="library" name="fastutil-min" level="project" />
</component>
</module>

View File

@@ -0,0 +1,5 @@
<idea-plugin>
<extensions defaultExtensionNs="com.intellij">
<enterHandlerDelegate implementation="com.jetbrains.python.editor.PythonEnterHandler"/>
</extensions>
</idea-plugin>

View File

@@ -25,6 +25,8 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.ast.*;
import com.jetbrains.python.ast.docstring.DocStringUtilCore;
import com.jetbrains.python.codeInsight.PyCodeInsightSettings;
import com.jetbrains.python.documentation.docstrings.*;
import com.jetbrains.python.formatter.PyWhiteSpaceFormattingStrategy;
@@ -51,7 +53,7 @@ public final class PythonEnterHandler extends EnterHandlerDelegateAdapter {
editor = InjectedLanguageUtil.getTopLevelEditor(editor);
offset = editor.getCaretModel().getOffset();
}
if (!(file instanceof PyFile)) {
if (!(file instanceof PyAstFile)) {
return Result.Continue;
}
final Boolean isSplitLine = DataManager.getInstance().loadFromDataContext(dataContext, SplitLineAction.SPLIT_LINE_KEY);
@@ -86,9 +88,9 @@ public final class PythonEnterHandler extends EnterHandlerDelegateAdapter {
PsiElement elementParent = element.getParent();
final IElementType nodeType = node.getElementType();
if (nodeType == PyTokenTypes.LPAR) elementParent = elementParent.getParent();
if (elementParent instanceof PyParenthesizedExpression || elementParent instanceof PyGeneratorExpression) return Result.Continue;
if (elementParent instanceof PyAstParenthesizedExpression || elementParent instanceof PyAstGeneratorExpression) return Result.Continue;
final PyStringElement stringElement = PsiTreeUtil.getParentOfType(element, PyStringElement.class, false);
final PyAstStringElement stringElement = PsiTreeUtil.getParentOfType(element, PyAstStringElement.class, false);
if (stringElement == null && prevElement == element) {
return Result.Continue;
}
@@ -191,10 +193,10 @@ public final class PythonEnterHandler extends EnterHandlerDelegateAdapter {
private static void parenthesise(@NotNull PsiElement wrappable, @NotNull Document doc) {
TextRange rangeToParenthesise;
if (wrappable instanceof PyFromImportStatement fromImportStatement) {
if (wrappable instanceof PyAstFromImportStatement fromImportStatement) {
rangeToParenthesise = getRangeForPsiElementArray(fromImportStatement.getImportElements());
}
else if (wrappable instanceof PyWithStatement withStatement) {
else if (wrappable instanceof PyAstWithStatement withStatement) {
rangeToParenthesise = getRangeForPsiElementArray(withStatement.getWithItems());
}
else {
@@ -219,7 +221,7 @@ public final class PythonEnterHandler extends EnterHandlerDelegateAdapter {
}
private static void insertDocStringStub(Editor editor, PsiElement element, DocstringState state) {
PyDocStringOwner docOwner = PsiTreeUtil.getParentOfType(element, PyDocStringOwner.class);
PyAstDocStringOwner docOwner = PsiTreeUtil.getParentOfType(element, PyAstDocStringOwner.class);
if (docOwner != null) {
final int caretOffset = editor.getCaretModel().getOffset();
final Document document = editor.getDocument();
@@ -242,7 +244,7 @@ public final class PythonEnterHandler extends EnterHandlerDelegateAdapter {
public Result postProcessEnter(@NotNull PsiFile file,
@NotNull Editor editor,
@NotNull DataContext dataContext) {
if (!(file instanceof PyFile)) {
if (!(file instanceof PyAstFile)) {
return Result.Continue;
}
if (myPostprocessShift > 0) {
@@ -260,10 +262,10 @@ public final class PythonEnterHandler extends EnterHandlerDelegateAdapter {
final PsiElement element = file.findElementAt(offset);
if (element != null) {
// Insert additional indentation after section header in Google code style docstrings
final PyStringLiteralExpression pyString = DocStringUtil.getParentDefinitionDocString(element);
final PyAstStringLiteralExpression pyString = DocStringUtilCore.getParentDefinitionDocString(element);
if (pyString != null) {
final String docStringText = pyString.getText();
final DocStringFormat format = DocStringUtil.guessDocStringFormat(docStringText, pyString);
final DocStringFormat format = DocStringParser.guessDocStringFormat(docStringText, pyString);
if (format == DocStringFormat.GOOGLE && offset + 1 < document.getTextLength()) {
final int lineNum = document.getLineNumber(offset);
final TextRange lineRange = TextRange.create(document.getLineStartOffset(lineNum - 1), document.getLineEndOffset(lineNum - 1));
@@ -292,7 +294,7 @@ public final class PythonEnterHandler extends EnterHandlerDelegateAdapter {
if (!quotes.equals("\"\"\"") && !quotes.equals("'''")) {
return DocstringState.NONE;
}
final PyStringLiteralExpression pyString = DocStringUtil.getParentDefinitionDocString(element);
final PyAstStringLiteralExpression pyString = DocStringUtilCore.getParentDefinitionDocString(element);
if (pyString != null) {
String nodeText = element.getText();

View File

@@ -20,10 +20,7 @@ import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.jetbrains.python.codeInsight.editorActions.smartEnter.PySmartEnterProcessor;
import com.jetbrains.python.documentation.docstrings.DocStringFormat;
import com.jetbrains.python.documentation.docstrings.DocStringUtil;
import com.jetbrains.python.documentation.docstrings.GoogleCodeStyleDocStringBuilder;
import com.jetbrains.python.documentation.docstrings.SectionBasedDocString;
import com.jetbrains.python.documentation.docstrings.*;
import com.jetbrains.python.psi.PyIndentUtil;
import com.jetbrains.python.psi.PyStringLiteralExpression;
import org.jetbrains.annotations.NotNull;
@@ -39,7 +36,7 @@ public class GoogleDocStringSectionFixer extends PyFixer<PyStringLiteralExpressi
@Override
protected boolean isApplicable(@NotNull Editor editor, @NotNull PyStringLiteralExpression pyString) {
return DocStringUtil.getParentDefinitionDocString(pyString) == pyString &&
DocStringUtil.guessDocStringFormat(pyString.getText(), pyString) == DocStringFormat.GOOGLE;
DocStringParser.guessDocStringFormat(pyString.getText(), pyString) == DocStringFormat.GOOGLE;
}
@Override

View File

@@ -12,9 +12,11 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.platform.DirectoryProjectConfigurator;
import com.jetbrains.python.PythonModuleTypeBase;
import com.jetbrains.python.ReSTService;
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareModuleConfiguratorImpl;
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareService;
import com.jetbrains.python.defaultProjectAwareService.PyDefaultProjectAwareServiceModuleConfigurator;
import com.jetbrains.python.documentation.PyDocumentationSettings;
import com.jetbrains.python.documentation.PyDocumentationSettingsDetector;
import com.jetbrains.python.packaging.PyPackageRequirementsSettings;
import com.jetbrains.python.testing.TestRunnerService;
import org.jetbrains.annotations.NotNull;
@@ -45,11 +47,13 @@ final class PyDefaultProjectAwareServiceConfigurator implements DirectoryProject
private static void updateServices(@NotNull Module module, boolean newProject) {
List<PyDefaultProjectAwareServiceModuleConfigurator> configurators = Arrays.asList(
TestRunnerService.getConfigurator(),
PyDocumentationSettings.getConfigurator(),
new PyDefaultProjectAwareModuleConfiguratorImpl<>(PyDocumentationSettings.SERVICE_CLASSES, PY_DOCUMENTATION_SETTINGS_DETECTOR),
ReSTService.getConfigurator(),
PyPackageRequirementsSettings.getConfigurator());
for (PyDefaultProjectAwareServiceModuleConfigurator configurator : configurators) {
configurator.configureModule(module, newProject);
}
}
private static final PyDocumentationSettingsDetector PY_DOCUMENTATION_SETTINGS_DETECTOR = new PyDocumentationSettingsDetector();
}

View File

@@ -17,7 +17,6 @@ import com.intellij.lang.annotation.AnnotationBuilder;
import com.intellij.lang.annotation.AnnotationHolder;
import com.intellij.lang.annotation.ExternalAnnotator;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.modcommand.ModCommandAction;
import com.intellij.openapi.application.ApplicationInfo;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
@@ -37,7 +36,7 @@ import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.*;
import com.jetbrains.python.codeInsight.imports.OptimizeImportsQuickFix;
import com.jetbrains.python.documentation.docstrings.DocStringUtil;
import com.jetbrains.python.documentation.docstrings.DocStringParser;
import com.jetbrains.python.formatter.PyCodeStyleSettings;
import com.jetbrains.python.inspections.PyPep8Inspection;
import com.jetbrains.python.inspections.flake8.Flake8InspectionSuppressor;
@@ -155,7 +154,7 @@ public final class Pep8ExternalAnnotator extends ExternalAnnotator<Pep8ExternalA
if (vFile == null || !FileTypeRegistry.getInstance().isFileOfType(vFile, PythonFileType.INSTANCE)) {
return null;
}
Sdk sdk = PythonSdkType.findLocalCPython(DocStringUtil.getModuleForElement(file));
Sdk sdk = PythonSdkType.findLocalCPython(DocStringParser.getModuleForElement(file));
if (sdk == null) {
if (!myReportedMissingInterpreter) {
myReportedMissingInterpreter = true;

View File

@@ -13,6 +13,7 @@
<xi:include href="/META-INF/PythonPsi.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="/META-INF/PythonPsiImpl.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="/META-INF/PythonSdk.xml" xpointer="xpointer(/idea-plugin/*)"/>
<xi:include href="/META-INF/PythonSyntax.xml" xpointer="xpointer(/idea-plugin/*)"/>
<extensions defaultExtensionNs="com.intellij">
@@ -96,7 +97,6 @@
<annotator language="Python" implementationClass="com.jetbrains.python.inspections.PyCompatibilityInspectionAdvertiser"/>
<quoteHandler fileType="Python" className="com.jetbrains.python.editor.PythonQuoteHandler"/>
<enterHandlerDelegate implementation="com.jetbrains.python.editor.PythonEnterHandler"/>
<enterHandlerDelegate implementation="com.jetbrains.python.editor.PyEnterAtIndentHandler" order="first"/>
<enterBetweenBracesDelegate language="Python"
implementationClass="com.intellij.codeInsight.editorActions.enter.EnterBetweenBracesAndBracketsDelegate"/>
@@ -469,8 +469,6 @@
<!-- Code-insight IDE bridge -->
<applicationService serviceInterface="com.jetbrains.python.PythonRuntimeService"
serviceImplementation="com.jetbrains.python.PythonRuntimeServiceImpl"/>
<applicationService serviceInterface="com.jetbrains.python.PythonCodeStyleService"
serviceImplementation="com.jetbrains.python.PythonCodeStyleServiceImpl"/>
<applicationService serviceInterface="com.jetbrains.python.PythonUiService"
serviceImplementation="com.jetbrains.python.PythonUiServiceImpl"/>
<applicationService serviceInterface="com.jetbrains.python.codeInsight.imports.PyImportCollectorFactory"

View File

@@ -300,7 +300,7 @@ public class PySectionBasedDocStringTest extends PyTestCase {
// PY-16766
public void testGoogleDocStringContentDetection() {
assertTrue(DocStringUtil.isLikeGoogleDocString(
assertTrue(DocStringParser.isLikeGoogleDocString(
"""
My Section:
@@ -399,7 +399,7 @@ public class PySectionBasedDocStringTest extends PyTestCase {
// PY-17657, PY-16303
public void testNotGoogleFormatIfDocstringContainTags() {
assertEquals(DocStringFormat.REST, DocStringUtil.guessDocStringFormat("""
assertEquals(DocStringFormat.REST, DocStringParser.guessDocStringFormat("""
""\"
:type sub_field: FieldDescriptor | () -> FieldDescriptor
:param sub_field: The type of field in this collection
@@ -408,7 +408,7 @@ public class PySectionBasedDocStringTest extends PyTestCase {
addresses = field.Collection(AddressObject)
""\""""));
assertEquals(DocStringFormat.REST, DocStringUtil.guessDocStringFormat("""
assertEquals(DocStringFormat.REST, DocStringParser.guessDocStringFormat("""
""\"
Args:
:param Tuple[int, int] name: Some description