diff --git a/python/build/pycharm_build.gant b/python/build/pycharm_build.gant index 9ea10b0e1c75..c1107837a3eb 100644 --- a/python/build/pycharm_build.gant +++ b/python/build/pycharm_build.gant @@ -108,7 +108,7 @@ def zipSources() { target('default': "Build artifacts") { def modules = [ - "python", "python-ide", "python-py", platformApiModules(), platformImplementationModules(), pluginFilter + "python", "python-ide", platformApiModules(), platformImplementationModules(), pluginFilter ].flatten() loadProject() @@ -343,7 +343,7 @@ def buildTarGz() { def layoutFull(String target, Set usedJars) { def openapiModules = platformApiModules() - def implementationModules = [platformImplementationModules(), "python", "python-ide", "python-py", "sass"].flatten() + def implementationModules = [platformImplementationModules(), "python", "python-ide", "sass"].flatten() layout(target) { dir("lib") { diff --git a/python/layout.xml b/python/layout.xml index 709346ac7029..62c0cdfbed14 100644 --- a/python/layout.xml +++ b/python/layout.xml @@ -15,9 +15,6 @@ - - - diff --git a/python/python-py.iml b/python/python-py.iml deleted file mode 100644 index aad973e47667..000000000000 --- a/python/python-py.iml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/python/src/META-INF/python-plugin-common.xml b/python/src/META-INF/python-plugin-common.xml index e19d92a37b7f..64adafd7c20d 100644 --- a/python/src/META-INF/python-plugin-common.xml +++ b/python/src/META-INF/python-plugin-common.xml @@ -152,22 +152,25 @@ order="last, before default"/> - + + + - - - + + + - + - + - + + + + + + + + + - + + implementationClass="com.jetbrains.appengine.lang.gql.highlighting.GqlHighlighterFactory"/> @@ -248,7 +262,8 @@ - + @@ -289,7 +304,8 @@ - + @@ -353,11 +369,11 @@ - + - + diff --git a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java index 065cefc40902..d9aed1c3c4de 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleRunner.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleRunner.java @@ -12,6 +12,8 @@ import com.intellij.execution.ui.ConsoleViewContentType; import com.intellij.execution.ui.RunContentDescriptor; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.EditorModificationUtil; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.Sdk; @@ -20,10 +22,14 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.encoding.EncodingManager; import com.intellij.psi.PsiElement; +import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.psi.impl.source.codeStyle.Helper; +import com.intellij.psi.impl.source.codeStyle.HelperFactory; import com.intellij.util.PathUtil; import com.intellij.util.net.NetUtils; import com.jetbrains.django.run.Runner; import com.jetbrains.django.util.DjangoUtil; +import com.jetbrains.python.PythonFileType; import com.jetbrains.python.PythonHelpersLocator; import com.jetbrains.python.console.pydev.ICallback; import com.jetbrains.python.console.pydev.InterpreterResponse; @@ -46,6 +52,8 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory { private PydevConsoleCommunication myPydevConsoleCommunication; public static Key CONSOLE_KEY = new Key("PYDEV_CONSOLE_KEY"); private static final String PYTHON_ENV_COMMAND = "import sys; print('Python %s on %s' % (sys.version, sys.platform))\n"; + private Helper myHelper; + private int currentPythonIndentSize; protected PydevConsoleRunner(@NotNull final Project project, @NotNull final String consoleTitle, @@ -54,6 +62,8 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory { int[] ports) { super(project, consoleTitle, provider, workingDir); myPorts = ports; + myHelper = HelperFactory.createHelper(PythonFileType.INSTANCE, myProject); + currentPythonIndentSize = CodeStyleSettingsManager.getSettings(myProject).getIndentSize(PythonFileType.INSTANCE); } public static void run(@NotNull final Project project, @@ -198,6 +208,7 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory { @Override public void sendInput(final String input) { + if (myPydevConsoleCommunication != null){ myPydevConsoleCommunication.execInterpreter(input, new ICallback() { public Object call(final InterpreterResponse interpreterResponse) { @@ -206,6 +217,9 @@ public class PydevConsoleRunner extends AbstractConsoleRunnerWithHistory { if (interpreterResponse.more){ if (!PyConsoleHighlightingUtil.INDENT_PROMPT.equals(console.getPrompt())){ console.setPrompt(PyConsoleHighlightingUtil.INDENT_PROMPT); + // In this case we can insert indent automatically + final int indent = myHelper.getIndent(input, false); + EditorModificationUtil.insertStringAtCaret(console.getConsoleEditor(), myHelper.fillIndent(indent + currentPythonIndentSize)); } } else { if (!PyConsoleHighlightingUtil.ORDINARY_PROMPT.equals(console.getPrompt())){ diff --git a/python/src/com/jetbrains/python/editor/BaseQuoteHandler.java b/python/src/com/jetbrains/python/editor/BaseQuoteHandler.java new file mode 100644 index 000000000000..de28ca9c3673 --- /dev/null +++ b/python/src/com/jetbrains/python/editor/BaseQuoteHandler.java @@ -0,0 +1,69 @@ +package com.jetbrains.python.editor; + +import com.intellij.codeInsight.editorActions.SimpleTokenSetQuoteHandler; +import com.intellij.openapi.editor.Document; +import com.intellij.openapi.editor.highlighter.HighlighterIterator; +import com.intellij.psi.tree.TokenSet; +import com.jetbrains.python.PyTokenTypes; + +import java.util.Arrays; + +/** + * @author yole + */ +public class BaseQuoteHandler extends SimpleTokenSetQuoteHandler { + + private final char[] ourAutoClosingChars; // we add auto-close quotes before these + + public BaseQuoteHandler(TokenSet tokenSet, char[] autoClosingChars) { + super(tokenSet); + ourAutoClosingChars = autoClosingChars; + Arrays.sort(ourAutoClosingChars); + } + + @Override + public boolean isOpeningQuote(HighlighterIterator iterator, int offset) { + // don't assume an opening quote unless we're in an explicitly "blank" context + CharSequence text = iterator.getDocument().getCharsSequence(); + if (offset+1 >= text.length() || Arrays.binarySearch(ourAutoClosingChars, text.charAt(offset+1)) >= 0) { + char the_quote = text.charAt(offset); + // if we're next to two same quotes, don't auto-close, the user may want a triple quote + if ( + offset >= 2 && + text.charAt(offset-1) == the_quote && + text.charAt(offset-2) == the_quote && + (offset < 3 || text.charAt(offset-3) != the_quote) + ) return false; + // handle string literal context + if (super.isOpeningQuote(iterator, offset)) { + return true; + } + if (myLiteralTokenSet.contains(iterator.getTokenType())) { + int start = iterator.getStart(); + if (offset - start <= 2) { + if (getLiteralStartOffset(text, start) == offset) return true; + } + } + } + return false; + } + + private static int getLiteralStartOffset(CharSequence text, int start) { + char c = Character.toUpperCase(text.charAt(start)); + if (c == 'U' || c == 'B') { + start++; + c = Character.toUpperCase(text.charAt(start)); + } + if (c == 'R') { + start++; + } + return start; + } + + @Override + protected boolean isNonClosedLiteral(HighlighterIterator iterator, CharSequence chars) { + if (getLiteralStartOffset(chars, iterator.getStart()) >= iterator.getEnd() - 1) return true; + if (chars.charAt(iterator.getEnd() - 1) != '\"' && chars.charAt(iterator.getEnd() - 1) != '\'') return true; + return false; + } +} diff --git a/python/src/com/jetbrains/python/editor/PythonQuoteHandler.java b/python/src/com/jetbrains/python/editor/PythonQuoteHandler.java index de52b1b2837c..3ad479a24ae6 100644 --- a/python/src/com/jetbrains/python/editor/PythonQuoteHandler.java +++ b/python/src/com/jetbrains/python/editor/PythonQuoteHandler.java @@ -1,70 +1,13 @@ package com.jetbrains.python.editor; -import com.intellij.codeInsight.editorActions.SimpleTokenSetQuoteHandler; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.highlighter.HighlighterIterator; +import com.intellij.psi.tree.TokenSet; import com.jetbrains.python.PyTokenTypes; -import java.util.Arrays; - /** - * @author yole + * @author traff */ -public class PythonQuoteHandler extends SimpleTokenSetQuoteHandler { - - private static final char[] ourAutoClosingChars; // we add auto-close quotes before these - static { - ourAutoClosingChars = new char[]{'}', ']', ')', ',', ':', ';', ' ', '\t', '\n'}; - Arrays.sort(ourAutoClosingChars); - } - +public class PythonQuoteHandler extends BaseQuoteHandler { public PythonQuoteHandler() { - super(PyTokenTypes.STRING_LITERAL); - } - - @Override - public boolean isOpeningQuote(HighlighterIterator iterator, int offset) { - // don't assume an opening quote unless we're in an explicitly "blank" context - CharSequence text = iterator.getDocument().getCharsSequence(); - if (offset+1 >= text.length() || Arrays.binarySearch(ourAutoClosingChars, text.charAt(offset+1)) >= 0) { - char the_quote = text.charAt(offset); - // if we're next to two same quotes, don't auto-close, the user may want a triple quote - if ( - offset >= 2 && - text.charAt(offset-1) == the_quote && - text.charAt(offset-2) == the_quote && - (offset < 3 || text.charAt(offset-3) != the_quote) - ) return false; - // handle string literal context - if (super.isOpeningQuote(iterator, offset)) { - return true; - } - if (myLiteralTokenSet.contains(iterator.getTokenType())) { - int start = iterator.getStart(); - if (offset - start <= 2) { - if (getLiteralStartOffset(text, start) == offset) return true; - } - } - } - return false; - } - - private static int getLiteralStartOffset(CharSequence text, int start) { - char c = Character.toUpperCase(text.charAt(start)); - if (c == 'U' || c == 'B') { - start++; - c = Character.toUpperCase(text.charAt(start)); - } - if (c == 'R') { - start++; - } - return start; - } - - @Override - protected boolean isNonClosedLiteral(HighlighterIterator iterator, CharSequence chars) { - if (getLiteralStartOffset(chars, iterator.getStart()) >= iterator.getEnd() - 1) return true; - if (chars.charAt(iterator.getEnd() - 1) != '\"' && chars.charAt(iterator.getEnd() - 1) != '\'') return true; - return false; + super(TokenSet.create(PyTokenTypes.STRING_LITERAL), new char[]{'}', ']', ')', ',', ':', ';', ' ', '\t', '\n'}); } } diff --git a/python/src/com/jetbrains/python/findUsages/PythonFindUsagesProvider.java b/python/src/com/jetbrains/python/findUsages/PythonFindUsagesProvider.java index afd5df018cb2..ee18db521ef4 100644 --- a/python/src/com/jetbrains/python/findUsages/PythonFindUsagesProvider.java +++ b/python/src/com/jetbrains/python/findUsages/PythonFindUsagesProvider.java @@ -22,7 +22,7 @@ public class PythonFindUsagesProvider implements FindUsagesProvider { @NotNull public String getType(@NotNull PsiElement element) { - if (element instanceof PyNamedParameter) return "parameter"; + if (element instanceof PyNamedParameter) return "parameter"; //TODO: replace strings to messages if (element instanceof PyFunction) { if (((PyFunction) element).getContainingClass() != null) { return "method"; diff --git a/python/src/com/jetbrains/python/psi/PyFileElementType.java b/python/src/com/jetbrains/python/psi/PyFileElementType.java index be653d45660c..aea16600df97 100644 --- a/python/src/com/jetbrains/python/psi/PyFileElementType.java +++ b/python/src/com/jetbrains/python/psi/PyFileElementType.java @@ -24,7 +24,7 @@ public class PyFileElementType extends IStubFileElementType { @Override public int getStubVersion() { - return 22; + return 23; } @Override diff --git a/python/src/com/jetbrains/python/psi/impl/PyParameterListImpl.java b/python/src/com/jetbrains/python/psi/impl/PyParameterListImpl.java index 13fa9b8e5f40..9db74d800e75 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyParameterListImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyParameterListImpl.java @@ -115,6 +115,9 @@ public class PyParameterListImpl extends PyBaseElementImpl if (nextParameter.isKeywordContainer() || nextParameter.isPositionalContainer()) { ++i; } + while (nextParameter.isKeywordContainer() && j if (nextParameter.isKeywordContainer() || nextParameter.isPositionalContainer()) { ++j; } + while (nextParameter.isKeywordContainer() && i= parametersLength) && (j >= anotherParametersLength); diff --git a/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java b/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java index e654f7546c99..1f63926ba567 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyQualifiedReferenceImpl.java @@ -126,8 +126,8 @@ public class PyQualifiedReferenceImpl extends PyReferenceImpl { PyType qualifierType = qualifier.getType(TypeEvalContext.slow()); ProcessingContext ctx = new ProcessingContext(); - final Set names_already = new HashSet(); - ctx.put(PyType.CTX_NAMES, names_already); + final Set namesAlready = new HashSet(); + ctx.put(PyType.CTX_NAMES, namesAlready); if (qualifierType != null) { Collection variants = new ArrayList(); if (qualifier instanceof PyQualifiedExpression) { @@ -135,12 +135,12 @@ public class PyQualifiedReferenceImpl extends PyReferenceImpl { variants.addAll(attrs); for (PyExpression ex : attrs) { if (ex instanceof PyReferenceExpression) { - PyReferenceExpression refex = (PyReferenceExpression)ex; - names_already.add(refex.getReferencedName()); + PyReferenceExpression refExpr = (PyReferenceExpression)ex; + namesAlready.add(refExpr.getReferencedName()); } else if (ex instanceof PyTargetExpression) { PyTargetExpression targetExpr = (PyTargetExpression)ex; - names_already.add(targetExpr.getName()); + namesAlready.add(targetExpr.getName()); } } Collections.addAll(variants, qualifierType.getCompletionVariants(myElement, ctx)); diff --git a/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionManipulator.java b/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionManipulator.java new file mode 100644 index 000000000000..5f51caa67534 --- /dev/null +++ b/python/src/com/jetbrains/python/psi/impl/PyStringLiteralExpressionManipulator.java @@ -0,0 +1,20 @@ +package com.jetbrains.python.psi.impl; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.AbstractElementManipulator; +import com.jetbrains.django.util.DjangoStringUtil; +import com.jetbrains.python.psi.PyElementGenerator; + +/** + * @author traff + */ +public class PyStringLiteralExpressionManipulator extends AbstractElementManipulator { + public PyStringLiteralExpressionImpl handleContentChange(PyStringLiteralExpressionImpl element, TextRange range, String newContent) { + String newName = range.replace(element.getText(), newContent); + if (!DjangoStringUtil.isQuoted(newName)) { + newName = '\'' + newName + '\''; + } + return (PyStringLiteralExpressionImpl)element + .replace(PyElementGenerator.getInstance(element.getProject()).createStringLiteralAlreadyEscaped(newName)); + } +} diff --git a/python/src/com/jetbrains/python/psi/resolve/VariantsProcessor.java b/python/src/com/jetbrains/python/psi/resolve/VariantsProcessor.java index 1e055e906e04..37bca0aabaaa 100644 --- a/python/src/com/jetbrains/python/psi/resolve/VariantsProcessor.java +++ b/python/src/com/jetbrains/python/psi/resolve/VariantsProcessor.java @@ -12,6 +12,8 @@ import com.intellij.util.Icons; import com.jetbrains.python.codeInsight.PyClassInsertHandler; import com.jetbrains.python.codeInsight.PyFunctionInsertHandler; import com.jetbrains.python.psi.*; +import com.jetbrains.python.psi.types.PyType; +import com.jetbrains.python.psi.types.TypeEvalContext; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -37,10 +39,10 @@ public class VariantsProcessor implements PsiScopeProcessor { myNodeFilter = nodefilter; } - public VariantsProcessor(PsiElement context, final Condition nodefilter, final Condition namefilter) { + public VariantsProcessor(PsiElement context, final Condition nodeFilter, final Condition nameFilter) { myContext = context; - myNodeFilter = nodefilter; - myNameFilter = namefilter; + myNodeFilter = nodeFilter; + myNameFilter = nameFilter; } public void setNotice(@Nullable String notice) { @@ -110,18 +112,25 @@ public class VariantsProcessor implements PsiScopeProcessor { // things like PyTargetExpression cannot have a general icon, but here we only have variables if (icon == null) icon = Icons.VARIABLE_ICON; if (nameIsAcceptable(referencedName)) { - LookupElementBuilder lookup_item = setupItem(LookupElementBuilder.create(referencedName).setIcon(icon)); + LookupElementBuilder lookupItem = setupItem(LookupElementBuilder.create(referencedName).setIcon(icon)); if (definer instanceof PyImportElement) { // set notice to imported module name if needed - PsiElement maybe_from_import = definer.getParent(); - if (maybe_from_import instanceof PyFromImportStatement) { - final PyFromImportStatement from_import = (PyFromImportStatement)maybe_from_import; - PyReferenceExpression src = from_import.getImportSource(); + PsiElement maybeFromImport = definer.getParent(); + if (maybeFromImport instanceof PyFromImportStatement) { + final PyFromImportStatement fromImport = (PyFromImportStatement)maybeFromImport; + PyReferenceExpression src = fromImport.getImportSource(); if (src != null) { - lookup_item = setItemNotice(lookup_item, src.getName()); + lookupItem = setItemNotice(lookupItem, src.getName()); } } } - myVariants.put(referencedName, lookup_item); + if (definer instanceof PyAssignmentStatement) { + PyExpression value = ((PyAssignmentStatement)definer).getAssignedValue(); + PyType type = value.getType(TypeEvalContext.fast()); + if (type != null) { + lookupItem = lookupItem.setTypeText(type.getName()); + } + } + myVariants.put(referencedName, lookupItem); } } } diff --git a/python/src/com/jetbrains/python/psi/search/PyStringReferenceSearch.java b/python/src/com/jetbrains/python/psi/search/PyStringReferenceSearch.java index ef03d156678a..74009f580378 100644 --- a/python/src/com/jetbrains/python/psi/search/PyStringReferenceSearch.java +++ b/python/src/com/jetbrains/python/psi/search/PyStringReferenceSearch.java @@ -1,6 +1,8 @@ package com.jetbrains.python.psi.search; +import com.intellij.ide.highlighter.HtmlFileType; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; @@ -15,7 +17,9 @@ import com.intellij.psi.search.UsageSearchContext; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.util.Processor; import com.intellij.util.QueryExecutor; +import com.jetbrains.django.lang.template.DjangoTemplateFileType; import com.jetbrains.django.lang.template.psi.impl.DjangoTemplateFileImpl; +import com.jetbrains.django.util.PythonUtil; import com.jetbrains.python.PythonFileType; import com.jetbrains.python.psi.PyElement; import com.jetbrains.python.psi.PyFile; @@ -31,43 +35,13 @@ public class PyStringReferenceSearch implements QueryExecutor() { - public String compute() { - if (element instanceof PyFile) { - return FileUtil.getNameWithoutExtension(((PyFile)element).getName()); - } else - if (element instanceof PsiDirectory) { - return ((PsiDirectory) element).getName(); - } - if (element instanceof DjangoTemplateFileImpl) { - return ((DjangoTemplateFileImpl) element).getName(); - } - else { - return ((PyElement)element).getName(); - } - } - }); - if (StringUtil.isEmpty(name)) { - return true; - } + SearchScope searchScope = params.getEffectiveSearchScope(); if (searchScope instanceof GlobalSearchScope) { - searchScope = GlobalSearchScope.getScopeRestrictedByFileTypes((GlobalSearchScope)searchScope, PythonFileType.INSTANCE); + searchScope = GlobalSearchScope.getScopeRestrictedByFileTypes((GlobalSearchScope)searchScope, PythonFileType.INSTANCE + ); } - final TextOccurenceProcessor processor = new TextOccurenceProcessor() { - public boolean execute(PsiElement e, int offsetInElement) { - final PsiReference[] refs = e.getReferences(); - for (PsiReference ref : refs) { - if (ref.isReferenceTo(element)) { - return consumer.process(ref); - } - } - return true; - } - }; - - return PsiManager.getInstance(element.getProject()).getSearchHelper(). - processElementsWithWord(processor, searchScope, name, UsageSearchContext.IN_STRINGS, true); + return PythonUtil.searchElementStringReferences(consumer, element, searchScope); } } diff --git a/python/src/com/jetbrains/python/psi/types/PyClassType.java b/python/src/com/jetbrains/python/psi/types/PyClassType.java index 880feba9653a..c9f1bd72a060 100644 --- a/python/src/com/jetbrains/python/psi/types/PyClassType.java +++ b/python/src/com/jetbrains/python/psi/types/PyClassType.java @@ -146,31 +146,31 @@ public class PyClassType implements PyType { } public Object[] getCompletionVariants(final PyQualifiedExpression referenceExpression, ProcessingContext context) { - List class_list = new ParentMatcher(PyClass.class).search(referenceExpression); - boolean within_our_class = class_list != null && class_list.get(0) == this; - Set names_already = context.get(CTX_NAMES); + List classList = new ParentMatcher(PyClass.class).search(referenceExpression); + boolean withinOurClass = classList != null && classList.get(0) == this; + Set namesAlready = context.get(CTX_NAMES); List ret = new ArrayList(); - Condition underscore_filter = new PyUtil.UnderscoreFilter(PyUtil.getInitialUnderscores(referenceExpression.getName())); + Condition underscoreFilter = new PyUtil.UnderscoreFilter(PyUtil.getInitialUnderscores(referenceExpression.getName())); // from providers for (PyClassMembersProvider provider : Extensions.getExtensions(PyClassMembersProvider.EP_NAME)) { for (PyDynamicMember member : provider.getMembers(myClass)) { final String name = member.getName(); - if (underscore_filter.value(name) || provider.hasUnderscoreWildCard(name)) { + if (underscoreFilter.value(name) || provider.hasUnderscoreWildCard(name)) { ret.add(LookupElementBuilder.create(name).setIcon(member.getIcon()).setTypeText(member.getShortType())); } } } // from our own class final VariantsProcessor processor = new VariantsProcessor( - referenceExpression, new PyResolveUtil.FilterNotInstance(myClass), underscore_filter + referenceExpression, new PyResolveUtil.FilterNotInstance(myClass), underscoreFilter ); myClass.processDeclarations(processor, ResolveState.initial(), null, referenceExpression); - if (names_already != null) { + if (namesAlready != null) { for (LookupElement le : processor.getResultList()) { String name = le.getLookupString(); - if (names_already.contains(name)) continue; - if (!within_our_class && isClassPrivate(name)) continue; - names_already.add(name); + if (namesAlready.contains(name)) continue; + if (!withinOurClass && isClassPrivate(name)) continue; + namesAlready.add(name); ret.add(le); } } @@ -181,8 +181,8 @@ public class PyClassType implements PyType { Object[] ancestry = (new PyClassType(ancestor, true)).getCompletionVariants(referenceExpression, context); for (Object ob : ancestry) { if (ob instanceof LookupElementBuilder) { - final LookupElementBuilder lookup_elt = (LookupElementBuilder)ob; - if (!isClassPrivate(lookup_elt.getLookupString())) ret.add(lookup_elt.setTypeText(ancestor.getName())); + final LookupElementBuilder lookupElt = (LookupElementBuilder)ob; + if (!isClassPrivate(lookupElt.getLookupString())) ret.add(lookupElt.setTypeText(ancestor.getName())); } else { if (!isClassPrivate(ob.toString())) ret.add(ob); diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java index e0580cb844f4..90aa522cb108 100644 --- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java +++ b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java @@ -6,6 +6,7 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; @@ -344,9 +345,16 @@ public class PyExtractMethodUtil { if (name == null){ name = "foo"; } - final String result = validator.check(name); - if (result != null){ - throw new CommonRefactoringUtil.RefactoringErrorHintException(result); + final String error = validator.check(name); + if (error != null){ + if (ApplicationManager.getApplication().isUnitTestMode()){ + throw new CommonRefactoringUtil.RefactoringErrorHintException(error); + } + final StringBuilder builder = new StringBuilder(); + builder.append(error).append(". ").append(RefactoringBundle.message("do.you.wish.to.continue")); + if (Messages.showOkCancelDialog(builder.toString(), RefactoringBundle.message("warning.title"), Messages.getWarningIcon()) != 0){ + throw new CommonRefactoringUtil.RefactoringErrorHintException(error); + } } final List data = new ArrayList(); for (String in : fragment.getInputVariables()) { diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyInlineLocalTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyInlineLocalTest.java index 18edd4372ff0..da92365383a4 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyInlineLocalTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyInlineLocalTest.java @@ -1,5 +1,6 @@ package com.jetbrains.python.refactoring; +import com.intellij.openapi.util.Comparing; import com.intellij.psi.PsiElement; import com.jetbrains.python.fixtures.LightMarkedTestCase; import com.jetbrains.python.psi.PyReferenceExpression; @@ -22,14 +23,21 @@ public class PyInlineLocalTest extends LightMarkedTestCase { try { PsiElement element = map.values().iterator().next().getParent(); PyReferenceExpression ref = null; - if (element instanceof PyReferenceExpression) { + while (element instanceof PyReferenceExpression) { ref = (PyReferenceExpression)element; - element = ((PyReferenceExpression)element).getReference().resolve(); + PsiElement newElement = ((PyReferenceExpression)element).getReference().resolve(); + if (element == newElement) { + break; + } + element = newElement; } PyInlineLocalHandler.invoke(myFixture.getProject(), myFixture.getEditor(), (PyTargetExpression)element, ref); if (expectedError != null) fail("expected error: '" + expectedError + "', got none"); } catch (Exception e) { + if (!Comparing.equal(e.getMessage(), expectedError)) { + e.printStackTrace(); + } assertEquals(expectedError, e.getMessage()); return; }