diff --git a/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java b/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java index 11bfb09e04c9..dbd1daa85837 100644 --- a/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java +++ b/python/src/com/jetbrains/python/codeInsight/imports/PythonReferenceImporter.java @@ -41,6 +41,7 @@ import org.jetbrains.annotations.Nullable; import java.util.*; public class PythonReferenceImporter implements ReferenceImporter { + @Override public boolean autoImportReferenceAtCursor(@NotNull final Editor editor, @NotNull final PsiFile file) { if (!(file instanceof PyFile)) return false; int caretOffset = editor.getCaretModel().getOffset(); @@ -68,7 +69,27 @@ public class PythonReferenceImporter implements ReferenceImporter { return false; } - private static TokenSet IS_IMPORT_STATEMENT = TokenSet.create(PyElementTypes.IMPORT_STATEMENT); + @Override + public boolean autoImportReferenceAt(@NotNull Editor editor, @NotNull PsiFile file, int offset) { + if (!(file instanceof PyFile)) return false; + PsiReference element = file.findReferenceAt(offset); + if (element instanceof PyReferenceExpression && isImportable((PsiElement)element)) { + final PyReferenceExpression refExpr = (PyReferenceExpression)element; + if (refExpr.getQualifier() == null) { + final PsiPolyVariantReference reference = refExpr.getReference(); + if (reference.resolve() == null) { + AutoImportQuickFix fix = proposeImportFix(refExpr, reference); + if (fix != null && fix.getCandidatesCount() == 1) { + fix.invoke(file); + } + return true; + } + } + } + return false; + } + + private static final TokenSet IS_IMPORT_STATEMENT = TokenSet.create(PyElementTypes.IMPORT_STATEMENT); @Nullable public static AutoImportQuickFix proposeImportFix(final PyElement node, PsiReference reference) { @@ -87,7 +108,6 @@ public class PythonReferenceImporter implements ReferenceImporter { return null; } - PsiFile existing_import_file = null; // if there's a matching existing import, this it the file it imports AutoImportQuickFix fix = new AutoImportQuickFix(node, reference, !PyCodeInsightSettings.getInstance().PREFER_FROM_IMPORT); Set seen_file_names = new HashSet(); // true import names // maybe the name is importable via some existing 'import foo' statement, and only needs a qualifier. @@ -95,7 +115,8 @@ public class PythonReferenceImporter implements ReferenceImporter { CollectProcessor import_prc = new CollectProcessor(IS_IMPORT_STATEMENT); PyResolveUtil.treeCrawlUp(import_prc, node); List result = import_prc.getResult(); - if (result.size() > 0) { + PsiFile existing_import_file = null; // if there's a matching existing import, this it the file it imports + if (!result.isEmpty()) { for (PsiElement stmt : import_prc.getResult()) { for (PyImportElement ielt : ((PyImportStatement)stmt).getImportElements()) { final PyReferenceExpression src = ielt.getImportReferenceExpression(); @@ -130,7 +151,7 @@ public class PythonReferenceImporter implements ReferenceImporter { if (isPossibleModuleReference(node)) { symbols.addAll(findImportableModules(node.getContainingFile(), refText, project, scope)); } - if (symbols.size() > 0) { + if (!symbols.isEmpty()) { for (PsiElement symbol : symbols) { if (isIndexableTopLevel(symbol)) { // we only want top-level symbols PsiFileSystemItem srcfile = symbol instanceof PsiFileSystemItem ? ((PsiFileSystemItem)symbol).getParent() : symbol.getContainingFile(); @@ -171,10 +192,7 @@ public class PythonReferenceImporter implements ReferenceImporter { } private static boolean isQualifier(PyElement node) { - if (node.getParent() instanceof PyReferenceExpression) { - return node == ((PyReferenceExpression) node.getParent()).getQualifier(); - } - return false; + return node.getParent() instanceof PyReferenceExpression && node == ((PyReferenceExpression)node.getParent()).getQualifier(); } private static boolean isPossibleModuleReference(PyElement node) { @@ -227,7 +245,7 @@ public class PythonReferenceImporter implements ReferenceImporter { return symbol instanceof PyTargetExpression; } - private final static String[] AS_PREFIXES = {"other_", "one_more_", "different_", "pseudo_", "true_"}; + private static final String[] AS_PREFIXES = {"other_", "one_more_", "different_", "pseudo_", "true_"}; // a no-frills recursive accumulating scan private static void collectIdentifiers(PsiElement node, Collection dst) { diff --git a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java index 6f3a856a881a..198c56e27936 100644 --- a/python/src/com/jetbrains/python/editor/PythonEnterHandler.java +++ b/python/src/com/jetbrains/python/editor/PythonEnterHandler.java @@ -13,7 +13,7 @@ import com.intellij.openapi.editor.actions.SplitLineAction; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; -import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; +import com.intellij.psi.impl.source.tree.injected.InjectedLanguageFacadeImpl; import com.intellij.psi.util.PsiTreeUtil; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.codeInsight.PyCodeInsightSettings; @@ -54,8 +54,8 @@ public class PythonEnterHandler extends EnterHandlerDelegateAdapter { EditorActionHandler originalHandler) { int offset = caretOffset.get(); if (editor instanceof EditorWindow) { - file = InjectedLanguageUtil.getTopLevelFile(file); - editor = InjectedLanguageUtil.getTopLevelEditor(editor); + file = InjectedLanguageFacadeImpl.getTopLevelFile(file); + editor = InjectedLanguageFacadeImpl.getTopLevelEditor(editor); offset = editor.getCaretModel().getOffset(); } if (!(file instanceof PyFile)) { diff --git a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java index 4f817901fcc5..95babb81c43e 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java @@ -102,8 +102,9 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression { addImportElementDeclaration(importElement, localDeclarations, ambiguousDeclarations); if (PyNames.INIT_DOT_PY.equals(getName())) { final PyQualifiedName qName = importElement.getImportedQName(); - String parentPackage = getContainingDirectory().getName(); - if (qName != null && qName.getComponentCount() > 1) { + final PsiDirectory containingDirectory = getContainingDirectory(); + if (qName != null && qName.getComponentCount() > 1 && containingDirectory != null) { + String parentPackage = containingDirectory.getName(); final List components = qName.getComponents(); if (components.get(components.size() - 2).equals(parentPackage)) { localDeclarations.put(components.get(components.size()-1), importElement); @@ -188,14 +189,16 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression { @Nullable private PsiElement findNameInNameDefiners(String name) { - long oocbModCount = myModificationTracker.getOutOfCodeBlockModificationCount(); - if (oocbModCount != myNameDefinerOOCBModCount) { - myNameDefinerNegativeCache.clear(); - myNameDefinerOOCBModCount = oocbModCount; - } - else { - if (myNameDefinerNegativeCache.contains(name)) { - return null; + synchronized (myNameDefinerNegativeCache) { + long oocbModCount = myModificationTracker.getOutOfCodeBlockModificationCount(); + if (oocbModCount != myNameDefinerOOCBModCount) { + myNameDefinerNegativeCache.clear(); + myNameDefinerOOCBModCount = oocbModCount; + } + else { + if (myNameDefinerNegativeCache.contains(name)) { + return null; + } } } for (PsiElement definer : myNameDefiners) { @@ -204,13 +207,15 @@ public class PyFileImpl extends PsiFileBase implements PyFile, PyExpression { result = findNameInStarImport(name, (PyFromImportStatement)definer); } else { - result = ((NameDefiner) definer).getElementNamed(name); + result = ((NameDefiner)definer).getElementNamed(name); } if (result != null) { return result; } } - myNameDefinerNegativeCache.add(name); + synchronized (myNameDefinerNegativeCache) { + myNameDefinerNegativeCache.add(name); + } return null; } diff --git a/python/testSrc/com/jetbrains/python/refactoring/classes/PyPushDownTest.java b/python/testSrc/com/jetbrains/python/refactoring/classes/PyPushDownTest.java index 0b7ef4d01d9f..7618a28e5d9e 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/classes/PyPushDownTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/classes/PyPushDownTest.java @@ -54,7 +54,7 @@ public class PyPushDownTest extends PyClassRefactoringTest { myFixture.checkResultByFile(baseName + ".after.py"); } catch (Exception e) { if (expectedError == null) throw e; - assertEquals(expectedError, e.getMessage()); + assertTrue(e.getMessage(), e.getMessage().contains(expectedError)); } } }