diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerUtil.java b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerUtil.java index a27469416e80..e53f9d5a520e 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerUtil.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/SuspendManagerUtil.java @@ -27,9 +27,8 @@ public class SuspendManagerUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.debugger.engine.SuspendManagerUtil"); public static boolean isEvaluating(SuspendManager suspendManager, ThreadReferenceProxyImpl thread) { - for (Iterator iterator = ((SuspendManagerImpl) suspendManager).getEventContexts().iterator(); iterator.hasNext();) { - SuspendContextImpl suspendContext = iterator.next(); - if(suspendContext.isEvaluating() && thread.equals(suspendContext.getThread())) { + for (SuspendContextImpl suspendContext : suspendManager.getEventContexts()) { + if (suspendContext.isEvaluating() && thread.equals(suspendContext.getThread())) { return true; } } @@ -121,8 +120,8 @@ public class SuspendManagerUtil { } public static SuspendContextImpl getEvaluatingContext(SuspendManager suspendManager, ThreadReferenceProxyImpl thread) { - for (SuspendContextImpl suspendContext : ((SuspendManagerImpl)suspendManager).getEventContexts()) { - if (suspendContext.isEvaluating() && suspendContext.getThread() == thread) { + for (SuspendContextImpl suspendContext : suspendManager.getEventContexts()) { + if (!suspendContext.isResumed() && suspendContext.isEvaluating() && suspendContext.getThread() == thread) { return suspendContext; } } diff --git a/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java b/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java index 7a6c730b8da7..c1c2978f2265 100644 --- a/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/jdi/StackFrameProxyImpl.java @@ -154,7 +154,8 @@ public class StackFrameProxyImpl extends JdiProxy implements StackFrameProxy { return getStackFrame().location(); } catch (InvalidStackFrameException e) { - throw EvaluateExceptionUtil.createEvaluateException(DebuggerBundle.message("evaluation.error.thread.resumed"), e); + clearCaches(); + return location(); } } diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/CreateNewLibraryAction.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/CreateNewLibraryAction.java index 414d42d6f6fd..bf9f876e832b 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/CreateNewLibraryAction.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/libraryEditor/CreateNewLibraryAction.java @@ -98,6 +98,7 @@ public class CreateNewLibraryAction extends AnAction { private void appendLibraryToModules(final ModuleStructureConfigurable rootConfigurable, final Library libraryToSelect) { final List modules = new ArrayList(); ContainerUtil.addAll(modules, rootConfigurable.getModules()); + if (modules.isEmpty()) return; final ChooseModulesDialog dlg = new ChooseModulesDialog(myProject, modules, ProjectBundle.message("choose.modules.dialog.title"), ProjectBundle diff --git a/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java b/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java index b57f3897bdba..380acf25d810 100644 --- a/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java @@ -219,7 +219,7 @@ public class CodeInsightUtil { public static boolean areExpressionsEquivalent(PsiExpression expr1, PsiExpression expr2) { return PsiEquivalenceUtil.areElementsEquivalent(expr1, expr2, new Comparator() { public int compare(PsiElement o1, PsiElement o2) { - if (o1 instanceof PsiParameter && o2 instanceof PsiParameter) { + if (o1 instanceof PsiParameter && o2 instanceof PsiParameter && ((PsiParameter)o1).getDeclarationScope() instanceof PsiMethod) { return ((PsiParameter)o1).getName().compareTo(((PsiParameter)o2).getName()); } return 1; diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageFix.java index b45c7a264f6c..576c12e8f65f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageFix.java @@ -238,6 +238,7 @@ public class ChangeMethodSignatureFromUsageFix implements IntentionAction { } int ei = 0; int pi = 0; + PsiParameter varargParam = targetMethod.isVarArgs() ? parameters[parameters.length - 1] : null; while (ei < expressions.length || pi < parameters.length) { PsiExpression expression = ei < expressions.length ? expressions[ei] : null; PsiParameter parameter = pi < parameters.length ? parameters[pi] : null; @@ -248,7 +249,16 @@ public class ChangeMethodSignatureFromUsageFix implements IntentionAction { pi++; ei++; } + else if (isArgumentInVarargPosition(expressions, ei, varargParam, substitutor)) { + if (pi == parameters.length - 1) { + assert varargParam != null; + result.add(new ParameterInfoImpl(pi, varargParam.getName(), varargParam.getType())); + } + pi++; + ei++; + } else if (expression != null) { + if (varargParam != null && pi >= parameters.length) return null; PsiType exprType = RefactoringUtil.getTypeByExpression(expression); if (exprType == null) return null; JavaCodeStyleManager codeStyleManager = JavaCodeStyleManager.getInstance(expression.getProject()); @@ -257,7 +267,7 @@ public class ChangeMethodSignatureFromUsageFix implements IntentionAction { ei++; } } - if (result.size() != expressions.length) return null; + if (result.size() != expressions.length && varargParam == null) return null; } else { //parameter type changed @@ -291,6 +301,17 @@ public class ChangeMethodSignatureFromUsageFix implements IntentionAction { return result.toArray(new ParameterInfoImpl[result.size()]); } + private static boolean isArgumentInVarargPosition(PsiExpression[] expressions, int ei, PsiParameter varargParam, PsiSubstitutor substitutor) { + if (varargParam == null) return false; + final PsiExpression expression = expressions[ei]; + if (expression == null || TypeConversionUtil.areTypesAssignmentCompatible(substitutor.substitute(((PsiEllipsisType)varargParam.getType()).getComponentType()), expression)) { + final int lastExprIdx = expressions.length - 1; + if (ei == lastExprIdx) return true; + return expressions[lastExprIdx].getType() != PsiType.NULL; + } + return false; + } + private static String suggestUniqueParameterName(JavaCodeStyleManager codeStyleManager, PsiExpression expression, PsiType exprType, diff --git a/java/java-impl/src/com/intellij/lang/java/JavaParserDefinition.java b/java/java-impl/src/com/intellij/lang/java/JavaParserDefinition.java index a6379a2b8a9a..d322d439329f 100644 --- a/java/java-impl/src/com/intellij/lang/java/JavaParserDefinition.java +++ b/java/java-impl/src/com/intellij/lang/java/JavaParserDefinition.java @@ -40,14 +40,16 @@ import org.jetbrains.annotations.NotNull; * @author max */ public class JavaParserDefinition implements ParserDefinition { + public static final boolean USE_NEW_PARSER = false; @NotNull - public Lexer createLexer(Project project) { - return new JavaLexer(LanguageLevelProjectExtension.getInstance(project).getLanguageLevel()); + public Lexer createLexer(final Project project) { + final LanguageLevel languageLevel = LanguageLevelProjectExtension.getInstance(project).getLanguageLevel(); + return createLexer(languageLevel); } @NotNull - public static Lexer createLexer(LanguageLevel languageLevel) { + public static Lexer createLexer(final LanguageLevel languageLevel) { return new JavaLexer(languageLevel); } @@ -72,11 +74,11 @@ public class JavaParserDefinition implements ParserDefinition { @NotNull public PsiParser createParser(final Project project) { - return PsiUtil.NULL_PARSER; + throw new UnsupportedOperationException("Should not be called directly"); } @NotNull - public PsiElement createElement(ASTNode node) { + public PsiElement createElement(final ASTNode node) { final IElementType type = node.getElementType(); if (type instanceof JavaStubElementType) { return ((JavaStubElementType)type).createPsi(node); @@ -85,7 +87,7 @@ public class JavaParserDefinition implements ParserDefinition { return PsiUtil.NULL_PSI_ELEMENT; } - public PsiFile createFile(FileViewProvider viewProvider) { + public PsiFile createFile(final FileViewProvider viewProvider) { return new PsiJavaFileImpl(viewProvider); } diff --git a/java/java-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java b/java/java-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java index 13e2cd8a1bc4..0436eacf5b61 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/JavaParserUtil.java @@ -172,7 +172,6 @@ public class JavaParserUtil { return builder; } - @NotNull public static ASTNode parseFragment(final ASTNode chameleon, final ParserWrapper wrapper) { final PsiElement psi = (chameleon.getTreeParent() != null ? chameleon.getTreeParent().getPsi() : chameleon.getPsi()); assert psi != null : chameleon; diff --git a/java/java-impl/src/com/intellij/psi/impl/java/stubs/JavaStubElementTypes.java b/java/java-impl/src/com/intellij/psi/impl/java/stubs/JavaStubElementTypes.java index 1356956b46fd..09341620fc05 100644 --- a/java/java-impl/src/com/intellij/psi/impl/java/stubs/JavaStubElementTypes.java +++ b/java/java-impl/src/com/intellij/psi/impl/java/stubs/JavaStubElementTypes.java @@ -22,30 +22,31 @@ package com.intellij.psi.impl.java.stubs; import com.intellij.psi.impl.source.JavaFileElementType; import com.intellij.psi.tree.IStubFileElementType; -public class JavaStubElementTypes { - public static final JavaClassElementType CLASS = new JavaClassElementType("CLASS"); - public static final JavaClassElementType ANONYMOUS_CLASS = new JavaClassElementType("ANONYMOUS_CLASS"); - public static final JavaClassElementType ENUM_CONSTANT_INITIALIZER = new JavaClassElementType("ENUM_CONSTANT_INITIALIZER"); +public interface JavaStubElementTypes { + JavaClassElementType CLASS = new JavaClassElementType("CLASS"); + JavaClassElementType ANONYMOUS_CLASS = new JavaClassElementType("ANONYMOUS_CLASS"); + JavaClassElementType ENUM_CONSTANT_INITIALIZER = new JavaClassElementType("ENUM_CONSTANT_INITIALIZER"); - public static final JavaModifierListElementType MODIFIER_LIST = new JavaModifierListElementType(); - public static final JavaMethodElementType METHOD = new JavaMethodElementType("METHOD"); - public static final JavaMethodElementType ANNOTATION_METHOD = new JavaMethodElementType("ANNOTATION_METHOD"); - public static final JavaFieldStubElementType FIELD = new JavaFieldStubElementType("FIELD"); - public static final JavaFieldStubElementType ENUM_CONSTANT = new JavaFieldStubElementType("ENUM_CONSTANT"); + JavaModifierListElementType MODIFIER_LIST = new JavaModifierListElementType(); + JavaMethodElementType METHOD = new JavaMethodElementType("METHOD"); + JavaMethodElementType ANNOTATION_METHOD = new JavaMethodElementType("ANNOTATION_METHOD"); + JavaFieldStubElementType FIELD = new JavaFieldStubElementType("FIELD"); + JavaFieldStubElementType ENUM_CONSTANT = new JavaFieldStubElementType("ENUM_CONSTANT"); - public static final JavaAnnotationElementType ANNOTATION = new JavaAnnotationElementType(); - public static final JavaClassReferenceListElementType EXTENDS_LIST = new JavaClassReferenceListElementType("EXTENDS_LIST"); - public static final JavaClassReferenceListElementType IMPLEMENTS_LIST = new JavaClassReferenceListElementType("IMPLEMENTS_LIST"); - public static final JavaClassReferenceListElementType THROWS_LIST = new JavaClassReferenceListElementType("THROWS_LIST"); - public static final JavaClassReferenceListElementType EXTENDS_BOUND_LIST = new JavaClassReferenceListElementType("EXTENDS_BOUND_LIST"); + JavaAnnotationElementType ANNOTATION = new JavaAnnotationElementType(); + JavaClassReferenceListElementType EXTENDS_LIST = new JavaClassReferenceListElementType("EXTENDS_LIST"); + JavaClassReferenceListElementType IMPLEMENTS_LIST = new JavaClassReferenceListElementType("IMPLEMENTS_LIST"); + JavaClassReferenceListElementType THROWS_LIST = new JavaClassReferenceListElementType("THROWS_LIST"); + JavaClassReferenceListElementType EXTENDS_BOUND_LIST = new JavaClassReferenceListElementType("EXTENDS_BOUND_LIST"); - public static final JavaParameterElementType PARAMETER = new JavaParameterElementType(); - public static final JavaParameterListElementType PARAMETER_LIST = new JavaParameterListElementType(); - public static final JavaTypeParameterElementType TYPE_PARAMETER = new JavaTypeParameterElementType(); - public static final JavaTypeParameterListElementType TYPE_PARAMETER_LIST = new JavaTypeParameterListElementType(); - public static final JavaClassInitializerElementType CLASS_INITIALIZER = new JavaClassInitializerElementType(); - public static final JavaImportListElementType IMPORT_LIST = new JavaImportListElementType(); - public static final JavaImportStatementElementType IMPORT_STATEMENT = new JavaImportStatementElementType("IMPORT_STATEMENT"); - public static final JavaImportStatementElementType IMPORT_STATIC_STATEMENT = new JavaImportStatementElementType("IMPORT_STATIC_STATEMENT"); - public static final IStubFileElementType JAVA_FILE = new JavaFileElementType(); + JavaParameterElementType PARAMETER = new JavaParameterElementType(); + JavaParameterListElementType PARAMETER_LIST = new JavaParameterListElementType(); + JavaTypeParameterElementType TYPE_PARAMETER = new JavaTypeParameterElementType(); + JavaTypeParameterListElementType TYPE_PARAMETER_LIST = new JavaTypeParameterListElementType(); + JavaClassInitializerElementType CLASS_INITIALIZER = new JavaClassInitializerElementType(); + JavaImportListElementType IMPORT_LIST = new JavaImportListElementType(); + JavaImportStatementElementType IMPORT_STATEMENT = new JavaImportStatementElementType("IMPORT_STATEMENT"); + JavaImportStatementElementType IMPORT_STATIC_STATEMENT = new JavaImportStatementElementType("IMPORT_STATIC_STATEMENT"); + + IStubFileElementType JAVA_FILE = new JavaFileElementType(); } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/psi/impl/java/stubs/JavaTypeParameterElementType.java b/java/java-impl/src/com/intellij/psi/impl/java/stubs/JavaTypeParameterElementType.java index 340631d81455..da82b0cc0fa3 100644 --- a/java/java-impl/src/com/intellij/psi/impl/java/stubs/JavaTypeParameterElementType.java +++ b/java/java-impl/src/com/intellij/psi/impl/java/stubs/JavaTypeParameterElementType.java @@ -34,7 +34,7 @@ import java.io.IOException; public class JavaTypeParameterElementType extends JavaStubElementType { public JavaTypeParameterElementType() { - super("TYPE_PARAMETR"); + super("TYPE_PARAMETER"); } public PsiTypeParameter createPsi(final PsiTypeParameterStub stub) { diff --git a/java/java-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java b/java/java-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java index 7733e9f9e14c..599833736b43 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/JavaFileElementType.java @@ -20,7 +20,11 @@ package com.intellij.psi.impl.source; import com.intellij.lang.ASTNode; +import com.intellij.lang.PsiBuilder; import com.intellij.lang.StdLanguages; +import com.intellij.lang.java.JavaParserDefinition; +import com.intellij.lang.java.parser.FileParser; +import com.intellij.lang.java.parser.JavaParserUtil; import com.intellij.lexer.JavaLexer; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.pom.java.LanguageLevel; @@ -41,7 +45,7 @@ import com.intellij.util.io.StringRef; import java.io.IOException; public class JavaFileElementType extends IStubFileElementType { - public static final int STUB_VERSION = 3; + public static final int STUB_VERSION = JavaParserDefinition.USE_NEW_PARSER ? 4 : 3; public JavaFileElementType() { super("java.FILE", StdLanguages.JAVA); @@ -61,7 +65,17 @@ public class JavaFileElementType extends IStubFileElementType { return dir == null || dir.getUserData(LanguageLevel.KEY) != null; } - public ASTNode parseContents(ASTNode chameleon) { + public ASTNode parseContents(final ASTNode chameleon) { + if (JavaParserDefinition.USE_NEW_PARSER) { + final PsiBuilder builder = JavaParserUtil.createBuilder(chameleon); + + final PsiBuilder.Marker root = builder.mark(); + FileParser.parse(builder); + root.done(this); + + return builder.getTreeBuilt().getFirstChildNode(); + } + FileElement node = (FileElement)chameleon; final CharSequence seq = node.getChars(); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java index 5e640c2d1dcf..405efbf3d657 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaElementType.java @@ -15,8 +15,12 @@ */ package com.intellij.psi.impl.source.tree; -import com.intellij.lang.ASTNode; -import com.intellij.lang.StdLanguages; +import com.intellij.lang.*; +import com.intellij.lang.java.JavaParserDefinition; +import com.intellij.lang.java.parser.ExpressionParser; +import com.intellij.lang.java.parser.JavaParserUtil; +import com.intellij.lang.java.parser.ReferenceParser; +import com.intellij.lang.java.parser.StatementParser; import com.intellij.lexer.JavaLexer; import com.intellij.lexer.Lexer; import com.intellij.openapi.project.Project; @@ -31,12 +35,13 @@ import com.intellij.psi.text.BlockSupport; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IErrorCounterReparseableElementType; import com.intellij.psi.tree.ILazyParseableElementType; +import com.intellij.psi.tree.ILightLazyParseableElementType; import com.intellij.psi.tree.java.IJavaElementType; import com.intellij.psi.util.PsiUtil; import com.intellij.util.CharTable; +import com.intellij.util.diff.FlyweightCapableTreeStructure; public interface JavaElementType { - IElementType CLASS = JavaStubElementTypes.CLASS; IElementType ANONYMOUS_CLASS = JavaStubElementTypes.ANONYMOUS_CLASS; IElementType ENUM_CONSTANT_INITIALIZER = JavaStubElementTypes.ENUM_CONSTANT_INITIALIZER; @@ -120,13 +125,24 @@ public interface JavaElementType { IElementType ANNOTATION_PARAMETER_LIST = new IJavaElementType("ANNOTATION_PARAMETER_LIST", true); IElementType METHOD_RECEIVER = new IJavaElementType("METHOD_RECEIVER"); - ILazyParseableElementType CODE_BLOCK = new IErrorCounterReparseableElementType("CODE_BLOCK", StdLanguages.JAVA) { + class ICodeBlockElementType extends IErrorCounterReparseableElementType { + private ICodeBlockElementType() { + super("CODE_BLOCK", StdLanguages.JAVA); + } + @Override - public ASTNode createNode(CharSequence text) { + public ASTNode createNode(final CharSequence text) { return new PsiCodeBlockImpl(text); } - public ASTNode parseContents(ASTNode chameleon) { + @Override + public ASTNode parseContents(final ASTNode chameleon) { + if (JavaParserDefinition.USE_NEW_PARSER) { + final PsiBuilder builder = JavaParserUtil.createBuilder(chameleon); + StatementParser.parseCodeBlockDeep(builder, true); + return builder.getTreeBuilt().getFirstChildNode(); + } + final CharSequence seq = chameleon.getChars(); ASTNode original = chameleon.getUserData(BlockSupport.TREE_TO_BE_REPARSED); @@ -137,20 +153,20 @@ public interface JavaElementType { final LanguageLevel languageLevel = PsiUtil.getLanguageLevel(TreeUtil.getFileElement((TreeElement)context).getPsi()); JavaParsingContext parsingContext = new JavaParsingContext(table, languageLevel); return parsingContext.getStatementParsing().parseCodeBlockText(manager, new JavaLexer(languageLevel), - seq, 0, seq.length(), 0).getFirstChildNode(); + seq, 0, seq.length(), 0).getFirstChildNode(); } - public int getErrorsCount(CharSequence seq, Project project) { + public int getErrorsCount(final CharSequence seq, final Project project) { final Lexer lexer = new JavaLexer(LanguageLevel.HIGHEST); lexer.start(seq); - if(lexer.getTokenType() != JavaTokenType.LBRACE) return FATAL_ERROR; + if (lexer.getTokenType() != JavaTokenType.LBRACE) return FATAL_ERROR; lexer.advance(); int balance = 1; - while(true){ + while (true) { IElementType type = lexer.getTokenType(); if (type == null) break; - if(balance == 0) return FATAL_ERROR; + if (balance == 0) return FATAL_ERROR; if (type == JavaTokenType.LBRACE) { balance++; } @@ -161,11 +177,29 @@ public interface JavaElementType { } return balance; } - }; + } + + class ICodeBlockNewElementType extends ICodeBlockElementType implements ILightLazyParseableElementType { + public FlyweightCapableTreeStructure parseContents(final LighterLazyParseableNode chameleon) { + final PsiBuilder builder = JavaParserUtil.createBuilder(chameleon); + StatementParser.parseCodeBlockDeep(builder, true); + return builder.getLightTree(); + } + } + + ILazyParseableElementType CODE_BLOCK = JavaParserDefinition.USE_NEW_PARSER ? new ICodeBlockNewElementType() : new ICodeBlockElementType(); + + IElementType STATEMENTS = new ICodeFragmentElementType("STATEMENTS", StdLanguages.JAVA) { + public ASTNode parseContents(final ASTNode chameleon) { + if (JavaParserDefinition.USE_NEW_PARSER) { + return JavaParserUtil.parseFragment(chameleon, + new JavaParserUtil.ParserWrapper() { + public void parse(final PsiBuilder builder) { + StatementParser.parseStatements(builder); + } + }); + } - //The following are the children of code fragment - IElementType STATEMENTS = new ICodeFragmentElementType("STATEMENTS", StdLanguages.JAVA){ - public ASTNode parseContents(ASTNode chameleon) { final CharSequence chars = chameleon.getChars(); final PsiManager manager = ((FileElement)chameleon).getManager(); final CharTable table = SharedImplUtil.findCharTableByTree(chameleon); @@ -174,8 +208,17 @@ public interface JavaElementType { } }; - IElementType EXPRESSION_TEXT = new ICodeFragmentElementType("EXPRESSION_TEXT", StdLanguages.JAVA){ - public ASTNode parseContents(ASTNode chameleon) { + IElementType EXPRESSION_TEXT = new ICodeFragmentElementType("EXPRESSION_TEXT", StdLanguages.JAVA) { + public ASTNode parseContents(final ASTNode chameleon) { + if (JavaParserDefinition.USE_NEW_PARSER) { + return JavaParserUtil.parseFragment(chameleon, + new JavaParserUtil.ParserWrapper() { + public void parse(final PsiBuilder builder) { + ExpressionParser.parse(builder); + } + }); + } + final CharSequence chars = chameleon.getChars(); final PsiManager manager = ((FileElement)chameleon).getManager(); final JavaParsingContext context = new JavaParsingContext(SharedImplUtil.findCharTableByTree(chameleon), LanguageLevel.HIGHEST); @@ -183,17 +226,37 @@ public interface JavaElementType { } }; - IElementType REFERENCE_TEXT = new ICodeFragmentElementType("REFERENCE_TEXT", StdLanguages.JAVA){ - public ASTNode parseContents(ASTNode chameleon) { + IElementType REFERENCE_TEXT = new ICodeFragmentElementType("REFERENCE_TEXT", StdLanguages.JAVA) { + public ASTNode parseContents(final ASTNode chameleon) { + if (JavaParserDefinition.USE_NEW_PARSER) { + return JavaParserUtil.parseFragment(chameleon, + new JavaParserUtil.ParserWrapper() { + public void parse(final PsiBuilder builder) { + ReferenceParser.parseJavaCodeReference(builder, false, true, false, false); + } + }); + } + final CharSequence chars = chameleon.getChars(); - return Parsing.parseJavaCodeReferenceText(((FileElement)chameleon).getManager(), chars, 0, chars.length(), SharedImplUtil.findCharTableByTree(chameleon), true); + return Parsing.parseJavaCodeReferenceText(((FileElement)chameleon).getManager(), chars, 0, chars.length(), + SharedImplUtil.findCharTableByTree(chameleon), true); } }; - IElementType TYPE_TEXT = new ICodeFragmentElementType("TYPE_TEXT", StdLanguages.JAVA){ - public ASTNode parseContents(ASTNode chameleon) { + IElementType TYPE_TEXT = new ICodeFragmentElementType("TYPE_TEXT", StdLanguages.JAVA) { + public ASTNode parseContents(final ASTNode chameleon) { + if (JavaParserDefinition.USE_NEW_PARSER) { + return JavaParserUtil.parseFragment(chameleon, + new JavaParserUtil.ParserWrapper() { + public void parse(final PsiBuilder builder) { + ReferenceParser.parseType(builder); + } + }); + } + final CharSequence chars = chameleon.getChars(); - return Parsing.parseTypeText(((FileElement)chameleon).getManager(), null, chars, 0, chars.length(), 0, SharedImplUtil.findCharTableByTree(chameleon)); + return Parsing.parseTypeText(((FileElement)chameleon).getManager(), null, chars, 0, chars.length(), 0, + SharedImplUtil.findCharTableByTree(chameleon)); } }; } diff --git a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeInfoImpl.java b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeInfoImpl.java index 1f2a8ee61b94..6557f79c4285 100644 --- a/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeInfoImpl.java +++ b/java/java-impl/src/com/intellij/refactoring/changeSignature/JavaChangeInfoImpl.java @@ -411,7 +411,7 @@ class JavaChangeInfoImpl implements JavaChangeInfo { if (!newName.equals(that.newName)) return false; if (newNameIdentifier != null ? !newNameIdentifier.equals(that.newNameIdentifier) : that.newNameIdentifier != null) return false; if (!Arrays.equals(newParms, that.newParms)) return false; - if (newReturnType != null ? !Comparing.strEqual(newReturnType.getTypeText(), that.newReturnType.getTypeText()) : that.newReturnType != null) return false; + if (newReturnType != null ? that.newReturnType == null || !Comparing.strEqual(newReturnType.getTypeText(), that.newReturnType.getTypeText()) : that.newReturnType != null) return false; if (newVisibility != null ? !newVisibility.equals(that.newVisibility) : that.newVisibility != null) return false; if (!oldName.equals(that.oldName)) return false; if (!Arrays.equals(oldParameterNames, that.oldParameterNames)) return false; diff --git a/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceConstantDialog.java b/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceConstantDialog.java index ad7deb60bc12..cbfa0d58a4e9 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceConstantDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceConstantDialog.java @@ -124,6 +124,7 @@ class IntroduceConstantDialog extends DialogWrapper { myVPanel.setVisibility(JavaRefactoringSettings.getInstance().INTRODUCE_CONSTANT_VISIBILITY); myIntroduceEnumConstantCb.setEnabled(EnumConstantsUtil.isSuitableForEnumConstant(getSelectedType(), myTargetClass)); updateVisibilityPanel(); + updateButtons(); } public String getEnteredName() { @@ -172,7 +173,11 @@ class IntroduceConstantDialog extends DialogWrapper { myNameField = new NameSuggestionsField(myProject); myNameSuggestionPanel.setLayout(new BorderLayout()); - + myNameField.addDataChangedListener(new NameSuggestionsField.DataChanged() { + public void dataChanged() { + updateButtons(); + } + }); myNameSuggestionPanel.add(myNameField.getComponent(), BorderLayout.CENTER); myNameSuggestionLabel.setLabelFor(myNameField.getFocusableComponent()); @@ -280,6 +285,10 @@ class IntroduceConstantDialog extends DialogWrapper { return myPanel; } + private void updateButtons() { + setOKActionEnabled(JavaPsiFacade.getInstance(myProject).getNameHelper().isIdentifier(getEnteredName())); + } + private void targetClassChanged() { final String targetClassName = getTargetClassName(); myTargetClass = JavaPsiFacade.getInstance(myProject).findClass(targetClassName, GlobalSearchScope.projectScope(myProject)); diff --git a/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceFieldDialog.java b/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceFieldDialog.java index 07e75a9ca071..191968ba39fc 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceFieldDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceField/IntroduceFieldDialog.java @@ -30,7 +30,6 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.HelpID; import com.intellij.refactoring.JavaRefactoringSettings; import com.intellij.refactoring.RefactoringBundle; -import static com.intellij.refactoring.introduceField.BaseExpressionToFieldHandler.InitializationPlace.*; import com.intellij.refactoring.ui.*; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.RefactoringMessageUtil; @@ -43,6 +42,8 @@ import java.awt.*; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; +import static com.intellij.refactoring.introduceField.BaseExpressionToFieldHandler.InitializationPlace.*; + class IntroduceFieldDialog extends DialogWrapper { private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.introduceField.IntroduceFieldDialog"); @@ -103,7 +104,7 @@ class IntroduceFieldDialog extends DialogWrapper { init(); initializeControls(initializerExpression); - + updateButtons(); } private void initializeControls(PsiExpression initializerExpression) { @@ -270,6 +271,11 @@ class IntroduceFieldDialog extends DialogWrapper { gbConstraints.gridy = 1; myNameField = new NameSuggestionsField(myProject); panel.add(myNameField.getComponent(), gbConstraints); + myNameField.addDataChangedListener(new NameSuggestionsField.DataChanged() { + public void dataChanged() { + updateButtons(); + } + }); namePrompt.setLabelFor(myNameField.getFocusableComponent()); myNameSuggestionsManager = new NameSuggestionsManager(myTypeSelector, myNameField, createGenerator()); @@ -278,6 +284,10 @@ class IntroduceFieldDialog extends DialogWrapper { return panel; } + private void updateButtons() { + setOKActionEnabled(JavaPsiFacade.getInstance(myProject).getNameHelper().isIdentifier(getEnteredName())); + } + private String getTypeLabel() { return myWillBeDeclaredStatic ? RefactoringBundle.message("introduce.field.static.field.of.type") : diff --git a/java/java-impl/src/com/intellij/refactoring/rename/RenameAliasingPomTargetProcessor.java b/java/java-impl/src/com/intellij/refactoring/rename/RenameAliasingPomTargetProcessor.java index 22a9c1422019..328e1800f188 100644 --- a/java/java-impl/src/com/intellij/refactoring/rename/RenameAliasingPomTargetProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/rename/RenameAliasingPomTargetProcessor.java @@ -17,6 +17,7 @@ package com.intellij.refactoring.rename; import com.intellij.openapi.extensions.Extensions; import com.intellij.pom.PomTarget; +import com.intellij.pom.PomTargetPsiElement; import com.intellij.pom.references.PomService; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiTarget; @@ -30,14 +31,14 @@ public class RenameAliasingPomTargetProcessor extends RenamePsiElementProcessor @Override public boolean canProcessElement(@Nullable PsiElement element) { - return element instanceof PomTarget; + return element instanceof PomTargetPsiElement; } @Override public void prepareRenaming(PsiElement element, String newName, Map allRenames) { - if (element instanceof PsiTarget) { + if (element instanceof PomTargetPsiElement) { for (AliasingPsiTargetMapper mapper : Extensions.getExtensions(AliasingPsiTargetMapper.EP_NAME)) { - for (AliasingPsiTarget psiTarget : mapper.getTargets((PsiTarget)element)) { + for (AliasingPsiTarget psiTarget : mapper.getTargets(((PomTargetPsiElement)element).getTarget())) { allRenames.put(PomService.convertToPsi(psiTarget), psiTarget.getNameAlias(newName)); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/afterVarargs1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/afterVarargs1.java new file mode 100644 index 000000000000..1a184a35cf55 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/afterVarargs1.java @@ -0,0 +1,6 @@ +// "Change signature of 'f(int...)' to 'f(String, int...)'" "true" + public class S { + void f(String s, int... args) { + f("",1,1); + } + } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/afterVarargs2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/afterVarargs2.java new file mode 100644 index 000000000000..ee911a36a67c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/afterVarargs2.java @@ -0,0 +1,6 @@ +// "Change signature of 'f(int...)' to 'f(String, int...)'" "true" + public class S { + void f(String s, int... args) { + f("",null); + } + } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/beforeVarargs1.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/beforeVarargs1.java new file mode 100644 index 000000000000..dc0b261bdea0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/beforeVarargs1.java @@ -0,0 +1,6 @@ +// "Change signature of 'f(int...)' to 'f(String, int...)'" "true" + public class S { + void f(int... args) { + f("",1,1); + } + } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/beforeVarargs2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/beforeVarargs2.java new file mode 100644 index 000000000000..624af318f71f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/beforeVarargs2.java @@ -0,0 +1,6 @@ +// "Change signature of 'f(int...)' to 'f(String, int...)'" "true" + public class S { + void f(int... args) { + f("",null); + } + } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/beforeVarargs2Array.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/beforeVarargs2Array.java new file mode 100644 index 000000000000..4f5842e826bf --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/changeMethodSignatureFromUsage/beforeVarargs2Array.java @@ -0,0 +1,6 @@ +// "Change signature of 'f(int...)' to 'f(int..., String)'" "false" + public class S { + void f(int... args) { + f(1,1, ""); + } + } diff --git a/java/java-tests/testData/refactoring/introduceVariable/DifferentForeachParameters.after.java b/java/java-tests/testData/refactoring/introduceVariable/DifferentForeachParameters.after.java new file mode 100644 index 000000000000..a021e09a2144 --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceVariable/DifferentForeachParameters.after.java @@ -0,0 +1,12 @@ +class A { + { + List l = new ArrayList(); + for (Integer i : l) { + final String tostr = i.toString(); + System.out.println(tostr); + } + for (Integer i : l) { + System.out.println(i.toString()); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/introduceVariable/DifferentForeachParameters.java b/java/java-tests/testData/refactoring/introduceVariable/DifferentForeachParameters.java new file mode 100644 index 000000000000..3683bfac097b --- /dev/null +++ b/java/java-tests/testData/refactoring/introduceVariable/DifferentForeachParameters.java @@ -0,0 +1,11 @@ +class A { + { + List l = new ArrayList(); + for (Integer i : l) { + System.out.println(i.toString()); + } + for (Integer i : l) { + System.out.println(i.toString()); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/JavaParsingTestCase.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/JavaParsingTestCase.java index ad44fd557b02..bcd536811c1f 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/JavaParsingTestCase.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/JavaParsingTestCase.java @@ -18,6 +18,7 @@ package com.intellij.lang.java.parser; import com.intellij.lang.ASTNode; import com.intellij.lang.PsiBuilder; import com.intellij.lang.StdLanguages; +import com.intellij.lang.java.JavaParserDefinition; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.pom.java.LanguageLevel; @@ -120,7 +121,9 @@ public abstract class JavaParsingTestCase extends ParsingTestCase { @Override protected void doTest(final boolean checkResult) { doTestDefaultParser(checkResult); - doTestNewParser(); + if (JavaParserDefinition.USE_NEW_PARSER) { + doTestNewParser(); + } } protected void doTestDefaultParser(final boolean checkResult) { diff --git a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java index bf100c8ea6f6..6d10533e1aeb 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/IntroduceVariableTest.java @@ -218,6 +218,10 @@ public class IntroduceVariableTest extends LightCodeInsightTestCase { doTest(new MockIntroduceVariableHandler("foo1", true, true, false, "Foo")); } + public void testDifferentForeachParameters () throws Exception { + doTest(new MockIntroduceVariableHandler("tostr", true, true, false, "java.lang.String")); + } + public void testSiblingInnerClassType() throws Exception { doTest(new MockIntroduceVariableHandler("vari", true, false, false, "A.B"){ @Override diff --git a/java/openapi/src/com/intellij/psi/PsiJavaFile.java b/java/openapi/src/com/intellij/psi/PsiJavaFile.java index b7a5eedc6747..0b1357f5fc9f 100644 --- a/java/openapi/src/com/intellij/psi/PsiJavaFile.java +++ b/java/openapi/src/com/intellij/psi/PsiJavaFile.java @@ -57,7 +57,7 @@ public interface PsiJavaFile extends PsiImportHolder, PsiClassOwner { @NotNull PsiElement[] getOnDemandImports(boolean includeImplicit, @Deprecated boolean checkIncludes); /** - * Returns the list of classs which have been imported as + * Returns the list of classes which have been imported as * single-class imports. * * @param checkIncludes deprecated, no longer used. diff --git a/platform/lang-api/src/com/intellij/codeInsight/lookup/LookupElement.java b/platform/lang-api/src/com/intellij/codeInsight/lookup/LookupElement.java index f347e1ad2674..10ad5a4984c0 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/lookup/LookupElement.java +++ b/platform/lang-api/src/com/intellij/codeInsight/lookup/LookupElement.java @@ -18,6 +18,7 @@ package com.intellij.codeInsight.lookup; import com.intellij.codeInsight.completion.InsertionContext; import com.intellij.codeInsight.completion.PrefixMatcher; import com.intellij.openapi.util.UserDataHolderBase; +import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -61,6 +62,14 @@ public abstract class LookupElement extends UserDataHolderBase { return this; } + public boolean isValid() { + final Object object = getObject(); + if (object instanceof PsiElement) { + return ((PsiElement)object).isValid(); + } + return true; + } + public void handleInsert(InsertionContext context) { } diff --git a/platform/lang-api/src/com/intellij/openapi/project/ProjectLocatorImpl.java b/platform/lang-api/src/com/intellij/openapi/project/ProjectLocatorImpl.java index d5a1b0ff3790..4b4c86e1d670 100644 --- a/platform/lang-api/src/com/intellij/openapi/project/ProjectLocatorImpl.java +++ b/platform/lang-api/src/com/intellij/openapi/project/ProjectLocatorImpl.java @@ -21,13 +21,19 @@ package com.intellij.openapi.project; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.HashSet; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; + public class ProjectLocatorImpl extends ProjectLocator { + + @Override @Nullable public Project guessProjectForFile(final VirtualFile file) { ProjectManager projectManager = ProjectManager.getInstance(); - if (projectManager == null) return null; + if (projectManager == null || file == null) return null; final Project[] projects = projectManager.getOpenProjects(); if (projects.length == 0) return null; if (projects.length == 1 && !projects[0].isDisposed()) return projects[0]; @@ -40,4 +46,23 @@ public class ProjectLocatorImpl extends ProjectLocator { return !projects[0].isDisposed() ? projects[0] : null; } + + @Override + @NotNull + public Collection getProjectsForFile(VirtualFile file) { + final Collection result = new HashSet(); + + final ProjectManager projectManager = ProjectManager.getInstance(); + if (projectManager == null || file == null) { return result; } + final Project[] openProjects = projectManager.getOpenProjects(); + if (openProjects.length == 0) { return result; } + + for (Project project : openProjects) { + if (project.isInitialized() && !project.isDisposed() && ProjectRootManager.getInstance(project).getFileIndex().isInContent(file)) { + result.add(project); + } + } + + return result; + } } \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/psi/targets/AliasingPsiTargetMapper.java b/platform/lang-api/src/com/intellij/psi/targets/AliasingPsiTargetMapper.java index a3bdd72841c9..ed4c81f6d91c 100644 --- a/platform/lang-api/src/com/intellij/psi/targets/AliasingPsiTargetMapper.java +++ b/platform/lang-api/src/com/intellij/psi/targets/AliasingPsiTargetMapper.java @@ -10,5 +10,5 @@ public interface AliasingPsiTargetMapper { ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.aliasingPsiTargetMapper"); @NotNull - Set getTargets(@NotNull PomTarget psiTarget); + Set getTargets(@NotNull PomTarget target); } diff --git a/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java b/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java index 3b91e799430a..8e52ff63a496 100644 --- a/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java +++ b/platform/lang-impl/src/com/intellij/application/options/codeStyle/OptionTableWithPreviewPanel.java @@ -304,15 +304,6 @@ public abstract class OptionTableWithPreviewPanel extends MultilanguageCodeStyle return false; } - protected void addOption(Class clazz, @NotNull String fieldName, @NotNull String title, @Nullable String groupName) { - myOptions.add(new BooleanOption(clazz, fieldName, title, groupName)); - } - - protected void addOption(Class clazz, @NotNull String fieldName, @NotNull String title, @Nullable String groupName, - @NotNull String[] options, @NotNull int[] values) { - myOptions.add(new SelectionOption(clazz, fieldName, title, groupName, options, values)); - } - protected void addOption(@NotNull String fieldName, @NotNull String title) { addOption(fieldName, title, null); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java b/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java index dd345b787bb8..84f68406ecfd 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/AutoPopupController.java @@ -96,12 +96,8 @@ public class AutoPopupController implements Disposable { new CodeCompletionHandlerBase(CompletionType.BASIC, false, false).invoke(myProject, editor, file); } }; - // invoke later prevents cancelling request by keyPressed from the same action - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - myAlarm.addRequest(request, settings.AUTO_LOOKUP_DELAY); - } - }); + + myAlarm.addRequest(request, settings.AUTO_LOOKUP_DELAY); } } @@ -112,12 +108,7 @@ public class AutoPopupController implements Disposable { currentCompletion.closeAndFinish(); } - // invoke later prevents cancelling request by keyPressed from the same action - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - myAlarm.addRequest(request, delay); - } - }); + myAlarm.addRequest(request, delay); } public void autoPopupParameterInfo(final Editor editor, final PsiElement highlightedMethod){ @@ -145,12 +136,8 @@ public class AutoPopupController implements Disposable { } } }; - // invoke later prevents cancelling request by keyPressed from the same action - ApplicationManager.getApplication().invokeLater(new Runnable() { - public void run() { - myAlarm.addRequest(request, settings.PARAMETER_INFO_DELAY); - } - }); + + myAlarm.addRequest(request, settings.PARAMETER_INFO_DELAY); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java index 231c333f5494..ff48b86419dd 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java @@ -539,7 +539,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement } public boolean isRepeatedInvocation(CompletionType completionType, Editor editor) { - return completionType == myParameters.getCompletionType() && editor == myEditor; + return completionType == myParameters.getCompletionType() && editor == myEditor && !isAutopopupCompletion(); } @Override diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupCellRenderer.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupCellRenderer.java index 6b33ce20c23c..73be3712c072 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupCellRenderer.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupCellRenderer.java @@ -116,11 +116,8 @@ public class LookupCellRenderer implements ListCellRenderer { boolean hasFocus) { - if (isSelected && !myLookup.isFocused() && myLookup.isCompletion()) { - //myPanel.setBorder(new DottedBorder(Color.gray)); + if (!myLookup.isFocused()) { isSelected = false; - } else { - //myPanel.setBorder(null); } final LookupElement item = (LookupElement)value; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java index 956b574eafc7..e3553d0d19f7 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/LookupImpl.java @@ -112,7 +112,7 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable { public LookupImpl(Project project, Editor editor, @NotNull LookupArranger arranger){ super(new JPanel(new BorderLayout())); - setForceShowAsPopup(!SystemInfo.isLinux); + setForceShowAsPopup(true); myProject = project; myEditor = editor; myArranger = arranger; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemReplaceAction.java b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemReplaceAction.java index 108e31c830ac..632cdad20288 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemReplaceAction.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/lookup/impl/actions/ChooseItemReplaceAction.java @@ -42,6 +42,6 @@ public class ChooseItemReplaceAction extends EditorAction { public void update(Editor editor, Presentation presentation, DataContext dataContext){ LookupImpl lookup = (LookupImpl)LookupManager.getActiveLookup(editor); - presentation.setEnabled(lookup != null && lookup.isFocused()); + presentation.setEnabled(lookup != null && (lookup.isFocused() || lookup.isCompletion() && !lookup.getItems().isEmpty())); } } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.form b/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.form index ea5041d0e3e2..f92d8034f5be 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.form +++ b/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.form @@ -1,6 +1,6 @@
- + @@ -17,21 +17,14 @@ - + - - - - - - - @@ -50,6 +43,17 @@ + + + + + + + + + + + diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.java b/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.java index b90e2dfc4f18..d285dea2b374 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateDialog.java @@ -41,60 +41,19 @@ import java.awt.event.KeyEvent; * @author peter */ public class CreateFileFromTemplateDialog extends DialogWrapper { - private static final Logger LOG = Logger.getInstance("#com.intellij.ide.actions.CreateFileFromTemplateDialog"); private JTextField myNameField; - private JComboBox myKindCombo; + private TemplateKindCombo myKindCombo; private JPanel myPanel; private JLabel myUpDownHint; + private JLabel myKindLabel; private ElementCreator myCreator; private CreateFileFromTemplateDialog(@NotNull Project project) { super(project, true); - myKindCombo.setRenderer(new DefaultListCellRenderer() { - @Override - public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); - @SuppressWarnings({"unchecked"}) Trinity trinity = (Trinity) value; - setText(trinity == null ? "" : trinity.first); - setIcon(trinity == null ? null : trinity.second); - return this; - } - }); - - //myNameLabel.setText(prompt); - - new ComboboxSpeedSearch(myKindCombo) { - @Override - protected String getElementText(Object element) { - return ((Trinity)element).first; - } - }; - - final AnAction arrow = new AnAction() { - @Override - public void actionPerformed(AnActionEvent e) { - if (e.getInputEvent() instanceof KeyEvent) { - final int code = ((KeyEvent)e.getInputEvent()).getKeyCode(); - final int delta = code == KeyEvent.VK_DOWN ? 1 : code == KeyEvent.VK_UP ? -1 : 0; - - final int size = myKindCombo.getModel().getSize(); - int next = myKindCombo.getSelectedIndex() + delta; - if (next < 0 || next >= size) { - if (!UISettings.getInstance().CYCLE_SCROLLING) { - return; - } - next = (next + size) % size; - } - myKindCombo.setSelectedIndex(next); - } - } - }; - final KeyboardShortcut up = new KeyboardShortcut(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), null); - final KeyboardShortcut down = new KeyboardShortcut(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), null); - arrow.registerCustomShortcutSet(new CustomShortcutSet(up, down), myNameField); - + myKindLabel.setLabelFor(myKindCombo); + myKindCombo.registerUpDownHint(myNameField); myUpDownHint.setIcon(Icons.UP_DOWN_ARROWS); init(); } @@ -103,16 +62,6 @@ public class CreateFileFromTemplateDialog extends DialogWrapper { return myNameField.getText(); } - private String getTemplateName() { - //noinspection unchecked - final Trinity trinity = (Trinity)myKindCombo.getSelectedItem(); - if (trinity == null) { - LOG.error("Model: " + myKindCombo.getModel()); - } - - return trinity.third; - } - @Override protected JComponent createCenterPanel() { return myPanel; @@ -143,36 +92,23 @@ public class CreateFileFromTemplateDialog extends DialogWrapper { } public Builder addKind(@NotNull String name, @Nullable Icon icon, @NotNull String templateName) { - dialog.myKindCombo.addItem(new Trinity(name, icon, templateName)); + dialog.myKindCombo.addItem(name, icon, templateName); return this; } public T show(@NotNull String errorTitle, @Nullable String selectedTemplateName, @NotNull final FileCreator creator) { final Ref created = Ref.create(null); - if (selectedTemplateName != null) { - Object item = null; - ComboBoxModel model = dialog.myKindCombo.getModel(); - for (int i = 0, n = model.getSize(); i < n; i++) { - Trinity trinity = (Trinity)model.getElementAt(i); - if (selectedTemplateName.equals(trinity.third)) { - item = trinity; - break; - } - } - if (item != null) { - dialog.myKindCombo.setSelectedItem(item); - } - } + dialog.myKindCombo.setSelectedName(selectedTemplateName); dialog.myCreator = new ElementCreator(project, errorTitle) { @Override protected void checkBeforeCreate(String newName) throws IncorrectOperationException { - creator.checkBeforeCreate(newName, dialog.getTemplateName()); + creator.checkBeforeCreate(newName, dialog.myKindCombo.getSelectedName()); } @Override protected PsiElement[] create(String newName) throws Exception { - final T element = creator.createFile(dialog.getEnteredName(), dialog.getTemplateName()); + final T element = creator.createFile(dialog.getEnteredName(), dialog.myKindCombo.getSelectedName()); created.set(element); if (element != null) { return new PsiElement[]{element}; @@ -182,7 +118,7 @@ public class CreateFileFromTemplateDialog extends DialogWrapper { @Override protected String getActionName(String newName) { - return creator.getActionName(newName, dialog.getTemplateName()); + return creator.getActionName(newName, dialog.myKindCombo.getSelectedName()); } }; dialog.show(); diff --git a/platform/lang-impl/src/com/intellij/ide/actions/TemplateKindCombo.java b/platform/lang-impl/src/com/intellij/ide/actions/TemplateKindCombo.java new file mode 100644 index 000000000000..9b2937d1f0c8 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/actions/TemplateKindCombo.java @@ -0,0 +1,119 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.ide.actions; + +import com.intellij.ide.ui.ListCellRendererWrapper; +import com.intellij.ide.ui.UISettings; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CustomShortcutSet; +import com.intellij.openapi.actionSystem.KeyboardShortcut; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Trinity; +import com.intellij.ui.ComboboxSpeedSearch; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.event.KeyEvent; + +public class TemplateKindCombo extends JComboBox { + private static final Logger LOG = Logger.getInstance("#com.intellij.ide.actions.TemplateKindCombo"); + + public TemplateKindCombo() { + super(); + + setRenderer(new ListCellRendererWrapper(getRenderer()) { + @Override + public String getDisplayedName(Object value) { + if (value instanceof Trinity) { + return (String)((Trinity)value).first; + } + return null; + } + + @Override + public Icon getIcon(Object value) { + if (value instanceof Trinity) { + return (Icon)((Trinity)value).second; + } + return null; + } + }); + + new ComboboxSpeedSearch(this) { + @Override + protected String getElementText(Object element) { + if (element instanceof Trinity) { + return (String)((Trinity)element).first; + } + return null; + } + }; + } + + public void addItem(String presentableName, Icon icon, String templateName) { + addItem(new Trinity(presentableName, icon, templateName)); + } + + public String getSelectedName() { + //noinspection unchecked + final Trinity trinity = (Trinity)getSelectedItem(); + if (trinity == null) { + LOG.error("Model: " + getModel()); + } + return trinity.third; + } + + public void setSelectedName(@Nullable String name) { + if (name == null) return; + ComboBoxModel model = getModel(); + for (int i = 0, n = model.getSize(); i < n; i++) { + Trinity trinity = (Trinity)model.getElementAt(i); + if (name.equals(trinity.third)) { + setSelectedItem(trinity); + return; + } + } + } + + public void registerUpDownHint(JComponent component) { + final AnAction arrow = new AnAction() { + @Override + public void actionPerformed(AnActionEvent e) { + if (e.getInputEvent() instanceof KeyEvent) { + final int code = ((KeyEvent)e.getInputEvent()).getKeyCode(); + scrollBy(code == KeyEvent.VK_DOWN ? 1 : code == KeyEvent.VK_UP ? -1 : 0); + } + } + }; + final KeyboardShortcut up = new KeyboardShortcut(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), null); + final KeyboardShortcut down = new KeyboardShortcut(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), null); + arrow.registerCustomShortcutSet(new CustomShortcutSet(up, down), component); + } + + private void scrollBy(int delta) { + final int size = getModel().getSize(); + int next = getSelectedIndex() + delta; + if (next < 0 || next >= size) { + if (!UISettings.getInstance().CYCLE_SCROLLING) { + return; + } + next = (next + size) % size; + } + setSelectedIndex(next); + } + +} diff --git a/platform/lang-impl/src/com/intellij/ide/impl/dataRules/NavigatableRule.java b/platform/lang-impl/src/com/intellij/ide/impl/dataRules/NavigatableRule.java index 91daabb008c9..a4aa0efa9d76 100644 --- a/platform/lang-impl/src/com/intellij/ide/impl/dataRules/NavigatableRule.java +++ b/platform/lang-impl/src/com/intellij/ide/impl/dataRules/NavigatableRule.java @@ -34,8 +34,10 @@ public class NavigatableRule implements GetDataRule { return openFileDescriptor; } } - final PsiElement element = LangDataKeys.PSI_ELEMENT.getData(dataProvider); + if (element instanceof Navigatable) { + return element; + } if (element != null) { return EditSourceUtil.getDescriptor(element); } diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextConfigurable.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextConfigurable.java index debfaf854b92..9dc58225f6a3 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextConfigurable.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/LangScriptingContextConfigurable.java @@ -16,28 +16,31 @@ package com.intellij.ide.scriptingContext; import com.intellij.ide.scriptingContext.ui.ScriptingLibrariesPanel; +import com.intellij.openapi.application.Application; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ModifiableRootModel; +import com.intellij.openapi.roots.libraries.LibraryTable; import org.jetbrains.annotations.Nls; import javax.swing.*; /** - * @author Rustam Vishnyakov + * @author Rustam Vishnyakov */ public class LangScriptingContextConfigurable implements Configurable { private ScriptingLibrariesPanel myPanel; private LangScriptingContextProvider myProvider; private ModifiableRootModel myRootModel; - public LangScriptingContextConfigurable(Project project, LangScriptingContextProvider provider) { - myRootModel = ScriptingLibraryManager.getRootModel(project); - if (myRootModel != null) { - myPanel = new ScriptingLibrariesPanel(myRootModel.getModuleLibraryTable()); - } + public LangScriptingContextConfigurable(ModifiableRootModel rootModel, LangScriptingContextProvider provider) { + LibraryTable libTable = rootModel != null ? + rootModel.getModuleLibraryTable() : null; + myPanel = new ScriptingLibrariesPanel(libTable); myProvider = provider; + myRootModel = rootModel; } @Nls @@ -63,12 +66,19 @@ public class LangScriptingContextConfigurable implements Configurable { @Override public boolean isModified() { - return false; //To change body of implemented methods use File | Settings | File Templates. + return myPanel.isModified(); } @Override public void apply() throws ConfigurationException { - //To change body of implemented methods use File | Settings | File Templates. + if (myRootModel != null) { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + myRootModel.commit(); + } + }); + } } @Override @@ -78,6 +88,6 @@ public class LangScriptingContextConfigurable implements Configurable { @Override public void disposeUIResources() { - myRootModel.dispose(); + } } diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ScriptingContextsConfigurable.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ScriptingContextsConfigurable.java index 3e905752bb16..c04705b24006 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ScriptingContextsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ScriptingContextsConfigurable.java @@ -16,9 +16,11 @@ package com.intellij.ide.scriptingContext; import com.intellij.ide.scriptingContext.ui.MainScriptingContextsPanel; +import com.intellij.openapi.Disposable; import com.intellij.openapi.options.Configurable; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ModifiableRootModel; import org.jetbrains.annotations.Nls; import javax.swing.*; @@ -32,11 +34,13 @@ public class ScriptingContextsConfigurable implements Configurable, Configurable private MainScriptingContextsPanel myPanel; private String myLangNames; private Project myProject; + private ModifiableRootModel myRootModel; public ScriptingContextsConfigurable(Project project) { myPanel = new MainScriptingContextsPanel(); myLangNames = getLangNames(); myProject = project; + myRootModel = ScriptingLibraryManager.getRootModel(project); } @Nls @@ -67,7 +71,9 @@ public class ScriptingContextsConfigurable implements Configurable, Configurable @Override public void apply() throws ConfigurationException { - //To change body of implemented methods use File | Settings | File Templates. + if (myRootModel != null) { + myRootModel.commit(); + } } @Override @@ -77,14 +83,14 @@ public class ScriptingContextsConfigurable implements Configurable, Configurable @Override public void disposeUIResources() { - //To change body of implemented methods use File | Settings | File Templates. + disposeModel(); } @Override public Configurable[] getConfigurables() { ArrayList configurables = new ArrayList(); for (LangScriptingContextProvider provider : LangScriptingContextProvider.getProviders()) { - configurables.add(new LangScriptingContextConfigurable(myProject, provider)); + configurables.add(new LangScriptingContextConfigurable(myRootModel, provider)); } return configurables.toArray(new LangScriptingContextConfigurable[configurables.size()]); } @@ -97,4 +103,10 @@ public class ScriptingContextsConfigurable implements Configurable, Configurable String result = buf.toString(); return result.substring(0, result.length() - 1); } + + public void disposeModel() { + if (myRootModel != null && !myRootModel.isDisposed()) { + myRootModel.dispose(); + } + } } diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/EditLibraryDialog.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/EditLibraryDialog.java index f85c173e20ce..57f17d321bcb 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/EditLibraryDialog.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/EditLibraryDialog.java @@ -36,4 +36,8 @@ public class EditLibraryDialog extends DialogWrapper { protected JComponent createCenterPanel() { return contentPane; } + + public String getLibName() { + return myLibName.getText(); + } } diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingLibrariesPanel.form b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingLibrariesPanel.form index 556426636b74..afef1273f22e 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingLibrariesPanel.form +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingLibrariesPanel.form @@ -29,7 +29,7 @@ - + @@ -45,7 +45,7 @@ - + diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingLibrariesPanel.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingLibrariesPanel.java index 4c60594b1c00..dd5957d05115 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingLibrariesPanel.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingLibrariesPanel.java @@ -15,10 +15,7 @@ */ package com.intellij.ide.scriptingContext.ui; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.libraries.LibraryTable; -import com.intellij.ui.components.JBList; import com.intellij.ui.table.JBTable; import javax.swing.*; @@ -30,20 +27,29 @@ import java.awt.event.ActionListener; */ public class ScriptingLibrariesPanel { private JPanel myTopPanel; - private JButton myAddLibrarytButton; + private JButton myAddLibraryButton; private JButton myRemoveLibraryButton; - private JButton myEditLibrarytButton; + private JButton myEditLibraryButton; private JPanel myScriptingLibrariesPanel; private JBTable myLibraryTable; + private ScriptingLibraryTableModel myLibTableModel; + private boolean myModified; public ScriptingLibrariesPanel(LibraryTable libTable) { - myLibraryTable.setModel(new ScriptingLibraryTableModel(libTable)); - myAddLibrarytButton.addActionListener(new ActionListener(){ + myLibTableModel = new ScriptingLibraryTableModel(libTable); + myLibraryTable.setModel(myLibTableModel); + myAddLibraryButton.addActionListener(new ActionListener(){ @Override public void actionPerformed(ActionEvent e) { addLibrary(); } }); + if (libTable == null) { + myAddLibraryButton.setEnabled(false); + } + myRemoveLibraryButton.setEnabled(false); + myEditLibraryButton.setEnabled(false); + myModified = false; } public JPanel getPanel() { @@ -54,8 +60,18 @@ public class ScriptingLibrariesPanel { EditLibraryDialog editLibDialog = new EditLibraryDialog(); editLibDialog.show(); if (editLibDialog.isOK()) { - //TODO: Implement + createLibrary(editLibDialog.getLibName()); + myModified = true; } } + private void createLibrary(String name) { + myLibTableModel.getLibraryTable().createLibrary(name); + myLibraryTable.repaint(); + } + + public boolean isModified() { + return myModified; + } + } diff --git a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingLibraryTableModel.java b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingLibraryTableModel.java index d9b40834e584..0a298b429c39 100644 --- a/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingLibraryTableModel.java +++ b/platform/lang-impl/src/com/intellij/ide/scriptingContext/ui/ScriptingLibraryTableModel.java @@ -38,7 +38,10 @@ public class ScriptingLibraryTableModel extends AbstractTableModel { @Override public int getRowCount() { - return myLibTable.getLibraries().length; + if (myLibTable != null) { + return myLibTable.getLibraries().length; + } + return 0; } @Override @@ -62,4 +65,8 @@ public class ScriptingLibraryTableModel extends AbstractTableModel { return "?"; } + public LibraryTable getLibraryTable() { + return myLibTable; + } + } diff --git a/platform/lang-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java b/platform/lang-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java index bf7dc2dbf724..8ac84ef144a2 100644 --- a/platform/lang-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java +++ b/platform/lang-impl/src/com/intellij/lang/impl/PsiBuilderImpl.java @@ -924,6 +924,7 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { } else if (item instanceof DoneMarker) { curNode = nodes.pop(); + item.next = i < myProduction.size() - 1 ? myProduction.get(i + 1) : null; } else if (item instanceof ErrorItem) { int curToken = item.myLexemeIndex; @@ -986,10 +987,8 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { CompositeElement curNode = rootNode; int lexIndex = rootMarker.myLexemeIndex; - int lastErrorIndex = -1; - for (int i = myProduction.indexOf(rootMarker) + 1; i <= myProduction.indexOf(rootMarker.myDoneMarker); i++) { - final ProductionMarker item = myProduction.get(i); - + ProductionMarker item = rootMarker.firstChild != null ? rootMarker.firstChild : rootMarker.myDoneMarker; + while (item != null) { lexIndex = insertLeaves(lexIndex, item.myLexemeIndex, curNode); if (item instanceof StartMarker) { @@ -1002,25 +1001,24 @@ public class PsiBuilderImpl extends UserDataHolderBase implements PsiBuilder { curNode.rawAddChildren(childNode); nodes.push(curNode); curNode = childNode; + + item = marker.firstChild != null ? marker.firstChild : marker.myDoneMarker; } else { lexIndex = collapseLeaves(curNode, marker); - //noinspection AssignmentToForLoopParameter - i = myProduction.indexOf(marker.myDoneMarker); + item = marker.myDoneMarker.next; } } + else if (item instanceof ErrorItem) { + final PsiErrorElementImpl errorElement = new PsiErrorElementImpl(); + errorElement.setErrorDescription(((ErrorItem)item).myMessage); + curNode.rawAddChildren(errorElement); + item = item.next != null ? item.next : curMarker.myDoneMarker; + } else if (item instanceof DoneMarker) { curMarker = markers.pop(); curNode = nodes.pop(); - } - else if (item instanceof ErrorItem) { - int curToken = item.myLexemeIndex; - if (curToken == lastErrorIndex) continue; - lastErrorIndex = curToken; - - final PsiErrorElementImpl errorElement = new PsiErrorElementImpl(); - errorElement.setErrorDescription(((ErrorItem)item).myMessage); - curNode.rawAddChildren(errorElement); + item = item.next; } } } diff --git a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureGestureDetector.java b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureGestureDetector.java index fabf3212dd4c..b80cdf463418 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureGestureDetector.java +++ b/platform/lang-impl/src/com/intellij/refactoring/changeSignature/ChangeSignatureGestureDetector.java @@ -15,6 +15,7 @@ */ package com.intellij.refactoring.changeSignature; +import com.intellij.openapi.Disposable; import com.intellij.openapi.command.CommandProcessor; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.diagnostic.Logger; @@ -29,6 +30,7 @@ import com.intellij.openapi.editor.event.EditorFactoryListener; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; +import com.intellij.openapi.util.Disposer; import com.intellij.psi.*; import com.intellij.util.ArrayUtil; import com.intellij.util.containers.HashMap; @@ -92,13 +94,17 @@ public class ChangeSignatureGestureDetector extends PsiTreeChangeAdapter impleme public void projectOpened() { myPsiManager.addPsiTreeChangeListener(this); EditorFactory.getInstance().addEditorFactoryListener(this); + Disposer.register(myPsiManager.getProject(), new Disposable() { + public void dispose() { + myPsiManager.removePsiTreeChangeListener(ChangeSignatureGestureDetector.this); + EditorFactory.getInstance().removeEditorFactoryListener(ChangeSignatureGestureDetector.this); + myListenerMap.clear(); + } + }); } @Override public void projectClosed() { - myListenerMap.clear(); - myPsiManager.removePsiTreeChangeListener(this); - EditorFactory.getInstance().removeEditorFactoryListener(this); } @NotNull diff --git a/platform/platform-api/src/com/intellij/ide/BrowserUtil.java b/platform/platform-api/src/com/intellij/ide/BrowserUtil.java index e13078b07530..0c3c9fd315c7 100644 --- a/platform/platform-api/src/com/intellij/ide/BrowserUtil.java +++ b/platform/platform-api/src/com/intellij/ide/BrowserUtil.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2009 JetBrains s.r.o. + * Copyright 2000-2010 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -213,7 +213,7 @@ public class BrowserUtil { @NotNull public static String escapeUrl(@NotNull @NonNls String url) { if (SystemInfo.isWindows) { - return url.indexOf(' ') > 0? "\"" + url + "\"" : url; + return (url.indexOf(' ') > 0 || url.indexOf('&') > -1) ? "\"" + url + "\"" : url; } else { return url.replaceAll(" ", "%20"); @@ -383,15 +383,11 @@ public class BrowserUtil { } public static boolean canStartDefaultBrowser() { - if (SystemInfo.isMac) { + if (SystemInfo.isMac || SystemInfo.isWindows) { return true; } - if (SystemInfo.isWindows) { - return true; - } - - if (SystemInfo.isLinux && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { + if (SystemInfo.isLinux && Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { return true; } diff --git a/platform/platform-api/src/com/intellij/openapi/project/ProjectLocator.java b/platform/platform-api/src/com/intellij/openapi/project/ProjectLocator.java index 9e1b4077387e..6cf5c44f1238 100644 --- a/platform/platform-api/src/com/intellij/openapi/project/ProjectLocator.java +++ b/platform/platform-api/src/com/intellij/openapi/project/ProjectLocator.java @@ -21,13 +21,33 @@ package com.intellij.openapi.project; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collection; + public abstract class ProjectLocator { + public static ProjectLocator getInstance() { return ServiceManager.getService(ProjectLocator.class); } + /** + * Returns an open project which contains the given file. + * This is a guess-method, so if several projects contain the file, only one will be returned. + * Also a project may be returned though it doesn't contain the file for sure (see implementations). + * @param file file to be located in projects. + * @return project which probably contains the file, or null if couldn't guess (for example, there are no open projects). + */ @Nullable public abstract Project guessProjectForFile(VirtualFile file); + + /** + * Gets all open projects containing the given file. + * If none does, an empty list is returned. + * @param file file to be located in projects. + * @return list of open projects containing this file. + */ + @NotNull + public abstract Collection getProjectsForFile(VirtualFile file); } \ No newline at end of file diff --git a/platform/platform-api/src/com/intellij/openapi/ui/Messages.java b/platform/platform-api/src/com/intellij/openapi/ui/Messages.java index e78863f4e033..b88b73c2606a 100644 --- a/platform/platform-api/src/com/intellij/openapi/ui/Messages.java +++ b/platform/platform-api/src/com/intellij/openapi/ui/Messages.java @@ -219,6 +219,17 @@ public class Messages { return showDialog(message, title, new String[]{OK_BUTTON, CANCEL_BUTTON}, 0, icon); } + public static int shpwTwoStepConfirmationDialog(String message, String title, String checkboxText, Icon icon) { + if (isApplicationInUnitTestOrHeadless()) { + return ourTestImplementation.show(message); + } + else { + TwoStepConfirmationDialog dialog = new TwoStepConfirmationDialog(message, title, checkboxText, icon); + dialog.show(); + return dialog.getExitCode(); + } + } + public static void showErrorDialog(Project project, @Nls String message, @Nls String title) { showDialog(project, message, title, new String[]{OK_BUTTON}, 0, getErrorIcon()); } @@ -548,7 +559,11 @@ public class Messages { this(message, title, options, defaultOptionIndex, -1, icon, doNotAskOption); } - private void _init(String title, String message, String[] options, int defaultOptionIndex, int focusedOptionIndex, Icon icon, DoNotAskOption doNotAskOption) { + protected MessageDialog() { + super(false); + } + + protected void _init(String title, String message, String[] options, int defaultOptionIndex, int focusedOptionIndex, Icon icon, DoNotAskOption doNotAskOption) { setTitle(title); myMessage = message; myOptions = options; @@ -673,6 +688,62 @@ public class Messages { } } + protected static class TwoStepConfirmationDialog extends MessageDialog { + private JCheckBox myCheckBox; + private String myCheckboxText; + + public TwoStepConfirmationDialog(String message, String title, String checkboxText, Icon icon) { + myCheckboxText = checkboxText; + _init(title, message, new String[] {OK_BUTTON, CANCEL_BUTTON}, -1, -1, icon, null); + } + + @Override + protected JComponent createNorthPanel() { + JPanel panel = new JPanel(new BorderLayout(15, 0)); + if (myIcon != null) { + JLabel iconLabel = new JLabel(myIcon); + Container container = new Container(); + container.setLayout(new BorderLayout()); + container.add(iconLabel, BorderLayout.NORTH); + panel.add(container, BorderLayout.WEST); + } + + JPanel messagePanel = new JPanel(new BorderLayout()); + if (myMessage != null) { + JLabel textLabel = new JLabel(myMessage); + textLabel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0)); + textLabel.setUI(new MultiLineLabelUI()); + messagePanel.add(textLabel, BorderLayout.NORTH); + } + + final JPanel checkboxPanel = new JPanel(); + checkboxPanel.setLayout(new BoxLayout(checkboxPanel, BoxLayout.X_AXIS)); + + myCheckBox = new JCheckBox(myCheckboxText); + myCheckBox.setSelected(true); + messagePanel.add(myCheckBox, BorderLayout.SOUTH); + panel.add(messagePanel, BorderLayout.CENTER); + + return panel; + } + + @Override + public int getExitCode() { + final int exitCode = super.getExitCode(); + return exitCode == OK_EXIT_CODE ? myCheckBox.isSelected() ? OK_EXIT_CODE : CANCEL_EXIT_CODE : CANCEL_EXIT_CODE; + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myCheckBox; + } + + @Override + protected JComponent createCenterPanel() { + return null; + } + } + protected static class InputDialog extends MessageDialog { private JTextComponent myField; private InputValidator myValidator; diff --git a/platform/platform-api/src/com/intellij/openapi/util/registry/Registry.java b/platform/platform-api/src/com/intellij/openapi/util/registry/Registry.java index 43f84fe84061..5d9f169dda42 100644 --- a/platform/platform-api/src/com/intellij/openapi/util/registry/Registry.java +++ b/platform/platform-api/src/com/intellij/openapi/util/registry/Registry.java @@ -25,6 +25,7 @@ import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.util.*; import java.util.List; +import java.util.concurrent.ConcurrentHashMap; public class Registry { @@ -35,8 +36,7 @@ public class Registry { private final LinkedHashMap myUserProperties = new LinkedHashMap(); private final Map myLoadedUserProperties = new HashMap(); - - private final Map myValues = new HashMap(); + private final Map myValues = new ConcurrentHashMap(); private static final Registry ourInstance = new Registry(); diff --git a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCreateEvent.java b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCreateEvent.java index 10ae851caf56..2ff6b3fe7421 100644 --- a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCreateEvent.java +++ b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/events/VFileCreateEvent.java @@ -56,6 +56,11 @@ public class VFileCreateEvent extends VFileEvent { return myParent.getPath() + "/" + myChildName; } + @Override + public VirtualFile getFile() { + return myParent.findChild(myChildName); + } + public VirtualFileSystem getFileSystem() { return myParent.getFileSystem(); } diff --git a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/events/VFileEvent.java b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/events/VFileEvent.java index b903e8d24f4f..c592a57af35f 100644 --- a/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/events/VFileEvent.java +++ b/platform/platform-api/src/com/intellij/openapi/vfs/newvfs/events/VFileEvent.java @@ -19,7 +19,9 @@ */ package com.intellij.openapi.vfs.newvfs.events; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileSystem; +import org.jetbrains.annotations.Nullable; public abstract class VFileEvent { private final boolean myIsFromRefresh; @@ -40,6 +42,13 @@ public abstract class VFileEvent { public abstract String getPath(); + /** + * Returns the VirtualFile which this event belongs to. + * In some cases it may be null - it is not guaranteed that there is such file. + */ + @Nullable + public abstract VirtualFile getFile(); + public abstract VirtualFileSystem getFileSystem(); public abstract boolean isValid(); diff --git a/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java b/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java index 1404efbc1f37..4a6b17238756 100644 --- a/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java +++ b/platform/platform-api/src/com/intellij/ui/treeStructure/Tree.java @@ -171,8 +171,31 @@ public class Tree extends JTree implements ComponentWithEmptyText, ComponentWith public void paint(Graphics g) { final Rectangle visible = getVisibleRect(); - if (!AbstractTreeBuilder.isToPaintSelection(this)) { - mySelectionModel.holdSelection(); + boolean canHoldSelection = false; + TreePath[] paths = getSelectionModel().getSelectionPaths(); + if (paths != null) { + for (TreePath each : paths) { + Rectangle selection = getPathBounds(each); + if (g.getClipBounds().intersects(selection) || g.getClipBounds().contains(selection)) { + if (myBusy) { + Rectangle busyIconBounds = myBusyIcon.getBounds(); + if (selection.contains(busyIconBounds) || selection.intersects(busyIconBounds)) { + canHoldSelection = false; + break; + } else { + canHoldSelection = true; + } + } else { + canHoldSelection = true; + } + } + } + } + + if (canHoldSelection) { + if (!AbstractTreeBuilder.isToPaintSelection(this)) { + mySelectionModel.holdSelection(); + } } try { diff --git a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoRedoStacksHolder.java b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoRedoStacksHolder.java index e9538e235964..4e5a58b244bc 100644 --- a/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoRedoStacksHolder.java +++ b/platform/platform-impl/src/com/intellij/openapi/command/impl/UndoRedoStacksHolder.java @@ -121,50 +121,78 @@ class UndoRedoStacksHolder { } public void addToStacks(UndoableGroup group) { - if (group.isGlobal()) doAddToStack(myGlobalStack, group, UndoManagerImpl.GLOBAL_UNDO_LIMIT); - for (DocumentReference each : group.getAffectedDocuments()) { - doAddToStack(getStack(each), group, UndoManagerImpl.LOCAL_UNDO_LIMIT); + for (LinkedList each : getAffectedStacks(group)) { + doAddToStack(each, group, each == myGlobalStack ? UndoManagerImpl.GLOBAL_UNDO_LIMIT : UndoManagerImpl.LOCAL_UNDO_LIMIT); } } private void doAddToStack(LinkedList stack, UndoableGroup group, int limit) { if (!group.isUndoable() && stack.isEmpty()) return; - + stack.addLast(group); while (stack.size() > limit) { - stack.removeFirst(); + clearStacksFrom(stack.getFirst()); } } public void removeFromStacks(UndoableGroup group) { - if (group.getAffectedDocuments().isEmpty()) return; - - if (group.isGlobal()) { - assert myGlobalStack.getLast() == group; - myGlobalStack.removeLast(); - } - for (DocumentReference each : group.getAffectedDocuments()) { - LinkedList stack = getStack(each); - assert stack.getLast() == group; - stack.removeLast(); + for (LinkedList each : getAffectedStacks(group)) { + assert each.getLast() == group; + each.removeLast(); } } - public void clearStacks(boolean clearGlobal, Set affectedDocuments) { - if (clearGlobal) myGlobalStack.clear(); - for (DocumentReference ref : affectedDocuments) { - List stack = getStack(ref); - stack.clear(); - - if (ref.getFile() != null) { - myDocumentStacks.remove(ref); - } - else { - Document d = ref.getDocument(); - d.putUserData(STACK_IN_DOCUMENT_KEY, null); - myDocumentsWithStacks.remove(d); + public void clearStacks(boolean clearGlobal, Set refs) { + for (LinkedList each : getAffectedStacks(clearGlobal, refs)) { + while(!each.isEmpty()) { + clearStacksFrom(each.getLast()); } } + + Set stacksToDrop = new THashSet(); + for (Map.Entry> each : myDocumentStacks.entrySet()) { + if (each.getValue().isEmpty()) stacksToDrop.add(each.getKey()); + } + for (DocumentReference each : stacksToDrop) { + myDocumentStacks.remove(each); + } + + + Set docsToDrop = new THashSet(); + for (Document each : myDocumentsWithStacks) { + LinkedList stack = each.getUserData(STACK_IN_DOCUMENT_KEY); + if (stack != null && stack.isEmpty()) { + each.putUserData(STACK_IN_DOCUMENT_KEY, null); + docsToDrop.add(each); + } + } + myDocumentsWithStacks.removeAll(docsToDrop); + } + + private void clearStacksFrom(UndoableGroup from) { + for (LinkedList each : getAffectedStacks(from)) { + int pos = each.indexOf(from); + if (pos == -1) continue; + + if (pos > 0) { + int top = each.size() - pos; + clearStacksFrom(each.get(pos - 1)); + assert each.size() == top && each.indexOf(from) == 0; + } + each.removeFirst(); + } + } + + private List> getAffectedStacks(UndoableGroup group) { + return getAffectedStacks(group.isGlobal(), group.getAffectedDocuments()); + } + private List> getAffectedStacks(boolean global, Collection refs) { + List> result = new ArrayList>(); + if (global) result.add(myGlobalStack); + for (DocumentReference each : refs) { + result.add(getStack(each)); + } + return result; } public void clearAllStacksInTests() { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java index ab2690862115..f405f8f0aaaf 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/EditorImpl.java @@ -513,6 +513,12 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (myInitialMouseEvent != null) { myIgnoreMouseEventsConsecutiveToInitial = true; } + + // There is a possible case that 'use soft wrap' setting value is changed and we need to repaint all affected lines then. + repaintToScreenBottom(getCaretModel().getLogicalPosition().line); + int y = getCaretModel().getVisualLineStart() * getLineHeight(); + myGutterComponent.repaint(0, y, myGutterComponent.getWidth(), myGutterComponent.getHeight() - y); + getCaretModel().moveToOffset(getCaretModel().getOffset()); } private void initTabPainter() { @@ -933,60 +939,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi offset = region.getEndOffset(); } else { - //SoftWrap softWrap = mySoftWrapModel.getSoftWrap(offset); - //if (softWrap != null) { - // // There is a possible case that soft wrap contains more than one line feed inside and we need to start counting not - // // from its first line. - // int softWrapLinesToSkip = activeSoftWrapProcessed ? 0 : logicalPosition.softWrapLinesOnCurrentLogicalLine; - // - // // Process 'before soft wrap' drawing. - // if (softWrapLinesToSkip <= 0) { - // prevX = x; - // charWidth = getSoftWrapModel().getMinDrawingWidthInPixels(SoftWrapDrawingType.BEFORE_SOFT_WRAP_LINE_FEED); - // x += charWidth; - // if (x >= px) { - // onSoftWrapDrawing = true; - // break outer; - // } - // column++; - // } - // - // CharSequence softWrapText = softWrap.getText(); - // for (int i = 0; i < softWrapText.length(); i++) { - // c = softWrapText.charAt(i); - // if (softWrapLinesToSkip > 0) { - // if (c == '\n') { - // softWrapLinesToSkip--; - // } - // continue; - // } - // prevX = x; - // - // charWidth = charToVisibleWidth(c, fontType, x); - // if (charWidth == 0) { - // charWidth = spaceSize; - // break outer; - // } - // - // x += charWidth; - // if (x >= px) { - // break outer; - // } - // column += EditorUtil.columnsNumber(c, x, prevX, spaceSize); - // } - // activeSoftWrapProcessed = true; - // - // // Process 'after soft wrap' sign. - // prevX = x; - // charWidth = mySoftWrapModel.getMinDrawingWidthInPixels(SoftWrapDrawingType.AFTER_SOFT_WRAP); - // x += charWidth; - // if (x >= px) { - // onSoftWrapDrawing = true; - // break outer; - // } - // column++; - //} - prevX = x; c = text.charAt(offset); charWidth = charToVisibleWidth(c, fontType, x); @@ -1091,24 +1043,6 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi private int logicalToVisualLine(int line) { assertReadAccess(); return logicalToVisualPosition(new LogicalPosition(line, 0)).line; - //if (!myFoldingModel.isFoldingEnabled()) return line; - // - // - //int offset = line >= myDocument.getLineCount() ? myDocument.getTextLength() : myDocument.getLineStartOffset(line); - // - //FoldRegion outermostCollapsed = myFoldingModel.getCollapsedRegionAtOffset(offset); - //if (outermostCollapsed != null && offset > outermostCollapsed.getStartOffset()) { - // if (offset < getDocument().getTextLength()) { - // offset = outermostCollapsed.getStartOffset(); - // return offsetToVisualLine(offset); - // } - // else { - // offset = outermostCollapsed.getEndOffset() + 3; // WTF? - // } - //} - // - //line -= myFoldingModel.getFoldedLinesCountBefore(offset); - //return line; } @NotNull @@ -1707,6 +1641,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi return; } + boolean locateBeforeSoftWrap = !SoftWrapHelper.isCaretAfterSoftWrap(this); int start = logicalPositionToOffset(logicalPosition); getSoftWrapModel().registerSoftWrapsIfNecessary(clip, start); @@ -1807,22 +1742,21 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi paintAfterFileEndBackground(iterationState, g, position, clip, lineHeight, defaultBackground, caretRowPainted); } - //TODO den check // Perform additional activity if soft wrap is added or removed during repainting. - //if (mySoftWrapsChanged) { - // mySoftWrapsChanged = false; - // validateSize(); - // - // // Repaint editor to the bottom in order to ensure that its content is shown correctly after new soft wrap introduction. - // repaintToScreenBottom(xyToLogicalPosition(position).line); - // - // // Repaint gutter at all space that is located after active clip in order to ensure that line numbers are correctly redrawn - // // in accordance with the newly introduced soft wrap(s). - // myGutterComponent.repaint(0, clip.y, myGutterComponent.getWidth(), myGutterComponent.getHeight() - clip.y); - // - // // Ask caret model to update visual caret position. - // getCaretModel().moveToOffset(getCaretModel().getOffset(), locateBeforeSoftWrap); - //} + if (mySoftWrapsChanged) { + mySoftWrapsChanged = false; + validateSize(); + + // Repaint editor to the bottom in order to ensure that its content is shown correctly after new soft wrap introduction. + repaintToScreenBottom(xyToLogicalPosition(position).line); + + // Repaint gutter at all space that is located after active clip in order to ensure that line numbers are correctly redrawn + // in accordance with the newly introduced soft wrap(s). + myGutterComponent.repaint(0, clip.y, myGutterComponent.getWidth(), myGutterComponent.getHeight() - clip.y); + + // Ask caret model to update visual caret position. + getCaretModel().moveToOffset(getCaretModel().getOffset(), locateBeforeSoftWrap); + } } private void paintRectangularSelection(Graphics g) { @@ -4326,13 +4260,13 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi if (myCommandProcessor != null) { Runnable runnable = new Runnable() { public void run() { - processMousePressed(e, e.getSource() == getContentComponent()); + processMousePressed(e); } }; myCommandProcessor.executeCommand(myProject, runnable, "", DocCommandGroupId.noneGroupId(getDocument()), UndoConfirmationPolicy.DEFAULT, getDocument()); } else { - processMousePressed(e, e.getSource() == getContentComponent()); + processMousePressed(e); } } @@ -4393,7 +4327,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } } - private void processMousePressed(MouseEvent e, boolean moveCaretToClickPosition) { + private void processMousePressed(MouseEvent e) { myInitialMouseEvent = e; if (myMouseSelectionState != MOUSE_SELECTION_STATE_NONE && System.currentTimeMillis() - myMouseSelectionChangeTimestamp > 1000) { @@ -4435,9 +4369,7 @@ public final class EditorImpl extends UserDataHolderBase implements EditorEx, Hi } int oldSelectionStart = mySelectionModel.getLeadSelectionOffset(); - if (moveCaretToClickPosition) { - moveCaretToScreenPos(x, y); - } + moveCaretToScreenPos(x, y); if (e.isPopupTrigger()) return; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java index 11be4100ecd6..b767a5cddb0b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java @@ -367,7 +367,7 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList myCaretPositionSaved = oldCaretPositionSaved; - if (!hasBlockSelection) { + if (!hasBlockSelection && selectionStart < myEditor.getDocument().getTextLength()) { myEditor.getSelectionModel().setSelection(selectionStart, selectionEnd); } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java index b0c65e929f49..d074594e9289 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileEditor/impl/FileDocumentManagerImpl.java @@ -438,9 +438,10 @@ public class FileDocumentManagerImpl extends FileDocumentManager implements Appl LOG.info(" documentStamp:" + documentStamp); LOG.info(" oldFileStamp:" + oldFileStamp); + final boolean unitTestMode = ApplicationManager.getApplication().isUnitTestMode(); Runnable askReloadRunnable = new Runnable() { public void run() { - IdeEventQueue.getInstance().removeIdleListener(this); + if (!unitTestMode) IdeEventQueue.getInstance().removeIdleListener(this); if (!file.isValid()) return; if (askReloadFromDisk(file, document)) { reloadFromDisk(document); @@ -448,7 +449,11 @@ public class FileDocumentManagerImpl extends FileDocumentManager implements Appl } }; - IdeEventQueue.getInstance().addIdleListener(askReloadRunnable, 2000); + if (unitTestMode) { + askReloadRunnable.run(); + } else { + IdeEventQueue.getInstance().addIdleListener(askReloadRunnable, 2000); + } } else { reloadFromDisk(document); diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/DefaultProjectLocator.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/DefaultProjectLocator.java index 8d9197772078..c69dda4e3457 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/DefaultProjectLocator.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/DefaultProjectLocator.java @@ -23,8 +23,14 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectLocator; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.HashSet; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + public class DefaultProjectLocator extends ProjectLocator { @Nullable public Project guessProjectForFile(final VirtualFile file) { @@ -39,4 +45,14 @@ public class DefaultProjectLocator extends ProjectLocator { return null; } } + + @NotNull + @Override + public Collection getProjectsForFile(VirtualFile file) { + final ProjectManager projectManager = ProjectManager.getInstance(); + if (projectManager == null || file == null) { return new HashSet(); } + final Project[] openProjects = projectManager.getOpenProjects(); + return Arrays.asList(openProjects); + } + } \ No newline at end of file diff --git a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java index 158349393bf5..acca41f03a40 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/impl/ProjectManagerImpl.java @@ -693,7 +693,7 @@ public class ProjectManagerImpl extends ProjectManagerEx implements NamedJDOMExt message = ProjectBundle.message("project.reload.external.change.multiple", filesBuilder.toString()); } - return Messages.showYesNoDialog(project, message, ProjectBundle.message("project.reload.external.change.title"), Messages.getQuestionIcon()) == 0; + return Messages.shpwTwoStepConfirmationDialog(message, ProjectBundle.message("project.reload.external.change.title"), "Reload project", Messages.getQuestionIcon()) == 0; } public boolean isFileSavedToBeReloaded(VirtualFile candidate) { diff --git a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java index 962be73e8e83..72a2a120a896 100644 --- a/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/BalloonImpl.java @@ -37,7 +37,6 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.EmptyBorder; -import javax.swing.border.LineBorder; import java.awt.*; import java.awt.event.*; import java.awt.geom.GeneralPath; @@ -234,10 +233,10 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi } private void show(PositionTracker tracker, Position position) { - if (isVisible()) return; - assert !myDisposed : "Balloon is already disposed"; - assert tracker.getComponent().isShowing() : "Target component is not showing: " + tracker; + + if (isVisible()) return; + if (!tracker.getComponent().isShowing()) return; myTracker = tracker; myTracker.init(this); diff --git a/platform/platform-resources-en/src/messages/ProjectBundle.properties b/platform/platform-resources-en/src/messages/ProjectBundle.properties index 7fbb0db5df30..7cd51825a81b 100644 --- a/platform/platform-resources-en/src/messages/ProjectBundle.properties +++ b/platform/platform-resources-en/src/messages/ProjectBundle.properties @@ -13,8 +13,8 @@ project.load.undefined.path.variables.all.needed=All path variables should be de project.load.undefined.path.variables.title=Path Variables Not Defined project.load.default.error=Error Loading Default Project project.load.progress=Loading Project -project.reload.external.change.single=Project file {0} has been changed externally.\n\nReload project? -project.reload.external.change.multiple=Project files:\n{0}\nhave been changed externally.\n\nReload project? +project.reload.external.change.single=Project file {0} has been changed externally.\nIt is recommended to reload project for changes to take effect. +project.reload.external.change.multiple=Project files:\n{0}\nhave been changed externally.\nIt is recommended to reload project for changes to take effect. project.reload.external.change.title=Project Files Changed project.reload.write.failed=Error writing to file ''{0}''. Project may reload incorrectly. project.reload.write.failed.title=Write error diff --git a/platform/testFramework/src/com/intellij/testFramework/AbstractVcsTestCase.java b/platform/testFramework/src/com/intellij/testFramework/AbstractVcsTestCase.java index a6ce605be9a4..1388f95c8d63 100644 --- a/platform/testFramework/src/com/intellij/testFramework/AbstractVcsTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/AbstractVcsTestCase.java @@ -181,13 +181,23 @@ public class AbstractVcsTestCase { return result.get(); } + /** + * Creates directory inside a write action and returns the resulting reference to it. + * If the directory already exists, does nothing. + * @param parent Parent directory. + * @param name Name of the directory. + * @return reference to the created or already existing directory. + */ protected VirtualFile createDirInCommand(final VirtualFile parent, final String name) { final Ref result = new Ref(); new WriteCommandAction.Simple(myProject) { @Override protected void run() throws Throwable { try { - VirtualFile dir = parent.createChildDirectory(this, name); + VirtualFile dir = parent.findChild(name); + if (dir == null) { + dir = parent.createChildDirectory(this, name); + } result.set(dir); } catch (IOException e) { @@ -317,6 +327,10 @@ public class AbstractVcsTestCase { }.execute(); } + protected void editFileInCommand(final VirtualFile file, final String newContent) { + editFileInCommand(myProject, file, newContent); + } + public static void editFileInCommand(final Project project, final VirtualFile file, final String newContent) { new WriteCommandAction.Simple(project) { @Override diff --git a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java index f21934227a6e..35ed153875e3 100644 --- a/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java +++ b/platform/testFramework/src/com/intellij/testFramework/fixtures/impl/CodeInsightTestFixtureImpl.java @@ -1060,6 +1060,14 @@ public class CodeInsightTestFixtureImpl extends BaseFixture implements CodeInsig myProjectFixture.tearDown(); myTempDirFixture.tearDown(); + myEditor = null; + myFile = null; + myPsiManager = null; + myInspections = null; + myAvailableLocalTools.clear(); + myAvailableTools.clear(); + myFileContext = null; + super.tearDown(); } diff --git a/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java b/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java index d85f52e969f9..5b7e60d9a560 100644 --- a/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java +++ b/platform/util/src/com/intellij/psi/codeStyle/NameUtil.java @@ -19,10 +19,10 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; -import dk.brics.automaton.Automaton; -import dk.brics.automaton.DatatypesAutomatonProvider; -import dk.brics.automaton.RegExp; -import dk.brics.automaton.RunAutomaton; +import org.apache.oro.text.regex.MalformedPatternException; +import org.apache.oro.text.regex.Pattern; +import org.apache.oro.text.regex.Perl5Compiler; +import org.apache.oro.text.regex.Perl5Matcher; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -384,17 +384,19 @@ public class NameUtil { private static class OptimizedMatcher implements Matcher { private final char[] myPreparedPattern; private final boolean myEnsureFirstSymbolsMatch; - private final RunAutomaton myRunAutomaton; + private final Perl5Matcher myMatcher; + private final Pattern myPattern; public OptimizedMatcher(String pattern, String regexp) { myPreparedPattern = preparePattern(pattern).toCharArray(); myEnsureFirstSymbolsMatch = pattern.length() > 0 && Character.isLetterOrDigit(pattern.charAt(0)); - - //final long t = System.currentTimeMillis(); - final RegExp regExp = new RegExp(regexp); - final Automaton automaton = regExp.toAutomaton(new DatatypesAutomatonProvider()); - myRunAutomaton = new RunAutomaton(automaton, true); - //System.out.println("t = " + (System.currentTimeMillis() - t)); + try { + myPattern = new Perl5Compiler().compile(regexp); + } + catch (MalformedPatternException e) { + throw new RuntimeException(e); + } + myMatcher = new Perl5Matcher(); } public boolean matches(String name) { @@ -402,7 +404,7 @@ public class NameUtil { return false; } - return myRunAutomaton.run(name); + return myMatcher.matches(name, myPattern); } private static String preparePattern(String pattern) { diff --git a/platform/util/util.iml b/platform/util/util.iml index 3ba3ceb3fdb9..2ccdc5f15106 100644 --- a/platform/util/util.iml +++ b/platform/util/util.iml @@ -16,18 +16,8 @@ - - - - - - - - - - - + diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeVfsListener.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeVfsListener.java index 4061186ceaa5..dd653c55f792 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeVfsListener.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/changes/VcsDirtyScopeVfsListener.java @@ -28,10 +28,12 @@ import com.intellij.openapi.vfs.newvfs.events.*; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.HashSet; import com.intellij.util.messages.MessageBusConnection; +import com.intellij.vcsUtil.VcsUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.Collection; import java.util.List; import java.util.Map; @@ -68,62 +70,50 @@ public class VcsDirtyScopeVfsListener implements ApplicationComponent, BulkFileL final FileAndDirsCollector dirtyFilesAndDirs = new FileAndDirsCollector(); // collect files and directories - sources of events for (VFileEvent event : events) { - final String path = event.getPath(); + final VirtualFile file = getFileForEvent(event); + if (file == null) { continue; } + if (event instanceof VFileDeleteEvent) { - final VirtualFile file = ((VFileDeleteEvent)event).getFile(); - if (!file.isInLocalFileSystem()) { return; } + if (!file.isInLocalFileSystem()) { continue; } dirtyFilesAndDirs.add(file); - } else if (event instanceof VFileMoveEvent) { - final VirtualFile file = ((VFileMoveEvent)event).getFile(); - dirtyFilesAndDirs.addToFiles(file); - } else if (event instanceof VFilePropertyChangeEvent) { - final VirtualFile file = ((VFilePropertyChangeEvent)event).getFile(); + } else if (event instanceof VFileMoveEvent || event instanceof VFilePropertyChangeEvent) { dirtyFilesAndDirs.addToFiles(file); } } + // and notify VCSDirtyScopeManager dirtyFilesAndDirs.markDirty(); } @Override public void after(List events) { final FileAndDirsCollector dirtyFilesAndDirs = new FileAndDirsCollector(); - + // collect files and directories - sources of events for (VFileEvent event : events) { - if (event instanceof VFileContentChangeEvent) { - final VirtualFile file = ((VFileContentChangeEvent)event).getFile(); + final VirtualFile file = getFileForEvent(event); + if (file == null) { continue; } + + if (event instanceof VFileContentChangeEvent || event instanceof VFileCopyEvent || event instanceof VFileCreateEvent) { dirtyFilesAndDirs.addToFiles(file); - } else if (event instanceof VFileCopyEvent) { - final VirtualFile file = ((VFileCopyEvent)event).getFile(); - dirtyFilesAndDirs.addToFiles(file); - } else if (event instanceof VFileCreateEvent) { - final VFileCreateEvent ce = ((VFileCreateEvent)event); - final VirtualFile file = ce.getParent().findChild(ce.getChildName()); - dirtyFilesAndDirs.addToFiles(file); - } else if (event instanceof VFileMoveEvent) { - dirtyFilesAndDirs.add(((VFileMoveEvent)event).getFile()); } else if (event instanceof VFilePropertyChangeEvent) { final VFilePropertyChangeEvent pce = (VFilePropertyChangeEvent) event; - VirtualFile file = pce.getFile(); - // if file was renamed, then mark dirty the file and its parent directory + if (pce.getPropertyName().equals(VirtualFile.PROP_NAME)) { - if (file.getParent() != null) { - file = file.getParent(); - } - dirtyFilesAndDirs.add(file); + // if a file was renamed, then the file is dirty and its parent directory is dirty too; + // if a directory was renamed, all its children are recursively dirty, the parent dir is also dirty but not recursively. + dirtyFilesAndDirs.add(file); // the file is dirty recursively + dirtyFilesAndDirs.addToFiles(file.getParent()); // directory is dirty alone. if parent is null - is checked in the method } else { dirtyFilesAndDirs.addToFiles(file); } } } + // and notify VCSDirtyScopeManager dirtyFilesAndDirs.markDirty(); } @Nullable - private VcsDirtyScopeManager getManager(final VirtualFile file) { - if (file == null) { return null; } - final Project project = myProjectLocator.guessProjectForFile(file); - if (project == null) { return null; } - return VcsDirtyScopeManager.getInstance(project); + private static VirtualFile getFileForEvent(VFileEvent event) { + return VcsUtil.getVirtualFile(event.getPath()); } /** @@ -144,28 +134,38 @@ public class VcsDirtyScopeVfsListener implements ApplicationComponent, BulkFileL * @param addToFiles If true, then add to dirty files even if it is a directory. Otherwise add to the proper set. */ private void add(VirtualFile file, boolean addToFiles) { - final VcsDirtyScopeManager manager = getManager(file); - if (manager == null) { return; } - Pair, HashSet> filesAndDirs = map.get(manager); - if (filesAndDirs == null) { - filesAndDirs = Pair.create(new HashSet(), new HashSet()); - map.put(manager, filesAndDirs); - } - + if (file == null) { return; } final boolean isDirectory = file.isDirectory(); // need to create FilePath explicitly without referring to VirtualFile because the path of VirtualFile may change final FilePathImpl path = new FilePathImpl(new File(file.getPath()), isDirectory); - if (addToFiles || isDirectory) { - filesAndDirs.first.add(path); - } else { - filesAndDirs.second.add(path); + + final Collection managers = getManagers(file); + for (VcsDirtyScopeManager manager : managers) { + Pair, HashSet> filesAndDirs = map.get(manager); + if (filesAndDirs == null) { + filesAndDirs = Pair.create(new HashSet(), new HashSet()); + map.put(manager, filesAndDirs); + } + + if (addToFiles || !isDirectory) { + filesAndDirs.first.add(path); + } else { + filesAndDirs.second.add(path); + } } } + /** + * Adds files to the collection of files and directories - to the collection of directories (which are handled recursively). + */ void add(VirtualFile file) { add(file, false); } + /** + * Adds to the collection of files. A file (even if it is a directory) is marked dirty alone (not recursively). + * Use this method, when you want directory not to be marked dirty recursively. + */ void addToFiles(VirtualFile file) { add(file, true); } @@ -180,4 +180,23 @@ public class VcsDirtyScopeVfsListener implements ApplicationComponent, BulkFileL } } + /** + * Returns all VcsDirtyScopeManagers which serve the given file. + * There may be none of them or there may be several (if a file is contained in several open projects, for instance), + * though usually there is one. + */ + @NotNull + private Collection getManagers(final VirtualFile file) { + final Collection result = new HashSet(); + if (file == null) { return result; } + final Collection projects = myProjectLocator.getProjectsForFile(file); + for (Project project : projects) { + final VcsDirtyScopeManager manager = VcsDirtyScopeManager.getInstance(project); + if (manager != null) { + result.add(manager); + } + } + return result; + } + } diff --git a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties index f6a93a30b33a..89f563978a6a 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/src/com/siyeh/InspectionGadgetsBundle.properties @@ -1768,6 +1768,7 @@ ignore.single.field.static.imports.option=Ignore single &field static imports ignore.single.method.static.imports.option=Ignore single &method static imports ignore.methods.with.boolean.return.type.option=Ignore methods with &Boolean return type ignore.boolean.methods.in.an.interface.option=Ignore boolean methods in an @&interface +ignore.methods.overriding.super.method=Ignore methods &overriding a super method ignored.io.resource.types=Ignored I/O resource types choose.io.resource.type.to.ignore=Choose I/O resource type to ignore ignore.accesses.from.the.same.class=ignore accesses from the same class diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspection.java index cbc1f8d6a48d..014539fe1e9b 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/naming/BooleanMethodNameMustStartWithQuestionInspection.java @@ -15,23 +15,26 @@ */ package com.siyeh.ig.naming; -import com.intellij.codeInspection.ui.AddAction; import com.intellij.codeInspection.ui.ListTable; import com.intellij.codeInspection.ui.ListWrappingTableModel; -import com.intellij.codeInspection.ui.RemoveAction; +import com.intellij.openapi.actionSystem.ActionToolbar; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiType; +import com.intellij.psi.search.searches.SuperMethodsSearch; +import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; import com.intellij.ui.ScrollPaneFactory; +import com.intellij.util.Query; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.RenameFix; import com.siyeh.ig.psiutils.LibraryUtil; -import com.siyeh.ig.ui.*; +import com.siyeh.ig.ui.CheckBox; +import com.siyeh.ig.ui.UiUtils; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -50,6 +53,9 @@ public class BooleanMethodNameMustStartWithQuestionInspection @SuppressWarnings({"PublicField"}) public boolean ignoreInAnnotationInterface = true; + @SuppressWarnings({"PublicField"}) + public boolean onlyWarnOnBaseMethods = true; + /** @noinspection PublicField*/ @NonNls public String questionString = "is,can,has,should,could,will,shall,check,contains,equals,add," + @@ -94,41 +100,28 @@ public class BooleanMethodNameMustStartWithQuestionInspection new ListTable(new ListWrappingTableModel(questionList, InspectionGadgetsBundle.message( "boolean.method.name.must.start.with.question.table.column.name"))); - final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(table); + final JScrollPane scrollPane = + ScrollPaneFactory.createScrollPane(table); + final ActionToolbar toolbar = + UiUtils.createAddRemoveToolbar(table); final GridBagConstraints constraints = new GridBagConstraints(); constraints.gridx = 0; constraints.gridy = 0; - constraints.gridheight = 3; + constraints.fill = GridBagConstraints.HORIZONTAL; + panel.add(toolbar.getComponent(), constraints); + + constraints.gridy = 1; constraints.weightx = 1.0; constraints.weighty = 1.0; constraints.fill = GridBagConstraints.BOTH; panel.add(scrollPane, constraints); - final JButton addButton = new JButton(new AddAction(table)); - constraints.gridx = 1; - constraints.gridheight = 1; - constraints.weightx = 0.0; - constraints.weighty = 0.0; - constraints.fill = GridBagConstraints.HORIZONTAL; - panel.add(addButton, constraints); - - final JButton removeButton = new JButton(new RemoveAction(table)); - constraints.gridy = 1; - panel.add(removeButton, constraints); - - final BlankFiller filler = new BlankFiller(); - constraints.gridy = 2; - constraints.weighty = 1.0; - panel.add(filler, constraints); - final CheckBox checkBox1 = new CheckBox(InspectionGadgetsBundle.message( "ignore.methods.with.boolean.return.type.option"), this, "ignoreBooleanMethods"); - constraints.gridy = 3; - constraints.gridx = 0; - constraints.gridwidth = 2; + constraints.gridy = 2; constraints.weighty = 0.0; panel.add(checkBox1, constraints); @@ -136,8 +129,15 @@ public class BooleanMethodNameMustStartWithQuestionInspection new CheckBox(InspectionGadgetsBundle.message( "ignore.boolean.methods.in.an.interface.option"), this, "ignoreInAnnotationInterface"); - constraints.gridy = 4; + constraints.gridy = 3; panel.add(checkBox2, constraints); + + final CheckBox checkBox3 = + new CheckBox(InspectionGadgetsBundle.message( + "ignore.methods.overriding.super.method"), + this, "onlyWarnOnBaseMethods"); + constraints.gridy = 4; + panel.add(checkBox3, constraints); return panel; } @@ -182,7 +182,13 @@ public class BooleanMethodNameMustStartWithQuestionInspection return; } } - if(LibraryUtil.isOverrideOfLibraryMethod(method)){ + if (onlyWarnOnBaseMethods) { + final Query superSearch = + SuperMethodsSearch.search(method, null, true, false); + if (superSearch.findFirst() != null) { + return; + } + } else if (LibraryUtil.isOverrideOfLibraryMethod(method)) { return; } registerMethodError(method); diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/naming/NonBooleanMethodNameMayNotStartWithQuestionInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/naming/NonBooleanMethodNameMayNotStartWithQuestionInspection.java index 796133cc85ec..62516b0a0913 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/naming/NonBooleanMethodNameMayNotStartWithQuestionInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/naming/NonBooleanMethodNameMayNotStartWithQuestionInspection.java @@ -15,22 +15,25 @@ */ package com.siyeh.ig.naming; -import com.intellij.codeInspection.ui.AddAction; import com.intellij.codeInspection.ui.ListTable; import com.intellij.codeInspection.ui.ListWrappingTableModel; -import com.intellij.codeInspection.ui.RemoveAction; +import com.intellij.openapi.actionSystem.ActionToolbar; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiType; +import com.intellij.psi.search.searches.SuperMethodsSearch; +import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; import com.intellij.ui.ScrollPaneFactory; +import com.intellij.util.Query; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.RenameFix; import com.siyeh.ig.psiutils.LibraryUtil; -import com.siyeh.ig.ui.*; +import com.siyeh.ig.ui.CheckBox; +import com.siyeh.ig.ui.UiUtils; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -51,6 +54,9 @@ public class NonBooleanMethodNameMayNotStartWithQuestionInspection @SuppressWarnings({"PublicField"}) public boolean ignoreBooleanMethods = false; + @SuppressWarnings({"PublicField"}) + public boolean onlyWarnOnBaseMethods = true; + List questionList = new ArrayList(32); public NonBooleanMethodNameMayNotStartWithQuestionInspection(){ @@ -90,43 +96,36 @@ public class NonBooleanMethodNameMayNotStartWithQuestionInspection new ListTable(new ListWrappingTableModel(questionList, InspectionGadgetsBundle.message( "boolean.method.name.must.start.with.question.table.column.name"))); - final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(table); + final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(table); + final ActionToolbar toolbar = + UiUtils.createAddRemoveToolbar(table); final GridBagConstraints constraints = new GridBagConstraints(); constraints.gridx = 0; constraints.gridy = 0; - constraints.gridheight = 3; + constraints.fill = GridBagConstraints.HORIZONTAL; + panel.add(toolbar.getComponent(), constraints); + + constraints.gridy = 1; constraints.weightx = 1.0; constraints.weighty = 1.0; constraints.fill = GridBagConstraints.BOTH; panel.add(scrollPane, constraints); - final JButton addButton = new JButton(new AddAction(table)); - constraints.gridx = 1; - constraints.gridheight = 1; - constraints.weightx = 0.0; - constraints.weighty = 0.0; - constraints.fill = GridBagConstraints.HORIZONTAL; - panel.add(addButton, constraints); - - final JButton removeButton = new JButton(new RemoveAction(table)); - constraints.gridy = 1; - panel.add(removeButton, constraints); - - final BlankFiller filler = new BlankFiller(); - constraints.gridy = 2; - constraints.weighty = 1.0; - panel.add(filler, constraints); - - final CheckBox checkBox = + final CheckBox checkBox1 = new CheckBox(InspectionGadgetsBundle.message( "ignore.methods.with.boolean.return.type.option"), this, "ignoreBooleanMethods"); - constraints.gridy = 3; - constraints.gridx = 0; - constraints.gridwidth = 2; + constraints.gridy = 2; constraints.weighty = 0.0; - panel.add(checkBox, constraints); + panel.add(checkBox1, constraints); + + final CheckBox checkBox2 = + new CheckBox(InspectionGadgetsBundle.message( + "ignore.methods.overriding.super.method"), + this, "onlyWarnOnBaseMethods"); + constraints.gridy = 3; + panel.add(checkBox2, constraints); return panel; } @@ -173,8 +172,16 @@ public class NonBooleanMethodNameMayNotStartWithQuestionInspection } } } - if(!startsWithQuestionWord || - LibraryUtil.isOverrideOfLibraryMethod(method)){ + if(!startsWithQuestionWord){ + return; + } + if (onlyWarnOnBaseMethods) { + final Query superSearch = + SuperMethodsSearch.search(method, null, true, false); + if (superSearch.findFirst() != null) { + return; + } + } else if (LibraryUtil.isOverrideOfLibraryMethod(method)) { return; } registerMethodError(method); diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/naming/QuestionableName.form b/plugins/InspectionGadgets/src/com/siyeh/ig/naming/QuestionableName.form deleted file mode 100644 index df5de472d92f..000000000000 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/naming/QuestionableName.form +++ /dev/null @@ -1,49 +0,0 @@ - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/naming/QuestionableNameInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/naming/QuestionableNameInspection.java index 807207c0176d..ba5ec6381e14 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/naming/QuestionableNameInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/naming/QuestionableNameInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Dave Griffith, Bas Leijdekkers + * Copyright 2003-2010 Dave Griffith, Bas Leijdekkers * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,25 +15,27 @@ */ package com.siyeh.ig.naming; -import com.intellij.codeInspection.ui.RemoveAction; +import com.intellij.codeInspection.ui.ListTable; +import com.intellij.codeInspection.ui.ListWrappingTableModel; +import com.intellij.openapi.actionSystem.ActionToolbar; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiVariable; +import com.intellij.ui.ScrollPaneFactory; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspection; import com.siyeh.ig.BaseInspectionVisitor; import com.siyeh.ig.InspectionGadgetsFix; import com.siyeh.ig.fixes.RenameFix; -import com.intellij.codeInspection.ui.AddAction; -import com.intellij.codeInspection.ui.ListTable; -import com.intellij.codeInspection.ui.ListWrappingTableModel; +import com.siyeh.ig.ui.UiUtils; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import javax.swing.*; +import java.awt.*; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -54,41 +56,69 @@ public class QuestionableNameInspection extends BaseInspection { parseString(nameString, nameList); } + @Override @NotNull public String getDisplayName(){ return InspectionGadgetsBundle.message( "questionable.name.display.name"); } + @Override @NotNull public String buildErrorString(Object... infos){ return InspectionGadgetsBundle.message( "questionable.name.problem.descriptor"); } + @Override public void readSettings(Element element) throws InvalidDataException{ super.readSettings(element); parseString(nameString, nameList); } + @Override public void writeSettings(Element element) throws WriteExternalException{ nameString = formatString(nameList); super.writeSettings(element); } + @Override public JComponent createOptionsPanel(){ - final Form form = new Form(); - return form.getContentPanel(); + final JPanel panel = new JPanel(new GridBagLayout()); + final ListTable table = new ListTable(new ListWrappingTableModel( + nameList, InspectionGadgetsBundle.message( + "questionable.name.column.title"))); + final JScrollPane scrollPane = + ScrollPaneFactory.createScrollPane(table); + final ActionToolbar toolbar = + UiUtils.createAddRemoveToolbar(table); + + final GridBagConstraints constraints = new GridBagConstraints(); + constraints.gridx = 0; + constraints.gridy = 0; + constraints.fill = GridBagConstraints.HORIZONTAL; + panel.add(toolbar.getComponent(), constraints); + + constraints.gridy = 1; + constraints.weightx = 1.0; + constraints.weighty = 1.0; + constraints.fill = GridBagConstraints.BOTH; + panel.add(scrollPane, constraints); + + return panel; } + @Override protected InspectionGadgetsFix buildFix(Object... infos){ return new RenameFix(); } + @Override protected boolean buildQuickFixesOnlyForOnTheFlyErrors(){ return true; } + @Override public BaseInspectionVisitor buildVisitor(){ return new QuestionableNameVisitor(); } @@ -118,28 +148,4 @@ public class QuestionableNameInspection extends BaseInspection { } } } - - private class Form{ - - JPanel contentPanel; - JButton addButton; - JButton removeButton; - ListTable table; - - Form(){ - super(); - addButton.setAction(new AddAction(table)); - removeButton.setAction(new RemoveAction(table)); - } - - private void createUIComponents() { - table = new ListTable(new ListWrappingTableModel( - nameList, InspectionGadgetsBundle.message( - "questionable.name.column.title"))); - } - - public JComponent getContentPanel(){ - return contentPanel; - } - } } \ No newline at end of file diff --git a/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java b/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java new file mode 100644 index 000000000000..241267dbf3e4 --- /dev/null +++ b/plugins/git4idea/tests/git4idea/tests/GitChangeProviderTest.java @@ -0,0 +1,212 @@ +/* + * Copyright 2000-2010 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package git4idea.tests; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.progress.EmptyProgressIndicator; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.FilePathImpl; +import com.intellij.openapi.vcs.FileStatus; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.ChangeListManager; +import com.intellij.openapi.vcs.changes.VcsAppendableDirtyScope; +import com.intellij.openapi.vcs.changes.VcsDirtyScopeImpl; +import com.intellij.openapi.vcs.changes.pending.MockChangeListManagerGate; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.testFramework.vcs.MockChangelistBuilder; +import com.intellij.vcsUtil.VcsUtil; +import git4idea.GitVcs; +import git4idea.changes.GitChangeProvider; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.intellij.openapi.vcs.FileStatus.ADDED; +import static com.intellij.openapi.vcs.FileStatus.MODIFIED; +import static com.intellij.openapi.vcs.FileStatus.DELETED; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +/** + * Tests GitChangeProvider functionality. Scenario is the same for all tests: + * 1. Modifies files on disk (creates, edits, deletes, etc.) + * 2. Manually adds them to a dirty scope (better to use VcsDirtyScopeManagerImpl, but it's too asynchronous - couldn't overcome this for now. + * 3. Calls ChangeProvider.getChanges() and checks that the changes are there. + * TODO: change VirtualFile to FilePath or path declared by String - and add tests on move etc. Otherwise VirtualFile is not fuctional for move and harder to test deletes. + * TODO: there is almost nothing special about git. Expand to all version controls. But beware that tests should be modified on VCs which track directories. + * @author Kirill Likhodedov + */ +public class GitChangeProviderTest extends GitTestCase { + + private GitChangeProvider myChangeProvider; + private VcsAppendableDirtyScope myDirtyScope; + private Map myFiles; + private VirtualFile afile; + + @BeforeMethod + @Override + protected void setUp() throws Exception { + super.setUp(); + myChangeProvider = (GitChangeProvider) GitVcs.getInstance(myProject).getChangeProvider(); + myDirtyScope = new VcsDirtyScopeImpl(GitVcs.getInstance(myProject), myProject); + + myFiles = createFileStructure("a.txt", "b.txt", "dir/c.txt", "dir/subdir/d.txt"); + afile = myFiles.get("a.txt"); // the file is commonly used, so save it in a field. + myRepo.commit(); + } + + @Test + public void testCreateFile() throws Exception { + VirtualFile bfile = myRepo.createFile("new.txt"); + assertChanges(bfile, ADDED); + } + + @Test + public void testCreateFileInDir() throws Exception { + VirtualFile dir = createDirInCommand(myRepo.getDir(), "newdir"); + VirtualFile bfile = createFileInCommand(dir, "new.txt", "initial b"); + assertChanges(new VirtualFile[] {bfile, dir}, new FileStatus[] { ADDED, null} ); + } + + @Test + public void testEditFile() throws Exception { + editFileInCommand(afile, "new content"); + assertChanges(afile, MODIFIED); + } + + @Test + public void testDeleteFile() throws Exception { + deleteFileInCommand(afile); + assertChanges(afile, DELETED); + } + + @Test + public void testDeleteDirRecursively() throws Exception { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override public void run() { + FileUtil.delete(new File(myRepo.getDir().getPath(), "dir")); + } + }); + assertChanges(new VirtualFile[] { myFiles.get("dir/c.txt"), myFiles.get("dir/subdir/d.txt") }, new FileStatus[] { DELETED, DELETED }); + } + + @Test + public void testSimultaneousOperationsOnMultipleFiles() throws Exception { + VirtualFile dfile = myFiles.get("dir/subdir/d.txt"); + VirtualFile cfile = myFiles.get("dir/c.txt"); + + editFileInCommand(afile, "new content"); + editFileInCommand(cfile, "new content"); + deleteFileInCommand(dfile); + VirtualFile newfile = createFileInCommand("newfile.txt", "new content"); + + assertChanges(new VirtualFile[] {afile, cfile, dfile, newfile}, new FileStatus[] {MODIFIED, MODIFIED, DELETED, ADDED}); + } + + /** + * Checks that the given files have respective statuses in the change list retrieved from myChangesProvider. + * Pass null in the fileStatuses array to indicate that proper file has not changed. + */ + private void assertChanges(VirtualFile[] virtualFiles, FileStatus[] fileStatuses) throws VcsException { + Map result = getChanges(virtualFiles); + for (int i = 0; i < virtualFiles.length; i++) { + VirtualFile vf = virtualFiles[i]; + FileStatus status = fileStatuses[i]; + if (status == null) { + assertFalse(result.containsKey(vf), "File [" + vf + " shouldn't be in the change list, but it was."); + continue; + } + assertTrue(result.containsKey(vf), "File [" + vf + "] didn't change. Changes: " + result); + assertEquals(result.get(vf).getFileStatus(), status, "File statuses don't match for file [" + vf + "]"); + } + } + + private void assertChanges(VirtualFile virtualFile, FileStatus fileStatus) throws VcsException { + assertChanges(new VirtualFile[] { virtualFile }, new FileStatus[] { fileStatus }); + } + + /** + * Marks the given files dirty in myDirtyScope, gets changes from myChangeProvider and groups the changes in the map. + * Assumes that only one change for a file happened. + */ + private Map getChanges(VirtualFile... changedFiles) throws VcsException { + // populate dirty scope + for (VirtualFile vf : changedFiles) { + myDirtyScope.addDirtyFile(new FilePathImpl(vf)); + } + + // get changes + MockChangelistBuilder builder = new MockChangelistBuilder(); + myChangeProvider.getChanges(myDirtyScope, builder, new EmptyProgressIndicator(), new MockChangeListManagerGate(ChangeListManager.getInstance(myProject))); + List changes = builder.getChanges(); + + // get changes for files + Map result = new HashMap(); + for (Change change : changes) { + VirtualFile file = change.getVirtualFile(); + if (file == null) { // if a file was deleted, just find the reference in the original list of files and use it. + String path = change.getBeforeRevision().getFile().getPath(); + for (VirtualFile vf : changedFiles) { + if (vf.getPath().equals(path)) { + file = vf; + break; + } + } + } + result.put(file, change); + } + return result; + } + + /** + *

Creates file structure for given paths. Path element should be a relative (from project root) + * path to a file or a directory. All intermediate paths will be created if needed. + * To create a dir without creating a file pass "dir/" as a parameter.

+ *

Usage example: + * createFileStructure("a.txt", "b.txt", "dir/c.txt", "dir/subdir/d.txt", "anotherdir/");

+ *

This will create files a.txt and b.txt in the project dir, create directories dir, dir/subdir and anotherdir, + * and create file c.txt in dir and d.txt in dir/subdir.

+ *

Note: use forward slash to denote directories, even if it is backslash that separates dirs in your system.

+ *

All files are populated with "initial content" string.

+ */ + private Map createFileStructure(String... paths) { + Map result = new HashMap(); + + for (String path : paths) { + String[] pathElements = path.split("/"); + boolean lastIsDir = path.endsWith("/"); + VirtualFile currentParent = myRepo.getDir(); + for (int i = 0; i < pathElements.length-1; i++) { + currentParent = createDirInCommand(currentParent, pathElements[i]); + } + + String lastElement = pathElements[pathElements.length-1]; + currentParent = lastIsDir ? createDirInCommand(currentParent, lastElement) : createFileInCommand(currentParent, lastElement, "initial content"); + result.put(path, currentParent); + } + + return result; + } + +} diff --git a/plugins/git4idea/tests/git4idea/tests/GitTestCase.java b/plugins/git4idea/tests/git4idea/tests/GitTestCase.java index 42d4de9f058a..ba5047f218e3 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitTestCase.java +++ b/plugins/git4idea/tests/git4idea/tests/GitTestCase.java @@ -23,7 +23,6 @@ import com.intellij.openapi.vcs.VcsShowConfirmationOption; import com.intellij.testFramework.AbstractVcsTestCase; import git4idea.GitVcs; import org.jetbrains.annotations.Nullable; -import org.testng.Assert; import org.testng.annotations.BeforeMethod; import java.io.File; diff --git a/plugins/git4idea/tests/git4idea/tests/GitTestRepository.java b/plugins/git4idea/tests/git4idea/tests/GitTestRepository.java index 48fa25f5826d..13a593b4d556 100644 --- a/plugins/git4idea/tests/git4idea/tests/GitTestRepository.java +++ b/plugins/git4idea/tests/git4idea/tests/GitTestRepository.java @@ -16,6 +16,9 @@ package git4idea.tests; import com.intellij.execution.process.ProcessOutput; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.command.WriteCommandAction; +import com.intellij.openapi.vfs.CharsetToolkit; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.testFramework.AbstractVcsTestCase; import com.intellij.testFramework.fixtures.IdeaTestFixtureFactory; @@ -24,6 +27,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.*; +import java.util.concurrent.atomic.AtomicReference; /** * Representation of a Git repository for tests purposes. @@ -87,56 +91,83 @@ public class GitTestRepository { } /** - * Natively executes the given mercurial command. + * Natively executes the given mercurial command inside write or read action. + * @param writeAction If true, the command will be executed in a write action, otherwise - inside a read action. * @param commandWithParameters Mercurial command with parameters. E.g. ["status", "-a"] */ - public ProcessOutput execute(String... commandWithParameters) throws IOException { - return myTest.executeCommand(new File(myDirFixture.getTempDirPath()), commandWithParameters); + public ProcessOutput execute(boolean writeAction, final String... commandWithParameters) throws IOException { + final AtomicReference result = new AtomicReference(); + final Runnable action = new Runnable() { + @Override public void run() { + try { + result.set(myTest.executeCommand(new File(myDirFixture.getTempDirPath()), commandWithParameters)); + } catch (IOException e) { + result.set(null); + } + } + }; + if (ApplicationManager.getApplication() == null) { // application may be not initialized yet. OK to just run the action then. + action.run(); + return result.get(); + } + if (writeAction) { + ApplicationManager.getApplication().runWriteAction(action); + } else { + ApplicationManager.getApplication().runReadAction(action); + } + return result.get(); } public void add() throws IOException { - execute("add", "."); + execute(true, "add", "."); } public void commit(@Nullable String commitMessage) throws IOException { if (commitMessage == null) { commitMessage = "Sample commit message"; } - execute("commit", "-m", commitMessage); + execute(true, "commit", "-m", commitMessage); + } + + /** + * Commit with a sample commit message. Use this when commit message doesn't matter to your test. + */ + public void commit() throws IOException { + commit(null); } public void config(String... parameters) throws IOException { String[] pars = new String[parameters.length+1]; pars[0] = "config"; System.arraycopy(parameters, 0, pars, 1, parameters.length); - execute(pars); + execute(true, pars); } public ProcessOutput log(String... parameters) throws IOException { String[] pars = new String[parameters.length+1]; pars[0] = "log"; System.arraycopy(parameters, 0, pars, 1, parameters.length); - return execute(pars); + return execute(false, pars); } public void merge() throws IOException { - execute("merge"); + execute(true, "merge"); } public void mv(VirtualFile file, String newPath) throws IOException { - execute("mv", file.getPath(), newPath); + execute(true, "mv", file.getPath(), newPath); } public void pull() throws IOException { - execute("pull"); + execute(true, "pull"); } public void push() throws IOException { - execute("push"); + execute(true, "push"); } public void update() throws IOException { - execute("update"); + execute(true, "update"); } /** diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java index 1da79c4a4629..3a5659ac1c93 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/codeInspection/utils/ControlFlowUtils.java @@ -37,6 +37,7 @@ import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.ControlFlowBuilder import org.jetbrains.plugins.groovy.lang.psi.controlFlow.impl.MaybeReturnInstruction; import java.util.ArrayList; +import java.util.Collections; import java.util.List; @SuppressWarnings({"OverlyComplexClass"}) @@ -442,17 +443,18 @@ public class ControlFlowUtils { return false; } - public static List collectReturns(PsiElement element) { + public static List collectReturns(@Nullable PsiElement element) { return collectReturns(element, element instanceof GrCodeBlock); } - public static List collectReturns(PsiElement element, final boolean allExitPoints) { + public static List collectReturns(@Nullable PsiElement element, final boolean allExitPoints) { + if (element == null) return Collections.emptyList(); + final Instruction[] flow; if (element instanceof GrCodeBlock) { flow = ((GrCodeBlock)element).getControlFlow(); } else { - flow = new ControlFlowBuilder(element.getProject()).buildControlFlow( - (GroovyPsiElement)element, null, null); + flow = new ControlFlowBuilder(element.getProject()).buildControlFlow((GroovyPsiElement)element, null, null); } boolean[] visited = new boolean[flow.length]; final List res = new ArrayList(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/CustomMembersGenerator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/CustomMembersGenerator.java index 0853ae4e9eba..a08f9503065a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/CustomMembersGenerator.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/CustomMembersGenerator.java @@ -2,6 +2,7 @@ package org.jetbrains.plugins.groovy.dsl; import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.search.GlobalSearchScope; import groovy.lang.Closure; import org.jetbrains.annotations.Nullable; @@ -25,10 +26,9 @@ public class CustomMembersGenerator implements GdslMembersHolderConsumer { private final GroovyClassDescriptor myDescriptor; private final String myQualifiedName; - public CustomMembersGenerator(GroovyClassDescriptor descriptor) { + public CustomMembersGenerator(GroovyClassDescriptor descriptor, PsiType type) { myDescriptor = descriptor; myProject = descriptor.getProject(); - final PsiType type = descriptor.getPsiType(); if (type instanceof PsiClassType) { final PsiClass psiClass = ((PsiClassType)type).resolve(); if (psiClass != null) { @@ -72,9 +72,13 @@ public class CustomMembersGenerator implements GdslMembersHolderConsumer { @Nullable public CustomMembersHolder getMembersHolder() { - // Add non-code members holder if (!myMethods.isEmpty()) { - addMemberHolder(NonCodeMembersHolder.generateMembers(myMethods, myDescriptor.getPlaceFile())); + addMemberHolder(new CustomMembersHolder() { + @Override + public boolean processMembers(GroovyClassDescriptor descriptor, PsiScopeProcessor processor, ResolveState state) { + return NonCodeMembersHolder.generateMembers(myMethods, descriptor.getPlaceFile()).processMembers(descriptor, processor, state); + } + }); } return myDepot; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/ExtensibleCustomMembersGenerator.groovy b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/ExtensibleCustomMembersGenerator.groovy index b8132af68fd7..2e995b575176 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/ExtensibleCustomMembersGenerator.groovy +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/ExtensibleCustomMembersGenerator.groovy @@ -1,8 +1,7 @@ package org.jetbrains.plugins.groovy.dsl; -import com.intellij.openapi.project.Project -import com.intellij.psi.PsiElement +import com.intellij.psi.PsiType import org.jetbrains.plugins.groovy.dsl.dsltop.GdslMembersProvider /** @@ -10,8 +9,8 @@ import org.jetbrains.plugins.groovy.dsl.dsltop.GdslMembersProvider */ public class ExtensibleCustomMembersGenerator extends CustomMembersGenerator { - public ExtensibleCustomMembersGenerator(GroovyClassDescriptor descriptor) { - super(descriptor) + public ExtensibleCustomMembersGenerator(GroovyClassDescriptor descriptor, PsiType type) { + super(descriptor, type) } def methodMissing(String name, args) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/FactorTree.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/FactorTree.java new file mode 100644 index 000000000000..5ab742f548c9 --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/FactorTree.java @@ -0,0 +1,67 @@ +package org.jetbrains.plugins.groovy.dsl; + +import com.intellij.psi.PsiElement; +import com.intellij.util.containers.ConcurrentHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.plugins.groovy.dsl.holders.CustomMembersHolder; + +import java.util.Map; + +/** + * @author peter + */ +public class FactorTree { + private static final Object ourHolderKey = new Object(); + private final Map myCache = new ConcurrentHashMap(); + + public void cache(GroovyClassDescriptor descriptor, CustomMembersHolder holder) { + Map current = myCache; + for (Factor factor : descriptor.affectingFactors) { + Object key; + switch (factor) { + case placeElement: key = descriptor.getPlace(); break; + //case placeFile: key = descriptor.getPlaceFile(); break; + case qualifierType: key = descriptor.getPsiType().getCanonicalText(); break; + default: throw new IllegalStateException("Unknown variant: "+ factor); + } + Map next = (Map)current.get(key); + if (next == null) current.put(key, next = new ConcurrentHashMap()); + current = next; + } + + current.put(ourHolderKey, holder); + } + + @Nullable + public CustomMembersHolder retrieve(PsiElement place, String qualifierType) { + return retrieveImpl(place, qualifierType, myCache); + + } + + @Nullable + private static CustomMembersHolder retrieveImpl(@NotNull PsiElement place, + @NotNull String qualifierType, @Nullable Map current) { + if (current == null) return null; + + CustomMembersHolder result; + + result = (CustomMembersHolder)current.get(ourHolderKey); + if (result != null) return result; + + result = retrieveImpl(place, qualifierType, (Map)current.get(qualifierType)); + if (result != null) return result; + + /* + result = retrieveImpl(place, placeFile, qualifierType, (Map)current.get(placeFile)); + if (result != null) return result; + + */ + return retrieveImpl(place, qualifierType, (Map)current.get(place)); + } + +} + +enum Factor { + qualifierType, placeElement//, placeFile +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyClassDescriptor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyClassDescriptor.java index a731e8e3fa27..179afe830579 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyClassDescriptor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyClassDescriptor.java @@ -26,6 +26,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; +import java.util.LinkedHashSet; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; /** @@ -44,13 +46,13 @@ public class GroovyClassDescriptor { private final PsiType myPsiType; private final PsiElement myPlace; private final PsiFile myFile; - private final boolean myPlaceDependent; - private boolean myPlaceElementAccessed; - public GroovyClassDescriptor(@NotNull PsiType psiType, GroovyPsiElement place, boolean placeDependent, final PsiFile placeFile) { + @SuppressWarnings({"SetReplaceableByEnumSet"}) //order is important + final Set affectingFactors = new LinkedHashSet(); + + public GroovyClassDescriptor(@NotNull PsiType psiType, GroovyPsiElement place, final PsiFile placeFile) { myPsiType = psiType; myPlace = place; - myPlaceDependent = placeDependent; myFile = placeFile; } @@ -59,58 +61,32 @@ public class GroovyClassDescriptor { } public GlobalSearchScope getResolveScope() { + //affectingFactors.add(Factor.placeFile); return myPlace.getResolveScope(); } @Nullable public String getTypeText() { - return myPsiType.getCanonicalText(); + return getPsiType().getCanonicalText(); } public boolean isInheritor(String qname) { - return InheritanceUtil.isInheritor(myPsiType, qname); + return InheritanceUtil.isInheritor(getPsiType(), qname); } public PsiElement getPlace() { - myPlaceElementAccessed = true; + affectingFactors.add(Factor.placeElement); return myPlace; } public PsiType getPsiType() { + affectingFactors.add(Factor.qualifierType); return myPsiType; } public PsiFile getPlaceFile() { + //affectingFactors.add(Factor.placeFile); return myFile; } - public boolean placeAccessed() { - return myPlaceElementAccessed; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - GroovyClassDescriptor that = (GroovyClassDescriptor)o; - - if (!myPsiType.isValid() || !that.myPsiType.isValid()) return false; - if (!myPsiType.equals(that.myPsiType)) return false; - - if (myPlaceDependent) { - return myPlace.equals(that.myPlace); - } - - return myFile.equals(that.myFile); - } - - @Override - public int hashCode() { - int result = myPsiType.hashCode() * 31 + myFile.hashCode(); - if (myPlaceDependent) { - return result * 31 + myPlace.hashCode(); - } - return result; - } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyDslScript.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyDslScript.java index 4d9090aec95a..3ff6f3a0133a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyDslScript.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/GroovyDslScript.java @@ -23,10 +23,6 @@ import org.jetbrains.plugins.groovy.dsl.toplevel.ClassContextFilter; import org.jetbrains.plugins.groovy.dsl.toplevel.ContextFilter; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - /** * @author peter */ @@ -35,18 +31,16 @@ public class GroovyDslScript { public final Project project; public final VirtualFile file; public final GroovyDslExecutor executor; - private final CachedValue, ConcurrentMap>> myMaps; + private final CachedValue myMaps; public GroovyDslScript(final Project project, VirtualFile file, GroovyDslExecutor executor) { this.project = project; this.file = file; this.executor = executor; - myMaps = CachedValuesManager.getManager(project).createCachedValue(new CachedValueProvider, ConcurrentMap>>() { + myMaps = CachedValuesManager.getManager(project).createCachedValue(new CachedValueProvider() { @Override - public Result, ConcurrentMap>> compute() { - ConcurrentMap specificities = new ConcurrentHashMap(); - ConcurrentMap holders = new ConcurrentHashMap(); - return Result.create(Pair.create(specificities, holders), PsiModificationTracker.MODIFICATION_COUNT, ProjectRootManager.getInstance(project)); + public Result compute() { + return Result.create(new FactorTree(), PsiModificationTracker.MODIFICATION_COUNT, ProjectRootManager.getInstance(project)); } }, false); } @@ -57,41 +51,26 @@ public class GroovyDslScript { final GroovyPsiElement place, final PsiFile placeFile, final String qname) { - final Pair, ConcurrentMap> maps = myMaps.getValue(); - Map specificities = maps.first; - boolean firstTime = !specificities.containsKey(qname); - - final boolean placeDependent = firstTime || specificities.get(qname); - GroovyClassDescriptor descriptor = new GroovyClassDescriptor(psiType, place, placeDependent, placeFile); - - final ConcurrentMap members = maps.second; - CustomMembersHolder holder = members.get(descriptor); + final FactorTree cache = myMaps.getValue(); + CustomMembersHolder holder = cache.retrieve(place, qname); + GroovyClassDescriptor descriptor = new GroovyClassDescriptor(psiType, place, placeFile); if (holder == null) { - holder = addGdslMembers(descriptor, qname); - - if (firstTime) { - final boolean placeAccessed = descriptor.placeAccessed(); - specificities.put(qname, placeAccessed); - final GroovyClassDescriptor newDescriptor = new GroovyClassDescriptor(psiType, place, placeAccessed, placeFile); - members.putIfAbsent(newDescriptor, holder); - } - else { - members.putIfAbsent(descriptor, holder); - } + holder = addGdslMembers(descriptor, qname, psiType); + cache.cache(descriptor, holder); } return holder.processMembers(descriptor, processor, ResolveState.initial()); } - private CustomMembersHolder addGdslMembers(GroovyClassDescriptor descriptor, String qname) { + private CustomMembersHolder addGdslMembers(GroovyClassDescriptor descriptor, String qname, final PsiType psiType) { final ProcessingContext ctx = new ProcessingContext(); - ctx.put(ClassContextFilter.getClassKey(qname), descriptor.getPsiType()); + ctx.put(ClassContextFilter.getClassKey(qname), psiType); try { if (!isApplicable(executor, descriptor, ctx)) { return CustomMembersHolder.EMPTY; } - final ExtensibleCustomMembersGenerator generator = new ExtensibleCustomMembersGenerator(descriptor); + final ExtensibleCustomMembersGenerator generator = new ExtensibleCustomMembersGenerator(descriptor, psiType); executor.processVariants(descriptor, generator, ctx); return generator.getMembersHolder(); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/CategoryMethodProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/CategoryMethodProvider.java deleted file mode 100644 index bfac54afb614..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/toplevel/CategoryMethodProvider.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2000-2010 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.jetbrains.plugins.groovy.dsl.toplevel; - -import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ProjectRootManager; -import com.intellij.openapi.util.Key; -import com.intellij.psi.*; -import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.*; -import com.intellij.util.Function; -import com.intellij.util.containers.MultiMap; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; -import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; - -/** - * @author Maxim.Medvedev - */ -public class CategoryMethodProvider { - private static final Key>> METHOD_KEY = Key.create("Category methods"); - - private CategoryMethodProvider() { - } - - public static List provideMethods(@NotNull PsiType psiType, - final Project project, - String className, - final GlobalSearchScope scope, - final Function converter) { - final PsiClass categoryClass = JavaPsiFacade.getInstance(project).findClass(className, scope); - if (categoryClass == null) return Collections.emptyList(); - final MultiMap map = CachedValuesManager.getManager(project) - .getCachedValue(categoryClass, METHOD_KEY, new CachedValueProvider>() { - @Override - public Result> compute() { - MultiMap map = new MultiMap(); - PsiManager manager = PsiManager.getInstance(project); - for (PsiMethod m : categoryClass.getMethods()) { - final PsiParameter[] params = m.getParameterList().getParameters(); - if (params.length == 0) continue; - final PsiType parameterType = TypesUtil.boxPrimitiveType(params[0].getType(), manager, scope); - PsiType targetType = TypeConversionUtil.erasure(parameterType); - map.putValue(targetType.getCanonicalText(), converter.fun(m)); - } - return Result.create(map, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, ProjectRootManager.getInstance(project)); - } - }, false); - Set superTypes = ResolveUtil.getAllSuperTypes(psiType, project).keySet(); - List result = new ArrayList(); - for (String superType : superTypes) { - result.addAll(map.get(superType)); - } - return result; - } -} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/control/CreateParameterForFieldIntention.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/control/CreateParameterForFieldIntention.java index f746dc76991c..f95271261645 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/control/CreateParameterForFieldIntention.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/intentions/control/CreateParameterForFieldIntention.java @@ -24,11 +24,12 @@ import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.PopupChooserBuilder; import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Ref; import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.VariableKind; -import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.*; import com.intellij.refactoring.changeSignature.JavaThrownExceptionInfo; import com.intellij.refactoring.changeSignature.ThrownExceptionInfo; import com.intellij.ui.components.JBList; @@ -64,6 +65,7 @@ import java.util.*; */ public class CreateParameterForFieldIntention extends Intention { private static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.intentions.control.CreateParameterForFieldIntention"); + private static final Key>> FIELD_CANDIDATES = Key.create("Fields.candidates"); @NotNull @Override @@ -125,7 +127,6 @@ public class CreateParameterForFieldIntention extends Intention { }, GroovyIntentionsBundle.message("create.parameter.for.field.intention.name"), null); } }).createPopup().showInBestPositionFor(editor); - } private static void performForConstructor(PsiElement element, final Project project, Editor editor, List candidates) { @@ -243,12 +244,11 @@ public class CreateParameterForFieldIntention extends Intention { final PsiClass clazz = constructor.getContainingClass(); if (!(clazz instanceof GrTypeDefinition)) return null; - return findCandidates(constructor, (GrTypeDefinition)clazz); + return findCandidatesCached(constructor, (GrTypeDefinition)clazz); } private static List findCandidates(GrMethod constructor, final GrTypeDefinition clazz) { final List usedFields = new ArrayList(); - //ContainerUtil.addAll(fields, clazz.getFields()); final GrOpenBlock block = constructor.getBlock(); LOG.assertTrue(block != null); @@ -290,6 +290,21 @@ public class CreateParameterForFieldIntention extends Intention { return fields; } + private static List findCandidatesCached(final GrMethod constructor, final GrTypeDefinition clazz) { + final CachedValue> value = constructor.getUserData(FIELD_CANDIDATES); + if (value != null && value.getValue() != null) return value.getValue(); + final CachedValue> cachedValue = + CachedValuesManager.getManager(constructor.getProject()).createCachedValue(new CachedValueProvider>() { + @Override + public Result> compute() { + return Result.create(findCandidates(constructor, clazz), PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT); + } + }, false); + constructor.putUserData(FIELD_CANDIDATES, cachedValue); + return cachedValue.getValue(); + } + + @Nullable private static List findConstructorCandidates(PsiElement element) { final GrField field = PsiTreeUtil.getParentOfType(element, GrField.class); @@ -302,7 +317,7 @@ public class CreateParameterForFieldIntention extends Intention { final PsiMethod[] constructors = psiClass.getConstructors(); final PsiManager manager = field.getManager(); for (PsiMethod constructor : constructors) { - final List fields = findCandidates(((GrMethod)constructor), psiClass); + final List fields = findCandidatesCached(((GrMethod)constructor), psiClass); if (ContainerUtil.find(fields, new Condition() { @Override public boolean value(GrField grField) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/GdkMethodDslProvider.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/GdkMethodDslProvider.java index 1f157a03a107..4f1c89562d56 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/GdkMethodDslProvider.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/GdkMethodDslProvider.java @@ -15,16 +15,22 @@ */ package org.jetbrains.plugins.groovy.lang.resolve; -import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiType; -import com.intellij.psi.ResolveState; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.VolatileNotNullLazyValue; +import com.intellij.psi.*; import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.psi.util.*; import com.intellij.util.Function; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.dsl.GdslMembersHolderConsumer; import org.jetbrains.plugins.groovy.dsl.GroovyClassDescriptor; import org.jetbrains.plugins.groovy.dsl.dsltop.GdslMembersProvider; import org.jetbrains.plugins.groovy.dsl.holders.CustomMembersHolder; -import org.jetbrains.plugins.groovy.dsl.toplevel.CategoryMethodProvider; +import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrGdkMethodImpl; /** @@ -32,6 +38,8 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrGdkMethodImpl; */ @SuppressWarnings({"MethodMayBeStatic"}) public class GdkMethodDslProvider implements GdslMembersProvider { + private static final Key>> METHOD_KEY = Key.create("Category methods"); + public void category(String className, GdslMembersHolderConsumer consumer) { processCategoryMethods(className, consumer, new Function() { public PsiMethod fun(PsiMethod m) { @@ -53,19 +61,60 @@ public class GdkMethodDslProvider implements GdslMembersProvider { } public static void processCategoryMethods(final String className, final GdslMembersHolderConsumer consumer, final Function converter) { + final GlobalSearchScope scope = consumer.getResolveScope(); + final PsiClass categoryClass = JavaPsiFacade.getInstance(consumer.getProject()).findClass(className, scope); + if (categoryClass == null) { + return; + } + + final VolatileNotNullLazyValue> methodsMap = new VolatileNotNullLazyValue>() { + @NotNull + @Override + protected MultiMap compute() { + return retrieveMethodMap(consumer.getProject(), scope, converter, categoryClass); + } + }; + consumer.addMemberHolder(new CustomMembersHolder() { + @Override public boolean processMembers(GroovyClassDescriptor descriptor, PsiScopeProcessor processor, ResolveState state) { final PsiType psiType = descriptor.getPsiType(); if (psiType == null) return true; - for (PsiMethod method : CategoryMethodProvider.provideMethods(psiType, descriptor.getProject(), className, descriptor.getResolveScope(), converter)) { - if (!processor.execute(method, state)) { - return false; + final MultiMap map = methodsMap.getValue(); + for (String superType : ResolveUtil.getAllSuperTypes(psiType, descriptor.getProject()).keySet()) { + for (PsiMethod method : map.get(superType)) { + if (!processor.execute(method, state)) { + return false; + } } } + return true; } }); } + + public static MultiMap retrieveMethodMap(final Project project, + final GlobalSearchScope scope, + final Function converter, + @NotNull final PsiClass categoryClass) { + return CachedValuesManager.getManager(project) + .getCachedValue(categoryClass, METHOD_KEY, new CachedValueProvider>() { + @Override + public Result> compute() { + MultiMap map = new MultiMap(); + PsiManager manager = PsiManager.getInstance(project); + for (PsiMethod m : categoryClass.getMethods()) { + final PsiParameter[] params = m.getParameterList().getParameters(); + if (params.length == 0) continue; + final PsiType parameterType = TypesUtil.boxPrimitiveType(params[0].getType(), manager, scope); + PsiType targetType = TypeConversionUtil.erasure(parameterType); + map.putValue(targetType.getCanonicalText(), converter.fun(m)); + } + return Result.create(map, PsiModificationTracker.JAVA_STRUCTURE_MODIFICATION_COUNT, ProjectRootManager.getInstance(project)); + } + }, false); + } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/inline/GroovyInlineMethodUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/inline/GroovyInlineMethodUtil.java index 88f1026db23c..8cf6643ae07e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/inline/GroovyInlineMethodUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/inline/GroovyInlineMethodUtil.java @@ -80,38 +80,42 @@ public class GroovyInlineMethodUtil { return null; } - PsiReference reference = editor != null ? TargetElementUtil.findReference(editor, editor.getCaretModel().getOffset()) : null; - if (!invokedOnReference || reference == null) { - String message = GroovyRefactoringBundle.message("multiple.method.inline.is.not.suppored", REFACTORING_NAME); - showErrorMessage(message, project, editor); - return null; - } + if (invokedOnReference) { + PsiReference reference = editor != null ? TargetElementUtil.findReference(editor, editor.getCaretModel().getOffset()) : null; + if (reference == null) return null; - PsiElement element = reference.getElement(); + PsiElement element = reference.getElement(); - if (element.getContainingFile() instanceof GroovyFile) { - if (!(isStaticMethod(method) || areInSameClass(element, method))) { // todo implement for other cases + if (element.getContainingFile() instanceof GroovyFile) { + if (!(isStaticMethod(method) || areInSameClass(element, method))) { // todo implement for other cases // showErrorMessage("Other class support will be implemented soon", myProject); // return null; + } + } + + if (!(element instanceof GrExpression && element.getParent() instanceof GrCallExpression)) { + String message = GroovyRefactoringBundle.message("refactoring.is.available.only.for.method.calls", REFACTORING_NAME); + showErrorMessage(message, project, editor); + return null; + } + + GrCallExpression call = (GrCallExpression)element.getParent(); + + if (PsiTreeUtil.getParentOfType(element, GrParameter.class) != null) { + String message = GroovyRefactoringBundle.message("refactoring.is.not.supported.in.parameter.initializers", REFACTORING_NAME); + showErrorMessage(message, project, editor); + return null; + } + + + GroovyRefactoringUtil.highlightOccurrences(project, editor, new GrExpression[]{call}); + if (hasBadReturns(method) && !isTailMethodCall(call)) { + String message = GroovyRefactoringBundle + .message("refactoring.is.not.supported.when.return.statement.interrupts.the.execution.flow", REFACTORING_NAME); + showErrorMessage(message, project, editor); + return null; } } - - if (!(element instanceof GrExpression && element.getParent() instanceof GrCallExpression)) { - String message = GroovyRefactoringBundle.message("refactoring.is.available.only.for.method.calls", REFACTORING_NAME); - showErrorMessage(message, project, editor); - return null; - } - - GrCallExpression call = (GrCallExpression) element.getParent(); - - if (PsiTreeUtil.getParentOfType(element, GrParameter.class) != null) { - String message = GroovyRefactoringBundle.message("refactoring.is.not.supported.in.parameter.initializers", REFACTORING_NAME); - showErrorMessage(message, project, editor); - return null; - } - - - GroovyRefactoringUtil.highlightOccurrences(project, editor, new GrExpression[]{call}); if (method.getBlock() == null) { String message; if (method.hasModifierProperty(PsiModifier.ABSTRACT)) { @@ -123,12 +127,6 @@ public class GroovyInlineMethodUtil { return null; } - if (hasBadReturns(method) && !isTailMethodCall(call)) { - String message = GroovyRefactoringBundle.message("refactoring.is.not.supported.when.return.statement.interrupts.the.execution.flow", REFACTORING_NAME); - showErrorMessage(message, project, editor); - return null; - } - return inlineMethodDialogResult(method, project, invokedOnReference); } diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/dsl/GroovyTransformationsTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/dsl/GroovyTransformationsTest.groovy index aedc3ce53a0a..cbe09540691b 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/dsl/GroovyTransformationsTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/dsl/GroovyTransformationsTest.groovy @@ -48,11 +48,15 @@ class GroovyTransformationsTest extends LightCodeInsightFixtureTestCase { public void testVetoableTransform() throws Throwable { doPlainTest() } - public void testNewifyTransform1() throws Throwable { doVariantsTest('newInstance', 'new', 'new', 'new', + public void testNewifyTransform1() throws Throwable { doVariantsTest('newInstance', 'new', 'new', 'newInstance', 'newInstance0', 'newInstanceCallerCache', 'next') } public void testNewifyTransform2() throws Throwable { doVariantsTest('Leaf', 'Leaf', 'Leaf') } public void testNewifyTransform3() throws Throwable { doVariantsTest('Bazz', 'Bazz') } + @Newify rubyLikeNew() { + assert Integer.ne == 42 + } + }