From 57bac4acbd42069636846f493bdd7020575bef4e Mon Sep 17 00:00:00 2001 From: UNV Date: Sat, 8 Nov 2025 18:11:12 +0300 Subject: [PATCH] Some refactoring. --- .../impl/actions/CreatePackageAction.java | 15 +- .../codeInsight/imports/AddImportHelper.java | 890 +++++++++--------- .../console/PyOpenDebugConsoleAction.java | 12 +- .../documentation/doctest/PyDocReference.java | 207 ++-- .../doctest/PyDocstringFile.java | 39 +- .../doctest/PyDocstringLanguageDialect.java | 20 +- .../doctest/PyDocstringParserDefinition.java | 62 +- .../setupPy/SetupTaskChooserAction.java | 5 +- .../impl/psi/impl/PyPrefixExpressionImpl.java | 237 ++--- .../impl/references/PyImportReference.java | 30 +- .../impl/references/PyOperatorReference.java | 30 +- .../impl/references/PyTargetReference.java | 4 +- .../python/codeInsight/PyCustomMember.java | 1 - .../rest/PyRestDocstringLanguageInjector.java | 109 ++- 14 files changed, 857 insertions(+), 804 deletions(-) diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/actions/CreatePackageAction.java b/python-impl/src/main/java/com/jetbrains/python/impl/actions/CreatePackageAction.java index d0024903..fa1d9120 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/actions/CreatePackageAction.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/actions/CreatePackageAction.java @@ -50,11 +50,8 @@ public class CreatePackageAction extends DumbAwareAction { @Override @RequiredUIAccess public void actionPerformed(AnActionEvent e) { - IdeView view = e.getData(IdeView.KEY); - if (view == null) { - return; - } - final Project project = e.getData(Project.KEY); + IdeView view = e.getRequiredData(IdeView.KEY); + final Project project = e.getRequiredData(Project.KEY); final PsiDirectory directory = DirectoryChooserUtil.getOrChooseDirectory(view); if (directory == null) { @@ -62,13 +59,12 @@ public void actionPerformed(AnActionEvent e) { } CreateDirectoryOrPackageHandler validator = new CreateDirectoryOrPackageHandler(project, directory, consulo.ide.impl.actions.CreateDirectoryOrPackageType.Package, ".") { - @RequiredUIAccess @Override + @RequiredUIAccess protected void createDirectories(String subDirName) { super.createDirectories(subDirName); - PsiFileSystemItem element = getCreatedElement(); - if (element instanceof PsiDirectory) { - createInitPyInHierarchy((PsiDirectory) element, directory); + if (getCreatedElement() instanceof PsiDirectory subDir) { + createInitPyInHierarchy(subDir, directory); } } }; @@ -86,6 +82,7 @@ protected void createDirectories(String subDirName) { } } + @RequiredUIAccess public static void createInitPyInHierarchy(PsiDirectory created, PsiDirectory ancestor) { do { createInitPy(created); diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/imports/AddImportHelper.java b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/imports/AddImportHelper.java index d9242f8c..a3bbfc5e 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/imports/AddImportHelper.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/codeInsight/imports/AddImportHelper.java @@ -23,6 +23,8 @@ import com.jetbrains.python.psi.*; import com.jetbrains.python.impl.psi.resolve.QualifiedNameFinder; import com.jetbrains.python.impl.sdk.PythonSdkType; +import consulo.annotation.access.RequiredReadAction; +import consulo.annotation.access.RequiredWriteAction; import consulo.content.bundle.Sdk; import consulo.ide.impl.idea.util.containers.ContainerUtil; import consulo.language.codeStyle.CodeStyleSettingsManager; @@ -31,7 +33,6 @@ import consulo.language.psi.util.PsiTreeUtil; import consulo.language.psi.util.QualifiedName; import consulo.language.util.IncorrectOperationException; -import consulo.language.util.ModuleUtilCore; import consulo.logging.Logger; import consulo.module.Module; import consulo.module.content.ProjectFileIndex; @@ -43,6 +44,7 @@ import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; + import java.util.ArrayList; import java.util.Comparator; import java.util.List; @@ -53,463 +55,489 @@ /** * Does the actual job of adding an import statement into a file. - * User: dcheryasov - * Date: Apr 24, 2009 3:17:59 AM + * + * @author dcheryasov + * @since 2009-04-24 */ public class AddImportHelper { - private static final Logger LOG = Logger.getInstance("#" + AddImportHelper.class.getName()); - - // normal imports go first, then "from" imports - private static final Comparator IMPORT_TYPE_COMPARATOR = (import1, import2) -> { - final int firstIsFromImport = import1 instanceof PyFromImportStatement ? 1 : 0; - final int secondIsFromImport = import2 instanceof PyFromImportStatement ? 1 : 0; - return firstIsFromImport - secondIsFromImport; - }; - - private static final Comparator IMPORT_NAMES_COMPARATOR = - (import1, import2) -> ContainerUtil.compareLexicographically(getSortNames(import1), getSortNames(import2)); - - @Nonnull - private static List getSortNames(@Nonnull PyImportStatementBase importStatement) { - final List result = new ArrayList<>(); - final PyFromImportStatement fromImport = as(importStatement, PyFromImportStatement.class); - if (fromImport != null) { - // because of that relative imports go to the end of an import block - result.add(StringUtil.repeatSymbol('.', fromImport.getRelativeLevel())); - final QualifiedName source = fromImport.getImportSourceQName(); - result.add(Objects.toString(source, "")); - if (fromImport.isStarImport()) { - result.add("*"); - } - } - else { - // fake relative level - result.add(""); - } + private static final Logger LOG = Logger.getInstance(AddImportHelper.class); + + // normal imports go first, then "from" imports + private static final Comparator IMPORT_TYPE_COMPARATOR = (import1, import2) -> { + int firstIsFromImport = import1 instanceof PyFromImportStatement ? 1 : 0; + int secondIsFromImport = import2 instanceof PyFromImportStatement ? 1 : 0; + return firstIsFromImport - secondIsFromImport; + }; + + private static final Comparator IMPORT_NAMES_COMPARATOR = + (import1, import2) -> ContainerUtil.compareLexicographically(getSortNames(import1), getSortNames(import2)); + + @Nonnull + private static List getSortNames(@Nonnull PyImportStatementBase importStatement) { + List result = new ArrayList<>(); + PyFromImportStatement fromImport = as(importStatement, PyFromImportStatement.class); + if (fromImport != null) { + // because of that relative imports go to the end of an import block + result.add(StringUtil.repeatSymbol('.', fromImport.getRelativeLevel())); + QualifiedName source = fromImport.getImportSourceQName(); + result.add(Objects.toString(source, "")); + if (fromImport.isStarImport()) { + result.add("*"); + } + } + else { + // fake relative level + result.add(""); + } - for (PyImportElement importElement : importStatement.getImportElements()) { - final QualifiedName qualifiedName = importElement.getImportedQName(); - result.add(Objects.toString(qualifiedName, "")); - result.add(StringUtil.notNullize(importElement.getAsName())); - } - return result; - } - - /** - * Creates and return comparator for import statements that compares them according to the rules specified in the code style settings. - * It's intended to be used for imports that have the same import priority in order to sort them within the corresponding group. - * - * @see ImportPriority - */ - @Nonnull - public static Comparator getSameGroupImportsComparator(@Nonnull Project project) { - final PyCodeStyleSettings settings = CodeStyleSettingsManager.getSettings(project).getCustomSettings(PyCodeStyleSettings.class); - if (settings.OPTIMIZE_IMPORTS_SORT_BY_TYPE_FIRST) { - return IMPORT_TYPE_COMPARATOR.thenComparing(IMPORT_NAMES_COMPARATOR); - } - else { - return IMPORT_NAMES_COMPARATOR.thenComparing(IMPORT_TYPE_COMPARATOR); + for (PyImportElement importElement : importStatement.getImportElements()) { + QualifiedName qualifiedName = importElement.getImportedQName(); + result.add(Objects.toString(qualifiedName, "")); + result.add(StringUtil.notNullize(importElement.getAsName())); + } + return result; + } + + /** + * Creates and return comparator for import statements that compares them according to the rules specified in the code style settings. + * It's intended to be used for imports that have the same import priority in order to sort them within the corresponding group. + * + * @see ImportPriority + */ + @Nonnull + public static Comparator getSameGroupImportsComparator(@Nonnull Project project) { + PyCodeStyleSettings settings = CodeStyleSettingsManager.getSettings(project).getCustomSettings(PyCodeStyleSettings.class); + if (settings.OPTIMIZE_IMPORTS_SORT_BY_TYPE_FIRST) { + return IMPORT_TYPE_COMPARATOR.thenComparing(IMPORT_NAMES_COMPARATOR); + } + else { + return IMPORT_NAMES_COMPARATOR.thenComparing(IMPORT_TYPE_COMPARATOR); + } } - } - - public enum ImportPriority { - FUTURE, - BUILTIN, - THIRD_PARTY, - PROJECT - } - private static final ImportPriority UNRESOLVED_SYMBOL_PRIORITY = ImportPriority.THIRD_PARTY; - - private AddImportHelper() { - } + public enum ImportPriority { + FUTURE, + BUILTIN, + THIRD_PARTY, + PROJECT + } - public static void addLocalImportStatement(@Nonnull PsiElement element, @Nonnull String name) { - final PyElementGenerator generator = PyElementGenerator.getInstance(element.getProject()); - final LanguageLevel languageLevel = LanguageLevel.forElement(element); + private static final ImportPriority UNRESOLVED_SYMBOL_PRIORITY = ImportPriority.THIRD_PARTY; - final PsiElement anchor = getLocalInsertPosition(element); - final PsiElement parentElement = sure(anchor).getParent(); - if (parentElement != null) { - parentElement.addBefore(generator.createImportStatement(languageLevel, name, null), anchor); + private AddImportHelper() { } - } - public static void addLocalFromImportStatement(@Nonnull PsiElement element, @Nonnull String qualifier, @Nonnull String name) { - final PyElementGenerator generator = PyElementGenerator.getInstance(element.getProject()); - final LanguageLevel languageLevel = LanguageLevel.forElement(element); + @RequiredWriteAction + public static void addLocalImportStatement(@Nonnull PsiElement element, @Nonnull String name) { + PyElementGenerator generator = PyElementGenerator.getInstance(element.getProject()); + LanguageLevel languageLevel = LanguageLevel.forElement(element); - final PsiElement anchor = getLocalInsertPosition(element); - final PsiElement parentElement = sure(anchor).getParent(); - if (parentElement != null) { - parentElement.addBefore(generator.createFromImportStatement(languageLevel, qualifier, name, null), anchor); + PsiElement anchor = getLocalInsertPosition(element); + PsiElement parentElement = sure(anchor).getParent(); + if (parentElement != null) { + parentElement.addBefore(generator.createImportStatement(languageLevel, name, null), anchor); + } } - } - - @Nullable - public static PsiElement getLocalInsertPosition(@Nonnull PsiElement anchor) { - return PsiTreeUtil.getParentOfType(anchor, PyStatement.class, false); - } - - @Nullable - public static PsiElement getFileInsertPosition(final PsiFile file) { - return getInsertPosition(file, null, null); - } - - @Nullable - private static PsiElement getInsertPosition(@Nonnull PsiElement insertParent, - @Nullable PyImportStatementBase newImport, - @Nullable ImportPriority priority) { - PsiElement feeler = insertParent.getFirstChild(); - if (feeler == null) { - return null; - } - // skip initial comments and whitespace and try to get just below the last import stmt - boolean skippedOverImports = false; - boolean skippedOverDoc = false; - PsiElement seeker = feeler; - final boolean isInjected = InjectedLanguageManager.getInstance(feeler.getProject()).isInjectedFragment(feeler.getContainingFile()); - PyImportStatementBase importAbove = null, importBelow = null; - do { - if (feeler instanceof PyImportStatementBase && !isInjected) { - final PyImportStatementBase existingImport = (PyImportStatementBase)feeler; - if (priority != null && newImport != null) { - if (shouldInsertBefore(newImport, existingImport, priority)) { - importBelow = existingImport; - break; - } - else { - importAbove = existingImport; - } - } - seeker = feeler; - feeler = feeler.getNextSibling(); - skippedOverImports = true; - } - else if (PyUtil.instanceOf(feeler, PsiWhiteSpace.class, PsiComment.class)) { - seeker = feeler; - feeler = feeler.getNextSibling(); - } - // maybe we arrived at the doc comment stmt; skip over it, too - else if (!skippedOverImports && !skippedOverDoc && insertParent instanceof PyFile) { - // this gives the literal; its parent is the expr seeker may have encountered - final PsiElement docElem = DocStringUtil.findDocStringExpression((PyElement)insertParent); - if (docElem != null && docElem.getParent() == feeler) { - feeler = feeler.getNextSibling(); - seeker = feeler; // skip over doc even if there's nothing below it - skippedOverDoc = true; - } - else { - break; // not a doc comment, stop on it + @RequiredWriteAction + public static void addLocalFromImportStatement(@Nonnull PsiElement element, @Nonnull String qualifier, @Nonnull String name) { + PyElementGenerator generator = PyElementGenerator.getInstance(element.getProject()); + LanguageLevel languageLevel = LanguageLevel.forElement(element); + + PsiElement anchor = getLocalInsertPosition(element); + PsiElement parentElement = sure(anchor).getParent(); + if (parentElement != null) { + parentElement.addBefore(generator.createFromImportStatement(languageLevel, qualifier, name, null), anchor); } - } - else { - break; // some other statement, stop - } - } - while (feeler != null); - final ImportPriority priorityAbove = importAbove != null ? getImportPriority(importAbove) : null; - final ImportPriority priorityBelow = importBelow != null ? getImportPriority(importBelow) : null; - if (newImport != null && (priorityAbove == null || priorityAbove.compareTo(priority) < 0)) { - newImport.putCopyableUserData(PyBlock.IMPORT_GROUP_BEGIN, true); - } - if (priorityBelow != null) { - // actually not necessary because existing import with higher priority (i.e. lower import group) - // probably should have IMPORT_GROUP_BEGIN flag already, but we add it anyway just for safety - if (priorityBelow.compareTo(priority) > 0) { - importBelow.putCopyableUserData(PyBlock.IMPORT_GROUP_BEGIN, true); - } - else if (priorityBelow == priority) { - importBelow.putCopyableUserData(PyBlock.IMPORT_GROUP_BEGIN, null); - } - } - return seeker; - } - - private static boolean shouldInsertBefore(@Nullable PyImportStatementBase newImport, - @Nonnull PyImportStatementBase existingImport, - @Nonnull ImportPriority priority) { - final ImportPriority existingImportPriority = getImportPriority(existingImport); - final int byPriority = priority.compareTo(existingImportPriority); - if (byPriority != 0) { - return byPriority < 0; - } - if (newImport == null) { - return false; - } - return getSameGroupImportsComparator(existingImport.getProject()).compare(newImport, existingImport) < 0; - } - - @Nonnull - public static ImportPriority getImportPriority(@Nonnull PyImportStatementBase importStatement) { - final PsiElement resolved; - if (importStatement instanceof PyFromImportStatement) { - final PyFromImportStatement fromImportStatement = (PyFromImportStatement)importStatement; - if (fromImportStatement.isFromFuture()) { - return ImportPriority.FUTURE; - } - if (fromImportStatement.getRelativeLevel() > 0) { - return ImportPriority.PROJECT; - } - resolved = fromImportStatement.resolveImportSource(); - } - else { - final PyImportElement firstImportElement = ArrayUtil.getFirstElement(importStatement.getImportElements()); - if (firstImportElement == null) { - return UNRESOLVED_SYMBOL_PRIORITY; - } - resolved = firstImportElement.resolve(); - } - if (resolved == null) { - return UNRESOLVED_SYMBOL_PRIORITY; } - final PsiFileSystemItem resolvedFileOrDir; - if (resolved instanceof PsiDirectory) { - resolvedFileOrDir = (PsiFileSystemItem)resolved; - } - // resolved symbol may be PsiPackage in Jython - else if (resolved instanceof PsiDirectoryContainer) { - resolvedFileOrDir = ArrayUtil.getFirstElement(((PsiDirectoryContainer)resolved).getDirectories()); - } - else { - resolvedFileOrDir = resolved.getContainingFile(); + @Nullable + public static PsiElement getLocalInsertPosition(@Nonnull PsiElement anchor) { + return PsiTreeUtil.getParentOfType(anchor, PyStatement.class, false); } - if (resolvedFileOrDir == null) { - return UNRESOLVED_SYMBOL_PRIORITY; + @Nullable + @RequiredReadAction + public static PsiElement getFileInsertPosition(PsiFile file) { + return getInsertPosition(file, null, null); } - return getImportPriority(importStatement, resolvedFileOrDir); - } + @Nullable + @RequiredReadAction + private static PsiElement getInsertPosition( + @Nonnull PsiElement insertParent, + @Nullable PyImportStatementBase newImport, + @Nullable ImportPriority priority + ) { + PsiElement feeler = insertParent.getFirstChild(); + if (feeler == null) { + return null; + } + // skip initial comments and whitespace and try to get just below the last import stmt + boolean skippedOverImports = false; + boolean skippedOverDoc = false; + PsiElement seeker = feeler; + boolean isInjected = InjectedLanguageManager.getInstance(feeler.getProject()).isInjectedFragment(feeler.getContainingFile()); + PyImportStatementBase importAbove = null, importBelow = null; + do { + if (feeler instanceof PyImportStatementBase && !isInjected) { + PyImportStatementBase existingImport = (PyImportStatementBase) feeler; + if (priority != null && newImport != null) { + if (shouldInsertBefore(newImport, existingImport, priority)) { + importBelow = existingImport; + break; + } + else { + importAbove = existingImport; + } + } + seeker = feeler; + feeler = feeler.getNextSibling(); + skippedOverImports = true; + } + else if (PyUtil.instanceOf(feeler, PsiWhiteSpace.class, PsiComment.class)) { + seeker = feeler; + feeler = feeler.getNextSibling(); + } + // maybe we arrived at the doc comment stmt; skip over it, too + else if (!skippedOverImports && !skippedOverDoc && insertParent instanceof PyFile) { + // this gives the literal; its parent is the expr seeker may have encountered + PsiElement docElem = DocStringUtil.findDocStringExpression((PyElement) insertParent); + if (docElem != null && docElem.getParent() == feeler) { + feeler = feeler.getNextSibling(); + seeker = feeler; // skip over doc even if there's nothing below it + skippedOverDoc = true; + } + else { + break; // not a doc comment, stop on it + } + } + else { + break; // some other statement, stop + } + } + while (feeler != null); - @Nonnull - public static ImportPriority getImportPriority(@Nonnull PsiElement importLocation, @Nonnull PsiFileSystemItem toImport) { - final VirtualFile vFile = toImport.getVirtualFile(); - if (vFile == null) { - return UNRESOLVED_SYMBOL_PRIORITY; - } - final ProjectRootManager projectRootManager = ProjectRootManager.getInstance(toImport.getProject()); - final ProjectFileIndex fileIndex = projectRootManager.getFileIndex(); - if (fileIndex.isInContent(vFile) && !fileIndex.isInLibraryClasses(vFile)) { - return ImportPriority.PROJECT; - } - final Module module = ModuleUtilCore.findModuleForPsiElement(importLocation); - final Sdk pythonSdk = module != null ? PythonSdkType.findPythonSdk(module) : null; - - return PythonSdkType.isStdLib(vFile, pythonSdk) ? ImportPriority.BUILTIN : ImportPriority.THIRD_PARTY; - } - - /** - * Adds an import statement, if it doesn't exist yet, presumably below all other initial imports in the file. - * - * @param file where to operate - * @param name which to import (qualified is OK) - * @param asName optional name for 'as' clause - * @param anchor place where the imported name was used. It will be used to determine proper block where new import should be inserted, - * e.g. inside conditional block or try/except statement. Also if anchor is another import statement, new import statement - * will be inserted right after it. - * @return whether import statement was actually added - */ - public static boolean addImportStatement(@Nonnull PsiFile file, - @Nonnull String name, - @Nullable String asName, - @Nullable ImportPriority priority, - @Nullable PsiElement anchor) { - if (!(file instanceof PyFile)) { - return false; - } - final List existingImports = ((PyFile)file).getImportTargets(); - for (PyImportElement element : existingImports) { - final QualifiedName qName = element.getImportedQName(); - if (qName != null && name.equals(qName.toString())) { - if ((asName != null && asName.equals(element.getAsName())) || (asName == null && element.getAsName() == null)) { - return false; - } - } - } + ImportPriority priorityAbove = importAbove != null ? getImportPriority(importAbove) : null; + ImportPriority priorityBelow = importBelow != null ? getImportPriority(importBelow) : null; + if (newImport != null && (priorityAbove == null || priorityAbove.compareTo(priority) < 0)) { + newImport.putCopyableUserData(PyBlock.IMPORT_GROUP_BEGIN, true); + } + if (priorityBelow != null) { + // actually not necessary because existing import with higher priority (i.e. lower import group) + // probably should have IMPORT_GROUP_BEGIN flag already, but we add it anyway just for safety + if (priorityBelow.compareTo(priority) > 0) { + importBelow.putCopyableUserData(PyBlock.IMPORT_GROUP_BEGIN, true); + } + else if (priorityBelow == priority) { + importBelow.putCopyableUserData(PyBlock.IMPORT_GROUP_BEGIN, null); + } + } + return seeker; + } + + @RequiredReadAction + private static boolean shouldInsertBefore( + @Nullable PyImportStatementBase newImport, + @Nonnull PyImportStatementBase existingImport, + @Nonnull ImportPriority priority + ) { + ImportPriority existingImportPriority = getImportPriority(existingImport); + int byPriority = priority.compareTo(existingImportPriority); + if (byPriority != 0) { + return byPriority < 0; + } + if (newImport == null) { + return false; + } + return getSameGroupImportsComparator(existingImport.getProject()).compare(newImport, existingImport) < 0; + } + + @Nonnull + @RequiredReadAction + public static ImportPriority getImportPriority(@Nonnull PyImportStatementBase importStatement) { + PsiElement resolved; + if (importStatement instanceof PyFromImportStatement fromImportStatement) { + if (fromImportStatement.isFromFuture()) { + return ImportPriority.FUTURE; + } + if (fromImportStatement.getRelativeLevel() > 0) { + return ImportPriority.PROJECT; + } + resolved = fromImportStatement.resolveImportSource(); + } + else { + PyImportElement firstImportElement = ArrayUtil.getFirstElement(importStatement.getImportElements()); + if (firstImportElement == null) { + return UNRESOLVED_SYMBOL_PRIORITY; + } + resolved = firstImportElement.resolve(); + } + if (resolved == null) { + return UNRESOLVED_SYMBOL_PRIORITY; + } - final PyElementGenerator generator = PyElementGenerator.getInstance(file.getProject()); - final LanguageLevel languageLevel = LanguageLevel.forElement(file); - final PyImportStatement importNodeToInsert = generator.createImportStatement(languageLevel, name, asName); - final PyImportStatementBase importStatement = PsiTreeUtil.getParentOfType(anchor, PyImportStatementBase.class, false); - final PsiElement insertParent = - importStatement != null && importStatement.getContainingFile() == file ? importStatement.getParent() : file; - try { - if (anchor instanceof PyImportStatementBase) { - insertParent.addAfter(importNodeToInsert, anchor); - } - else { - insertParent.addBefore(importNodeToInsert, getInsertPosition(insertParent, importNodeToInsert, priority)); - } - } - catch (IncorrectOperationException e) { - LOG.error(e); - } - return true; - } - - /** - * Adds a new {@link PyFromImportStatement} statement within other top-level imports or as specified by anchor. - * - * @param file where to operate - * @param from import source (reference after {@code from} keyword) - * @param name imported name (identifier after {@code import} keyword) - * @param asName optional alias (identifier after {@code as} keyword) - * @param anchor place where the imported name was used. It will be used to determine proper block where new import should be inserted, - * e.g. inside conditional block or try/except statement. Also if anchor is another import statement, new import statement - * will be inserted right after it. - * @see #addOrUpdateFromImportStatement - */ - public static void addFromImportStatement(@Nonnull PsiFile file, - @Nonnull String from, - @Nonnull String name, - @Nullable String asName, - @Nullable ImportPriority priority, - @Nullable PsiElement anchor) { - final PyElementGenerator generator = PyElementGenerator.getInstance(file.getProject()); - final LanguageLevel languageLevel = LanguageLevel.forElement(file); - final PyFromImportStatement newImport = generator.createFromImportStatement(languageLevel, from, name, asName); - addFromImportStatement(file, newImport, priority, anchor); - } - - /** - * Adds a new {@link PyFromImportStatement} statement within other top-level imports or as specified by anchor. - * - * @param file where to operate - * @param newImport new "from import" statement to insert. It may be generated, because it won't be used for resolving anyway. - * You might want to use overloaded version of this method to generate such statement automatically. - * @param anchor place where the imported name was used. It will be used to determine proper block where new import should be inserted, - * e.g. inside conditional block or try/except statement. Also if anchor is another import statement, new import statement - * will be inserted right after it. - * @see #addFromImportStatement(PsiFile, String, String, String, ImportPriority, PsiElement) - * @see #addFromImportStatement - */ - public static void addFromImportStatement(@Nonnull PsiFile file, - @Nonnull PyFromImportStatement newImport, - @Nullable ImportPriority priority, - @Nullable PsiElement anchor) { - try { - final PyImportStatementBase parentImport = PsiTreeUtil.getParentOfType(anchor, PyImportStatementBase.class, false); - final PsiElement insertParent; - if (parentImport != null && parentImport.getContainingFile() == file) { - insertParent = parentImport.getParent(); - } - else { - insertParent = file; - } - if (InjectedLanguageManager.getInstance(file.getProject()).isInjectedFragment(file)) { - final PsiElement element = insertParent.addBefore(newImport, getInsertPosition(insertParent, newImport, priority)); - PsiElement whitespace = element.getNextSibling(); - if (!(whitespace instanceof PsiWhiteSpace)) { - whitespace = PsiParserFacade.SERVICE.getInstance(file.getProject()).createWhiteSpaceFromText(" >>> "); - } - insertParent.addBefore(whitespace, element); - } - else { - if (anchor instanceof PyImportStatementBase) { - insertParent.addAfter(newImport, anchor); + PsiFileSystemItem resolvedFileOrDir; + if (resolved instanceof PsiDirectory directory) { + resolvedFileOrDir = directory; + } + // resolved symbol may be PsiPackage in Jython + else if (resolved instanceof PsiDirectoryContainer directoryContainer) { + resolvedFileOrDir = ArrayUtil.getFirstElement(directoryContainer.getDirectories()); } else { - insertParent.addBefore(newImport, getInsertPosition(insertParent, newImport, priority)); + resolvedFileOrDir = resolved.getContainingFile(); } - } - } - catch (IncorrectOperationException e) { - LOG.error(e); + + if (resolvedFileOrDir == null) { + return UNRESOLVED_SYMBOL_PRIORITY; + } + + return getImportPriority(importStatement, resolvedFileOrDir); } - } - - /** - * Adds new {@link PyFromImportStatement} in file or append {@link PyImportElement} to - * existing from import statement. - * - * @param file module where import will be added - * @param from import source (reference after {@code from} keyword) - * @param name imported name (identifier after {@code import} keyword) - * @param asName optional alias (identifier after {@code as} keyword) - * @param priority optional import priority used to sort imports - * @param anchor place where the imported name was used. It will be used to determine proper block where new import should be inserted, - * e.g. inside conditional block or try/except statement. Also if anchor is another import statement, new import statement - * will be inserted right after it. - * @return whether import was actually added - * @see #addFromImportStatement - */ - public static boolean addOrUpdateFromImportStatement(@Nonnull PsiFile file, - @Nonnull String from, - @Nonnull String name, - @Nullable String asName, - @Nullable ImportPriority priority, - @Nullable PsiElement anchor) { - final List existingImports = ((PyFile)file).getFromImports(); - for (PyFromImportStatement existingImport : existingImports) { - if (existingImport.isStarImport()) { - continue; - } - final QualifiedName qName = existingImport.getImportSourceQName(); - if (qName != null && qName.toString().equals(from) && existingImport.getRelativeLevel() == 0) { - for (PyImportElement el : existingImport.getImportElements()) { - final QualifiedName importedQName = el.getImportedQName(); - if (importedQName != null && StringUtil.equals(name, importedQName.toString()) && StringUtil.equals(asName, el.getAsName())) { + + @Nonnull + @RequiredReadAction + public static ImportPriority getImportPriority(@Nonnull PsiElement importLocation, @Nonnull PsiFileSystemItem toImport) { + VirtualFile vFile = toImport.getVirtualFile(); + if (vFile == null) { + return UNRESOLVED_SYMBOL_PRIORITY; + } + ProjectRootManager projectRootManager = ProjectRootManager.getInstance(toImport.getProject()); + ProjectFileIndex fileIndex = projectRootManager.getFileIndex(); + if (fileIndex.isInContent(vFile) && !fileIndex.isInLibraryClasses(vFile)) { + return ImportPriority.PROJECT; + } + Module module = importLocation.getModule(); + Sdk pythonSdk = module != null ? PythonSdkType.findPythonSdk(module) : null; + + return PythonSdkType.isStdLib(vFile, pythonSdk) ? ImportPriority.BUILTIN : ImportPriority.THIRD_PARTY; + } + + /** + * Adds an import statement, if it doesn't exist yet, presumably below all other initial imports in the file. + * + * @param file where to operate + * @param name which to import (qualified is OK) + * @param asName optional name for 'as' clause + * @param anchor place where the imported name was used. It will be used to determine proper block where new import should be inserted, + * e.g. inside conditional block or try/except statement. Also if anchor is another import statement, new import statement + * will be inserted right after it. + * @return whether import statement was actually added + */ + @RequiredWriteAction + public static boolean addImportStatement( + @Nonnull PsiFile file, + @Nonnull String name, + @Nullable String asName, + @Nullable ImportPriority priority, + @Nullable PsiElement anchor + ) { + if (!(file instanceof PyFile pyFile)) { return false; - } } - final PyElementGenerator generator = PyElementGenerator.getInstance(file.getProject()); - final PyImportElement importElement = generator.createImportElement(LanguageLevel.forElement(file), name); - existingImport.add(importElement); - return false; - } - } - addFromImportStatement(file, from, name, asName, priority, anchor); - return true; - } - - /** - * Adds either {@link PyFromImportStatement} or {@link PyImportStatement} - * to specified target depending on user preferences and whether it's possible to import element via "from" form of import - * (e.g. consider top level module). - * - * @param target element import is pointing to - * @param file file where import will be inserted - * @param element used to determine where to insert import - * @see PyCodeInsightSettings#PREFER_FROM_IMPORT - * @see #addImportStatement - * @see #addOrUpdateFromImportStatement - */ - public static void addImport(final PsiNamedElement target, final PsiFile file, final PyElement element) { - final boolean useQualified = !PyCodeInsightSettings.getInstance().PREFER_FROM_IMPORT; - final PsiFileSystemItem toImport = - target instanceof PsiFileSystemItem ? ((PsiFileSystemItem)target).getParent() : target.getContainingFile(); - if (toImport == null) { - return; - } - final ImportPriority priority = getImportPriority(file, toImport); - final QualifiedName qName = QualifiedNameFinder.findCanonicalImportPath(target, element); - if (qName == null) { - return; - } - String path = qName.toString(); - if (target instanceof PsiFileSystemItem && qName.getComponentCount() == 1) { - addImportStatement(file, path, null, priority, element); + List existingImports = pyFile.getImportTargets(); + for (PyImportElement element : existingImports) { + QualifiedName qName = element.getImportedQName(); + if (qName != null && name.equals(qName.toString())) { + if ((asName != null && asName.equals(element.getAsName())) || (asName == null && element.getAsName() == null)) { + return false; + } + } + } + + PyElementGenerator generator = PyElementGenerator.getInstance(file.getProject()); + LanguageLevel languageLevel = LanguageLevel.forElement(file); + PyImportStatement importNodeToInsert = generator.createImportStatement(languageLevel, name, asName); + PyImportStatementBase importStatement = PsiTreeUtil.getParentOfType(anchor, PyImportStatementBase.class, false); + PsiElement insertParent = + importStatement != null && importStatement.getContainingFile() == file ? importStatement.getParent() : file; + try { + if (anchor instanceof PyImportStatementBase) { + insertParent.addAfter(importNodeToInsert, anchor); + } + else { + insertParent.addBefore(importNodeToInsert, getInsertPosition(insertParent, importNodeToInsert, priority)); + } + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + return true; + } + + /** + * Adds a new {@link PyFromImportStatement} statement within other top-level imports or as specified by anchor. + * + * @param file where to operate + * @param from import source (reference after {@code from} keyword) + * @param name imported name (identifier after {@code import} keyword) + * @param asName optional alias (identifier after {@code as} keyword) + * @param anchor place where the imported name was used. It will be used to determine proper block where new import should be inserted, + * e.g. inside conditional block or try/except statement. Also if anchor is another import statement, new import statement + * will be inserted right after it. + * @see #addOrUpdateFromImportStatement + */ + @RequiredWriteAction + public static void addFromImportStatement( + @Nonnull PsiFile file, + @Nonnull String from, + @Nonnull String name, + @Nullable String asName, + @Nullable ImportPriority priority, + @Nullable PsiElement anchor + ) { + PyElementGenerator generator = PyElementGenerator.getInstance(file.getProject()); + LanguageLevel languageLevel = LanguageLevel.forElement(file); + PyFromImportStatement newImport = generator.createFromImportStatement(languageLevel, from, name, asName); + addFromImportStatement(file, newImport, priority, anchor); + } + + /** + * Adds a new {@link PyFromImportStatement} statement within other top-level imports or as specified by anchor. + * + * @param file where to operate + * @param newImport new "from import" statement to insert. It may be generated, because it won't be used for resolving anyway. + * You might want to use overloaded version of this method to generate such statement automatically. + * @param anchor place where the imported name was used. It will be used to determine proper block where new import should be inserted, + * e.g. inside conditional block or try/except statement. Also if anchor is another import statement, new import statement + * will be inserted right after it. + * @see #addFromImportStatement(PsiFile, String, String, String, ImportPriority, PsiElement) + * @see #addFromImportStatement + */ + @RequiredWriteAction + public static void addFromImportStatement( + @Nonnull PsiFile file, + @Nonnull PyFromImportStatement newImport, + @Nullable ImportPriority priority, + @Nullable PsiElement anchor + ) { + try { + PyImportStatementBase parentImport = PsiTreeUtil.getParentOfType(anchor, PyImportStatementBase.class, false); + PsiElement insertParent; + if (parentImport != null && parentImport.getContainingFile() == file) { + insertParent = parentImport.getParent(); + } + else { + insertParent = file; + } + if (InjectedLanguageManager.getInstance(file.getProject()).isInjectedFragment(file)) { + PsiElement element = insertParent.addBefore(newImport, getInsertPosition(insertParent, newImport, priority)); + PsiElement whitespace = element.getNextSibling(); + if (!(whitespace instanceof PsiWhiteSpace)) { + whitespace = PsiParserFacade.SERVICE.getInstance(file.getProject()).createWhiteSpaceFromText(" >>> "); + } + insertParent.addBefore(whitespace, element); + } + else if (anchor instanceof PyImportStatementBase) { + insertParent.addAfter(newImport, anchor); + } + else { + insertParent.addBefore(newImport, getInsertPosition(insertParent, newImport, priority)); + } + } + catch (IncorrectOperationException e) { + LOG.error(e); + } } - else { - final QualifiedName toImportQName = QualifiedNameFinder.findCanonicalImportPath(toImport, element); - if (toImportQName == null) { - return; - } - if (useQualified) { - addImportStatement(file, path, null, priority, element); - final PyElementGenerator elementGenerator = PyElementGenerator.getInstance(file.getProject()); - final String targetName = PyUtil.getElementNameWithoutExtension(target); - element.replace(elementGenerator.createExpressionFromText(LanguageLevel.forElement(target), toImportQName + "." + targetName)); - } - else { - final String name = target.getName(); - if (name != null) { - addOrUpdateFromImportStatement(file, toImportQName.toString(), name, null, priority, element); - } - } + + /** + * Adds new {@link PyFromImportStatement} in file or append {@link PyImportElement} to + * existing from import statement. + * + * @param file module where import will be added + * @param from import source (reference after {@code from} keyword) + * @param name imported name (identifier after {@code import} keyword) + * @param asName optional alias (identifier after {@code as} keyword) + * @param priority optional import priority used to sort imports + * @param anchor place where the imported name was used. It will be used to determine proper block where new import should be inserted, + * e.g. inside conditional block or try/except statement. Also if anchor is another import statement, new import statement + * will be inserted right after it. + * @return whether import was actually added + * @see #addFromImportStatement + */ + @RequiredWriteAction + public static boolean addOrUpdateFromImportStatement( + @Nonnull PsiFile file, + @Nonnull String from, + @Nonnull String name, + @Nullable String asName, + @Nullable ImportPriority priority, + @Nullable PsiElement anchor + ) { + List existingImports = ((PyFile) file).getFromImports(); + for (PyFromImportStatement existingImport : existingImports) { + if (existingImport.isStarImport()) { + continue; + } + QualifiedName qName = existingImport.getImportSourceQName(); + if (qName != null && qName.toString().equals(from) && existingImport.getRelativeLevel() == 0) { + for (PyImportElement el : existingImport.getImportElements()) { + QualifiedName importedQName = el.getImportedQName(); + if (importedQName != null + && StringUtil.equals(name, importedQName.toString()) + && StringUtil.equals(asName, el.getAsName())) { + return false; + } + } + PyElementGenerator generator = PyElementGenerator.getInstance(file.getProject()); + PyImportElement importElement = generator.createImportElement(LanguageLevel.forElement(file), name); + existingImport.add(importElement); + return false; + } + } + addFromImportStatement(file, from, name, asName, priority, anchor); + return true; + } + + /** + * Adds either {@link PyFromImportStatement} or {@link PyImportStatement} + * to specified target depending on user preferences and whether it's possible to import element via "from" form of import + * (e.g. consider top level module). + * + * @param target element import is pointing to + * @param file file where import will be inserted + * @param element used to determine where to insert import + * @see PyCodeInsightSettings#PREFER_FROM_IMPORT + * @see #addImportStatement + * @see #addOrUpdateFromImportStatement + */ + @RequiredWriteAction + public static void addImport(PsiNamedElement target, PsiFile file, PyElement element) { + boolean useQualified = !PyCodeInsightSettings.getInstance().PREFER_FROM_IMPORT; + PsiFileSystemItem toImport = target instanceof PsiFileSystemItem fsItem ? fsItem.getParent() : target.getContainingFile(); + if (toImport == null) { + return; + } + ImportPriority priority = getImportPriority(file, toImport); + QualifiedName qName = QualifiedNameFinder.findCanonicalImportPath(target, element); + if (qName == null) { + return; + } + String path = qName.toString(); + if (target instanceof PsiFileSystemItem && qName.getComponentCount() == 1) { + addImportStatement(file, path, null, priority, element); + } + else { + QualifiedName toImportQName = QualifiedNameFinder.findCanonicalImportPath(toImport, element); + if (toImportQName == null) { + return; + } + if (useQualified) { + addImportStatement(file, path, null, priority, element); + PyElementGenerator elementGenerator = PyElementGenerator.getInstance(file.getProject()); + String targetName = PyUtil.getElementNameWithoutExtension(target); + element.replace(elementGenerator.createExpressionFromText( + LanguageLevel.forElement(target), + toImportQName + "." + targetName + )); + } + else { + String name = target.getName(); + if (name != null) { + addOrUpdateFromImportStatement(file, toImportQName.toString(), name, null, priority, element); + } + } + } } - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/console/PyOpenDebugConsoleAction.java b/python-impl/src/main/java/com/jetbrains/python/impl/console/PyOpenDebugConsoleAction.java index 6abdc3b9..1d1e962d 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/console/PyOpenDebugConsoleAction.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/console/PyOpenDebugConsoleAction.java @@ -53,15 +53,17 @@ public void update(AnActionEvent e) { @Override @RequiredUIAccess public void actionPerformed(AnActionEvent e) { - Project project = e.getData(Project.KEY); - if (project != null) { - selectRunningProcess(e.getDataContext(), project, view -> { + Project project = e.getRequiredData(Project.KEY); + selectRunningProcess( + e.getDataContext(), + project, + view -> { view.enableConsole(false); ApplicationIdeFocusManager.getInstance() .getInstanceForProject(project) .requestFocus(view.getPydevConsoleView().getComponent(), true); - }); - } + } + ); } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocReference.java b/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocReference.java index 17bf7941..a52328c3 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocReference.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocReference.java @@ -28,6 +28,7 @@ import com.jetbrains.python.impl.psi.impl.references.PyReferenceImpl; import com.jetbrains.python.psi.resolve.*; import com.jetbrains.python.psi.types.TypeEvalContext; +import consulo.annotation.access.RequiredReadAction; import consulo.document.util.TextRange; import consulo.language.ast.ASTNode; import consulo.language.editor.annotation.HighlightSeverity; @@ -43,128 +44,134 @@ import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; + import java.util.ArrayList; import java.util.List; /** - * User : ktisha + * @author ktisha */ public class PyDocReference extends PyReferenceImpl { - public PyDocReference(PyQualifiedExpression element, @Nonnull PyResolveContext context) { - super(element, context); - } + public PyDocReference(PyQualifiedExpression element, @Nonnull PyResolveContext context) { + super(element, context); + } - @Override - public HighlightSeverity getUnresolvedHighlightSeverity(TypeEvalContext context) { - return HighlightSeverity.WARNING; - } + @Override + public HighlightSeverity getUnresolvedHighlightSeverity(TypeEvalContext context) { + return HighlightSeverity.WARNING; + } - @Nonnull - @Override - public ResolveResult[] multiResolve(boolean incompleteCode) { - ResolveResult[] results = super.multiResolve(incompleteCode); - if (results.length == 0) { - final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myElement.getProject()); - final PsiLanguageInjectionHost host = languageManager.getInjectionHost(myElement); - final String referencedName = myElement.getReferencedName(); - if (referencedName == null) { - return ResolveResult.EMPTY_ARRAY; - } + @Nonnull + @Override + @RequiredReadAction + public ResolveResult[] multiResolve(boolean incompleteCode) { + ResolveResult[] results = super.multiResolve(incompleteCode); + if (results.length == 0) { + InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myElement.getProject()); + PsiLanguageInjectionHost host = languageManager.getInjectionHost(myElement); + String referencedName = myElement.getReferencedName(); + if (referencedName == null) { + return ResolveResult.EMPTY_ARRAY; + } - if (host != null) { - final List> files = languageManager.getInjectedPsiFiles(host); - if (files != null) { - for (Pair pair : files) { - final PyResolveProcessor processor = new PyResolveProcessor(referencedName); + if (host != null) { + List> files = languageManager.getInjectedPsiFiles(host); + if (files != null) { + for (Pair pair : files) { + PyResolveProcessor processor = new PyResolveProcessor(referencedName); - PyResolveUtil.scopeCrawlUp(processor, (ScopeOwner)pair.getFirst(), referencedName, pair.getFirst()); - final List resultList = - getResultsFromProcessor(referencedName, processor, pair.getFirst(), pair.getFirst()); - if (resultList.size() > 0) { - List ret = RatedResolveResult.sorted(resultList); - return ret.toArray(new RatedResolveResult[ret.size()]); + PyResolveUtil.scopeCrawlUp(processor, (ScopeOwner) pair.getFirst(), referencedName, pair.getFirst()); + List resultList = + getResultsFromProcessor(referencedName, processor, pair.getFirst(), pair.getFirst()); + if (resultList.size() > 0) { + List ret = RatedResolveResult.sorted(resultList); + return ret.toArray(new RatedResolveResult[ret.size()]); + } + } + } + PyResolveProcessor processor = new PyResolveProcessor(referencedName); + ScopeOwner scopeOwner = getHostScopeOwner(); + if (scopeOwner != null) { + PsiFile topLevel = scopeOwner.getContainingFile(); + PyResolveUtil.scopeCrawlUp(processor, scopeOwner, referencedName, topLevel); + PsiElement referenceAnchor = getScopeControlFlowAnchor(host); + List resultList = + getResultsFromProcessor(referencedName, processor, referenceAnchor, topLevel); + if (resultList.size() > 0) { + List ret = RatedResolveResult.sorted(resultList); + return ret.toArray(new RatedResolveResult[ret.size()]); + } + } } - } - } - final PyResolveProcessor processor = new PyResolveProcessor(referencedName); - final ScopeOwner scopeOwner = getHostScopeOwner(); - if (scopeOwner != null) { - final PsiFile topLevel = scopeOwner.getContainingFile(); - PyResolveUtil.scopeCrawlUp(processor, scopeOwner, referencedName, topLevel); - final PsiElement referenceAnchor = getScopeControlFlowAnchor(host); - final List resultList = getResultsFromProcessor(referencedName, processor, referenceAnchor, topLevel); - if (resultList.size() > 0) { - final List ret = RatedResolveResult.sorted(resultList); - return ret.toArray(new RatedResolveResult[ret.size()]); - } } - } + return results; } - return results; - } - @Nullable - private PsiElement getScopeControlFlowAnchor(@Nonnull PsiLanguageInjectionHost host) { - return isInsideFormattedStringNode(host) ? PsiTreeUtil.getParentOfType(host, PyStatement.class) : null; - } + @Nullable + @RequiredReadAction + private PsiElement getScopeControlFlowAnchor(@Nonnull PsiLanguageInjectionHost host) { + return isInsideFormattedStringNode(host) ? PsiTreeUtil.getParentOfType(host, PyStatement.class) : null; + } - private boolean isInsideFormattedStringNode(@Nonnull PsiLanguageInjectionHost host) { - if (host instanceof PyStringLiteralExpression) { - final ASTNode node = findContainingStringNode(getElement(), (PyStringLiteralExpression)host); - return node != null && new StringNodeInfo(node).isFormatted(); + @RequiredReadAction + private boolean isInsideFormattedStringNode(@Nonnull PsiLanguageInjectionHost host) { + if (host instanceof PyStringLiteralExpression stringLiteral) { + ASTNode node = findContainingStringNode(getElement(), stringLiteral); + return node != null && new StringNodeInfo(node).isFormatted(); + } + return false; } - return false; - } - @Nullable - private static ASTNode findContainingStringNode(@Nonnull PsiElement injectedElement, @Nonnull PyStringLiteralExpression host) { - final InjectedLanguageManager manager = InjectedLanguageManager.getInstance(host.getProject()); - final List> files = manager.getInjectedPsiFiles(host); - if (files != null) { - final PsiFile injectedFile = injectedElement.getContainingFile(); - final Pair first = ContainerUtil.find(files, pair -> pair.getFirst() == injectedFile); - if (first != null) { - final int hostOffset = -host.getTextRange().getStartOffset(); - for (ASTNode node : host.getStringNodes()) { - final TextRange relativeNodeRange = node.getTextRange().shiftRight(hostOffset); - if (relativeNodeRange.contains(first.getSecond())) { - return node; - } + @Nullable + @RequiredReadAction + private static ASTNode findContainingStringNode(@Nonnull PsiElement injectedElement, @Nonnull PyStringLiteralExpression host) { + InjectedLanguageManager manager = InjectedLanguageManager.getInstance(host.getProject()); + List> files = manager.getInjectedPsiFiles(host); + if (files != null) { + PsiFile injectedFile = injectedElement.getContainingFile(); + Pair first = ContainerUtil.find(files, pair -> pair.getFirst() == injectedFile); + if (first != null) { + int hostOffset = -host.getTextRange().getStartOffset(); + for (ASTNode node : host.getStringNodes()) { + TextRange relativeNodeRange = node.getTextRange().shiftRight(hostOffset); + if (relativeNodeRange.contains(first.getSecond())) { + return node; + } + } + } } - } + return null; } - return null; - } - @Nonnull - public Object[] getVariants() { - final ArrayList ret = Lists.newArrayList(super.getVariants()); - final PsiElement originalElement = CompletionUtilCore.getOriginalElement(myElement); - final PyQualifiedExpression element = - originalElement instanceof PyQualifiedExpression ? (PyQualifiedExpression)originalElement : myElement; + @Nonnull + @Override + @RequiredReadAction + public Object[] getVariants() { + ArrayList ret = Lists.newArrayList(super.getVariants()); + PsiElement originalElement = CompletionUtilCore.getOriginalElement(myElement); + PyQualifiedExpression element = originalElement instanceof PyQualifiedExpression qualifiedExpr ? qualifiedExpr : myElement; - final ScopeOwner scopeOwner = getHostScopeOwner(); - if (scopeOwner != null) { - final CompletionVariantsProcessor processor = new CompletionVariantsProcessor(element); - PyResolveUtil.scopeCrawlUp(processor, scopeOwner, null, null); - ret.addAll(processor.getResultList()); + ScopeOwner scopeOwner = getHostScopeOwner(); + if (scopeOwner != null) { + CompletionVariantsProcessor processor = new CompletionVariantsProcessor(element); + PyResolveUtil.scopeCrawlUp(processor, scopeOwner, null, null); + ret.addAll(processor.getResultList()); + } + return ret.toArray(); } - return ret.toArray(); - } - - @Nullable - private ScopeOwner getHostScopeOwner() { - final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myElement.getProject()); - final PsiLanguageInjectionHost host = languageManager.getInjectionHost(myElement); - if (host != null) { - final PsiFile file = host.getContainingFile(); - ScopeOwner result = ScopeUtil.getScopeOwner(host); - if (result == null && file instanceof ScopeOwner) { - result = (ScopeOwner)file; - } - return result; + @Nullable + private ScopeOwner getHostScopeOwner() { + InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(myElement.getProject()); + PsiLanguageInjectionHost host = languageManager.getInjectionHost(myElement); + if (host != null) { + PsiFile file = host.getContainingFile(); + ScopeOwner result = ScopeUtil.getScopeOwner(host); + if (result == null && file instanceof ScopeOwner scopeOwner) { + result = scopeOwner; + } + return result; + } + return null; } - return null; - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocstringFile.java b/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocstringFile.java index 3ea20515..f92430ad 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocstringFile.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocstringFile.java @@ -13,34 +13,39 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.documentation.doctest; +import consulo.annotation.access.RequiredReadAction; import consulo.language.inject.InjectedLanguageManager; import consulo.language.file.FileViewProvider; import consulo.language.psi.PsiLanguageInjectionHost; import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.impl.psi.impl.PyFileImpl; +import jakarta.annotation.Nonnull; /** - * User: ktisha + * @author ktisha */ public class PyDocstringFile extends PyFileImpl { + public PyDocstringFile(FileViewProvider viewProvider) { + super(viewProvider, PyDocstringLanguageDialect.INSTANCE); + } - public PyDocstringFile(FileViewProvider viewProvider) { - super(viewProvider, PyDocstringLanguageDialect.getInstance()); - } - - @Override - public String toString() { - return "DocstringFile:" + getName(); - } + @Override + @RequiredReadAction + public String toString() { + return "DocstringFile:" + getName(); + } - @Override - public LanguageLevel getLanguageLevel() { - final InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(getProject()); - final PsiLanguageInjectionHost host = languageManager.getInjectionHost(this); - if (host != null) return LanguageLevel.forElement(host.getContainingFile()); - return super.getLanguageLevel(); - } + @Nonnull + @Override + @RequiredReadAction + public LanguageLevel getLanguageLevel() { + InjectedLanguageManager languageManager = InjectedLanguageManager.getInstance(getProject()); + PsiLanguageInjectionHost host = languageManager.getInjectionHost(this); + if (host != null) { + return LanguageLevel.forElement(host.getContainingFile()); + } + return super.getLanguageLevel(); + } } \ No newline at end of file diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocstringLanguageDialect.java b/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocstringLanguageDialect.java index b58f88e8..15b78dd4 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocstringLanguageDialect.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocstringLanguageDialect.java @@ -21,18 +21,18 @@ import consulo.language.Language; /** - * User : ktisha + * @author ktisha */ public class PyDocstringLanguageDialect extends Language implements InjectableLanguage { - public static final PyDocstringLanguageDialect INSTANCE = new PyDocstringLanguageDialect(); + public static final PyDocstringLanguageDialect INSTANCE = new PyDocstringLanguageDialect(); - @Deprecated - public static PyDocstringLanguageDialect getInstance() { - return INSTANCE; - } + @Deprecated + public static PyDocstringLanguageDialect getInstance() { + return INSTANCE; + } - protected PyDocstringLanguageDialect() { - super(PythonLanguage.INSTANCE, "PyDocstring"); - putUserData(consulo.language.editor.impl.intention.QuickEditAction.EDIT_ACTION_AVAILABLE, false); - } + protected PyDocstringLanguageDialect() { + super(PythonLanguage.INSTANCE, "PyDocstring"); + putUserData(consulo.language.editor.impl.intention.QuickEditAction.EDIT_ACTION_AVAILABLE, false); + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocstringParserDefinition.java b/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocstringParserDefinition.java index e78e00fe..5836edd3 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocstringParserDefinition.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/documentation/doctest/PyDocstringParserDefinition.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.documentation.doctest; import com.jetbrains.python.impl.PythonParserDefinition; @@ -30,43 +29,46 @@ import jakarta.annotation.Nonnull; /** - * User : ktisha + * @author ktisha */ @ExtensionImpl public class PyDocstringParserDefinition extends PythonParserDefinition { - public static final IFileElementType PYTHON_DOCSTRING_FILE = new PyDocstringFileElementType(PyDocstringLanguageDialect.INSTANCE); + public static final IFileElementType PYTHON_DOCSTRING_FILE = new PyDocstringFileElementType(PyDocstringLanguageDialect.INSTANCE); - @Nonnull - @Override - public Language getLanguage() { - return PyDocstringLanguageDialect.getInstance(); - } + @Nonnull + @Override + public Language getLanguage() { + return PyDocstringLanguageDialect.INSTANCE; + } - @Nonnull - public Lexer createLexer(LanguageVersion languageVersion) { - return new PyDocstringLexer(); - } + @Nonnull + @Override + public Lexer createLexer(LanguageVersion languageVersion) { + return new PyDocstringLexer(); + } - @Nonnull - @Override - public PsiParser createParser(LanguageVersion languageVersion) { - return new PyDocstringParser(); - } + @Nonnull + @Override + public PsiParser createParser(LanguageVersion languageVersion) { + return new PyDocstringParser(); + } - @Nonnull - @Override - public TokenSet getWhitespaceTokens(LanguageVersion languageVersion) { - return TokenSet.orSet(super.getWhitespaceTokens(languageVersion), TokenSet.create(PyDocstringTokenTypes.DOTS)); - } + @Nonnull + @Override + public TokenSet getWhitespaceTokens(LanguageVersion languageVersion) { + return TokenSet.orSet(super.getWhitespaceTokens(languageVersion), TokenSet.create(PyDocstringTokenTypes.DOTS)); + } - @Override - public IFileElementType getFileNodeType() { - return PYTHON_DOCSTRING_FILE; - } + @Nonnull + @Override + public IFileElementType getFileNodeType() { + return PYTHON_DOCSTRING_FILE; + } - @Override - public PsiFile createFile(FileViewProvider viewProvider) { - return new PyDocstringFile(viewProvider); - } + @Nonnull + @Override + public PsiFile createFile(FileViewProvider viewProvider) { + return new PyDocstringFile(viewProvider); + } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/packaging/setupPy/SetupTaskChooserAction.java b/python-impl/src/main/java/com/jetbrains/python/impl/packaging/setupPy/SetupTaskChooserAction.java index ddcec4fd..6b79731c 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/packaging/setupPy/SetupTaskChooserAction.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/packaging/setupPy/SetupTaskChooserAction.java @@ -50,10 +50,7 @@ public SetupTaskChooserAction() { @Override @RequiredUIAccess public void actionPerformed(AnActionEvent e) { - final Module module = e.getData(Module.KEY); - if (module == null) { - return; - } + final Module module = e.getRequiredData(Module.KEY); Project project = module.getProject(); ListChooseByNameModel model = new ListChooseByNameModel<>(project, "Enter setup.py task name", "No tasks found", SetupTaskIntrospector.getTaskList(module)); diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/psi/impl/PyPrefixExpressionImpl.java b/python-impl/src/main/java/com/jetbrains/python/impl/psi/impl/PyPrefixExpressionImpl.java index 618045ef..eb193d57 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/psi/impl/PyPrefixExpressionImpl.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/psi/impl/PyPrefixExpressionImpl.java @@ -29,6 +29,7 @@ import com.jetbrains.python.psi.types.PyClassType; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; +import consulo.annotation.access.RequiredReadAction; import consulo.ide.impl.idea.util.containers.ContainerUtil; import consulo.language.ast.ASTNode; import consulo.language.psi.PsiElement; @@ -38,6 +39,7 @@ import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; + import java.util.ArrayList; import java.util.List; @@ -45,128 +47,131 @@ * @author yole */ public class PyPrefixExpressionImpl extends PyElementImpl implements PyPrefixExpression { - public PyPrefixExpressionImpl(ASTNode astNode) { - super(astNode); - } - - @Override - public PyExpression getOperand() { - return (PyExpression)childToPsi(PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens(), 0); - } - - @Nullable - public PsiElement getPsiOperator() { - final ASTNode node = getNode(); - final ASTNode child = node.findChildByType(PyElementTypes.UNARY_OPS); - return child != null ? child.getPsi() : null; - } - - @Nonnull - @Override - public PyElementType getOperator() { - final PsiElement op = getPsiOperator(); - assert op != null; - return (PyElementType)op.getNode().getElementType(); - } - - @Override - protected void acceptPyVisitor(PyElementVisitor pyVisitor) { - pyVisitor.visitPyPrefixExpression(this); - } - - @Override - public PsiReference getReference() { - return getReference(PyResolveContext.noImplicits()); - } - - @Nonnull - @Override - public PsiPolyVariantReference getReference(PyResolveContext context) { - return new PyOperatorReference(this, context); - } - - @Override - public PyType getType(@Nonnull TypeEvalContext context, @Nonnull TypeEvalContext.Key key) { - if (getOperator() == PyTokenTypes.NOT_KEYWORD) { - return PyBuiltinCache.getInstance(this).getBoolType(); + public PyPrefixExpressionImpl(ASTNode astNode) { + super(astNode); + } + + @Override + public PyExpression getOperand() { + return (PyExpression) childToPsi(PythonDialectsTokenSetProvider.INSTANCE.getExpressionTokens(), 0); + } + + @Nullable + @RequiredReadAction + public PsiElement getPsiOperator() { + ASTNode node = getNode(); + ASTNode child = node.findChildByType(PyElementTypes.UNARY_OPS); + return child != null ? child.getPsi() : null; + } + + @Nonnull + @Override + @RequiredReadAction + public PyElementType getOperator() { + PsiElement op = getPsiOperator(); + assert op != null; + return (PyElementType) op.getNode().getElementType(); } - final boolean isAwait = getOperator() == PyTokenTypes.AWAIT_KEYWORD; - if (isAwait) { - final PyExpression operand = getOperand(); - if (operand != null) { - final PyType operandType = context.getType(operand); - final PyType type = getGeneratorReturnType(operandType, context); - if (type != null) { - return type; + + @Override + protected void acceptPyVisitor(PyElementVisitor pyVisitor) { + pyVisitor.visitPyPrefixExpression(this); + } + + @Override + public PsiReference getReference() { + return getReference(PyResolveContext.noImplicits()); + } + + @Nonnull + @Override + public PsiPolyVariantReference getReference(PyResolveContext context) { + return new PyOperatorReference(this, context); + } + + @Override + @RequiredReadAction + public PyType getType(@Nonnull TypeEvalContext context, @Nonnull TypeEvalContext.Key key) { + if (getOperator() == PyTokenTypes.NOT_KEYWORD) { + return PyBuiltinCache.getInstance(this).getBoolType(); } - } + boolean isAwait = getOperator() == PyTokenTypes.AWAIT_KEYWORD; + if (isAwait) { + PyExpression operand = getOperand(); + if (operand != null) { + PyType operandType = context.getType(operand); + PyType type = getGeneratorReturnType(operandType, context); + if (type != null) { + return type; + } + } + } + PsiReference ref = getReference(PyResolveContext.noImplicits().withTypeEvalContext(context)); + PsiElement resolved = ref.resolve(); + if (resolved instanceof PyCallable callable) { + // TODO: Make PyPrefixExpression a PyCallSiteExpression, use getCallType() here and analyze it in PyTypeChecker.analyzeCallSite() + PyType returnType = callable.getReturnType(context, key); + return isAwait ? getGeneratorReturnType(returnType, context) : returnType; + } + return null; } - final PsiReference ref = getReference(PyResolveContext.noImplicits().withTypeEvalContext(context)); - final PsiElement resolved = ref.resolve(); - if (resolved instanceof PyCallable) { - // TODO: Make PyPrefixExpression a PyCallSiteExpression, use getCallType() here and analyze it in PyTypeChecker.analyzeCallSite() - final PyType returnType = ((PyCallable)resolved).getReturnType(context, key); - return isAwait ? getGeneratorReturnType(returnType, context) : returnType; + + @Override + public PyExpression getQualifier() { + return getOperand(); } - return null; - } - - @Override - public PyExpression getQualifier() { - return getOperand(); - } - - @Nullable - @Override - public QualifiedName asQualifiedName() { - return PyPsiUtils.asQualifiedName(this); - } - - @Override - public boolean isQualified() { - return getQualifier() != null; - } - - @Override - public String getReferencedName() { - PyElementType t = getOperator(); - if (t == PyTokenTypes.PLUS) { - return PyNames.POS; + + @Nullable + @Override + public QualifiedName asQualifiedName() { + return PyPsiUtils.asQualifiedName(this); } - else if (t == PyTokenTypes.MINUS) { - return PyNames.NEG; + + @Override + public boolean isQualified() { + return getQualifier() != null; } - return getOperator().getSpecialMethodName(); - } - - @Override - public ASTNode getNameElement() { - final PsiElement op = getPsiOperator(); - return op != null ? op.getNode() : null; - } - - @Nullable - private static PyType getGeneratorReturnType(@Nullable PyType type, @Nonnull TypeEvalContext context) { - if (type instanceof PyClassLikeType && type instanceof PyCollectionType) { - // TODO: Understand typing.Generator as well - final String classQName = ((PyClassLikeType)type).getClassQName(); - final PyCollectionType collectionType = (PyCollectionType)type; - if (PyNames.FAKE_GENERATOR.equals(classQName)) { - return ContainerUtil.getOrElse(collectionType.getElementTypes(context), 2, null); - } - else if (PyNames.FAKE_COROUTINE.equals(classQName) || type instanceof PyClassType && PyNames.AWAITABLE.equals(((PyClassType)type).getPyClass() - .getName())) { - return collectionType.getIteratedItemType(); - } + + @Override + @RequiredReadAction + public String getReferencedName() { + PyElementType t = getOperator(); + if (t == PyTokenTypes.PLUS) { + return PyNames.POS; + } + else if (t == PyTokenTypes.MINUS) { + return PyNames.NEG; + } + return getOperator().getSpecialMethodName(); + } + + @Override + @RequiredReadAction + public ASTNode getNameElement() { + PsiElement op = getPsiOperator(); + return op != null ? op.getNode() : null; } - else if (type instanceof PyUnionType) { - final List memberReturnTypes = new ArrayList<>(); - final PyUnionType unionType = (PyUnionType)type; - for (PyType member : unionType.getMembers()) { - memberReturnTypes.add(getGeneratorReturnType(member, context)); - } - return PyUnionType.union(memberReturnTypes); + + @Nullable + private static PyType getGeneratorReturnType(@Nullable PyType type, @Nonnull TypeEvalContext context) { + if (type instanceof PyClassLikeType classLikeType && type instanceof PyCollectionType collectionType) { + // TODO: Understand typing.Generator as well + String classQName = classLikeType.getClassQName(); + if (PyNames.FAKE_GENERATOR.equals(classQName)) { + return ContainerUtil.getOrElse(collectionType.getElementTypes(context), 2, null); + } + else if (PyNames.FAKE_COROUTINE.equals(classQName) + || type instanceof PyClassType classType && PyNames.AWAITABLE.equals(classType.getPyClass().getName())) { + return collectionType.getIteratedItemType(); + } + } + else if (type instanceof PyUnionType unionType) { + List memberReturnTypes = new ArrayList<>(); + for (PyType member : unionType.getMembers()) { + memberReturnTypes.add(getGeneratorReturnType(member, context)); + } + return PyUnionType.union(memberReturnTypes); + } + return null; } - return null; - } } diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/psi/impl/references/PyImportReference.java b/python-impl/src/main/java/com/jetbrains/python/impl/psi/impl/references/PyImportReference.java index 42be9a0b..bdd21f61 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/psi/impl/references/PyImportReference.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/psi/impl/references/PyImportReference.java @@ -27,6 +27,7 @@ import com.jetbrains.python.impl.psi.types.PyModuleType; import com.jetbrains.python.psi.types.PyType; import com.jetbrains.python.psi.types.TypeEvalContext; +import consulo.annotation.access.RequiredReadAction; import consulo.codeEditor.Editor; import consulo.document.Document; import consulo.language.ast.ASTNode; @@ -99,6 +100,7 @@ protected List resolveInner() @Nonnull @Override + @RequiredReadAction public Object[] getVariants() { // no completion in invalid import statements @@ -148,13 +150,12 @@ private static void replaceInsertHandler(Object[] variants, final InsertHandler< { continue; } - if(item instanceof LookupElementBuilder) + if(item instanceof LookupElementBuilder lookupElemBuilder) { - variants[i] = ((LookupElementBuilder) item).withInsertHandler(insertHandler); + variants[i] = lookupElemBuilder.withInsertHandler(insertHandler); } - else if(item instanceof PsiNamedElement) + else if(item instanceof PsiNamedElement element) { - final PsiNamedElement element = (PsiNamedElement) item; final String name = element.getName(); assert name != null; // it can't really have null name variants[i] = LookupElementBuilder.create(name).withIcon(IconDescriptorUpdaters.getIcon(element, 0)).withInsertHandler(insertHandler); @@ -165,13 +166,12 @@ else if(item instanceof PsiNamedElement) private static boolean hasChildPackages(Object item) { PsiElement itemElement = null; - if(item instanceof PsiElement) + if(item instanceof PsiElement element) { - itemElement = (PsiElement) item; + itemElement = element; } - else if(item instanceof LookupElement) + else if(item instanceof LookupElement lookupElement) { - LookupElement lookupElement = (LookupElement) item; final PsiElement element = lookupElement.getPsiElement(); if(element != null) { @@ -232,11 +232,10 @@ public Object[] execute() if(src != null) { PsiElement modCandidate = src.getReference().resolve(); - if(modCandidate instanceof PyExpression) + if(modCandidate instanceof PyExpression module) { addImportedNames(fromImport.getImportElements()); // don't propose already imported items - // try to collect submodules - PyExpression module = (PyExpression) modCandidate; + // try to collect sub-modules PyType qualifierType = myContext.getType(module); if(qualifierType != null) { @@ -246,9 +245,9 @@ public Object[] execute() } return myObjects.toArray(); } - else if(modCandidate instanceof PsiDirectory) + else if(modCandidate instanceof PsiDirectory directory) { - fillFromDir((PsiDirectory) modCandidate, ImportKeywordHandler.INSTANCE); + fillFromDir(directory, ImportKeywordHandler.INSTANCE); return myObjects.toArray(); } } @@ -350,10 +349,9 @@ private void fillFromDir(PsiDirectory targetDir, @Nullable InsertHandler resolveInner() { List res = new ArrayList(); - if (myElement instanceof PyBinaryExpression) { - final PyBinaryExpression expr = (PyBinaryExpression)myElement; + if (myElement instanceof PyBinaryExpression expr) { final String name = expr.getReferencedName(); if (PyNames.CONTAINS.equals(name)) { res = resolveMember(expr.getRightExpression(), name); @@ -57,18 +57,17 @@ protected List resolveInner() { resolveLeftAndRightOperators(res, expr, name); } } - else if (myElement instanceof PySubscriptionExpression) { - final PySubscriptionExpression expr = (PySubscriptionExpression)myElement; + else if (myElement instanceof PySubscriptionExpression expr) { res = resolveMember(expr.getOperand(), expr.getReferencedName()); } - else if (myElement instanceof PyPrefixExpression) { - final PyPrefixExpression expr = (PyPrefixExpression)myElement; + else if (myElement instanceof PyPrefixExpression expr) { res = resolveMember(expr.getOperand(), expr.getReferencedName()); } return res; } @Override + @RequiredReadAction public boolean isReferenceTo(PsiElement element) { if (element instanceof PyParameter || element instanceof PyTargetExpression) { return false; @@ -93,14 +92,14 @@ public String getReadableOperatorName() { @Nullable public PyExpression getReceiver() { - if (myElement instanceof PyBinaryExpression) { - return ((PyBinaryExpression)myElement).getLeftExpression(); + if (myElement instanceof PyBinaryExpression expr) { + return expr.getLeftExpression(); } - else if (myElement instanceof PySubscriptionExpression) { - return ((PySubscriptionExpression)myElement).getOperand(); + else if (myElement instanceof PySubscriptionExpression expr) { + return expr.getOperand(); } - else if (myElement instanceof PyPrefixExpression) { - return ((PyPrefixExpression)myElement).getOperand(); + else if (myElement instanceof PyPrefixExpression expr) { + return expr.getOperand(); } return null; } @@ -111,8 +110,7 @@ private static String leftToRightOperatorName(String name) { private static boolean isTrueDivEnabled(@Nonnull PyElement anchor) { final PsiFile file = anchor.getContainingFile(); - if (file instanceof PyFile) { - final PyFile pyFile = (PyFile)file; + if (file instanceof PyFile pyFile) { return FutureFeature.DIVISION.requiredAt(pyFile.getLanguageLevel()) || pyFile.hasImportFromFuture(FutureFeature.DIVISION); } return false; @@ -152,8 +150,8 @@ private List resolveMember(@Nullable PyExpression object, @N } else if (typeEvalContext.tracing()) { VirtualFile vFile = null; - if (type instanceof PyClassType) { - final PyClass pyClass = ((PyClassType)type).getPyClass(); + if (type instanceof PyClassType classType) { + final PyClass pyClass = classType.getPyClass(); vFile = pyClass.getContainingFile().getVirtualFile(); } type.resolveMember(name, object, AccessDirection.of(myElement), myContext); diff --git a/python-impl/src/main/java/com/jetbrains/python/impl/psi/impl/references/PyTargetReference.java b/python-impl/src/main/java/com/jetbrains/python/impl/psi/impl/references/PyTargetReference.java index 0f5356b6..e0695caa 100644 --- a/python-impl/src/main/java/com/jetbrains/python/impl/psi/impl/references/PyTargetReference.java +++ b/python-impl/src/main/java/com/jetbrains/python/impl/psi/impl/references/PyTargetReference.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.impl.psi.impl.references; +import consulo.annotation.access.RequiredReadAction; import jakarta.annotation.Nonnull; import consulo.language.psi.PsiElement; @@ -39,6 +39,7 @@ public PyTargetReference(PyQualifiedExpression element, PyResolveContext context @Nonnull @Override + @RequiredReadAction public ResolveResult[] multiResolve(boolean incompleteCode) { final ResolveResult[] results = super.multiResolve(incompleteCode); boolean shadowed = false; @@ -59,6 +60,7 @@ public ResolveResult[] multiResolve(boolean incompleteCode) { @Nonnull @Override + @RequiredReadAction public Object[] getVariants() { final PyImportElement importElement = PsiTreeUtil.getParentOfType(myElement, PyImportElement.class); // reference completion is useless in 'as' part of import statement (PY-2384) diff --git a/python-psi-api/src/main/java/com/jetbrains/python/codeInsight/PyCustomMember.java b/python-psi-api/src/main/java/com/jetbrains/python/codeInsight/PyCustomMember.java index 350633f0..eec43850 100644 --- a/python-psi-api/src/main/java/com/jetbrains/python/codeInsight/PyCustomMember.java +++ b/python-psi-api/src/main/java/com/jetbrains/python/codeInsight/PyCustomMember.java @@ -182,7 +182,6 @@ public Image getIcon() { @Nullable public PsiElement resolve(@Nonnull PsiElement context) { - if (myTarget != null) { return myTarget; } diff --git a/python-rest/src/main/java/com/jetbrains/python/rest/PyRestDocstringLanguageInjector.java b/python-rest/src/main/java/com/jetbrains/python/rest/PyRestDocstringLanguageInjector.java index 4cf87ca5..f629a476 100644 --- a/python-rest/src/main/java/com/jetbrains/python/rest/PyRestDocstringLanguageInjector.java +++ b/python-rest/src/main/java/com/jetbrains/python/rest/PyRestDocstringLanguageInjector.java @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.jetbrains.python.rest; import com.jetbrains.python.impl.documentation.doctest.PyDocstringLanguageDialect; import com.jetbrains.rest.psi.RestLine; +import consulo.annotation.access.RequiredReadAction; import consulo.annotation.component.ExtensionImpl; import consulo.document.util.TextRange; import consulo.language.inject.InjectedLanguagePlaces; @@ -26,66 +26,79 @@ import consulo.util.lang.StringUtil; import jakarta.annotation.Nonnull; + import java.util.List; /** - * User: ktisha + * @author ktisha */ @ExtensionImpl public class PyRestDocstringLanguageInjector implements LanguageInjector { - @Override - public void injectLanguages(@Nonnull final PsiLanguageInjectionHost host, @Nonnull final InjectedLanguagePlaces injectionPlacesRegistrar) { - if (host instanceof RestLine) { - int start = 0; - int end = host.getTextLength() - 1; - final String text = host.getText(); - final List strings = StringUtil.split(text, "\n", false); + @Override + @RequiredReadAction + public void injectLanguages( + @Nonnull PsiLanguageInjectionHost host, + @Nonnull InjectedLanguagePlaces injectionPlacesRegistrar + ) { + if (host instanceof RestLine) { + int start = 0; + int end = host.getTextLength() - 1; + String text = host.getText(); + List strings = StringUtil.split(text, "\n", false); - boolean gotExample = false; + boolean gotExample = false; - int currentPosition = 0; - boolean endsWithSlash = false; - for (String string : strings) { - final String trimmedString = string.trim(); - if (!trimmedString.startsWith(">>>") && !trimmedString.startsWith("...") && gotExample && start < end) { - gotExample = false; - if (!endsWithSlash) - injectionPlacesRegistrar.addPlace(PyDocstringLanguageDialect.getInstance(), TextRange.create(start, end), null, null); - } - if (endsWithSlash) { - endsWithSlash = false; - injectionPlacesRegistrar.addPlace(PyDocstringLanguageDialect.getInstance(), - TextRange.create(start, getEndOffset(currentPosition, string)), null, null); - } + int currentPosition = 0; + boolean endsWithSlash = false; + for (String string : strings) { + String trimmedString = string.trim(); + if (!trimmedString.startsWith(">>>") && !trimmedString.startsWith("...") && gotExample && start < end) { + gotExample = false; + if (!endsWithSlash) { + injectionPlacesRegistrar.addPlace(PyDocstringLanguageDialect.INSTANCE, TextRange.create(start, end), null, null); + } + } + if (endsWithSlash) { + endsWithSlash = false; + injectionPlacesRegistrar.addPlace( + PyDocstringLanguageDialect.INSTANCE, + TextRange.create(start, getEndOffset(currentPosition, string)), null, null + ); + } - if (trimmedString.startsWith(">>>")) { - if (trimmedString.endsWith("\\")) - endsWithSlash = true; + if (trimmedString.startsWith(">>>")) { + if (trimmedString.endsWith("\\")) { + endsWithSlash = true; + } - if (!gotExample) - start = currentPosition; + if (!gotExample) { + start = currentPosition; + } - gotExample = true; - end = getEndOffset(currentPosition, string); - } - else if (trimmedString.startsWith("...") && gotExample) { - if (trimmedString.endsWith("\\")) - endsWithSlash = true; + gotExample = true; + end = getEndOffset(currentPosition, string); + } + else if (trimmedString.startsWith("...") && gotExample) { + if (trimmedString.endsWith("\\")) { + endsWithSlash = true; + } - end = getEndOffset(currentPosition, string); + end = getEndOffset(currentPosition, string); + } + currentPosition += string.length(); + } + if (gotExample && start < end) { + injectionPlacesRegistrar.addPlace(PyDocstringLanguageDialect.INSTANCE, TextRange.create(start, end), null, null); + } } - currentPosition += string.length(); - } - if (gotExample && start < end) - injectionPlacesRegistrar.addPlace(PyDocstringLanguageDialect.getInstance(), TextRange.create(start, end), null, null); } - } - private int getEndOffset(int start, String s) { - int length = s.length(); - int end = start + length; - if (s.endsWith("\n")) - end -= 1; - return end; - } + private int getEndOffset(int start, String s) { + int length = s.length(); + int end = start + length; + if (s.endsWith("\n")) { + end -= 1; + } + return end; + } }