From 406f0a74f23e62b2b117721d9f16080b45240c54 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 28 Jul 2010 14:11:52 +0400 Subject: [PATCH 01/92] New Java parser (work in progress) --- .../lang/java/parser/DeclarationParser.java | 62 ++++++++++------- .../lang/java/parser/ExpressionParser.java | 4 +- .../lang/java/parser/StatementParser.java | 67 ++++++++++++++++++- .../JavaTypeParameterListElementType.java | 5 +- .../psi/impl/source/tree/JavaASTFactory.java | 4 ++ .../parser-partial/declarations/ClassInit.txt | 14 ++++ .../declarations/EmptyBody2.txt | 16 +++++ .../parser-partial/declarations/EnumBody6.txt | 45 +++++++++++++ .../EnumWithInitializedConstants.txt | 51 ++++++++++++++ .../declarations/EnumWithoutConstants.txt | 17 +++++ .../declarations/ExtraSemicolon.txt | 22 ++++++ .../declarations/IncompleteAnnotation.txt | 51 ++++++++++++++ .../declarations/MethodNormal0.txt | 1 - .../parser-partial/declarations/NoType.txt | 20 ++++++ .../declarations/ParameterizedClass.txt | 44 ++++++++++++ .../psi/parser-partial/declarations/Pines.txt | 56 ++++++++++++++++ .../declarations/Semicolons.txt | 44 ++++++++++++ .../testSrc/com/intellij/JavaTestUtil.java | 9 +-- .../parser/partial/DeclarationParserTest.java | 17 ++++- 19 files changed, 512 insertions(+), 37 deletions(-) create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/ClassInit.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/EmptyBody2.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/EnumBody6.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/EnumWithInitializedConstants.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/EnumWithoutConstants.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/ExtraSemicolon.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/IncompleteAnnotation.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/NoType.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/ParameterizedClass.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/Pines.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/Semicolons.txt diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index 63908d4e6e4c..873de14dd69c 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -18,7 +18,6 @@ package com.intellij.lang.java.parser; import com.intellij.codeInsight.daemon.JavaErrorMessages; import com.intellij.lang.LighterASTNode; import com.intellij.lang.PsiBuilder; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.Pair; import com.intellij.psi.JavaTokenType; import com.intellij.psi.impl.source.tree.ElementType; @@ -30,6 +29,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import static com.intellij.lang.PsiBuilderUtil.expect; +import static com.intellij.lang.PsiBuilderUtil.nextTokenType; import static com.intellij.lang.java.parser.JavaParserUtil.*; @@ -38,18 +38,14 @@ public class DeclarationParser { FILE, CLASS, CODE_BLOCK, ANNOTATION_INTERFACE } - private static final Logger LOG = Logger.getInstance("#com.intellij.lang.java.parser.DeclarationParser"); - private static final TokenSet AFTER_END_DECLARATION_SET = TokenSet.create(JavaElementType.FIELD, JavaElementType.METHOD); + private static final TokenSet BEFORE_LBRACE_ELEMENTS_SET = TokenSet.create( + JavaTokenType.IDENTIFIER, JavaTokenType.COMMA, JavaTokenType.EXTENDS_KEYWORD, JavaTokenType.IMPLEMENTS_KEYWORD); private DeclarationParser() { } - @Nullable - public static PsiBuilder.Marker parseClassBodyWithBraces(final PsiBuilder builder, final boolean isAnnotation, final boolean isEnum) { - if (builder.getTokenType() != JavaTokenType.LBRACE) return null; - - final PsiBuilder.Marker marker = builder.mark(); - marker.drop(); + public static void parseClassBodyWithBraces(final PsiBuilder builder, final boolean isAnnotation, final boolean isEnum) { + assert builder.getTokenType() == JavaTokenType.LBRACE : builder.getTokenType(); builder.advanceLexer(); final PsiBuilder builderWrapper = braceMatchingBuilder(builder); @@ -59,15 +55,38 @@ public class DeclarationParser { parseClassBodyDeclarations(builderWrapper, isAnnotation); expectOrError(builder, JavaTokenType.RBRACE, JavaErrorMessages.message("expected.rbrace")); - - return marker; } - @Nullable - private static PsiBuilder.Marker parseClassFromKeyword(final PsiBuilder builder, final PsiBuilder.Marker start, - final boolean isAnnotation) { - // todo: implement - throw new UnsupportedOperationException(builder.toString() + start + isAnnotation); + @NotNull + private static PsiBuilder.Marker parseClassFromKeyword(final PsiBuilder builder, final PsiBuilder.Marker declaration, + final boolean isAnnotation) { + final IElementType keywordTokenType = builder.getTokenType(); + assert ElementType.CLASS_KEYWORD_BIT_SET.contains(keywordTokenType) : keywordTokenType; + builder.advanceLexer(); + final boolean isEnum = (keywordTokenType == JavaTokenType.ENUM_KEYWORD); + + if (!expect(builder, JavaTokenType.IDENTIFIER)) { + error(builder, JavaErrorMessages.message("expected.identifier")); + declaration.drop(); + return declaration; + } + + ReferenceParser.parseTypeParameters(builder); + ReferenceParser.parseReferenceList(builder, JavaTokenType.EXTENDS_KEYWORD, JavaElementType.EXTENDS_LIST, JavaTokenType.COMMA); + ReferenceParser.parseReferenceList(builder, JavaTokenType.IMPLEMENTS_KEYWORD, JavaElementType.IMPLEMENTS_LIST, JavaTokenType.COMMA); + + if (builder.getTokenType() != JavaTokenType.LBRACE) { + final PsiBuilder.Marker error = builder.mark(); + while (BEFORE_LBRACE_ELEMENTS_SET.contains(builder.getTokenType())) { + builder.advanceLexer(); + } + error.error(JavaErrorMessages.message("expected.lbrace")); + } + + parseClassBodyWithBraces(builder, isAnnotation, isEnum); + + declaration.done(JavaElementType.CLASS); + return declaration; } private static void parseEnumConstants(final PsiBuilder builder) { @@ -276,7 +295,7 @@ public class DeclarationParser { } final PsiBuilder.Marker codeBlock = StatementParser.parseCodeBlock(builder); - LOG.assertTrue(codeBlock != null); + assert codeBlock != null : builder.getOriginalText(); if (typeParams != null) { final PsiBuilder.Marker error = typeParams.precede(); @@ -294,6 +313,7 @@ public class DeclarationParser { error = builder.mark(); } error.error(JavaErrorMessages.message("expected.identifier.or.type")); + declaration.drop(); return modList; } @@ -347,11 +367,7 @@ public class DeclarationParser { isEmpty = false; } else if (tokenType == JavaTokenType.AT) { - final PsiBuilder.Marker pos = builder.mark(); - builder.advanceLexer(); - final IElementType nextTokenType = builder.getTokenType(); - pos.rollbackTo(); - if (ElementType.KEYWORD_BIT_SET.contains(nextTokenType)) { + if (ElementType.KEYWORD_BIT_SET.contains(nextTokenType(builder))) { break; } parseAnnotation(builder); @@ -519,8 +535,8 @@ public class DeclarationParser { varType = JavaElementType.LOCAL_VARIABLE; } else { - LOG.error("Unexpected context: " + context); declaration.drop(); + assert false : "Unexpected context: " + context; return null; } diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java index b649997a7b72..294129d3b50a 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java @@ -34,8 +34,6 @@ import static com.intellij.lang.java.parser.JavaParserUtil.error; public class ExpressionParser { - private static final Logger LOG = Logger.getInstance("#com.intellij.lang.java.parser.ExpressionParser"); - private enum ExprType { CONDITIONAL_OR, CONDITIONAL_AND, OR, XOR, AND, EQUALITY, RELATIONAL, SHIFT, ADDITIVE, MULTIPLICATIVE, UNARY, TYPE } @@ -168,7 +166,7 @@ public class ExpressionParser { return typeInfo != null ? typeInfo.marker : null; default: - LOG.error("Unexpected type: " + type); + assert false : "Unexpected type: " + type; return null; } } diff --git a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java index d08d0ff88324..bbf17fed22c6 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java @@ -17,28 +17,93 @@ package com.intellij.lang.java.parser; import com.intellij.lang.PsiBuilder; import com.intellij.psi.JavaTokenType; +import com.intellij.psi.impl.source.tree.ElementType; import com.intellij.psi.impl.source.tree.JavaElementType; +import com.intellij.psi.tree.IElementType; +import com.intellij.util.SmartList; import org.jetbrains.annotations.Nullable; +import java.util.List; + public class StatementParser { + private static final boolean DEEP_PARSE_BLOCKS_IN_STATEMENTS = true; // todo: reset after testing done + private StatementParser() { } @Nullable public static PsiBuilder.Marker parseCodeBlock(final PsiBuilder builder) { if (builder.getTokenType() != JavaTokenType.LBRACE) return null; + else if (DEEP_PARSE_BLOCKS_IN_STATEMENTS) return parseCodeBlockDeep(builder); + + final PsiBuilder.Marker codeBlock = builder.mark(); + builder.advanceLexer(); + + int braceCount = 1; + while (true) { + final IElementType tokenType = builder.getTokenType(); + if (tokenType == null) { + break; + } + if (tokenType == JavaTokenType.LBRACE) { + braceCount++; + } + else if (tokenType == JavaTokenType.RBRACE) { + braceCount--; + } + builder.advanceLexer(); + + if (braceCount == 0) { + break; + } + else if (braceCount == 1 && (tokenType == JavaTokenType.SEMICOLON || tokenType == JavaTokenType.RBRACE)) { + final PsiBuilder.Marker position = builder.mark(); + final List list = new SmartList(); + while (true) { + final IElementType type = builder.getTokenType(); + if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(type) || ElementType.MODIFIER_BIT_SET.contains(type) || + type == JavaTokenType.IDENTIFIER || type == JavaTokenType.LT || type == JavaTokenType.GT || + type == JavaTokenType.GTGT || type == JavaTokenType.GTGTGT || type == JavaTokenType.COMMA || + type == JavaTokenType.DOT || type == JavaTokenType.EXTENDS_KEYWORD || type == JavaTokenType.IMPLEMENTS_KEYWORD) { + list.add(type); + builder.advanceLexer(); + } else { + break; + } + } + if (builder.getTokenType() == JavaTokenType.LPARENTH && list.size() >= 2) { + final IElementType last = list.get(list.size() - 1); + final IElementType prevLast = list.get(list.size() - 2); + if (last == JavaTokenType.IDENTIFIER && + (prevLast == JavaTokenType.IDENTIFIER || ElementType.PRIMITIVE_TYPE_BIT_SET.contains(prevLast))) { + position.rollbackTo(); + break; + } + } + position.drop(); + } + } + + codeBlock.collapse(JavaElementType.CODE_BLOCK); + return codeBlock; + } + + @Nullable + public static PsiBuilder.Marker parseCodeBlockDeep(final PsiBuilder builder) { + if (builder.getTokenType() != JavaTokenType.LBRACE) return null; final PsiBuilder.Marker codeBlock = builder.mark(); builder.advanceLexer(); // temp - if (builder.getTokenType() == JavaTokenType.RBRACE) { + if (builder.getTokenType() == JavaTokenType.RBRACE || builder.eof()) { builder.advanceLexer(); codeBlock.done(JavaElementType.CODE_BLOCK); return codeBlock; } // todo: implement + codeBlock.drop(); throw new UnsupportedOperationException(builder.toString()); } } diff --git a/java/java-impl/src/com/intellij/psi/impl/java/stubs/JavaTypeParameterListElementType.java b/java/java-impl/src/com/intellij/psi/impl/java/stubs/JavaTypeParameterListElementType.java index ff3c51c360d1..b2717330ff06 100644 --- a/java/java-impl/src/com/intellij/psi/impl/java/stubs/JavaTypeParameterListElementType.java +++ b/java/java-impl/src/com/intellij/psi/impl/java/stubs/JavaTypeParameterListElementType.java @@ -26,15 +26,14 @@ import com.intellij.psi.impl.java.stubs.impl.PsiTypeParameterListStubImpl; import com.intellij.psi.impl.source.tree.java.PsiTypeParameterListImpl; import com.intellij.psi.stubs.IndexSink; import com.intellij.psi.stubs.StubElement; -import com.intellij.psi.stubs.StubOutputStream; import com.intellij.psi.stubs.StubInputStream; -import com.intellij.util.io.PersistentStringEnumerator; +import com.intellij.psi.stubs.StubOutputStream; import java.io.IOException; public class JavaTypeParameterListElementType extends JavaStubElementType { public JavaTypeParameterListElementType() { - super("TYPE_PARAMETER_LIST"); + super("TYPE_PARAMETER_LIST", true); } public PsiTypeParameterList createPsi(final PsiTypeParameterListStub stub) { diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java index 50b46e9f927f..dcd61fa4e1b9 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java @@ -289,6 +289,10 @@ public class JavaASTFactory extends ASTFactory implements Constants { else if (type == METHOD_RECEIVER) { return new PsiMethodReceiverImpl(); } + else if (type == CODE_BLOCK) { + // deep code block parsing + return new PsiCodeBlockImpl(null); + } return new CompositePsiElement(type){}; } diff --git a/java/java-tests/testData/psi/parser-partial/declarations/ClassInit.txt b/java/java-tests/testData/psi/parser-partial/declarations/ClassInit.txt new file mode 100644 index 000000000000..443a3f440140 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/ClassInit.txt @@ -0,0 +1,14 @@ +PsiJavaFile:ClassInit.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiClassInitializer + PsiModifierList: + + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiComment(C_STYLE_COMMENT)('/*comment*/') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/EmptyBody2.txt b/java/java-tests/testData/psi/parser-partial/declarations/EmptyBody2.txt new file mode 100644 index 000000000000..63a6641d28eb --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/EmptyBody2.txt @@ -0,0 +1,16 @@ +PsiJavaFile:EmptyBody2.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiModifierList:@Null + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:Null + PsiIdentifier:Null('Null') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiErrorElement:Identifier or type expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/EnumBody6.txt b/java/java-tests/testData/psi/parser-partial/declarations/EnumBody6.txt new file mode 100644 index 000000000000..d0199d5c67b4 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/EnumBody6.txt @@ -0,0 +1,45 @@ +PsiJavaFile:EnumBody6.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiEnumConstant:RED + PsiModifierList: + + PsiIdentifier:RED('RED') + PsiExpressionList + + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiEnumConstant:GREEN + PsiModifierList: + + PsiIdentifier:GREEN('GREEN') + PsiExpressionList + + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiEnumConstant:BLUE + PsiModifierList: + + PsiIdentifier:BLUE('BLUE') + PsiExpressionList + + PsiErrorElement:',' or ';' expected + + PsiWhiteSpace('\n ') + PsiMethod:OurEnum + PsiModifierList: + + PsiTypeParameterList + + PsiIdentifier:OurEnum('OurEnum') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiWhiteSpace(' ') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/EnumWithInitializedConstants.txt b/java/java-tests/testData/psi/parser-partial/declarations/EnumWithInitializedConstants.txt new file mode 100644 index 000000000000..287b552cfaca --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/EnumWithInitializedConstants.txt @@ -0,0 +1,51 @@ +PsiJavaFile:EnumWithInitializedConstants.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiEnumConstant:A + PsiModifierList: + + PsiIdentifier:A('A') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiLiteralExpression:10 + PsiJavaToken:INTEGER_LITERAL('10') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiAnonymousClass (PsiEnumConstantInitializerImpl)): + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiJavaToken:COMMA(',') + PsiWhiteSpace('\n ') + PsiEnumConstant:B + PsiModifierList: + + PsiIdentifier:B('B') + PsiExpressionList + + PsiWhiteSpace(' ') + PsiAnonymousClass (PsiEnumConstantInitializerImpl)): + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:method + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:method('method') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiWhiteSpace(' ') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/EnumWithoutConstants.txt b/java/java-tests/testData/psi/parser-partial/declarations/EnumWithoutConstants.txt new file mode 100644 index 000000000000..75b0a6624bd4 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/EnumWithoutConstants.txt @@ -0,0 +1,17 @@ +PsiJavaFile:EnumWithoutConstants.java + PsiJavaToken:LBRACE('{') + PsiErrorElement:';' expected + + PsiWhiteSpace(' ') + PsiModifierList:private + PsiKeyword:private('private') + PsiWhiteSpace(' ') + PsiTypeElement:A + PsiJavaCodeReferenceElement:A + PsiIdentifier:A('A') + PsiReferenceParameterList + + PsiErrorElement:Identifier expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/ExtraSemicolon.txt b/java/java-tests/testData/psi/parser-partial/declarations/ExtraSemicolon.txt new file mode 100644 index 000000000000..b444db53a751 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/ExtraSemicolon.txt @@ -0,0 +1,22 @@ +PsiJavaFile:ExtraSemicolon.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiClass:C + PsiModifierList: + + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:C('C') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/IncompleteAnnotation.txt b/java/java-tests/testData/psi/parser-partial/declarations/IncompleteAnnotation.txt new file mode 100644 index 000000000000..4567aab462bb --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/IncompleteAnnotation.txt @@ -0,0 +1,51 @@ +PsiJavaFile:IncompleteAnnotation.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiClass:Foo + PsiModifierList:public + PsiKeyword:public('public') + PsiWhiteSpace(' ') + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:Foo('Foo') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:testSomething + PsiModifierList:public + PsiKeyword:public('public') + PsiTypeParameterList + + PsiWhiteSpace(' ') + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:testSomething('testSomething') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiModifierList:@Null + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:Null + PsiIdentifier:Null('Null') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiErrorElement:Identifier or type expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/MethodNormal0.txt b/java/java-tests/testData/psi/parser-partial/declarations/MethodNormal0.txt index f2383f5aaae4..c9f9ad5b179d 100644 --- a/java/java-tests/testData/psi/parser-partial/declarations/MethodNormal0.txt +++ b/java/java-tests/testData/psi/parser-partial/declarations/MethodNormal0.txt @@ -18,7 +18,6 @@ PsiJavaFile:MethodNormal0.java PsiWhiteSpace(' ') PsiCodeBlock PsiJavaToken:LBRACE('{') - PsiWhiteSpace(' ') PsiJavaToken:RBRACE('}') PsiWhiteSpace(' ') PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/NoType.txt b/java/java-tests/testData/psi/parser-partial/declarations/NoType.txt new file mode 100644 index 000000000000..5507c36b57ee --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/NoType.txt @@ -0,0 +1,20 @@ +PsiJavaFile:NoType.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiErrorElement:Unexpected token + PsiKeyword:new('new') + PsiWhiteSpace(' ') + PsiMethod:X + PsiModifierList: + + PsiTypeParameterList + + PsiIdentifier:X('X') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/declarations/ParameterizedClass.txt b/java/java-tests/testData/psi/parser-partial/declarations/ParameterizedClass.txt new file mode 100644 index 000000000000..262c0c8ce39d --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/ParameterizedClass.txt @@ -0,0 +1,44 @@ +PsiJavaFile:ParameterizedClass.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiClass:A + PsiModifierList:public + PsiKeyword:public('public') + PsiWhiteSpace(' ') + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:A('A') + PsiWhiteSpace(' ') + PsiTypeParameterList + PsiJavaToken:LT('<') + PsiTypeParameter:T + PsiIdentifier:T('T') + PsiWhiteSpace(' ') + PsiElement(EXTENDS_BOUND_LIST) + PsiKeyword:extends('extends') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:java.util.List + PsiJavaCodeReferenceElement:java.util + PsiJavaCodeReferenceElement:java + PsiIdentifier:java('java') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:util('util') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:List('List') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiReferenceList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/Pines.txt b/java/java-tests/testData/psi/parser-partial/declarations/Pines.txt new file mode 100644 index 000000000000..afbe40ee6412 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/Pines.txt @@ -0,0 +1,56 @@ +PsiJavaFile:Pines.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiClass:A + PsiModifierList: + + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:A('A') + PsiTypeParameterList + PsiJavaToken:LT('<') + PsiTypeParameter:T + PsiIdentifier:T('T') + PsiWhiteSpace(' ') + PsiElement(EXTENDS_BOUND_LIST) + PsiKeyword:extends('extends') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:List + PsiIdentifier:List('List') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:String + PsiJavaCodeReferenceElement:String + PsiIdentifier:String('String') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiJavaToken:GT('>') + PsiWhiteSpace(' ') + PsiReferenceList + PsiKeyword:extends('extends') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:List> + PsiIdentifier:List('List') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:List + PsiJavaCodeReferenceElement:List + PsiIdentifier:List('List') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:Integer + PsiJavaCodeReferenceElement:Integer + PsiIdentifier:Integer('Integer') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiJavaToken:GT('>') + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/Semicolons.txt b/java/java-tests/testData/psi/parser-partial/declarations/Semicolons.txt new file mode 100644 index 000000000000..0327c01fd55b --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/Semicolons.txt @@ -0,0 +1,44 @@ +PsiJavaFile:Semicolons.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:f + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:f('f') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiWhiteSpace(' ') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiMethod:g + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:g('g') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiWhiteSpace(' ') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testSrc/com/intellij/JavaTestUtil.java b/java/java-tests/testSrc/com/intellij/JavaTestUtil.java index 7e9c239d60ab..4ea7a14d3e37 100644 --- a/java/java-tests/testSrc/com/intellij/JavaTestUtil.java +++ b/java/java-tests/testSrc/com/intellij/JavaTestUtil.java @@ -1,12 +1,12 @@ package com.intellij; import com.intellij.lang.PsiBuilder; -import com.intellij.lang.impl.PsiBuilderImpl; +import com.intellij.lang.PsiBuilderFactory; +import com.intellij.lang.StdLanguages; import com.intellij.lang.java.parser.JavaParserUtil; import com.intellij.lexer.JavaLexer; import com.intellij.openapi.application.ex.PathManagerEx; import com.intellij.pom.java.LanguageLevel; -import com.intellij.psi.JavaTokenType; /** * @author yole @@ -20,10 +20,11 @@ public class JavaTestUtil { return PathManagerEx.getTestDataPath(); } - public static PsiBuilder getJavaBuilder(final String source) { + public static PsiBuilder getJavaBuilder(final CharSequence source) { final LanguageLevel languageLevel = LanguageLevel.HIGHEST; final JavaLexer lexer = new JavaLexer(languageLevel); - final PsiBuilder builder = new PsiBuilderImpl(lexer, JavaTokenType.WHITESPACE_BIT_SET, JavaTokenType.COMMENT_BIT_SET, source); + final PsiBuilder builder = PsiBuilderFactory.getInstance().createBuilder(lexer, StdLanguages.JAVA, source); + builder.setDebugMode(true); JavaParserUtil.setLanguageLevel(builder, languageLevel); return builder; } diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java index 577e74a93c53..977c9d6ea852 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java @@ -20,6 +20,7 @@ import com.intellij.lang.java.parser.DeclarationParser; import com.intellij.lang.java.parser.JavaParsingTestCase; +// todo: fix parser and uncomment tests public class DeclarationParserTest extends JavaParsingTestCase { public DeclarationParserTest() { super("parser-partial/declarations"); @@ -27,6 +28,14 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testEmptyBody0() { doParserTest("{ }", false, false); } public void testEmptyBody1() { doParserTest("{ ", false, false); } + public void testEmptyBody2() { doParserTest("{ @Null }", false, false); } + + public void testNoType() { doParserTest("{ new X(); }", false, false); } + public void testExtraSemicolon() { doParserTest("{ class C { }; }", false, false); } + public void testParameterizedClass() { doParserTest("{ public class A { } }", false, false); } + public void testPines() { doParserTest("{ class A> extends List> { } }", false, false); } + public void testIncompleteAnnotation() { doParserTest("{ public class Foo { public void testSomething(); @Null } }", false, false); } + public void testClassInit() { doParserTest("{ { /*comment*/ } }", false, false); } public void testEnumBody0() { doParserTest("{ ; }", false, true); } public void testEnumBody1() { doParserTest("{ RED, GREEN, BLUE; }", false, true); } @@ -34,6 +43,9 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testEnumBody3() { doParserTest("{ RED, GREEN, BLUE, }", false, true); } public void testEnumBody4() { doParserTest("{ RED(0), GREEN(1), BLUE(2); }", false, true); } public void testEnumBody5() { doParserTest("{ @ANNOTATION A(10) }", false, true); } + //public void testEnumBody6() { doParserTest("{ RED, GREEN, BLUE\n OurEnum() {} }", false, true); } + public void testEnumWithInitializedConstants() { doParserTest("{ A(10) { },\n B { void method() {} } }", false, true); } + public void testEnumWithoutConstants() { doParserTest("{ private A }", false, true); } public void testFieldSimple() { doParserTest("{ int field = 0; }", false, false); } public void testFieldMulti() { doParserTest("{ int field1 = 0, field2; }", false, false); } @@ -42,10 +54,11 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testUnclosedComma() { doParserTest("{ int field, }", false, false); } public void testUnclosedSemicolon() { doParserTest("{ int field }", false, false); } public void testMissingInitializerExpression() { doParserTest("{ int field=; }", false, false); } - //public void testMultiLineUnclosed() { doParserTest("{ int \n Object o; }", false, false); } // todo: implement + //public void testMultiLineUnclosed() { doParserTest("{ int \n Object o; }", false, false); } - //public void testMethodNormal0() { doParserTest("{ void f() { } }", false, false); } // todo: parse code block correctly + public void testMethodNormal0() { doParserTest("{ void f() {} }", false, false); } public void testMethodNormal1() { doParserTest("{ void f(); }", false, false); } + public void testSemicolons() { doParserTest("{ void f() {}; void g() {}; }", false, false); } public void testUnclosed0() { doParserTest("{ void f() }", false, false); } public void testUnclosed1() { doParserTest("{ void f( }", false, false); } public void testUnclosed2() { doParserTest("{ void f()\n void g(); }", false, false); } From 3019fa87d1168ef4284237417eeb1b2d859a8ef5 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 28 Jul 2010 18:04:41 +0400 Subject: [PATCH 02/92] New Java parser (work in progress) --- .../lang/java/parser/DeclarationParser.java | 5 +- .../lang/java/parser/ExpressionParser.java | 71 +++++++------------ .../lang/java/parser/JavaParserUtil.java | 5 ++ 3 files changed, 34 insertions(+), 47 deletions(-) diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index 873de14dd69c..800c78568333 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -16,7 +16,6 @@ package com.intellij.lang.java.parser; import com.intellij.codeInsight.daemon.JavaErrorMessages; -import com.intellij.lang.LighterASTNode; import com.intellij.lang.PsiBuilder; import com.intellij.openapi.util.Pair; import com.intellij.psi.JavaTokenType; @@ -185,7 +184,7 @@ public class DeclarationParser { } @Nullable - private static PsiBuilder.Marker parseDeclaration(final PsiBuilder builder, final Context context) { + public static PsiBuilder.Marker parseDeclaration(final PsiBuilder builder, final Context context) { final IElementType tokenType = builder.getTokenType(); if (tokenType == null) return null; @@ -231,7 +230,7 @@ public class DeclarationParser { while (builder.getTokenType() != null && builder.getTokenType() != JavaTokenType.RBRACE) { final PsiBuilder.Marker position = builder.mark(); final PsiBuilder.Marker element = parseDeclaration(builder, Context.CLASS); - if (element != null && AFTER_END_DECLARATION_SET.contains(((LighterASTNode)element).getTokenType())) { + if (element != null && AFTER_END_DECLARATION_SET.contains(exprType(element))) { if (!declarationsAfterEnd) { element.precede().error(JavaErrorMessages.message("expected.class.or.interface")); } diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java index 294129d3b50a..57d95f7c9251 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java @@ -18,8 +18,6 @@ package com.intellij.lang.java.parser; import com.intellij.codeInsight.daemon.JavaErrorMessages; import com.intellij.lang.PsiBuilder; import com.intellij.lang.PsiBuilderUtil; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Pair; import com.intellij.psi.JavaTokenType; import com.intellij.psi.TokenType; import com.intellij.psi.impl.source.tree.ElementType; @@ -29,8 +27,7 @@ import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import static com.intellij.lang.java.parser.JavaParserUtil.areTypeAnnotationsSupported; -import static com.intellij.lang.java.parser.JavaParserUtil.error; +import static com.intellij.lang.java.parser.JavaParserUtil.*; public class ExpressionParser { @@ -309,15 +306,12 @@ public class ExpressionParser { private static PsiBuilder.Marker parsePrimary(final PsiBuilder builder, final boolean planB) { PsiBuilder.Marker startMarker = builder.mark(); - final Pair exprTypePair = parsePrimaryExpressionStart(builder); - if (exprTypePair == null) { + PsiBuilder.Marker expr = parsePrimaryExpressionStart(builder); + if (expr == null) { startMarker.drop(); return null; } - PsiBuilder.Marker expr = exprTypePair.first; - IElementType exprType = exprTypePair.second; - while (true) { final IElementType tokenType = builder.getTokenType(); if (tokenType == JavaTokenType.DOT) { @@ -325,7 +319,7 @@ public class ExpressionParser { builder.advanceLexer(); final IElementType dotTokenType = builder.getTokenType(); - if (dotTokenType == JavaTokenType.CLASS_KEYWORD && exprType == JavaElementType.REFERENCE_EXPRESSION) { + if (dotTokenType == JavaTokenType.CLASS_KEYWORD && exprType(expr) == JavaElementType.REFERENCE_EXPRESSION) { if (planB) { error(builder, JavaErrorMessages.message("expected.identifier")); PsiBuilderUtil.drop(startMarker, dotPos); @@ -343,15 +337,13 @@ public class ExpressionParser { startMarker = copy; expr = classObjAccess; - exprType = JavaElementType.CLASS_OBJECT_ACCESS_EXPRESSION; } else if (dotTokenType == JavaTokenType.NEW_KEYWORD) { dotPos.drop(); expr = parseNew(builder, expr); - exprType = JavaElementType.NEW_EXPRESSION; } else if ((dotTokenType == JavaTokenType.THIS_KEYWORD || dotTokenType == JavaTokenType.SUPER_KEYWORD) && - exprType == JavaElementType.REFERENCE_EXPRESSION) { + exprType(expr) == JavaElementType.REFERENCE_EXPRESSION) { if (planB) { dotPos.rollbackTo(); startMarker.drop(); @@ -375,15 +367,14 @@ public class ExpressionParser { builder.advanceLexer(); startMarker = copy; - exprType = dotTokenType == JavaTokenType.THIS_KEYWORD ? JavaElementType.THIS_EXPRESSION : JavaElementType.SUPER_EXPRESSION; expr = ref.precede(); - expr.done(exprType); + expr.done(dotTokenType == JavaTokenType.THIS_KEYWORD ? JavaElementType.THIS_EXPRESSION : JavaElementType.SUPER_EXPRESSION); } else if (dotTokenType == JavaTokenType.SUPER_KEYWORD) { dotPos.drop(); final PsiBuilder.Marker refExpr = expr.precede(); builder.advanceLexer(); - refExpr.done(exprType = JavaElementType.REFERENCE_EXPRESSION); + refExpr.done(JavaElementType.REFERENCE_EXPRESSION); expr = refExpr; } else { @@ -397,13 +388,13 @@ public class ExpressionParser { return refExpr; } - refExpr.done(exprType = JavaElementType.REFERENCE_EXPRESSION); + refExpr.done(JavaElementType.REFERENCE_EXPRESSION); expr = refExpr; } } else if (tokenType == JavaTokenType.LPARENTH) { - if (exprType != JavaElementType.REFERENCE_EXPRESSION) { - if (exprType == JavaElementType.SUPER_EXPRESSION) { + if (exprType(expr) != JavaElementType.REFERENCE_EXPRESSION) { + if (exprType(expr) == JavaElementType.SUPER_EXPRESSION) { if (planB) { startMarker.drop(); return expr; @@ -412,14 +403,14 @@ public class ExpressionParser { final PsiBuilder.Marker copy = startMarker.precede(); startMarker.rollbackTo(); - final Pair qualifier = parsePrimaryExpressionStart(builder); + final PsiBuilder.Marker qualifier = parsePrimaryExpressionStart(builder); if (qualifier != null) { - final PsiBuilder.Marker refExpr = qualifier.first.precede(); + final PsiBuilder.Marker refExpr = qualifier.precede(); if (builder.getTokenType() == JavaTokenType.DOT) { builder.advanceLexer(); if (builder.getTokenType() == JavaTokenType.SUPER_KEYWORD) { builder.advanceLexer(); - refExpr.done(exprType = JavaElementType.REFERENCE_EXPRESSION); + refExpr.done(JavaElementType.REFERENCE_EXPRESSION); expr = refExpr; startMarker = copy; continue; @@ -438,7 +429,7 @@ public class ExpressionParser { final PsiBuilder.Marker callExpr = expr.precede(); parseArgumentList(builder); - callExpr.done(exprType = JavaElementType.METHOD_CALL_EXPRESSION); + callExpr.done(JavaElementType.METHOD_CALL_EXPRESSION); expr = callExpr; } else if (tokenType == JavaTokenType.LBRACKET) { @@ -449,7 +440,7 @@ public class ExpressionParser { builder.advanceLexer(); - if (builder.getTokenType() == JavaTokenType.RBRACKET && exprType == JavaElementType.REFERENCE_EXPRESSION) { + if (builder.getTokenType() == JavaTokenType.RBRACKET && exprType(expr) == JavaElementType.REFERENCE_EXPRESSION) { final PsiBuilder.Marker copy = startMarker.precede(); startMarker.rollbackTo(); @@ -461,7 +452,6 @@ public class ExpressionParser { startMarker = copy; expr = classObjAccess; - exprType = JavaElementType.CLASS_OBJECT_ACCESS_EXPRESSION; } else { final PsiBuilder.Marker arrayAccess = expr.precede(); @@ -482,7 +472,7 @@ public class ExpressionParser { } builder.advanceLexer(); - arrayAccess.done(exprType = JavaElementType.ARRAY_ACCESS_EXPRESSION); + arrayAccess.done(JavaElementType.ARRAY_ACCESS_EXPRESSION); expr = arrayAccess; } } @@ -494,14 +484,14 @@ public class ExpressionParser { } @Nullable - private static Pair parsePrimaryExpressionStart(final PsiBuilder builder) { + private static PsiBuilder.Marker parsePrimaryExpressionStart(final PsiBuilder builder) { IElementType tokenType = builder.getTokenType(); if (LITERALS.contains(tokenType)) { final PsiBuilder.Marker literal = builder.mark(); builder.advanceLexer(); literal.done(JavaElementType.LITERAL_EXPRESSION); - return Pair.create(literal, JavaElementType.LITERAL_EXPRESSION); + return literal; } if (tokenType == JavaTokenType.LPARENTH) { final PsiBuilder.Marker parenth = builder.mark(); @@ -515,11 +505,10 @@ public class ExpressionParser { JavaParserUtil.expectOrError(builder, JavaTokenType.RPARENTH, JavaErrorMessages.message("expected.rparen")); parenth.done(JavaElementType.PARENTH_EXPRESSION); - return Pair.create(parenth, JavaElementType.PARENTH_EXPRESSION); + return parenth; } if (tokenType == JavaTokenType.LBRACE) { - final PsiBuilder.Marker arrayInit = parseArrayInitializer(builder); - return arrayInit != null ? Pair.create(arrayInit, JavaElementType.ARRAY_INITIALIZER_EXPRESSION) : null; + return parseArrayInitializer(builder); } PsiBuilder.Marker annotation = null; @@ -544,7 +533,7 @@ public class ExpressionParser { builder.advanceLexer(); refExpr.done(JavaElementType.REFERENCE_EXPRESSION); beforeAnnotation.drop(); - return Pair.create(refExpr, JavaElementType.REFERENCE_EXPRESSION); + return refExpr; } if (annotation != null) { @@ -559,27 +548,21 @@ public class ExpressionParser { final PsiBuilder.Marker expr = builder.mark(); builder.mark().done(JavaElementType.REFERENCE_PARAMETER_LIST); builder.advanceLexer(); - final IElementType type = builder.getTokenType() != JavaTokenType.LPARENTH ? - JavaElementType.THIS_EXPRESSION : JavaElementType.REFERENCE_EXPRESSION; - expr.done(type); - return Pair.create(expr, type); + expr.done(builder.getTokenType() != JavaTokenType.LPARENTH ? JavaElementType.THIS_EXPRESSION : JavaElementType.REFERENCE_EXPRESSION); + return expr; } if (tokenType == JavaTokenType.SUPER_KEYWORD) { final PsiBuilder.Marker expr = builder.mark(); builder.mark().done(JavaElementType.REFERENCE_PARAMETER_LIST); builder.advanceLexer(); - final IElementType type = builder.getTokenType() != JavaTokenType.LPARENTH ? - JavaElementType.SUPER_EXPRESSION : JavaElementType.REFERENCE_EXPRESSION; - expr.done(type); - return Pair.create(expr, type); + expr.done(builder.getTokenType() != JavaTokenType.LPARENTH ? JavaElementType.SUPER_EXPRESSION : JavaElementType.REFERENCE_EXPRESSION); + return expr; } if (tokenType == JavaTokenType.NEW_KEYWORD) { - final PsiBuilder.Marker newExpr = parseNew(builder, null); - return Pair.create(newExpr, JavaElementType.NEW_EXPRESSION); + return parseNew(builder, null); } if (ElementType.PRIMITIVE_TYPE_BIT_SET.contains(tokenType)) { - final PsiBuilder.Marker classObjAccess = parseClassObjectAccess(builder); - return classObjAccess != null ? Pair.create(classObjAccess, JavaElementType.CLASS_OBJECT_ACCESS_EXPRESSION) : null; + return parseClassObjectAccess(builder); } return null; 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 6a3597e5f897..2aa5b8da3976 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 @@ -48,6 +48,11 @@ public class JavaParserUtil { return level; } + @Nullable + public static IElementType exprType(@Nullable final PsiBuilder.Marker marker) { + return marker != null ? ((LighterASTNode)marker).getTokenType() : null; + } + // used instead of PsiBuilder.error() as it drops all but first subsequent error messages public static void error(final PsiBuilder builder, final String message) { builder.mark().error(message); From 7280d024d76db644c97e427eeaaa6cbb36ea6a1f Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Wed, 28 Jul 2010 22:12:51 +0400 Subject: [PATCH 03/92] New Java parser (work in progress) --- .../lang/java/parser/DeclarationParser.java | 2 +- .../lang/java/parser/StatementParser.java | 490 +++++++++++++++++- .../statements/AnonymousInSmartCompletion.txt | 109 ++++ .../statements/AssertNormal0.txt | 14 + .../statements/AssertNormal1.txt | 21 + .../statements/AssignmentSimple.txt | 36 ++ .../parser-partial/statements/BlockSimple.txt | 10 + .../psi/parser-partial/statements/For.txt | 83 +++ .../psi/parser-partial/statements/ForEach.txt | 39 ++ .../statements/ForIncomplete0.txt | 10 + .../statements/ForIncomplete1.txt | 11 + .../statements/ForIncomplete2.txt | 25 + .../statements/ForIncomplete3.txt | 36 ++ .../statements/ForIncomplete4.txt | 37 ++ .../statements/ForIncomplete5.txt | 45 ++ .../statements/ForIncomplete6.txt | 46 ++ .../statements/ForIncomplete7.txt | 23 + .../statements/ForIncomplete8.txt | 37 ++ .../statements/ForIncomplete9.txt | 48 ++ .../parser-partial/statements/ForNormal0.txt | 47 ++ .../parser-partial/statements/ForNormal1.txt | 27 + .../statements/IfIncomplete0.txt | 10 + .../statements/IfIncomplete1.txt | 12 + .../statements/IfIncomplete2.txt | 23 + .../statements/IfIncomplete3.txt | 16 + .../statements/IfIncomplete4.txt | 17 + .../statements/IfIncomplete5.txt | 24 + .../statements/IfIncomplete6.txt | 30 ++ .../statements/IfNormalNoElse.txt | 26 + .../statements/IfNormalWithElse.txt | 49 ++ .../parser-partial/statements/LabelSimple.txt | 19 + .../parser-partial/statements/LocalVar.txt | 24 + .../statements/WhileIncomplete0.txt | 10 + .../statements/WhileIncomplete1.txt | 12 + .../statements/WhileIncomplete2.txt | 22 + .../statements/WhileIncomplete3.txt | 15 + .../statements/WhileIncomplete4.txt | 16 + .../statements/WhileIncomplete5.txt | 23 + .../parser-partial/statements/WhileNormal.txt | 24 + .../parser/partial/StatementParserTest.java | 88 ++++ 40 files changed, 1648 insertions(+), 8 deletions(-) create mode 100644 java/java-tests/testData/psi/parser-partial/statements/AnonymousInSmartCompletion.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/AssertNormal0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/AssertNormal1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/AssignmentSimple.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/BlockSimple.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/For.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ForEach.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ForIncomplete0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ForIncomplete1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ForIncomplete2.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ForIncomplete3.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ForIncomplete4.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ForIncomplete5.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ForIncomplete6.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ForIncomplete7.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ForIncomplete8.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ForIncomplete9.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ForNormal0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ForNormal1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/IfIncomplete0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/IfIncomplete1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/IfIncomplete2.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/IfIncomplete3.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/IfIncomplete4.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/IfIncomplete5.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/IfIncomplete6.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/IfNormalNoElse.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/IfNormalWithElse.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/LabelSimple.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/LocalVar.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete2.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete3.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete4.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete5.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/WhileNormal.txt create mode 100644 java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index 800c78568333..eb0c01af1bac 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -495,7 +495,7 @@ public class DeclarationParser { } @Nullable - private static PsiBuilder.Marker parseParameter(final PsiBuilder builder, final boolean ellipsis) { + public static PsiBuilder.Marker parseParameter(final PsiBuilder builder, final boolean ellipsis) { final PsiBuilder.Marker param = builder.mark(); final Pair modListInfo = parseModifierList(builder); diff --git a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java index bbf17fed22c6..1abd6483f2ef 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java @@ -15,26 +15,36 @@ */ package com.intellij.lang.java.parser; +import com.intellij.codeInsight.daemon.JavaErrorMessages; import com.intellij.lang.PsiBuilder; import com.intellij.psi.JavaTokenType; import com.intellij.psi.impl.source.tree.ElementType; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.ILazyParseableElementType; import com.intellij.util.SmartList; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.List; +import static com.intellij.lang.PsiBuilderUtil.*; +import static com.intellij.lang.java.parser.JavaParserUtil.*; + public class StatementParser { private static final boolean DEEP_PARSE_BLOCKS_IN_STATEMENTS = true; // todo: reset after testing done + private enum BraceMode { + TILL_FIRST, TILL_LAST + } + private StatementParser() { } @Nullable public static PsiBuilder.Marker parseCodeBlock(final PsiBuilder builder) { if (builder.getTokenType() != JavaTokenType.LBRACE) return null; - else if (DEEP_PARSE_BLOCKS_IN_STATEMENTS) return parseCodeBlockDeep(builder); + else if (DEEP_PARSE_BLOCKS_IN_STATEMENTS) return parseCodeBlockDeep(builder, false); final PsiBuilder.Marker codeBlock = builder.mark(); builder.advanceLexer(); @@ -89,21 +99,487 @@ public class StatementParser { } @Nullable - public static PsiBuilder.Marker parseCodeBlockDeep(final PsiBuilder builder) { + public static PsiBuilder.Marker parseCodeBlockDeep(final PsiBuilder builder, final boolean parseUntilEof) { if (builder.getTokenType() != JavaTokenType.LBRACE) return null; final PsiBuilder.Marker codeBlock = builder.mark(); builder.advanceLexer(); - // temp - if (builder.getTokenType() == JavaTokenType.RBRACE || builder.eof()) { + parseStatements(builder, (parseUntilEof ? BraceMode.TILL_LAST : BraceMode.TILL_FIRST)); + + // todo: check closing brace + //final TreeElement lastChild = elementToAdd.getLastChildNode(); + //if (lastChild == null || lastChild.getElementType() != JavaTokenType.RBRACE){ + // CompositeElement errorElement = Factory.createErrorElement(JavaErrorMessages.message("expected.rbrace")); + // elementToAdd.rawAddChildren(errorElement); + // elementToAdd.putUserData(TreeUtil.UNCLOSED_ELEMENT_PROPERTY, ""); + //} + + codeBlock.done(JavaElementType.CODE_BLOCK); + return codeBlock; + } + + private static void parseStatements(final PsiBuilder builder, final BraceMode braceMode) { + while (builder.getTokenType() != null) { + final PsiBuilder.Marker statement = parseStatement(builder); + if (statement != null) continue; + + final IElementType tokenType = builder.getTokenType(); + final PsiBuilder.Marker error = builder.mark(); builder.advanceLexer(); - codeBlock.done(JavaElementType.CODE_BLOCK); - return codeBlock; + + if (tokenType == JavaTokenType.RBRACE) { + if (braceMode == BraceMode.TILL_FIRST) { + error.drop(); + return; + } + else if (braceMode == BraceMode.TILL_LAST) { + if (builder.getTokenType() == null) { + error.drop(); + return; + } + } + } + + if (tokenType == JavaTokenType.ELSE_KEYWORD) { + error.error(JavaErrorMessages.message("else.without.if")); + } + else if (tokenType == JavaTokenType.CATCH_KEYWORD) { + error.error(JavaErrorMessages.message("catch.without.try")); + } + else if (tokenType == JavaTokenType.FINAL_KEYWORD) { + error.error(JavaErrorMessages.message("finally.without.try")); + } + else { + error.error(JavaErrorMessages.message("unexpected.token")); + } + } + } + + @Nullable + private static PsiBuilder.Marker parseStatement(final PsiBuilder builder) { + final IElementType tokenType = builder.getTokenType(); + + // todo: custom parsers (?) + + if (tokenType == JavaTokenType.IF_KEYWORD) { + return parseIfStatement(builder); + } + else if (tokenType == JavaTokenType.WHILE_KEYWORD) { + return parseWhileStatement(builder); + } + else if (tokenType == JavaTokenType.FOR_KEYWORD) { + return parseForStatement(builder); + } + else if (tokenType == JavaTokenType.DO_KEYWORD) { + return parseDoWhileStatement(builder); + } + else if (tokenType == JavaTokenType.SWITCH_KEYWORD) { + return parseSwitchStatement(builder); + } + else if (tokenType == JavaTokenType.CASE_KEYWORD || tokenType == JavaTokenType.DEFAULT_KEYWORD) { + return parseSwitchLabelStatement(builder); + } + else if (tokenType == JavaTokenType.BREAK_KEYWORD) { + return parseBreakStatement(builder); + } + else if (tokenType == JavaTokenType.CONTINUE_KEYWORD) { + return parseContinueStatement(builder); + } + else if (tokenType == JavaTokenType.RETURN_KEYWORD) { + return parseReturnStatement(builder); + } + else if (tokenType == JavaTokenType.THROW_KEYWORD) { + return parseThrowStatement(builder); + } + else if (tokenType == JavaTokenType.SYNCHRONIZED_KEYWORD) { + return parseSynchronizedStatement(builder); + } + else if (tokenType == JavaTokenType.TRY_KEYWORD) { + return parseTryStatement(builder); + } + else if (tokenType == JavaTokenType.ASSERT_KEYWORD) { + return parseAssertStatement(builder); + } + else if (tokenType == JavaTokenType.LBRACE) { + return parseBlockStatement(builder); + } + else if (tokenType instanceof ILazyParseableElementType) { + final PsiBuilder.Marker marker = builder.mark(); + marker.drop(); + builder.advanceLexer(); + return marker; + } + else if (tokenType == JavaTokenType.SEMICOLON) { + final PsiBuilder.Marker empty = builder.mark(); + builder.advanceLexer(); + empty.done(JavaElementType.EMPTY_STATEMENT); + return empty; + } + else if (tokenType == JavaTokenType.IDENTIFIER || tokenType == JavaTokenType.AT) { + final PsiBuilder.Marker refPos = builder.mark(); + DeclarationParser.parseAnnotations(builder); + skipQualifiedName(builder); + final IElementType suspectedLT = builder.getTokenType(); + refPos.rollbackTo(); + if (suspectedLT == JavaTokenType.LT) { + final PsiBuilder.Marker declStatement = builder.mark(); + final PsiBuilder.Marker decl = DeclarationParser.parseDeclaration(builder, DeclarationParser.Context.CODE_BLOCK); + if (decl == null) { + error(builder, JavaErrorMessages.message("expected.identifier")); + } + declStatement.done(JavaElementType.DECLARATION_STATEMENT); + return declStatement; + } } + final PsiBuilder.Marker pos = builder.mark(); + final PsiBuilder.Marker expr = ExpressionParser.parse(builder); + + if (expr != null) { + int count = 1; + final PsiBuilder.Marker list = expr.precede(); + final PsiBuilder.Marker statement = list.precede(); + while (builder.getTokenType() == JavaTokenType.COMMA) { + final PsiBuilder.Marker commaPos = builder.mark(); + builder.advanceLexer(); + final PsiBuilder.Marker expr1 = ExpressionParser.parse(builder); + if (expr1 == null) { + commaPos.rollbackTo(); + break; + } + commaPos.drop(); + count++; + } + if (count > 1) { + pos.drop(); + list.done(JavaElementType.EXPRESSION_LIST); + semicolon(builder); + statement.done(JavaElementType.EXPRESSION_LIST_STATEMENT); + return statement; + } + if (exprType(expr) != JavaElementType.REFERENCE_EXPRESSION) { + drop(list, pos); + semicolon(builder); + statement.done(JavaElementType.EXPRESSION_STATEMENT); + return statement; + } + pos.rollbackTo(); + } + else { + pos.drop(); + } + + final PsiBuilder.Marker decl = DeclarationParser.parseDeclaration(builder, DeclarationParser.Context.CODE_BLOCK); + if (decl != null) { + final PsiBuilder.Marker statement = decl.precede(); + statement.done(JavaElementType.DECLARATION_STATEMENT); + return statement; + } + + if (lookAhead(builder, JavaTokenType.IDENTIFIER, JavaTokenType.COLON)) { + final PsiBuilder.Marker statement = builder.mark(); + advance(builder, 2); + parseStatement(builder); + statement.done(JavaElementType.LABELED_STATEMENT); + return statement; + } + + if (expr != null) { + final PsiBuilder.Marker statement = builder.mark(); + ExpressionParser.parse(builder); + semicolon(builder); + statement.done(JavaElementType.EXPRESSION_STATEMENT); + return statement; + } + + return null; + } + + private static void skipQualifiedName(final PsiBuilder builder) { + if (!expect(builder, JavaTokenType.IDENTIFIER)) return; + while (lookAhead(builder, JavaTokenType.DOT, JavaTokenType.IDENTIFIER)) { + advance(builder, 2); + } + } + + private static void semicolon(final PsiBuilder builder) { + expectOrError(builder, JavaTokenType.SEMICOLON, JavaErrorMessages.message("expected.semicolon")); + } + + @NotNull + private static PsiBuilder.Marker parseIfStatement(final PsiBuilder builder) { + final PsiBuilder.Marker statement = builder.mark(); + builder.advanceLexer(); + + if (!parseExpressionInParenth(builder)) { + statement.done(JavaElementType.IF_STATEMENT); + return statement; + } + + final PsiBuilder.Marker thenStatement = parseStatement(builder); + if (thenStatement == null) { + error(builder, JavaErrorMessages.message("expected.statement")); + statement.done(JavaElementType.IF_STATEMENT); + return statement; + } + + if (!expect(builder, JavaTokenType.ELSE_KEYWORD)) { + statement.done(JavaElementType.IF_STATEMENT); + return statement; + } + + final PsiBuilder.Marker elseStatement = parseStatement(builder); + if (elseStatement == null) { + error(builder, JavaErrorMessages.message("expected.statement")); + } + + statement.done(JavaElementType.IF_STATEMENT); + return statement; + } + + @NotNull + private static PsiBuilder.Marker parseWhileStatement(final PsiBuilder builder) { + final PsiBuilder.Marker statement = builder.mark(); + builder.advanceLexer(); + + if (!parseExpressionInParenth(builder)) { + statement.done(JavaElementType.WHILE_STATEMENT); + return statement; + } + + final PsiBuilder.Marker bodyStatement = parseStatement(builder); + if (bodyStatement == null) { + error(builder, JavaErrorMessages.message("expected.statement")); + } + + statement.done(JavaElementType.WHILE_STATEMENT); + return statement; + } + + @NotNull + private static PsiBuilder.Marker parseForStatement(final PsiBuilder builder) { + final PsiBuilder.Marker statement = builder.mark(); + builder.advanceLexer(); + + if (!expect(builder, JavaTokenType.LPARENTH)) { + error(builder, JavaErrorMessages.message("expected.lparen")); + statement.done(JavaElementType.FOR_STATEMENT); + return statement; + } + + final PsiBuilder.Marker afterParenth = builder.mark(); + final PsiBuilder.Marker param = DeclarationParser.parseParameter(builder, false); + if (param == null || builder.getTokenType() != JavaTokenType.COLON) { + afterParenth.rollbackTo(); + return parseForLoopFromInitialization(builder, statement); + } + else { + afterParenth.drop(); + return parseForEachFromColon(builder, statement); + } + } + + @NotNull + private static PsiBuilder.Marker parseForLoopFromInitialization(final PsiBuilder builder, final PsiBuilder.Marker statement) { + final PsiBuilder.Marker init = parseStatement(builder); + if (init == null){ + error(builder, JavaErrorMessages.message("expected.statement")); + if (!expect(builder, JavaTokenType.RPARENTH)) { + statement.done(JavaElementType.FOR_STATEMENT); + return statement; + } + } + else { + ExpressionParser.parse(builder); + if (!expect(builder, JavaTokenType.SEMICOLON)) { + error(builder, JavaErrorMessages.message("expected.semicolon")); + if (!expect(builder, JavaTokenType.RPARENTH)) { + statement.done(JavaElementType.FOR_STATEMENT); + return statement; + } + } + else { + parseExpressionOrExpressionList(builder); + if (!expect(builder, JavaTokenType.RPARENTH)) { + error(builder, JavaErrorMessages.message("expected.rparen")); + statement.done(JavaElementType.FOR_STATEMENT); + return statement; + } + } + } + + final PsiBuilder.Marker bodyStatement = parseStatement(builder); + if (bodyStatement == null) { + error(builder, JavaErrorMessages.message("expected.statement")); + } + + statement.done(JavaElementType.FOR_STATEMENT); + return statement; + } + + private static void parseExpressionOrExpressionList(final PsiBuilder builder) { + final PsiBuilder.Marker expr = ExpressionParser.parse(builder); + if (expr == null) return; + + final PsiBuilder.Marker expressionStatement; + if (builder.getTokenType() != JavaTokenType.COMMA) { + expressionStatement = expr.precede(); + expressionStatement.done(JavaElementType.EXPRESSION_STATEMENT); + } + else { + final PsiBuilder.Marker expressionList = expr.precede(); + expressionStatement = expressionList.precede(); + + do { + builder.advanceLexer(); + final PsiBuilder.Marker nextExpression = ExpressionParser.parse(builder); + if (nextExpression == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + } + } + while (builder.getTokenType() == JavaTokenType.COMMA); + + expressionList.done(JavaElementType.EXPRESSION_LIST); + expressionStatement.done(JavaElementType.EXPRESSION_LIST_STATEMENT); + } + } + + @NotNull + private static PsiBuilder.Marker parseForEachFromColon(PsiBuilder builder, PsiBuilder.Marker statement) { + builder.advanceLexer(); + + final PsiBuilder.Marker expr = ExpressionParser.parse(builder); + if (expr == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + } + + if (expect(builder, JavaTokenType.RPARENTH)) { + final PsiBuilder.Marker bodyStatement = parseStatement(builder); + if (bodyStatement == null) { + error(builder, JavaErrorMessages.message("expected.statement")); + } + } + else { + error(builder, JavaErrorMessages.message("expected.rparen")); + } + + statement.done(JavaElementType.FOREACH_STATEMENT); + return statement; + } + + @NotNull + private static PsiBuilder.Marker parseDoWhileStatement(final PsiBuilder builder) { // todo: implement - codeBlock.drop(); throw new UnsupportedOperationException(builder.toString()); } + + @NotNull + private static PsiBuilder.Marker parseSwitchStatement(final PsiBuilder builder) { + // todo: implement + throw new UnsupportedOperationException(builder.toString()); + } + + @NotNull + private static PsiBuilder.Marker parseSwitchLabelStatement(final PsiBuilder builder) { + // todo: implement + throw new UnsupportedOperationException(builder.toString()); + } + + @NotNull + private static PsiBuilder.Marker parseBreakStatement(final PsiBuilder builder) { + // todo: implement + throw new UnsupportedOperationException(builder.toString()); + } + + @NotNull + private static PsiBuilder.Marker parseContinueStatement(final PsiBuilder builder) { + // todo: implement + throw new UnsupportedOperationException(builder.toString()); + } + + @NotNull + private static PsiBuilder.Marker parseReturnStatement(final PsiBuilder builder) { + // todo: implement + throw new UnsupportedOperationException(builder.toString()); + } + + @NotNull + private static PsiBuilder.Marker parseThrowStatement(final PsiBuilder builder) { + // todo: implement + throw new UnsupportedOperationException(builder.toString()); + } + + @NotNull + private static PsiBuilder.Marker parseSynchronizedStatement(final PsiBuilder builder) { + // todo: implement + throw new UnsupportedOperationException(builder.toString()); + } + + @NotNull + private static PsiBuilder.Marker parseTryStatement(final PsiBuilder builder) { + // todo: implement + throw new UnsupportedOperationException(builder.toString()); + } + + @NotNull + private static PsiBuilder.Marker parseAssertStatement(final PsiBuilder builder) { + final PsiBuilder.Marker statement = builder.mark(); + builder.advanceLexer(); + + final PsiBuilder.Marker expr = ExpressionParser.parse(builder); + if (expr == null) { + error(builder, JavaErrorMessages.message("expected.boolean.expression")); + statement.done(JavaElementType.ASSERT_STATEMENT); + return statement; + } + + if (expect(builder, JavaTokenType.COLON)) { + final PsiBuilder.Marker expr2 = ExpressionParser.parse(builder); + if (expr2 == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + statement.done(JavaElementType.ASSERT_STATEMENT); + return statement; + } + } + + semicolon(builder); + statement.done(JavaElementType.ASSERT_STATEMENT); + return statement; + } + + @NotNull + private static PsiBuilder.Marker parseBlockStatement(final PsiBuilder builder) { + final PsiBuilder.Marker statement = builder.mark(); + parseCodeBlock(builder); + statement.done(JavaElementType.BLOCK_STATEMENT); + return statement; + } + + private static boolean parseExpressionInParenth(final PsiBuilder builder) { + if (!expect(builder, JavaTokenType.LPARENTH)) { + error(builder, JavaErrorMessages.message("expected.lparen")); + return false; + } + + final PsiBuilder.Marker beforeExpr = builder.mark(); + final PsiBuilder.Marker expr = ExpressionParser.parse(builder); + if (expr == null || builder.getTokenType() == JavaTokenType.SEMICOLON) { + beforeExpr.rollbackTo(); + error(builder, JavaErrorMessages.message("expected.expression")); + if (builder.getTokenType() != JavaTokenType.RPARENTH) { + return false; + } + } + else { + beforeExpr.drop(); + if (builder.getTokenType() != JavaTokenType.RPARENTH) { + error(builder, JavaErrorMessages.message("expected.rparen")); + return false; + } + } + + builder.advanceLexer(); + return true; + } } diff --git a/java/java-tests/testData/psi/parser-partial/statements/AnonymousInSmartCompletion.txt b/java/java-tests/testData/psi/parser-partial/statements/AnonymousInSmartCompletion.txt new file mode 100644 index 000000000000..66f7b6dd68de --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/AnonymousInSmartCompletion.txt @@ -0,0 +1,109 @@ +PsiJavaFile:AnonymousInSmartCompletion.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiNewExpression:new Foo(hash +#) {} + PsiKeyword:new('new') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiAnonymousClass + PsiJavaCodeReferenceElement:Foo + PsiIdentifier:Foo('Foo') + PsiReferenceParameterList + + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:hash + PsiReferenceParameterList + + PsiIdentifier:hash('hash') + PsiErrorElement:',' or ')' expected + + PsiExpression(empty) + + PsiWhiteSpace('\n') + PsiElement(BAD_CHARACTER)('#') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n ') + PsiExpressionStatement + PsiNewExpression:new Foo(hash +#, bar) {} + PsiKeyword:new('new') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiAnonymousClass + PsiJavaCodeReferenceElement:Foo + PsiIdentifier:Foo('Foo') + PsiReferenceParameterList + + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:hash + PsiReferenceParameterList + + PsiIdentifier:hash('hash') + PsiErrorElement:',' or ')' expected + + PsiExpression(empty) + + PsiWhiteSpace('\n') + PsiElement(BAD_CHARACTER)('#') + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiReferenceExpression:bar + PsiReferenceParameterList + + PsiIdentifier:bar('bar') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n ') + PsiExpressionStatement + PsiNewExpression:new Foo(hash +#x) {} + PsiKeyword:new('new') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiAnonymousClass + PsiJavaCodeReferenceElement:Foo + PsiIdentifier:Foo('Foo') + PsiReferenceParameterList + + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:hash + PsiReferenceParameterList + + PsiIdentifier:hash('hash') + PsiErrorElement:',' or ')' expected + + PsiExpression(empty) + + PsiWhiteSpace('\n') + PsiElement(BAD_CHARACTER)('#') + PsiErrorElement:',' or ')' expected + + PsiExpression(empty) + + PsiReferenceExpression:x + PsiReferenceParameterList + + PsiIdentifier:x('x') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/AssertNormal0.txt b/java/java-tests/testData/psi/parser-partial/statements/AssertNormal0.txt new file mode 100644 index 000000000000..97cc2696b643 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/AssertNormal0.txt @@ -0,0 +1,14 @@ +PsiJavaFile:AssertNormal0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiAssertStatement + PsiKeyword:assert('assert') + PsiWhiteSpace(' ') + PsiReferenceExpression:cond + PsiReferenceParameterList + + PsiIdentifier:cond('cond') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/AssertNormal1.txt b/java/java-tests/testData/psi/parser-partial/statements/AssertNormal1.txt new file mode 100644 index 000000000000..6064494d9854 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/AssertNormal1.txt @@ -0,0 +1,21 @@ +PsiJavaFile:AssertNormal1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiAssertStatement + PsiKeyword:assert('assert') + PsiWhiteSpace(' ') + PsiReferenceExpression:cond + PsiReferenceParameterList + + PsiIdentifier:cond('cond') + PsiWhiteSpace(' ') + PsiJavaToken:COLON(':') + PsiWhiteSpace(' ') + PsiReferenceExpression:message + PsiReferenceParameterList + + PsiIdentifier:message('message') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/AssignmentSimple.txt b/java/java-tests/testData/psi/parser-partial/statements/AssignmentSimple.txt new file mode 100644 index 000000000000..7c17e2103e88 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/AssignmentSimple.txt @@ -0,0 +1,36 @@ +PsiJavaFile:AssignmentSimple.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiDeclarationStatement + PsiLocalVariable:a + PsiModifierList: + + PsiTypeElement:int[] + PsiTypeElement:int + PsiKeyword:int('int') + PsiJavaToken:LBRACKET('[') + PsiJavaToken:RBRACKET(']') + PsiWhiteSpace(' ') + PsiIdentifier:a('a') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n ') + PsiExpressionStatement + PsiAssignmentExpression:a[0] = 0 + PsiArrayAccessExpression:a[0] + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiJavaToken:LBRACKET('[') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:RBRACKET(']') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/BlockSimple.txt b/java/java-tests/testData/psi/parser-partial/statements/BlockSimple.txt new file mode 100644 index 000000000000..6258d217e3a6 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/BlockSimple.txt @@ -0,0 +1,10 @@ +PsiJavaFile:BlockSimple.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiBlockStatement + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/For.txt b/java/java-tests/testData/psi/parser-partial/statements/For.txt new file mode 100644 index 000000000000..2af0e7cc5269 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/For.txt @@ -0,0 +1,83 @@ +PsiJavaFile:For.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiForStatement + PsiKeyword:for('for') + PsiJavaToken:LPARENTH('(') + PsiDeclarationStatement + PsiLocalVariable:it + PsiModifierList: + + PsiTypeElement:Iterator + PsiJavaCodeReferenceElement:Iterator + PsiIdentifier:Iterator('Iterator') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:String + PsiJavaCodeReferenceElement:String + PsiIdentifier:String('String') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiWhiteSpace(' ') + PsiIdentifier:it('it') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:null + PsiJavaToken:NULL_KEYWORD('null') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiMethodCallExpression:it.hasNext() + PsiReferenceExpression:it.hasNext + PsiReferenceExpression:it + PsiReferenceParameterList + + PsiIdentifier:it('it') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:hasNext('hasNext') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiBlockStatement + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiDeclarationStatement + PsiLocalVariable:s + PsiModifierList: + + PsiTypeElement:String + PsiJavaCodeReferenceElement:String + PsiIdentifier:String('String') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:s('s') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiMethodCallExpression:it.next() + PsiReferenceExpression:it.next + PsiReferenceExpression:it + PsiReferenceParameterList + + PsiIdentifier:it('it') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:next('next') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ForEach.txt b/java/java-tests/testData/psi/parser-partial/statements/ForEach.txt new file mode 100644 index 000000000000..a8b598a8a9c6 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ForEach.txt @@ -0,0 +1,39 @@ +PsiJavaFile:ForEach.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiForeachStatement + PsiKeyword:for('for') + PsiJavaToken:LPARENTH('(') + PsiParameter:o + PsiModifierList: + + PsiTypeElement:Object + PsiJavaCodeReferenceElement:Object + PsiIdentifier:Object('Object') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:o('o') + PsiWhiteSpace(' ') + PsiJavaToken:COLON(':') + PsiWhiteSpace(' ') + PsiMethodCallExpression:map.entrySet() + PsiReferenceExpression:map.entrySet + PsiReferenceExpression:map + PsiReferenceParameterList + + PsiIdentifier:map('map') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:entrySet('entrySet') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiEmptyStatement + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete0.txt b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete0.txt new file mode 100644 index 000000000000..695f51742f79 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete0.txt @@ -0,0 +1,10 @@ +PsiJavaFile:ForIncomplete0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiForStatement + PsiKeyword:for('for') + PsiErrorElement:'(' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete1.txt b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete1.txt new file mode 100644 index 000000000000..3173c174742d --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete1.txt @@ -0,0 +1,11 @@ +PsiJavaFile:ForIncomplete1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiForStatement + PsiKeyword:for('for') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Statement expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete2.txt b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete2.txt new file mode 100644 index 000000000000..34ce18373442 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete2.txt @@ -0,0 +1,25 @@ +PsiJavaFile:ForIncomplete2.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiForStatement + PsiKeyword:for('for') + PsiJavaToken:LPARENTH('(') + PsiDeclarationStatement + PsiLocalVariable:i + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:i('i') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:SEMICOLON(';') + PsiErrorElement:';' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete3.txt b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete3.txt new file mode 100644 index 000000000000..9c0b75af706c --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete3.txt @@ -0,0 +1,36 @@ +PsiJavaFile:ForIncomplete3.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiForStatement + PsiKeyword:for('for') + PsiJavaToken:LPARENTH('(') + PsiDeclarationStatement + PsiLocalVariable:i + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:i('i') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiBinaryExpression:i < 10 + PsiReferenceExpression:i + PsiReferenceParameterList + + PsiIdentifier:i('i') + PsiWhiteSpace(' ') + PsiJavaToken:LT('<') + PsiWhiteSpace(' ') + PsiLiteralExpression:10 + PsiJavaToken:INTEGER_LITERAL('10') + PsiErrorElement:';' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete4.txt b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete4.txt new file mode 100644 index 000000000000..fd7b7536acce --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete4.txt @@ -0,0 +1,37 @@ +PsiJavaFile:ForIncomplete4.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiForStatement + PsiKeyword:for('for') + PsiJavaToken:LPARENTH('(') + PsiDeclarationStatement + PsiLocalVariable:i + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:i('i') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiBinaryExpression:i < 10 + PsiReferenceExpression:i + PsiReferenceParameterList + + PsiIdentifier:i('i') + PsiWhiteSpace(' ') + PsiJavaToken:LT('<') + PsiWhiteSpace(' ') + PsiLiteralExpression:10 + PsiJavaToken:INTEGER_LITERAL('10') + PsiJavaToken:SEMICOLON(';') + PsiErrorElement:')' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete5.txt b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete5.txt new file mode 100644 index 000000000000..a37325bd6cc3 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete5.txt @@ -0,0 +1,45 @@ +PsiJavaFile:ForIncomplete5.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiForStatement + PsiKeyword:for('for') + PsiJavaToken:LPARENTH('(') + PsiDeclarationStatement + PsiLocalVariable:i + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:i('i') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiBinaryExpression:i < 10 + PsiReferenceExpression:i + PsiReferenceParameterList + + PsiIdentifier:i('i') + PsiWhiteSpace(' ') + PsiJavaToken:LT('<') + PsiWhiteSpace(' ') + PsiLiteralExpression:10 + PsiJavaToken:INTEGER_LITERAL('10') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiPostfixExpression:i++ + PsiReferenceExpression:i + PsiReferenceParameterList + + PsiIdentifier:i('i') + PsiJavaToken:PLUSPLUS('++') + PsiErrorElement:')' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete6.txt b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete6.txt new file mode 100644 index 000000000000..033ba2c2dd9c --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete6.txt @@ -0,0 +1,46 @@ +PsiJavaFile:ForIncomplete6.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiForStatement + PsiKeyword:for('for') + PsiJavaToken:LPARENTH('(') + PsiDeclarationStatement + PsiLocalVariable:i + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:i('i') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiBinaryExpression:i < 10 + PsiReferenceExpression:i + PsiReferenceParameterList + + PsiIdentifier:i('i') + PsiWhiteSpace(' ') + PsiJavaToken:LT('<') + PsiWhiteSpace(' ') + PsiLiteralExpression:10 + PsiJavaToken:INTEGER_LITERAL('10') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiPostfixExpression:i++ + PsiReferenceExpression:i + PsiReferenceParameterList + + PsiIdentifier:i('i') + PsiJavaToken:PLUSPLUS('++') + PsiJavaToken:RPARENTH(')') + PsiErrorElement:Statement expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete7.txt b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete7.txt new file mode 100644 index 000000000000..58b2e381bda8 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete7.txt @@ -0,0 +1,23 @@ +PsiJavaFile:ForIncomplete7.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiForStatement + PsiKeyword:for('for') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Statement expected + + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete8.txt b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete8.txt new file mode 100644 index 000000000000..3d5f577370e7 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete8.txt @@ -0,0 +1,37 @@ +PsiJavaFile:ForIncomplete8.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiForStatement + PsiKeyword:for('for') + PsiJavaToken:LPARENTH('(') + PsiDeclarationStatement + PsiLocalVariable:i + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:i('i') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:SEMICOLON(';') + PsiErrorElement:';' expected + + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete9.txt b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete9.txt new file mode 100644 index 000000000000..a9ee21d92b41 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ForIncomplete9.txt @@ -0,0 +1,48 @@ +PsiJavaFile:ForIncomplete9.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiForStatement + PsiKeyword:for('for') + PsiJavaToken:LPARENTH('(') + PsiDeclarationStatement + PsiLocalVariable:i + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:i('i') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiBinaryExpression:i < 0 + PsiReferenceExpression:i + PsiReferenceParameterList + + PsiIdentifier:i('i') + PsiWhiteSpace(' ') + PsiJavaToken:LT('<') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiErrorElement:';' expected + + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ForNormal0.txt b/java/java-tests/testData/psi/parser-partial/statements/ForNormal0.txt new file mode 100644 index 000000000000..658837ef8302 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ForNormal0.txt @@ -0,0 +1,47 @@ +PsiJavaFile:ForNormal0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiForStatement + PsiKeyword:for('for') + PsiJavaToken:LPARENTH('(') + PsiDeclarationStatement + PsiLocalVariable:i + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:i('i') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiLiteralExpression:0 + PsiJavaToken:INTEGER_LITERAL('0') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiBinaryExpression:i < 10 + PsiReferenceExpression:i + PsiReferenceParameterList + + PsiIdentifier:i('i') + PsiWhiteSpace(' ') + PsiJavaToken:LT('<') + PsiWhiteSpace(' ') + PsiLiteralExpression:10 + PsiJavaToken:INTEGER_LITERAL('10') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiPostfixExpression:i++ + PsiReferenceExpression:i + PsiReferenceParameterList + + PsiIdentifier:i('i') + PsiJavaToken:PLUSPLUS('++') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace('\n ') + PsiEmptyStatement + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ForNormal1.txt b/java/java-tests/testData/psi/parser-partial/statements/ForNormal1.txt new file mode 100644 index 000000000000..68ebe2faf46d --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ForNormal1.txt @@ -0,0 +1,27 @@ +PsiJavaFile:ForNormal1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiForStatement + PsiKeyword:for('for') + PsiJavaToken:LPARENTH('(') + PsiWhiteSpace(' ') + PsiEmptyStatement + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete0.txt b/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete0.txt new file mode 100644 index 000000000000..d7eb909246f0 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete0.txt @@ -0,0 +1,10 @@ +PsiJavaFile:IfIncomplete0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiIfStatement + PsiKeyword:if('if') + PsiErrorElement:'(' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete1.txt b/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete1.txt new file mode 100644 index 000000000000..085acd062e23 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete1.txt @@ -0,0 +1,12 @@ +PsiJavaFile:IfIncomplete1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiIfStatement + PsiKeyword:if('if') + PsiWhiteSpace(' ') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete2.txt b/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete2.txt new file mode 100644 index 000000000000..c090dc125f5d --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete2.txt @@ -0,0 +1,23 @@ +PsiJavaFile:IfIncomplete2.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiIfStatement + PsiKeyword:if('if') + PsiWhiteSpace(' ') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiWhiteSpace('\n ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete3.txt b/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete3.txt new file mode 100644 index 000000000000..df9bee0c4064 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete3.txt @@ -0,0 +1,16 @@ +PsiJavaFile:IfIncomplete3.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiIfStatement + PsiKeyword:if('if') + PsiWhiteSpace(' ') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:cond + PsiReferenceParameterList + + PsiIdentifier:cond('cond') + PsiErrorElement:')' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete4.txt b/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete4.txt new file mode 100644 index 000000000000..2bcc1f48d28a --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete4.txt @@ -0,0 +1,17 @@ +PsiJavaFile:IfIncomplete4.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiIfStatement + PsiKeyword:if('if') + PsiWhiteSpace(' ') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:cond + PsiReferenceParameterList + + PsiIdentifier:cond('cond') + PsiJavaToken:RPARENTH(')') + PsiErrorElement:Statement expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete5.txt b/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete5.txt new file mode 100644 index 000000000000..0ecc8a928353 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete5.txt @@ -0,0 +1,24 @@ +PsiJavaFile:IfIncomplete5.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiIfStatement + PsiKeyword:if('if') + PsiWhiteSpace(' ') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete6.txt b/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete6.txt new file mode 100644 index 000000000000..4e3418ff43d0 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/IfIncomplete6.txt @@ -0,0 +1,30 @@ +PsiJavaFile:IfIncomplete6.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiIfStatement + PsiKeyword:if('if') + PsiWhiteSpace(' ') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:cond + PsiReferenceParameterList + + PsiIdentifier:cond('cond') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiKeyword:else('else') + PsiErrorElement:Statement expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/IfNormalNoElse.txt b/java/java-tests/testData/psi/parser-partial/statements/IfNormalNoElse.txt new file mode 100644 index 000000000000..a25d16a462ad --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/IfNormalNoElse.txt @@ -0,0 +1,26 @@ +PsiJavaFile:IfNormalNoElse.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiIfStatement + PsiKeyword:if('if') + PsiWhiteSpace(' ') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:f1() + PsiReferenceExpression:f1 + PsiReferenceParameterList + + PsiIdentifier:f1('f1') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/IfNormalWithElse.txt b/java/java-tests/testData/psi/parser-partial/statements/IfNormalWithElse.txt new file mode 100644 index 000000000000..9a9875b2c86b --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/IfNormalWithElse.txt @@ -0,0 +1,49 @@ +PsiJavaFile:IfNormalWithElse.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiIfStatement + PsiKeyword:if('if') + PsiWhiteSpace(' ') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiJavaToken:RPARENTH(')') + PsiBlockStatement + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:f1() + PsiReferenceExpression:f1 + PsiReferenceParameterList + + PsiIdentifier:f1('f1') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiKeyword:else('else') + PsiBlockStatement + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:f2() + PsiReferenceExpression:f2 + PsiReferenceParameterList + + PsiIdentifier:f2('f2') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/LabelSimple.txt b/java/java-tests/testData/psi/parser-partial/statements/LabelSimple.txt new file mode 100644 index 000000000000..4ffd8169570a --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/LabelSimple.txt @@ -0,0 +1,19 @@ +PsiJavaFile:LabelSimple.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiLabeledStatement + PsiIdentifier:Loop('Loop') + PsiJavaToken:COLON(':') + PsiWhiteSpace('\n ') + PsiWhileStatement + PsiKeyword:while('while') + PsiJavaToken:LPARENTH('(') + PsiLiteralExpression:true + PsiJavaToken:TRUE_KEYWORD('true') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiEmptyStatement + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/LocalVar.txt b/java/java-tests/testData/psi/parser-partial/statements/LocalVar.txt new file mode 100644 index 000000000000..a5404ae9fb28 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/LocalVar.txt @@ -0,0 +1,24 @@ +PsiJavaFile:LocalVar.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiDeclarationStatement + PsiLocalVariable:list + PsiModifierList: + + PsiTypeElement:List + PsiJavaCodeReferenceElement:List + PsiIdentifier:List('List') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:Integer + PsiJavaCodeReferenceElement:Integer + PsiIdentifier:Integer('Integer') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiWhiteSpace(' ') + PsiIdentifier:list('list') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete0.txt b/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete0.txt new file mode 100644 index 000000000000..e61f6628e513 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete0.txt @@ -0,0 +1,10 @@ +PsiJavaFile:WhileIncomplete0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiWhileStatement + PsiKeyword:while('while') + PsiErrorElement:'(' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete1.txt b/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete1.txt new file mode 100644 index 000000000000..a4ae59d1de1e --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete1.txt @@ -0,0 +1,12 @@ +PsiJavaFile:WhileIncomplete1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiWhileStatement + PsiKeyword:while('while') + PsiWhiteSpace(' ') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete2.txt b/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete2.txt new file mode 100644 index 000000000000..426d5fdbf0a4 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete2.txt @@ -0,0 +1,22 @@ +PsiJavaFile:WhileIncomplete2.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiWhileStatement + PsiKeyword:while('while') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiWhiteSpace('\n ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete3.txt b/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete3.txt new file mode 100644 index 000000000000..e56ebbcf1945 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete3.txt @@ -0,0 +1,15 @@ +PsiJavaFile:WhileIncomplete3.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiWhileStatement + PsiKeyword:while('while') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:cond + PsiReferenceParameterList + + PsiIdentifier:cond('cond') + PsiErrorElement:')' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete4.txt b/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete4.txt new file mode 100644 index 000000000000..a67718d60cfb --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete4.txt @@ -0,0 +1,16 @@ +PsiJavaFile:WhileIncomplete4.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiWhileStatement + PsiKeyword:while('while') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:cond + PsiReferenceParameterList + + PsiIdentifier:cond('cond') + PsiJavaToken:RPARENTH(')') + PsiErrorElement:Statement expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete5.txt b/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete5.txt new file mode 100644 index 000000000000..c158af6e7ea7 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/WhileIncomplete5.txt @@ -0,0 +1,23 @@ +PsiJavaFile:WhileIncomplete5.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiWhileStatement + PsiKeyword:while('while') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/WhileNormal.txt b/java/java-tests/testData/psi/parser-partial/statements/WhileNormal.txt new file mode 100644 index 000000000000..0e12ab587e96 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/WhileNormal.txt @@ -0,0 +1,24 @@ +PsiJavaFile:WhileNormal.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiWhileStatement + PsiKeyword:while('while') + PsiWhiteSpace(' ') + PsiJavaToken:LPARENTH('(') + PsiLiteralExpression:true + PsiJavaToken:TRUE_KEYWORD('true') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java new file mode 100644 index 000000000000..b86f822bc7e4 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java @@ -0,0 +1,88 @@ +/* + * 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.lang.java.parser.partial; + +import com.intellij.lang.PsiBuilder; +import com.intellij.lang.java.parser.JavaParsingTestCase; +import com.intellij.lang.java.parser.StatementParser; + + +public class StatementParserTest extends JavaParsingTestCase { + public StatementParserTest() { + super("parser-partial/statements"); + } + + public void testAssertNormal0() { doParserTest("{ assert cond; }"); } + public void testAssertNormal1() { doParserTest("{ assert cond : message; }"); } + + public void testAssignmentSimple() { doParserTest("{ int[] a;\n a[0] = 0; }"); } + + public void testBlockSimple() { doParserTest("{ {} }"); } + //public void testAnonymousInSmartCompletion() { doParserTest("{ new Foo(hash\n#) {};\n new Foo(hash\n#, bar) {};\n new Foo(hash\n#x) {}; }"); } // todo: spaces + + public void testLocalVar() { doParserTest("{ List list; }"); } + public void testFor() { doParserTest("{ for(Iterator it = null; it.hasNext();) { String s = it.next(); } }"); } + + //DoWhileParsingTest + + public void testForNormal0() { doParserTest("{ for(int i = 0; i < 10; i++)\n ; }"); } + public void testForNormal1() { doParserTest("{ for( ; ; ) foo(); }"); } + public void testForEach() { doParserTest("{ for(Object o : map.entrySet()) ; }"); } + public void testForIncomplete0() { doParserTest("{ for }"); } + public void testForIncomplete1() { doParserTest("{ for( }"); } + public void testForIncomplete2() { doParserTest("{ for(int i = 0; }"); } + public void testForIncomplete3() { doParserTest("{ for(int i = 0; i < 10 }"); } + public void testForIncomplete4() { doParserTest("{ for(int i = 0; i < 10; }"); } + public void testForIncomplete5() { doParserTest("{ for(int i = 0; i < 10; i++ }"); } + public void testForIncomplete6() { doParserTest("{ for(int i = 0; i < 10; i++) }"); } + public void testForIncomplete7() { doParserTest("{ for() foo(); }"); } + public void testForIncomplete8() { doParserTest("{ for(int i = 0;) foo(); }"); } + public void testForIncomplete9() { doParserTest("{ for(int i = 0; i < 0) foo(); }"); } + + public void testIfNormalWithElse() { doParserTest("{ if (a){ f1(); } else{ f2(); } }"); } + public void testIfNormalNoElse() { doParserTest("{ if (a) f1(); }"); } + public void testIfIncomplete0() { doParserTest("{ if }"); } + public void testIfIncomplete1() { doParserTest("{ if ( }"); } + public void testIfIncomplete2() { doParserTest("{ if (\n foo(); }"); } + public void testIfIncomplete3() { doParserTest("{ if (cond }"); } + public void testIfIncomplete4() { doParserTest("{ if (cond) }"); } + public void testIfIncomplete5() { doParserTest("{ if () foo(); }"); } + public void testIfIncomplete6() { doParserTest("{ if (cond) foo(); else }"); } + + public void testLabelSimple() { doParserTest("{ Loop:\n while(true) ; }"); } + + //ReturnParsingTest + //SwitchParsingTest + //SynchronizedParsingTest + //ThrowParsingTest + //TryParsingTest + + public void testWhileNormal() { doParserTest("{ while (true) foo(); }"); } + public void testWhileIncomplete0() { doParserTest("{ while }"); } + public void testWhileIncomplete1() { doParserTest("{ while ( }"); } + public void testWhileIncomplete2() { doParserTest("{ while(\n foo(); }"); } + public void testWhileIncomplete3() { doParserTest("{ while(cond }"); } + public void testWhileIncomplete4() { doParserTest("{ while(cond) }"); } + public void testWhileIncomplete5() { doParserTest("{ while() foo(); }"); } + + private void doParserTest(final String text) { + doParserTest(text, new Parser() { + public void parse(final PsiBuilder builder) { + StatementParser.parseCodeBlockDeep(builder, true); + } + }); + } +} From cf6f089bd0db41db9310700eb7f0db1dfed74e0e Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 29 Jul 2010 16:07:27 +0400 Subject: [PATCH 04/92] New Java parser (work in progress) --- .../lang/java/parser/DeclarationParser.java | 7 +- .../lang/java/parser/StatementParser.java | 98 +++++++++++++++++-- .../statements/SyncIncomplete0.txt | 10 ++ .../statements/SyncIncomplete1.txt | 11 +++ .../statements/SyncIncomplete2.txt | 15 +++ .../statements/SyncIncomplete3.txt | 16 +++ .../statements/SyncIncomplete4.txt | 15 +++ .../statements/SyncIncomplete5.txt | 22 +++++ .../parser-partial/statements/SyncNormal.txt | 17 ++++ .../statements/ThrowIncomplete0.txt | 10 ++ .../statements/ThrowIncomplete1.txt | 15 +++ .../parser-partial/statements/ThrowNormal.txt | 14 +++ .../statements/TryIncomplete0.txt | 10 ++ .../statements/TryIncomplete1.txt | 13 +++ .../statements/TryIncomplete2.txt | 15 +++ .../statements/TryIncomplete3.txt | 18 ++++ .../statements/TryIncomplete4.txt | 25 +++++ .../statements/TryIncomplete5.txt | 26 +++++ .../statements/TryIncomplete6.txt | 27 +++++ .../statements/TryIncomplete7.txt | 14 +++ .../parser-partial/statements/TryNormal0.txt | 28 ++++++ .../parser-partial/statements/TryNormal1.txt | 32 ++++++ .../parser-partial/statements/TryNormal2.txt | 15 +++ .../parser/partial/StatementParserTest.java | 27 ++++- 24 files changed, 488 insertions(+), 12 deletions(-) create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete2.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete3.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete4.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete5.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SyncNormal.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ThrowIncomplete0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ThrowIncomplete1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ThrowNormal.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete2.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete3.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete4.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete5.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete6.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryIncomplete7.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryNormal0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryNormal1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/TryNormal2.txt diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index eb0c01af1bac..698ae3e21fd6 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -514,13 +514,14 @@ public class DeclarationParser { if (expect(builder, JavaTokenType.IDENTIFIER)) { eatBrackets(builder); + param.done(JavaElementType.PARAMETER); + return param; } else { error(builder, JavaErrorMessages.message("expected.identifier")); + param.drop(); + return type; } - - param.done(JavaElementType.PARAMETER); - return param; } @Nullable diff --git a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java index 1abd6483f2ef..d9b1b0bb6d08 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java @@ -22,6 +22,7 @@ import com.intellij.psi.impl.source.tree.ElementType; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.ILazyParseableElementType; +import com.intellij.psi.tree.TokenSet; import com.intellij.util.SmartList; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,6 +40,8 @@ public class StatementParser { TILL_FIRST, TILL_LAST } + private static final TokenSet TRY_CLOSERS_SET = TokenSet.create(JavaTokenType.CATCH_KEYWORD, JavaTokenType.FINALLY_KEYWORD); + private StatementParser() { } @Nullable @@ -506,20 +509,103 @@ public class StatementParser { @NotNull private static PsiBuilder.Marker parseThrowStatement(final PsiBuilder builder) { - // todo: implement - throw new UnsupportedOperationException(builder.toString()); + final PsiBuilder.Marker statement = builder.mark(); + builder.advanceLexer(); + + final PsiBuilder.Marker expr = ExpressionParser.parse(builder); + if (expr == null) { + error(builder, JavaErrorMessages.message("expected.expression")); + statement.done(JavaElementType.THROW_STATEMENT); + return statement; + } + + semicolon(builder); + statement.done(JavaElementType.THROW_STATEMENT); + return statement; } @NotNull private static PsiBuilder.Marker parseSynchronizedStatement(final PsiBuilder builder) { - // todo: implement - throw new UnsupportedOperationException(builder.toString()); + final PsiBuilder.Marker statement = builder.mark(); + builder.advanceLexer(); + + if (!parseExpressionInParenth(builder)) { + statement.done(JavaElementType.SYNCHRONIZED_STATEMENT); + return statement; + } + + final PsiBuilder.Marker body = parseCodeBlock(builder); + if (body == null) { + error(builder, JavaErrorMessages.message("expected.lbrace")); + } + + statement.done(JavaElementType.SYNCHRONIZED_STATEMENT); + return statement; } @NotNull private static PsiBuilder.Marker parseTryStatement(final PsiBuilder builder) { - // todo: implement - throw new UnsupportedOperationException(builder.toString()); + final PsiBuilder.Marker statement = builder.mark(); + builder.advanceLexer(); + + final PsiBuilder.Marker tryBlock = parseCodeBlock(builder); + if (tryBlock == null) { + error(builder, JavaErrorMessages.message("expected.lbrace")); + statement.done(JavaElementType.TRY_STATEMENT); + return statement; + } + + if (!TRY_CLOSERS_SET.contains(builder.getTokenType())) { + error(builder, JavaErrorMessages.message("expected.catch.or.finally")); + statement.done(JavaElementType.TRY_STATEMENT); + return statement; + } + + while (builder.getTokenType() == JavaTokenType.CATCH_KEYWORD) { + if (!parseCatchBlock(builder)) break; + } + + if (expect(builder, JavaTokenType.FINALLY_KEYWORD)) { + final PsiBuilder.Marker finallyBlock = parseCodeBlock(builder); + if (finallyBlock == null) { + error(builder, JavaErrorMessages.message("expected.lbrace")); + } + } + + statement.done(JavaElementType.TRY_STATEMENT); + return statement; + } + + private static boolean parseCatchBlock(final PsiBuilder builder) { + final PsiBuilder.Marker section = builder.mark(); + builder.advanceLexer(); + + if (!expect(builder, JavaTokenType.LPARENTH)) { + error(builder, JavaErrorMessages.message("expected.lparen")); + section.done(JavaElementType.CATCH_SECTION); + return false; + } + + final PsiBuilder.Marker param = DeclarationParser.parseParameter(builder, false); + if (param == null) { + error(builder, JavaErrorMessages.message("expected.parameter")); + } + + if (!expect(builder, JavaTokenType.RPARENTH)) { + error(builder, JavaErrorMessages.message("expected.rparen")); + section.done(JavaElementType.CATCH_SECTION); + return false; + } + + final PsiBuilder.Marker body = parseCodeBlock(builder); + if (body == null) { + error(builder, JavaErrorMessages.message("expected.lbrace")); + section.done(JavaElementType.CATCH_SECTION); + return false; + } + + section.done(JavaElementType.CATCH_SECTION); + return true; } @NotNull diff --git a/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete0.txt b/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete0.txt new file mode 100644 index 000000000000..38d2c313305d --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete0.txt @@ -0,0 +1,10 @@ +PsiJavaFile:SyncIncomplete0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSynchronizedStatement + PsiKeyword:synchronized('synchronized') + PsiErrorElement:'(' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete1.txt b/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete1.txt new file mode 100644 index 000000000000..d3e449fcd310 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete1.txt @@ -0,0 +1,11 @@ +PsiJavaFile:SyncIncomplete1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSynchronizedStatement + PsiKeyword:synchronized('synchronized') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete2.txt b/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete2.txt new file mode 100644 index 000000000000..528cbe4fd35d --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete2.txt @@ -0,0 +1,15 @@ +PsiJavaFile:SyncIncomplete2.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSynchronizedStatement + PsiKeyword:synchronized('synchronized') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:o + PsiReferenceParameterList + + PsiIdentifier:o('o') + PsiErrorElement:')' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete3.txt b/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete3.txt new file mode 100644 index 000000000000..ad8f3af396b9 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete3.txt @@ -0,0 +1,16 @@ +PsiJavaFile:SyncIncomplete3.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSynchronizedStatement + PsiKeyword:synchronized('synchronized') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:o + PsiReferenceParameterList + + PsiIdentifier:o('o') + PsiJavaToken:RPARENTH(')') + PsiErrorElement:'{' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete4.txt b/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete4.txt new file mode 100644 index 000000000000..6b2fb30de3b8 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete4.txt @@ -0,0 +1,15 @@ +PsiJavaFile:SyncIncomplete4.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSynchronizedStatement + PsiKeyword:synchronized('synchronized') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete5.txt b/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete5.txt new file mode 100644 index 000000000000..442a25228d41 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SyncIncomplete5.txt @@ -0,0 +1,22 @@ +PsiJavaFile:SyncIncomplete5.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSynchronizedStatement + PsiKeyword:synchronized('synchronized') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiWhiteSpace('\n ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SyncNormal.txt b/java/java-tests/testData/psi/parser-partial/statements/SyncNormal.txt new file mode 100644 index 000000000000..2d2d42976e98 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SyncNormal.txt @@ -0,0 +1,17 @@ +PsiJavaFile:SyncNormal.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSynchronizedStatement + PsiKeyword:synchronized('synchronized') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:o + PsiReferenceParameterList + + PsiIdentifier:o('o') + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ThrowIncomplete0.txt b/java/java-tests/testData/psi/parser-partial/statements/ThrowIncomplete0.txt new file mode 100644 index 000000000000..9466d2cb5835 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ThrowIncomplete0.txt @@ -0,0 +1,10 @@ +PsiJavaFile:ThrowIncomplete0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiThrowStatement + PsiKeyword:throw('throw') + PsiErrorElement:Expression expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ThrowIncomplete1.txt b/java/java-tests/testData/psi/parser-partial/statements/ThrowIncomplete1.txt new file mode 100644 index 000000000000..84fabd5a24d2 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ThrowIncomplete1.txt @@ -0,0 +1,15 @@ +PsiJavaFile:ThrowIncomplete1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiThrowStatement + PsiKeyword:throw('throw') + PsiWhiteSpace(' ') + PsiReferenceExpression:e + PsiReferenceParameterList + + PsiIdentifier:e('e') + PsiErrorElement:';' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ThrowNormal.txt b/java/java-tests/testData/psi/parser-partial/statements/ThrowNormal.txt new file mode 100644 index 000000000000..970f7e335ecf --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ThrowNormal.txt @@ -0,0 +1,14 @@ +PsiJavaFile:ThrowNormal.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiThrowStatement + PsiKeyword:throw('throw') + PsiWhiteSpace(' ') + PsiReferenceExpression:e + PsiReferenceParameterList + + PsiIdentifier:e('e') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete0.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete0.txt new file mode 100644 index 000000000000..26280a174633 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete0.txt @@ -0,0 +1,10 @@ +PsiJavaFile:TryIncomplete0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiTryStatement + PsiKeyword:try('try') + PsiErrorElement:'{' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete1.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete1.txt new file mode 100644 index 000000000000..52577f536cb1 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete1.txt @@ -0,0 +1,13 @@ +PsiJavaFile:TryIncomplete1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiTryStatement + PsiKeyword:try('try') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiErrorElement:'catch' or 'finally' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete2.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete2.txt new file mode 100644 index 000000000000..5c4abc6d4083 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete2.txt @@ -0,0 +1,15 @@ +PsiJavaFile:TryIncomplete2.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiTryStatement + PsiKeyword:try('try') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiCatchSection + PsiKeyword:catch('catch') + PsiErrorElement:'(' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete3.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete3.txt new file mode 100644 index 000000000000..ffb30d602c45 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete3.txt @@ -0,0 +1,18 @@ +PsiJavaFile:TryIncomplete3.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiTryStatement + PsiKeyword:try('try') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiCatchSection + PsiKeyword:catch('catch') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Parameter expected + + PsiErrorElement:')' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete4.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete4.txt new file mode 100644 index 000000000000..464c9a838e30 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete4.txt @@ -0,0 +1,25 @@ +PsiJavaFile:TryIncomplete4.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiTryStatement + PsiKeyword:try('try') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiCatchSection + PsiKeyword:catch('catch') + PsiJavaToken:LPARENTH('(') + PsiModifierList: + + PsiTypeElement:E + PsiJavaCodeReferenceElement:E + PsiIdentifier:E('E') + PsiReferenceParameterList + + PsiErrorElement:Identifier expected + + PsiErrorElement:')' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete5.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete5.txt new file mode 100644 index 000000000000..c935d14a3d47 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete5.txt @@ -0,0 +1,26 @@ +PsiJavaFile:TryIncomplete5.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiTryStatement + PsiKeyword:try('try') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiCatchSection + PsiKeyword:catch('catch') + PsiJavaToken:LPARENTH('(') + PsiParameter:e + PsiModifierList: + + PsiTypeElement:E + PsiJavaCodeReferenceElement:E + PsiIdentifier:E('E') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:e('e') + PsiErrorElement:')' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete6.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete6.txt new file mode 100644 index 000000000000..d42266c54c8a --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete6.txt @@ -0,0 +1,27 @@ +PsiJavaFile:TryIncomplete6.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiTryStatement + PsiKeyword:try('try') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiCatchSection + PsiKeyword:catch('catch') + PsiJavaToken:LPARENTH('(') + PsiParameter:e + PsiModifierList: + + PsiTypeElement:E + PsiJavaCodeReferenceElement:E + PsiIdentifier:E('E') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:e('e') + PsiJavaToken:RPARENTH(')') + PsiErrorElement:'{' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete7.txt b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete7.txt new file mode 100644 index 000000000000..5ddbf4f156c0 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryIncomplete7.txt @@ -0,0 +1,14 @@ +PsiJavaFile:TryIncomplete7.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiTryStatement + PsiKeyword:try('try') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiKeyword:finally('finally') + PsiErrorElement:'{' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryNormal0.txt b/java/java-tests/testData/psi/parser-partial/statements/TryNormal0.txt new file mode 100644 index 000000000000..15760ab4aa6a --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryNormal0.txt @@ -0,0 +1,28 @@ +PsiJavaFile:TryNormal0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiTryStatement + PsiKeyword:try('try') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiCatchSection + PsiKeyword:catch('catch') + PsiJavaToken:LPARENTH('(') + PsiParameter:e + PsiModifierList: + + PsiTypeElement:E + PsiJavaCodeReferenceElement:E + PsiIdentifier:E('E') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:e('e') + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryNormal1.txt b/java/java-tests/testData/psi/parser-partial/statements/TryNormal1.txt new file mode 100644 index 000000000000..a93b5f624d18 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryNormal1.txt @@ -0,0 +1,32 @@ +PsiJavaFile:TryNormal1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiTryStatement + PsiKeyword:try('try') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiCatchSection + PsiKeyword:catch('catch') + PsiJavaToken:LPARENTH('(') + PsiParameter:e + PsiModifierList: + + PsiTypeElement:E + PsiJavaCodeReferenceElement:E + PsiIdentifier:E('E') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:e('e') + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiKeyword:finally('finally') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/TryNormal2.txt b/java/java-tests/testData/psi/parser-partial/statements/TryNormal2.txt new file mode 100644 index 000000000000..b97b9da92acc --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/TryNormal2.txt @@ -0,0 +1,15 @@ +PsiJavaFile:TryNormal2.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiTryStatement + PsiKeyword:try('try') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiKeyword:finally('finally') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java index b86f822bc7e4..1d5cfbccdb5a 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java @@ -66,9 +66,30 @@ public class StatementParserTest extends JavaParsingTestCase { //ReturnParsingTest //SwitchParsingTest - //SynchronizedParsingTest - //ThrowParsingTest - //TryParsingTest + + public void testSyncNormal() { doParserTest("{ synchronized(o){} }"); } + public void testSyncIncomplete0() { doParserTest("{ synchronized }"); } + public void testSyncIncomplete1() { doParserTest("{ synchronized( }"); } + public void testSyncIncomplete2() { doParserTest("{ synchronized(o }"); } + public void testSyncIncomplete3() { doParserTest("{ synchronized(o) }"); } + public void testSyncIncomplete4() { doParserTest("{ synchronized(){} }"); } + public void testSyncIncomplete5() { doParserTest("{ synchronized(\n foo(); }"); } + + public void testThrowNormal() { doParserTest("{ throw e; }"); } + public void testThrowIncomplete0() { doParserTest("{ throw }"); } + public void testThrowIncomplete1() { doParserTest("{ throw e }"); } + + public void testTryNormal0() { doParserTest("{ try{}catch(E e){} }"); } + public void testTryNormal1() { doParserTest("{ try{}catch(E e){}finally{} }"); } + public void testTryNormal2() { doParserTest("{ try{}finally{} }"); } + public void testTryIncomplete0() { doParserTest("{ try }"); } + public void testTryIncomplete1() { doParserTest("{ try{} }"); } + public void testTryIncomplete2() { doParserTest("{ try{}catch }"); } + public void testTryIncomplete3() { doParserTest("{ try{}catch( }"); } + public void testTryIncomplete4() { doParserTest("{ try{}catch(E }"); } + public void testTryIncomplete5() { doParserTest("{ try{}catch(E e }"); } + public void testTryIncomplete6() { doParserTest("{ try{}catch(E e) }"); } + public void testTryIncomplete7() { doParserTest("{ try{}finally }"); } public void testWhileNormal() { doParserTest("{ while (true) foo(); }"); } public void testWhileIncomplete0() { doParserTest("{ while }"); } From 2a0f9ec37842181ac7d71e6d23dd36050433ae42 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 29 Jul 2010 16:59:15 +0400 Subject: [PATCH 05/92] New Java parser (work in progress) --- .../lang/java/parser/StatementParser.java | 51 ++++++++++++++++--- .../statements/DoIncomplete0.txt | 10 ++++ .../statements/DoIncomplete1.txt | 21 ++++++++ .../statements/DoIncomplete2.txt | 23 +++++++++ .../statements/DoIncomplete3.txt | 24 +++++++++ .../statements/DoIncomplete4.txt | 26 ++++++++++ .../statements/DoIncomplete5.txt | 35 +++++++++++++ .../statements/DoIncomplete6.txt | 29 +++++++++++ .../parser-partial/statements/DoNormal.txt | 18 +++++++ .../statements/ReturnIncomplete0.txt | 10 ++++ .../statements/ReturnIncomplete1.txt | 15 ++++++ .../statements/ReturnNoResult.txt | 9 ++++ .../statements/ReturnWithResult.txt | 12 +++++ .../statements/SwitchIncomplete0.txt | 10 ++++ .../statements/SwitchIncomplete1.txt | 11 ++++ .../statements/SwitchIncomplete2.txt | 15 ++++++ .../statements/SwitchIncomplete3.txt | 16 ++++++ .../statements/SwitchIncomplete4.txt | 15 ++++++ .../statements/SwitchIncomplete5.txt | 22 ++++++++ .../statements/SwitchNormal.txt | 17 +++++++ .../parser/partial/StatementParserTest.java | 23 +++++++-- 21 files changed, 403 insertions(+), 9 deletions(-) create mode 100644 java/java-tests/testData/psi/parser-partial/statements/DoIncomplete0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/DoIncomplete1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/DoIncomplete2.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/DoIncomplete3.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/DoIncomplete4.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/DoIncomplete5.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/DoIncomplete6.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/DoNormal.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ReturnIncomplete0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ReturnIncomplete1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ReturnNoResult.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ReturnWithResult.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete2.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete3.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete4.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete5.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SwitchNormal.txt diff --git a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java index d9b1b0bb6d08..bb2cfc77a238 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java @@ -473,14 +473,47 @@ public class StatementParser { @NotNull private static PsiBuilder.Marker parseDoWhileStatement(final PsiBuilder builder) { - // todo: implement - throw new UnsupportedOperationException(builder.toString()); + final PsiBuilder.Marker statement = builder.mark(); + builder.advanceLexer(); + + final PsiBuilder.Marker body = parseStatement(builder); + if (body == null) { + error(builder, JavaErrorMessages.message("expected.statement")); + statement.done(JavaElementType.DO_WHILE_STATEMENT); + return statement; + } + + if (!expect(builder, JavaTokenType.WHILE_KEYWORD)) { + error(builder, JavaErrorMessages.message("expected.while")); + statement.done(JavaElementType.DO_WHILE_STATEMENT); + return statement; + } + + if (parseExpressionInParenth(builder)) { + semicolon(builder); + } + + statement.done(JavaElementType.DO_WHILE_STATEMENT); + return statement; } @NotNull private static PsiBuilder.Marker parseSwitchStatement(final PsiBuilder builder) { - // todo: implement - throw new UnsupportedOperationException(builder.toString()); + final PsiBuilder.Marker statement = builder.mark(); + builder.advanceLexer(); + + if (!parseExpressionInParenth(builder)) { + statement.done(JavaElementType.SWITCH_STATEMENT); + return statement; + } + + final PsiBuilder.Marker body = parseCodeBlock(builder); + if (body == null) { + error(builder, JavaErrorMessages.message("expected.lbrace")); + } + + statement.done(JavaElementType.SWITCH_STATEMENT); + return statement; } @NotNull @@ -503,8 +536,14 @@ public class StatementParser { @NotNull private static PsiBuilder.Marker parseReturnStatement(final PsiBuilder builder) { - // todo: implement - throw new UnsupportedOperationException(builder.toString()); + final PsiBuilder.Marker statement = builder.mark(); + builder.advanceLexer(); + + ExpressionParser.parse(builder); + + semicolon(builder); + statement.done(JavaElementType.RETURN_STATEMENT); + return statement; } @NotNull diff --git a/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete0.txt b/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete0.txt new file mode 100644 index 000000000000..8019cb954e02 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete0.txt @@ -0,0 +1,10 @@ +PsiJavaFile:DoIncomplete0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiDoWhileStatement + PsiKeyword:do('do') + PsiErrorElement:Statement expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete1.txt b/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete1.txt new file mode 100644 index 000000000000..3e8e225ae008 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete1.txt @@ -0,0 +1,21 @@ +PsiJavaFile:DoIncomplete1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiDoWhileStatement + PsiKeyword:do('do') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiErrorElement:'while' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete2.txt b/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete2.txt new file mode 100644 index 000000000000..26407dcdd083 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete2.txt @@ -0,0 +1,23 @@ +PsiJavaFile:DoIncomplete2.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiDoWhileStatement + PsiKeyword:do('do') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiKeyword:while('while') + PsiErrorElement:'(' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete3.txt b/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete3.txt new file mode 100644 index 000000000000..0b4b5352f8fc --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete3.txt @@ -0,0 +1,24 @@ +PsiJavaFile:DoIncomplete3.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiDoWhileStatement + PsiKeyword:do('do') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiKeyword:while('while') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete4.txt b/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete4.txt new file mode 100644 index 000000000000..5f8a81a6f54d --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete4.txt @@ -0,0 +1,26 @@ +PsiJavaFile:DoIncomplete4.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiDoWhileStatement + PsiKeyword:do('do') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiKeyword:while('while') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete5.txt b/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete5.txt new file mode 100644 index 000000000000..2970dcff0c60 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete5.txt @@ -0,0 +1,35 @@ +PsiJavaFile:DoIncomplete5.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiDoWhileStatement + PsiKeyword:do('do') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiKeyword:while('while') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiWhiteSpace('\n ') + PsiExpressionStatement + PsiMethodCallExpression:g() + PsiReferenceExpression:g + PsiReferenceParameterList + + PsiIdentifier:g('g') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete6.txt b/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete6.txt new file mode 100644 index 000000000000..a74d8fd11af6 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/DoIncomplete6.txt @@ -0,0 +1,29 @@ +PsiJavaFile:DoIncomplete6.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiDoWhileStatement + PsiKeyword:do('do') + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiKeyword:while('while') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:cond + PsiReferenceParameterList + + PsiIdentifier:cond('cond') + PsiJavaToken:RPARENTH(')') + PsiErrorElement:';' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/DoNormal.txt b/java/java-tests/testData/psi/parser-partial/statements/DoNormal.txt new file mode 100644 index 000000000000..cd14dca40ce9 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/DoNormal.txt @@ -0,0 +1,18 @@ +PsiJavaFile:DoNormal.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiDoWhileStatement + PsiKeyword:do('do') + PsiBlockStatement + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiKeyword:while('while') + PsiJavaToken:LPARENTH('(') + PsiLiteralExpression:true + PsiJavaToken:TRUE_KEYWORD('true') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ReturnIncomplete0.txt b/java/java-tests/testData/psi/parser-partial/statements/ReturnIncomplete0.txt new file mode 100644 index 000000000000..fed5ed866324 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ReturnIncomplete0.txt @@ -0,0 +1,10 @@ +PsiJavaFile:ReturnIncomplete0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiReturnStatement + PsiKeyword:return('return') + PsiErrorElement:';' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ReturnIncomplete1.txt b/java/java-tests/testData/psi/parser-partial/statements/ReturnIncomplete1.txt new file mode 100644 index 000000000000..0becbe1f4550 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ReturnIncomplete1.txt @@ -0,0 +1,15 @@ +PsiJavaFile:ReturnIncomplete1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiReturnStatement + PsiKeyword:return('return') + PsiWhiteSpace(' ') + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiErrorElement:';' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ReturnNoResult.txt b/java/java-tests/testData/psi/parser-partial/statements/ReturnNoResult.txt new file mode 100644 index 000000000000..a5dce502ce00 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ReturnNoResult.txt @@ -0,0 +1,9 @@ +PsiJavaFile:ReturnNoResult.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiReturnStatement + PsiKeyword:return('return') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ReturnWithResult.txt b/java/java-tests/testData/psi/parser-partial/statements/ReturnWithResult.txt new file mode 100644 index 000000000000..e88838f17b06 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ReturnWithResult.txt @@ -0,0 +1,12 @@ +PsiJavaFile:ReturnWithResult.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiReturnStatement + PsiKeyword:return('return') + PsiWhiteSpace(' ') + PsiLiteralExpression:10 + PsiJavaToken:INTEGER_LITERAL('10') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete0.txt b/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete0.txt new file mode 100644 index 000000000000..965e0dbc1307 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete0.txt @@ -0,0 +1,10 @@ +PsiJavaFile:SwitchIncomplete0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSwitchStatement + PsiKeyword:switch('switch') + PsiErrorElement:'(' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete1.txt b/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete1.txt new file mode 100644 index 000000000000..d45cf941e932 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete1.txt @@ -0,0 +1,11 @@ +PsiJavaFile:SwitchIncomplete1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSwitchStatement + PsiKeyword:switch('switch') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete2.txt b/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete2.txt new file mode 100644 index 000000000000..2de790590d7c --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete2.txt @@ -0,0 +1,15 @@ +PsiJavaFile:SwitchIncomplete2.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSwitchStatement + PsiKeyword:switch('switch') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:o + PsiReferenceParameterList + + PsiIdentifier:o('o') + PsiErrorElement:')' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete3.txt b/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete3.txt new file mode 100644 index 000000000000..a4ee0f242100 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete3.txt @@ -0,0 +1,16 @@ +PsiJavaFile:SwitchIncomplete3.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSwitchStatement + PsiKeyword:switch('switch') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:o + PsiReferenceParameterList + + PsiIdentifier:o('o') + PsiJavaToken:RPARENTH(')') + PsiErrorElement:'{' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete4.txt b/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete4.txt new file mode 100644 index 000000000000..ff547c4a4c33 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete4.txt @@ -0,0 +1,15 @@ +PsiJavaFile:SwitchIncomplete4.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSwitchStatement + PsiKeyword:switch('switch') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete5.txt b/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete5.txt new file mode 100644 index 000000000000..323d6838396a --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SwitchIncomplete5.txt @@ -0,0 +1,22 @@ +PsiJavaFile:SwitchIncomplete5.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSwitchStatement + PsiKeyword:switch('switch') + PsiJavaToken:LPARENTH('(') + PsiErrorElement:Expression expected + + PsiWhiteSpace('\n ') + PsiExpressionStatement + PsiMethodCallExpression:foo() + PsiReferenceExpression:foo + PsiReferenceParameterList + + PsiIdentifier:foo('foo') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SwitchNormal.txt b/java/java-tests/testData/psi/parser-partial/statements/SwitchNormal.txt new file mode 100644 index 000000000000..22acefb5bd3c --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SwitchNormal.txt @@ -0,0 +1,17 @@ +PsiJavaFile:SwitchNormal.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSwitchStatement + PsiKeyword:switch('switch') + PsiJavaToken:LPARENTH('(') + PsiReferenceExpression:o + PsiReferenceParameterList + + PsiIdentifier:o('o') + PsiJavaToken:RPARENTH(')') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java index 1d5cfbccdb5a..4c383b143cd5 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java @@ -36,7 +36,14 @@ public class StatementParserTest extends JavaParsingTestCase { public void testLocalVar() { doParserTest("{ List list; }"); } public void testFor() { doParserTest("{ for(Iterator it = null; it.hasNext();) { String s = it.next(); } }"); } - //DoWhileParsingTest + public void testDoNormal() { doParserTest("{ do{}while(true); }"); } + public void testDoIncomplete0() { doParserTest("{ do }"); } + public void testDoIncomplete1() { doParserTest("{ do foo(); }"); } + public void testDoIncomplete2() { doParserTest("{ do foo(); while }"); } + public void testDoIncomplete3() { doParserTest("{ do foo(); while( }"); } + public void testDoIncomplete4() { doParserTest("{ do foo(); while(); }"); } + public void testDoIncomplete5() { doParserTest("{ do foo(); while(\n g(); }"); } + public void testDoIncomplete6() { doParserTest("{ do foo(); while(cond) }"); } public void testForNormal0() { doParserTest("{ for(int i = 0; i < 10; i++)\n ; }"); } public void testForNormal1() { doParserTest("{ for( ; ; ) foo(); }"); } @@ -64,8 +71,18 @@ public class StatementParserTest extends JavaParsingTestCase { public void testLabelSimple() { doParserTest("{ Loop:\n while(true) ; }"); } - //ReturnParsingTest - //SwitchParsingTest + public void testReturnNoResult() { doParserTest("{ return; }"); } + public void testReturnWithResult() { doParserTest("{ return 10; }"); } + public void testReturnIncomplete0() { doParserTest("{ return }"); } + public void testReturnIncomplete1() { doParserTest("{ return a }"); } + + public void testSwitchNormal() { doParserTest("{ switch(o){} }"); } + public void testSwitchIncomplete0() { doParserTest("{ switch }"); } + public void testSwitchIncomplete1() { doParserTest("{ switch( }"); } + public void testSwitchIncomplete2() { doParserTest("{ switch(o }"); } + public void testSwitchIncomplete3() { doParserTest("{ switch(o) }"); } + public void testSwitchIncomplete4() { doParserTest("{ switch(){} }"); } + public void testSwitchIncomplete5() { doParserTest("{ switch(\n foo(); }"); } public void testSyncNormal() { doParserTest("{ synchronized(o){} }"); } public void testSyncIncomplete0() { doParserTest("{ synchronized }"); } From 487f40c3f5f69c17bee06a1223fa4d6e08947c39 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 29 Jul 2010 18:33:08 +0400 Subject: [PATCH 06/92] "finally without try" reporting fixed --- .../com/intellij/psi/impl/source/parsing/StatementParsing.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/psi/impl/source/parsing/StatementParsing.java b/java/java-impl/src/com/intellij/psi/impl/source/parsing/StatementParsing.java index c706eb3d1bb8..f64e120bceee 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/parsing/StatementParsing.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/parsing/StatementParsing.java @@ -252,7 +252,7 @@ public class StatementParsing extends Parsing { else if (tokenType == JavaTokenType.CATCH_KEYWORD){ error = JavaErrorMessages.message("catch.without.try"); } - else if (tokenType == JavaTokenType.FINAL_KEYWORD){ + else if (tokenType == JavaTokenType.FINALLY_KEYWORD){ error = JavaErrorMessages.message("finally.without.try"); } else{ From afb6253c83454b375e15158d77a8a03a5134a5ec Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 29 Jul 2010 18:41:24 +0400 Subject: [PATCH 07/92] New Java parser (work in progress) --- .../lang/java/parser/ExpressionParser.java | 2 +- .../lang/java/parser/StatementParser.java | 55 ++++++++++++------- .../psi/impl/source/tree/JavaElementType.java | 4 +- .../parser-partial/statements/BlockEmpty.txt | 8 +++ .../statements/BlockIncomplete0.txt | 6 ++ .../statements/BlockIncomplete1.txt | 11 ++++ .../statements/BlockIncomplete2.txt | 20 +++++++ .../statements/BreakNormal0.txt | 9 +++ .../statements/BreakNormal1.txt | 11 ++++ .../statements/ContinueNormal0.txt | 9 +++ .../statements/ContinueNormal1.txt | 11 ++++ .../statements/SwitchLabelsIncomplete0.txt | 8 +++ .../statements/SwitchLabelsIncomplete1.txt | 13 +++++ .../statements/SwitchLabelsIncomplete2.txt | 10 ++++ .../statements/SwitchLabelsIncomplete3.txt | 18 ++++++ .../statements/SwitchLabelsNormal.txt | 24 ++++++++ .../parser/partial/StatementParserTest.java | 18 +++++- 17 files changed, 213 insertions(+), 24 deletions(-) create mode 100644 java/java-tests/testData/psi/parser-partial/statements/BlockEmpty.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/BlockIncomplete0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/BlockIncomplete1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/BlockIncomplete2.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/BreakNormal0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/BreakNormal1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ContinueNormal0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/ContinueNormal1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete2.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete3.txt create mode 100644 java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsNormal.txt diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java index 57d95f7c9251..94d50c454349 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ExpressionParser.java @@ -773,7 +773,7 @@ public class ExpressionParser { } private static void emptyExpression(final PsiBuilder builder) { - builder.mark().done(JavaElementType.EMPTY_EXPRESSION); + emptyElement(builder, JavaElementType.EMPTY_EXPRESSION); } @Nullable diff --git a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java index bb2cfc77a238..9e9ba6bd6e68 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java @@ -110,13 +110,7 @@ public class StatementParser { parseStatements(builder, (parseUntilEof ? BraceMode.TILL_LAST : BraceMode.TILL_FIRST)); - // todo: check closing brace - //final TreeElement lastChild = elementToAdd.getLastChildNode(); - //if (lastChild == null || lastChild.getElementType() != JavaTokenType.RBRACE){ - // CompositeElement errorElement = Factory.createErrorElement(JavaErrorMessages.message("expected.rbrace")); - // elementToAdd.rawAddChildren(errorElement); - // elementToAdd.putUserData(TreeUtil.UNCLOSED_ELEMENT_PROPERTY, ""); - //} + expectOrError(builder, JavaTokenType.RBRACE, JavaErrorMessages.message("expected.rbrace")); codeBlock.done(JavaElementType.CODE_BLOCK); return codeBlock; @@ -128,29 +122,26 @@ public class StatementParser { if (statement != null) continue; final IElementType tokenType = builder.getTokenType(); - final PsiBuilder.Marker error = builder.mark(); - builder.advanceLexer(); - if (tokenType == JavaTokenType.RBRACE) { if (braceMode == BraceMode.TILL_FIRST) { - error.drop(); return; } else if (braceMode == BraceMode.TILL_LAST) { - if (builder.getTokenType() == null) { - error.drop(); + if (nextTokenType(builder) == null) { return; } } } + final PsiBuilder.Marker error = builder.mark(); + builder.advanceLexer(); if (tokenType == JavaTokenType.ELSE_KEYWORD) { error.error(JavaErrorMessages.message("else.without.if")); } else if (tokenType == JavaTokenType.CATCH_KEYWORD) { error.error(JavaErrorMessages.message("catch.without.try")); } - else if (tokenType == JavaTokenType.FINAL_KEYWORD) { + else if (tokenType == JavaTokenType.FINALLY_KEYWORD) { error.error(JavaErrorMessages.message("finally.without.try")); } else { @@ -516,22 +507,44 @@ public class StatementParser { return statement; } - @NotNull + @Nullable private static PsiBuilder.Marker parseSwitchLabelStatement(final PsiBuilder builder) { - // todo: implement - throw new UnsupportedOperationException(builder.toString()); + final PsiBuilder.Marker statement = builder.mark(); + final boolean isCase = builder.getTokenType() == JavaTokenType.CASE_KEYWORD; + builder.advanceLexer(); + + if (isCase) { + final PsiBuilder.Marker expr = ExpressionParser.parse(builder); + if (expr == null) { + statement.rollbackTo(); + return null; + } + } + + expectOrError(builder, JavaTokenType.COLON, JavaErrorMessages.message("expected.colon")); + + statement.done(JavaElementType.SWITCH_LABEL_STATEMENT); + return statement; } @NotNull private static PsiBuilder.Marker parseBreakStatement(final PsiBuilder builder) { - // todo: implement - throw new UnsupportedOperationException(builder.toString()); + final PsiBuilder.Marker statement = builder.mark(); + builder.advanceLexer(); + expect(builder, JavaTokenType.IDENTIFIER); + semicolon(builder); + statement.done(JavaElementType.BREAK_STATEMENT); + return statement; } @NotNull private static PsiBuilder.Marker parseContinueStatement(final PsiBuilder builder) { - // todo: implement - throw new UnsupportedOperationException(builder.toString()); + final PsiBuilder.Marker statement = builder.mark(); + builder.advanceLexer(); + expect(builder, JavaTokenType.IDENTIFIER); + semicolon(builder); + statement.done(JavaElementType.CONTINUE_STATEMENT); + return statement; } @NotNull 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 c648d69da366..206959232254 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 @@ -91,7 +91,7 @@ public interface JavaElementType { IElementType ARRAY_INITIALIZER_EXPRESSION = new IJavaElementType("ARRAY_INITIALIZER_EXPRESSION"); IElementType INSTANCE_OF_EXPRESSION = new IJavaElementType("INSTANCE_OF_EXPRESSION"); IElementType CLASS_OBJECT_ACCESS_EXPRESSION = new IJavaElementType("CLASS_OBJECT_ACCESS_EXPRESSION"); - IElementType EMPTY_EXPRESSION = new IJavaElementType("EMPTY_EXPRESSION"); + IElementType EMPTY_EXPRESSION = new IJavaElementType("EMPTY_EXPRESSION", true); IElementType EXPRESSION_LIST = new IJavaElementType("EXPRESSION_LIST", true); @@ -123,6 +123,8 @@ public interface JavaElementType { IElementType ANNOTATION_PARAMETER_LIST = new IJavaElementType("ANNOTATION_PARAMETER_LIST", true); IElementType METHOD_RECEIVER = new IJavaElementType("METHOD_RECEIVER"); + IElementType INSTANT_CODE_BLOCK = new IJavaElementType("CODE_BLOCK"); + ILazyParseableElementType CODE_BLOCK = new IErrorCounterReparseableElementType("CODE_BLOCK", StdLanguages.JAVA) { @Override public ASTNode createNode(CharSequence text) { diff --git a/java/java-tests/testData/psi/parser-partial/statements/BlockEmpty.txt b/java/java-tests/testData/psi/parser-partial/statements/BlockEmpty.txt new file mode 100644 index 000000000000..8c2a0030367b --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/BlockEmpty.txt @@ -0,0 +1,8 @@ +PsiJavaFile:BlockEmpty.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiEmptyStatement + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/statements/BlockIncomplete0.txt b/java/java-tests/testData/psi/parser-partial/statements/BlockIncomplete0.txt new file mode 100644 index 000000000000..3a5a40837d29 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/BlockIncomplete0.txt @@ -0,0 +1,6 @@ +PsiJavaFile:BlockIncomplete0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiErrorElement:'}' expected + + PsiWhiteSpace(' ') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/BlockIncomplete1.txt b/java/java-tests/testData/psi/parser-partial/statements/BlockIncomplete1.txt new file mode 100644 index 000000000000..4ec20c6bcc5d --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/BlockIncomplete1.txt @@ -0,0 +1,11 @@ +PsiJavaFile:BlockIncomplete1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiBlockStatement + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiErrorElement:'}' expected + \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/BlockIncomplete2.txt b/java/java-tests/testData/psi/parser-partial/statements/BlockIncomplete2.txt new file mode 100644 index 000000000000..27a21745d34b --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/BlockIncomplete2.txt @@ -0,0 +1,20 @@ +PsiJavaFile:BlockIncomplete2.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiErrorElement:'else' without 'if' + PsiKeyword:else('else') + PsiEmptyStatement + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiErrorElement:'catch' without 'try' + PsiKeyword:catch('catch') + PsiEmptyStatement + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiErrorElement:'finally' without 'try' + PsiKeyword:finally('finally') + PsiEmptyStatement + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/BreakNormal0.txt b/java/java-tests/testData/psi/parser-partial/statements/BreakNormal0.txt new file mode 100644 index 000000000000..3a99a374e357 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/BreakNormal0.txt @@ -0,0 +1,9 @@ +PsiJavaFile:BreakNormal0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiBreakStatement + PsiKeyword:break('break') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/BreakNormal1.txt b/java/java-tests/testData/psi/parser-partial/statements/BreakNormal1.txt new file mode 100644 index 000000000000..8669ea300d85 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/BreakNormal1.txt @@ -0,0 +1,11 @@ +PsiJavaFile:BreakNormal1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiBreakStatement + PsiKeyword:break('break') + PsiWhiteSpace(' ') + PsiIdentifier:LABEL('LABEL') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ContinueNormal0.txt b/java/java-tests/testData/psi/parser-partial/statements/ContinueNormal0.txt new file mode 100644 index 000000000000..fbb3711b8b20 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ContinueNormal0.txt @@ -0,0 +1,9 @@ +PsiJavaFile:ContinueNormal0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiContinueStatement + PsiKeyword:continue('continue') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/ContinueNormal1.txt b/java/java-tests/testData/psi/parser-partial/statements/ContinueNormal1.txt new file mode 100644 index 000000000000..454378d25d28 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/ContinueNormal1.txt @@ -0,0 +1,11 @@ +PsiJavaFile:ContinueNormal1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiContinueStatement + PsiKeyword:continue('continue') + PsiWhiteSpace(' ') + PsiIdentifier:LABEL('LABEL') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete0.txt b/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete0.txt new file mode 100644 index 000000000000..8abbea59c3ae --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete0.txt @@ -0,0 +1,8 @@ +PsiJavaFile:SwitchLabelsIncomplete0.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiErrorElement:Unexpected token + PsiKeyword:case('case') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete1.txt b/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete1.txt new file mode 100644 index 000000000000..5f1a6e3d6146 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete1.txt @@ -0,0 +1,13 @@ +PsiJavaFile:SwitchLabelsIncomplete1.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSwitchLabelStatement + PsiKeyword:case('case') + PsiWhiteSpace(' ') + PsiLiteralExpression:2 + PsiJavaToken:INTEGER_LITERAL('2') + PsiErrorElement:':' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete2.txt b/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete2.txt new file mode 100644 index 000000000000..093604b2fb6b --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete2.txt @@ -0,0 +1,10 @@ +PsiJavaFile:SwitchLabelsIncomplete2.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSwitchLabelStatement + PsiKeyword:default('default') + PsiErrorElement:':' expected + + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete3.txt b/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete3.txt new file mode 100644 index 000000000000..7465f8c6f7cf --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsIncomplete3.txt @@ -0,0 +1,18 @@ +PsiJavaFile:SwitchLabelsIncomplete3.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSwitchLabelStatement + PsiKeyword:default('default') + PsiErrorElement:':' expected + + PsiWhiteSpace(' ') + PsiExpressionStatement + PsiLiteralExpression:3 + PsiJavaToken:INTEGER_LITERAL('3') + PsiErrorElement:';' expected + + PsiErrorElement:Unexpected token + PsiJavaToken:COLON(':') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsNormal.txt b/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsNormal.txt new file mode 100644 index 000000000000..b5b7bbc46839 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/statements/SwitchLabelsNormal.txt @@ -0,0 +1,24 @@ +PsiJavaFile:SwitchLabelsNormal.java + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiSwitchLabelStatement + PsiKeyword:case('case') + PsiWhiteSpace(' ') + PsiLiteralExpression:1 + PsiJavaToken:INTEGER_LITERAL('1') + PsiJavaToken:COLON(':') + PsiWhiteSpace(' ') + PsiBreakStatement + PsiKeyword:break('break') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiSwitchLabelStatement + PsiKeyword:default('default') + PsiJavaToken:COLON(':') + PsiWhiteSpace(' ') + PsiBreakStatement + PsiKeyword:break('break') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java index 4c383b143cd5..489fc88c37ce 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java @@ -31,7 +31,17 @@ public class StatementParserTest extends JavaParsingTestCase { public void testAssignmentSimple() { doParserTest("{ int[] a;\n a[0] = 0; }"); } public void testBlockSimple() { doParserTest("{ {} }"); } - //public void testAnonymousInSmartCompletion() { doParserTest("{ new Foo(hash\n#) {};\n new Foo(hash\n#, bar) {};\n new Foo(hash\n#x) {}; }"); } // todo: spaces + public void testBlockEmpty() { doParserTest("{ ; }"); } + public void testAnonymousInSmartCompletion() { doParserTest("{ new Foo(hash\n#) {};\n new Foo(hash\n#, bar) {};\n new Foo(hash\n#x) {}; }"); } + public void testBlockIncomplete0() { doParserTest("{ "); } + public void testBlockIncomplete1() { doParserTest("{ { }"); } + public void testBlockIncomplete2() { doParserTest("{ else; catch; finally; }"); } + + public void testBreakNormal0() { doParserTest("{ break; }"); } + public void testBreakNormal1() { doParserTest("{ break LABEL; }"); } + + public void testContinueNormal0() { doParserTest("{ continue; }"); } + public void testContinueNormal1() { doParserTest("{ continue LABEL; }"); } public void testLocalVar() { doParserTest("{ List list; }"); } public void testFor() { doParserTest("{ for(Iterator it = null; it.hasNext();) { String s = it.next(); } }"); } @@ -84,6 +94,12 @@ public class StatementParserTest extends JavaParsingTestCase { public void testSwitchIncomplete4() { doParserTest("{ switch(){} }"); } public void testSwitchIncomplete5() { doParserTest("{ switch(\n foo(); }"); } + public void testSwitchLabelsNormal() { doParserTest("{ case 1: break; default: break; }"); } + public void testSwitchLabelsIncomplete0() { doParserTest("{ case }"); } + public void testSwitchLabelsIncomplete1() { doParserTest("{ case 2 }"); } + public void testSwitchLabelsIncomplete2() { doParserTest("{ default }"); } + public void testSwitchLabelsIncomplete3() { doParserTest("{ default 3: }"); } + public void testSyncNormal() { doParserTest("{ synchronized(o){} }"); } public void testSyncIncomplete0() { doParserTest("{ synchronized }"); } public void testSyncIncomplete1() { doParserTest("{ synchronized( }"); } From de59edc36796479d279d18414765c63d034ed40e Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Thu, 29 Jul 2010 20:29:27 +0400 Subject: [PATCH 08/92] New Java parser (work in progress) --- .../lang/java/parser/ReferenceParser.java | 2 +- .../declarations/AnnoDeclaration.txt | 22 ++ .../declarations/AnnoDefault.txt | 35 ++ .../parser-partial/declarations/AnnoInner.txt | 44 +++ .../declarations/AnnoNested.txt | 47 +++ .../declarations/AnnoOtherMembers.txt | 64 ++++ .../declarations/AnnoSimple.txt | 21 ++ .../declarations/EnumSmartTypeCompletion.txt | 143 ++++++++ .../declarations/ParameterAnnotation.txt | 45 +++ .../declarations/ParameterizedMethod.txt | 40 +++ .../parser-partial/declarations/TypeAnno.txt | 327 ++++++++++++++++++ .../psi/parser-partial/references/Type6.txt | 28 ++ .../parser/partial/DeclarationParserTest.java | 20 ++ .../parser/partial/ReferenceParserTest.java | 1 + 14 files changed, 838 insertions(+), 1 deletion(-) create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/AnnoDeclaration.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/AnnoDefault.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/AnnoInner.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/AnnoNested.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/AnnoOtherMembers.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/AnnoSimple.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/EnumSmartTypeCompletion.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/ParameterAnnotation.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/ParameterizedMethod.txt create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/TypeAnno.txt create mode 100644 java/java-tests/testData/psi/parser-partial/references/Type6.txt diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java index da8be88afb9e..4f06c802e8e2 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java @@ -98,10 +98,10 @@ public class ReferenceParser { } while (true) { + type.done(JavaElementType.TYPE); if (annotationsSupported) { DeclarationParser.parseAnnotations(builder); } - type.done(JavaElementType.TYPE); if (!expect(builder, JavaTokenType.LBRACKET)) { break; diff --git a/java/java-tests/testData/psi/parser-partial/declarations/AnnoDeclaration.txt b/java/java-tests/testData/psi/parser-partial/declarations/AnnoDeclaration.txt new file mode 100644 index 000000000000..487633305819 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/AnnoDeclaration.txt @@ -0,0 +1,22 @@ +PsiJavaFile:AnnoDeclaration.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiClass:Annotation + PsiModifierList:public + PsiKeyword:public('public') + PsiWhiteSpace(' ') + PsiJavaToken:AT('@') + PsiKeyword:interface('interface') + PsiWhiteSpace(' ') + PsiIdentifier:Annotation('Annotation') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/AnnoDefault.txt b/java/java-tests/testData/psi/parser-partial/declarations/AnnoDefault.txt new file mode 100644 index 000000000000..5d8e43524fb3 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/AnnoDefault.txt @@ -0,0 +1,35 @@ +PsiJavaFile:AnnoDefault.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiAnnotationMethod:foo + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:Class + PsiJavaCodeReferenceElement:Class + PsiIdentifier:Class('Class') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:foo('foo') + PsiWhiteSpace(' ') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiWhiteSpace(' ') + PsiKeyword:default('default') + PsiWhiteSpace(' ') + PsiClassObjectAccessExpression:String.class + PsiTypeElement:String + PsiJavaCodeReferenceElement:String + PsiIdentifier:String('String') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiKeyword:class('class') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/AnnoInner.txt b/java/java-tests/testData/psi/parser-partial/declarations/AnnoInner.txt new file mode 100644 index 000000000000..65cd85840fb2 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/AnnoInner.txt @@ -0,0 +1,44 @@ +PsiJavaFile:AnnoInner.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiClass:Inner + PsiModifierList: + + PsiJavaToken:AT('@') + PsiKeyword:interface('interface') + PsiWhiteSpace(' ') + PsiIdentifier:Inner('Inner') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiAnnotationMethod:bar + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:double + PsiKeyword:double('double') + PsiWhiteSpace(' ') + PsiIdentifier:bar('bar') + PsiWhiteSpace(' ') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiWhiteSpace(' ') + PsiKeyword:default('default') + PsiWhiteSpace(' ') + PsiLiteralExpression:0.0 + PsiJavaToken:DOUBLE_LITERAL('0.0') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/AnnoNested.txt b/java/java-tests/testData/psi/parser-partial/declarations/AnnoNested.txt new file mode 100644 index 000000000000..d0aaba008536 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/AnnoNested.txt @@ -0,0 +1,47 @@ +PsiJavaFile:AnnoNested.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiClass:Inner + PsiModifierList: + + PsiJavaToken:AT('@') + PsiKeyword:interface('interface') + PsiWhiteSpace(' ') + PsiIdentifier:Inner('Inner') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiAnnotationMethod:bar + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:String + PsiJavaCodeReferenceElement:String + PsiIdentifier:String('String') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:bar('bar') + PsiWhiteSpace(' ') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiWhiteSpace(' ') + PsiKeyword:default('default') + PsiWhiteSpace(' ') + PsiLiteralExpression:"" + PsiJavaToken:STRING_LITERAL('""') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/AnnoOtherMembers.txt b/java/java-tests/testData/psi/parser-partial/declarations/AnnoOtherMembers.txt new file mode 100644 index 000000000000..69eab6bedf17 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/AnnoOtherMembers.txt @@ -0,0 +1,64 @@ +PsiJavaFile:AnnoOtherMembers.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n ') + PsiAnnotationMethod:m + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:m('m') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiWhiteSpace(' ') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace('\n ') + PsiClass:C + PsiModifierList: + + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:C('C') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace('\n ') + PsiClass:I + PsiModifierList: + + PsiKeyword:interface('interface') + PsiWhiteSpace(' ') + PsiIdentifier:I('I') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/AnnoSimple.txt b/java/java-tests/testData/psi/parser-partial/declarations/AnnoSimple.txt new file mode 100644 index 000000000000..fbbe1ce06bbd --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/AnnoSimple.txt @@ -0,0 +1,21 @@ +PsiJavaFile:AnnoSimple.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiAnnotationMethod:foo + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:foo('foo') + PsiWhiteSpace(' ') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/EnumSmartTypeCompletion.txt b/java/java-tests/testData/psi/parser-partial/declarations/EnumSmartTypeCompletion.txt new file mode 100644 index 000000000000..102f64d2d490 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/EnumSmartTypeCompletion.txt @@ -0,0 +1,143 @@ +PsiJavaFile:EnumSmartTypeCompletion.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiClass:TimeTravel + PsiModifierList:@Preliminary(A.B +#) public + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:Preliminary + PsiIdentifier:Preliminary('Preliminary') + PsiReferenceParameterList + + PsiAnnotationParameterList + PsiJavaToken:LPARENTH('(') + PsiNameValuePair + PsiReferenceExpression:A.B + PsiReferenceExpression:A + PsiReferenceParameterList + + PsiIdentifier:A('A') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:B('B') + PsiErrorElement:',' or ')' expected + + PsiWhiteSpace('\n') + PsiElement(BAD_CHARACTER)('#') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiKeyword:public('public') + PsiWhiteSpace(' ') + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:TimeTravel('TimeTravel') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace('\n ') + PsiClass:TimeTravel + PsiModifierList:@Preliminary(a=A.B +#) public + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:Preliminary + PsiIdentifier:Preliminary('Preliminary') + PsiReferenceParameterList + + PsiAnnotationParameterList + PsiJavaToken:LPARENTH('(') + PsiNameValuePair + PsiIdentifier:a('a') + PsiJavaToken:EQ('=') + PsiReferenceExpression:A.B + PsiReferenceExpression:A + PsiReferenceParameterList + + PsiIdentifier:A('A') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:B('B') + PsiErrorElement:',' or ')' expected + + PsiWhiteSpace('\n') + PsiElement(BAD_CHARACTER)('#') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiKeyword:public('public') + PsiWhiteSpace(' ') + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:TimeTravel('TimeTravel') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace('\n ') + PsiClass:TimeTravel + PsiModifierList:@Preliminary(a=A.B +#, b=c) public + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:Preliminary + PsiIdentifier:Preliminary('Preliminary') + PsiReferenceParameterList + + PsiAnnotationParameterList + PsiJavaToken:LPARENTH('(') + PsiNameValuePair + PsiIdentifier:a('a') + PsiJavaToken:EQ('=') + PsiReferenceExpression:A.B + PsiReferenceExpression:A + PsiReferenceParameterList + + PsiIdentifier:A('A') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:B('B') + PsiErrorElement:',' or ')' expected + + PsiWhiteSpace('\n') + PsiElement(BAD_CHARACTER)('#') + PsiJavaToken:COMMA(',') + PsiWhiteSpace(' ') + PsiNameValuePair + PsiIdentifier:b('b') + PsiJavaToken:EQ('=') + PsiReferenceExpression:c + PsiReferenceParameterList + + PsiIdentifier:c('c') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiKeyword:public('public') + PsiWhiteSpace(' ') + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:TimeTravel('TimeTravel') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/ParameterAnnotation.txt b/java/java-tests/testData/psi/parser-partial/declarations/ParameterAnnotation.txt new file mode 100644 index 000000000000..c283ca4c8ebb --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/ParameterAnnotation.txt @@ -0,0 +1,45 @@ +PsiJavaFile:ParameterAnnotation.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:foo + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:void + PsiKeyword:void('void') + PsiWhiteSpace(' ') + PsiIdentifier:foo('foo') + PsiWhiteSpace(' ') + PsiParameterList:(@Annotation(value=77) int param) + PsiJavaToken:LPARENTH('(') + PsiParameter:param + PsiModifierList:@Annotation(value=77) + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:Annotation + PsiIdentifier:Annotation('Annotation') + PsiReferenceParameterList + + PsiAnnotationParameterList + PsiJavaToken:LPARENTH('(') + PsiNameValuePair + PsiIdentifier:value('value') + PsiJavaToken:EQ('=') + PsiLiteralExpression:77 + PsiJavaToken:INTEGER_LITERAL('77') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:param('param') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiWhiteSpace(' ') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/ParameterizedMethod.txt b/java/java-tests/testData/psi/parser-partial/declarations/ParameterizedMethod.txt new file mode 100644 index 000000000000..0128264c46b5 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/ParameterizedMethod.txt @@ -0,0 +1,40 @@ +PsiJavaFile:ParameterizedMethod.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiMethod:bar + PsiModifierList:@Nullable + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:Nullable + PsiIdentifier:Nullable('Nullable') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiTypeParameterList + PsiJavaToken:LT('<') + PsiTypeParameter:T + PsiIdentifier:T('T') + PsiElement(EXTENDS_BOUND_LIST) + + PsiJavaToken:GT('>') + PsiWhiteSpace(' ') + PsiTypeElement:T + PsiJavaCodeReferenceElement:T + PsiIdentifier:T('T') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:bar('bar') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiWhiteSpace(' ') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/declarations/TypeAnno.txt b/java/java-tests/testData/psi/parser-partial/declarations/TypeAnno.txt new file mode 100644 index 000000000000..72944a003f34 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/TypeAnno.txt @@ -0,0 +1,327 @@ +PsiJavaFile:TypeAnno.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiClass:C + PsiModifierList: + + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:C('C') + PsiTypeParameterList + PsiJavaToken:LT('<') + PsiTypeParameter:T + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:D + PsiIdentifier:D('D') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:T('T') + PsiWhiteSpace(' ') + PsiElement(EXTENDS_BOUND_LIST) + PsiKeyword:extends('extends') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:@F Object + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:F + PsiIdentifier:F('F') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:Object('Object') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiWhiteSpace(' ') + PsiReferenceList + PsiKeyword:extends('extends') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:@F Object + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:F + PsiIdentifier:F('F') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:Object('Object') + PsiReferenceParameterList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiWhiteSpace('\n ') + PsiMethod:method + PsiModifierList:@F + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:F + PsiIdentifier:F('F') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiTypeParameterList + + PsiWhiteSpace(' ') + PsiTypeElement:int @F[] + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:F + PsiIdentifier:F('F') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiJavaToken:LBRACKET('[') + PsiJavaToken:RBRACKET(']') + PsiWhiteSpace(' ') + PsiIdentifier:method('method') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiWhiteSpace(' ') + PsiElement(METHOD_RECEIVER) + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:F + PsiIdentifier:F('F') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiReferenceList + PsiKeyword:throws('throws') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:@F Exception + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:F + PsiIdentifier:F('F') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:Exception('Exception') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiCodeBlock + PsiJavaToken:LBRACE('{') + PsiWhiteSpace('\n ') + PsiExpressionStatement + PsiAssignmentExpression:a = this instanceof @F C + PsiReferenceExpression:a + PsiReferenceParameterList + + PsiIdentifier:a('a') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiInstanceofExpression:this instanceof @F C + PsiThisExpression:this + PsiReferenceParameterList + + PsiKeyword:this('this') + PsiWhiteSpace(' ') + PsiKeyword:instanceof('instanceof') + PsiWhiteSpace(' ') + PsiTypeElement:@F C + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:F + PsiIdentifier:F('F') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:C + PsiIdentifier:C('C') + PsiReferenceParameterList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n ') + PsiDeclarationStatement + PsiLocalVariable:c + PsiModifierList: + + PsiTypeElement:C<@F @G C> + PsiJavaCodeReferenceElement:C<@F @G C> + PsiIdentifier:C('C') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:@F @G C + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:F + PsiIdentifier:F('F') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:G + PsiIdentifier:G('G') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:C + PsiIdentifier:C('C') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiWhiteSpace(' ') + PsiIdentifier:c('c') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiNewExpression:new @Q C<@F C>() + PsiKeyword:new('new') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:@Q C<@F C> + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:Q + PsiIdentifier:Q('Q') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:C('C') + PsiReferenceParameterList + PsiJavaToken:LT('<') + PsiTypeElement:@F C + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:F + PsiIdentifier:F('F') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:C + PsiIdentifier:C('C') + PsiReferenceParameterList + + PsiJavaToken:GT('>') + PsiExpressionList + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n ') + PsiExpressionStatement + PsiAssignmentExpression:c = (@F Object)c + PsiReferenceExpression:c + PsiReferenceParameterList + + PsiIdentifier:c('c') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiTypeCastExpression:(@F Object)c + PsiJavaToken:LPARENTH('(') + PsiTypeElement:@F Object + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:F + PsiIdentifier:F('F') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:Object + PsiIdentifier:Object('Object') + PsiReferenceParameterList + + PsiJavaToken:RPARENTH(')') + PsiReferenceExpression:c + PsiReferenceParameterList + + PsiIdentifier:c('c') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n ') + PsiDeclarationStatement + PsiLocalVariable:c + PsiModifierList: + + PsiTypeElement:Class + PsiJavaCodeReferenceElement:Class + PsiIdentifier:Class('Class') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:c('c') + PsiWhiteSpace(' ') + PsiJavaToken:EQ('=') + PsiWhiteSpace(' ') + PsiClassObjectAccessExpression:@TA String.class + PsiTypeElement:@TA String + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:TA + PsiIdentifier:TA('TA') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:String + PsiIdentifier:String('String') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiKeyword:class('class') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n ') + PsiExpressionStatement + PsiPostfixExpression:@F C.field++ + PsiReferenceExpression:@F C.field + PsiReferenceExpression:@F C + PsiReferenceParameterList + + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:F + PsiIdentifier:F('F') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:C('C') + PsiJavaToken:DOT('.') + PsiReferenceParameterList + + PsiIdentifier:field('field') + PsiJavaToken:PLUSPLUS('++') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n ') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace('\n') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/references/Type6.txt b/java/java-tests/testData/psi/parser-partial/references/Type6.txt new file mode 100644 index 000000000000..3ced650bf7f8 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/references/Type6.txt @@ -0,0 +1,28 @@ +PsiJavaFile:Type6.java + PsiTypeElement:@English String @NonEmpty [] + PsiTypeElement:@English String + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:English + PsiIdentifier:English('English') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:String + PsiIdentifier:String('String') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:NonEmpty + PsiIdentifier:NonEmpty('NonEmpty') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACKET('[') + PsiJavaToken:RBRACKET(']') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java index 977c9d6ea852..a88fe03fbc66 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java @@ -36,6 +36,18 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testPines() { doParserTest("{ class A> extends List> { } }", false, false); } public void testIncompleteAnnotation() { doParserTest("{ public class Foo { public void testSomething(); @Null } }", false, false); } public void testClassInit() { doParserTest("{ { /*comment*/ } }", false, false); } + public void testAnnoDeclaration() { doParserTest("{ public @interface Annotation {} }", false, false); } + public void testEnumSmartTypeCompletion() { doParserTest("{ @Preliminary(A.B\n#) public class TimeTravel {}\n" + + " @Preliminary(a=A.B\n#) public class TimeTravel {}\n" + + " @Preliminary(a=A.B\n#, b=c) public class TimeTravel {} }", false, false); } + public void testTypeAnno() { doParserTest("{ class C<@D T extends @F Object> extends @F Object {\n" + + " @F int @F[] method() @F throws @F Exception {\n" + + " a = this instanceof @F C;\n" + + " C<@F @G C> c = new @Q C<@F C>();\n" + + " c = (@F Object)c;\n" + + " Class c = @TA String.class;\n" + + " @F C.field++;\n" + + " }\n} }", false, false); } public void testEnumBody0() { doParserTest("{ ; }", false, true); } public void testEnumBody1() { doParserTest("{ RED, GREEN, BLUE; }", false, true); } @@ -47,6 +59,12 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testEnumWithInitializedConstants() { doParserTest("{ A(10) { },\n B { void method() {} } }", false, true); } public void testEnumWithoutConstants() { doParserTest("{ private A }", false, true); } + public void testAnnoSimple() { doParserTest("{ int foo (); }", true, false); } + public void testAnnoDefault() { doParserTest("{ Class foo () default String.class; }", true, false); } + public void testAnnoNested() { doParserTest("{ @interface Inner { String bar () default \"\"; } }", true, false); } + public void testAnnoInner() { doParserTest("{ @interface Inner { double bar () default 0.0; } }", false, false); } + public void testAnnoOtherMembers() { doParserTest("{ int field;\n void m() {}\n class C {}\n interface I {} }", true, false); } + public void testFieldSimple() { doParserTest("{ int field = 0; }", false, false); } public void testFieldMulti() { doParserTest("{ int field1 = 0, field2; }", false, false); } public void testUnclosedBracket() { doParserTest("{ int field[ }", false, false); } @@ -73,6 +91,8 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testCompletionHack0() { doParserTest("{ \n String s = \"\"; }", false, false); } public void testCompletionHack1() { doParserTest("{ x(Collection x); }", false, false); } + public void testParameterAnnotation() { doParserTest("{ void foo (@Annotation(value=77) int param) {} }", false, false); } + public void testParameterizedMethod() { doParserTest("{ @Nullable T bar() {} }", false, false); } private void doParserTest(final String text, final boolean isAnnotation, final boolean isEnum) { doParserTest(text, new Parser() { diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java index 77e1e8cb3f1e..75d1ec4e6bc7 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java @@ -35,6 +35,7 @@ public class ReferenceParserTest extends JavaParsingTestCase { public void testType3() { doTypeParserTest("int[][", false); } public void testType4() { doTypeParserTest("Map>", false); } public void testType5() { doTypeParserTest("Object[]...", false); } + public void testType6() { doTypeParserTest("@English String @NonEmpty []", false); } public void testTypeParams0() { doTypeParamsParserTest(""); } public void testTypeParams1() { doTypeParamsParserTest(""); } From 160631140d038052cc09df7be1c20f9478746a96 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 30 Jul 2010 14:18:41 +0400 Subject: [PATCH 09/92] New Java parser (work in progress) --- .../lang/java/parser/DeclarationParser.java | 6 +- .../intellij/lang/java/parser/FileParser.java | 152 ++++++++++++++++++ .../intellij/lang/java/parser/JavaParser.java | 2 +- .../lang/java/parser/JavaParserUtil.java | 5 + .../lang/java/parser/ReferenceParser.java | 36 ++++- .../lang/java/parser/StatementParser.java | 8 +- .../parser-partial/files/AnnotatedPackage.txt | 31 ++++ .../psi/parser-partial/files/EmptyFile.txt | 3 + .../parser-partial/files/FileWithClass.txt | 54 +++++++ .../psi/parser-partial/files/Import0.txt | 17 ++ .../psi/parser-partial/files/Import1.txt | 20 +++ .../psi/parser-partial/files/Package.txt | 21 +++ .../parser-partial/files/StaticImport0.txt | 24 +++ .../parser-partial/files/StaticImport1.txt | 25 +++ .../parser-partial/files/UnclosedImport0.txt | 6 + .../parser-partial/files/UnclosedImport1.txt | 18 +++ .../parser-partial/files/UnclosedImport2.txt | 17 ++ .../parser-partial/files/UnclosedPackage0.txt | 5 + .../parser-partial/files/UnclosedPackage1.txt | 16 ++ .../java/parser/partial/FileParserTest.java | 55 +++++++ 20 files changed, 505 insertions(+), 16 deletions(-) create mode 100644 java/java-impl/src/com/intellij/lang/java/parser/FileParser.java create mode 100644 java/java-tests/testData/psi/parser-partial/files/AnnotatedPackage.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/EmptyFile.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/FileWithClass.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/Import0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/Import1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/Package.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/StaticImport0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/StaticImport1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/UnclosedImport0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/UnclosedImport1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/UnclosedImport2.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/UnclosedPackage0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/UnclosedPackage1.txt create mode 100644 java/java-tests/testSrc/com/intellij/lang/java/parser/partial/FileParserTest.java diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index 698ae3e21fd6..e485d89ae8c3 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -158,7 +158,7 @@ public class DeclarationParser { continue; } - final PsiBuilder.Marker declaration = parseDeclaration(builder, context); + final PsiBuilder.Marker declaration = parse(builder, context); if (declaration != null) { if (invalidElements != null) { invalidElements.errorBefore(JavaErrorMessages.message("unexpected.token"), declaration); @@ -184,7 +184,7 @@ public class DeclarationParser { } @Nullable - public static PsiBuilder.Marker parseDeclaration(final PsiBuilder builder, final Context context) { + public static PsiBuilder.Marker parse(final PsiBuilder builder, final Context context) { final IElementType tokenType = builder.getTokenType(); if (tokenType == null) return null; @@ -229,7 +229,7 @@ public class DeclarationParser { while (builder.getTokenType() != null && builder.getTokenType() != JavaTokenType.RBRACE) { final PsiBuilder.Marker position = builder.mark(); - final PsiBuilder.Marker element = parseDeclaration(builder, Context.CLASS); + final PsiBuilder.Marker element = parse(builder, Context.CLASS); if (element != null && AFTER_END_DECLARATION_SET.contains(exprType(element))) { if (!declarationsAfterEnd) { element.precede().error(JavaErrorMessages.message("expected.class.or.interface")); diff --git a/java/java-impl/src/com/intellij/lang/java/parser/FileParser.java b/java/java-impl/src/com/intellij/lang/java/parser/FileParser.java new file mode 100644 index 000000000000..c29ffef181ad --- /dev/null +++ b/java/java-impl/src/com/intellij/lang/java/parser/FileParser.java @@ -0,0 +1,152 @@ +/* + * 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.lang.java.parser; + +import com.intellij.codeInsight.daemon.JavaErrorMessages; +import com.intellij.lang.PsiBuilder; +import com.intellij.psi.JavaTokenType; +import com.intellij.psi.impl.source.tree.ElementType; +import com.intellij.psi.impl.source.tree.JavaElementType; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.TokenSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import static com.intellij.lang.PsiBuilderUtil.expect; +import static com.intellij.lang.java.parser.JavaParserUtil.semicolon; + + +public class FileParser { + private static final TokenSet IMPORT_LIST_STOPPER_SET = TokenSet.orSet( + ElementType.MODIFIER_BIT_SET, + TokenSet.create(JavaTokenType.CLASS_KEYWORD, JavaTokenType.INTERFACE_KEYWORD, JavaTokenType.ENUM_KEYWORD, JavaTokenType.AT)); + + private FileParser() { } + + public static void parse(final PsiBuilder builder) { + parsePackageStatement(builder); + + parseImportList(builder); + + PsiBuilder.Marker invalidElements = null; + while (!builder.eof()) { + if (builder.getTokenType() == JavaTokenType.SEMICOLON) { + if (invalidElements != null) { + invalidElements.error(JavaErrorMessages.message("expected.class.or.interface")); + invalidElements = null; + } + builder.advanceLexer(); + continue; + } + + final PsiBuilder.Marker decl = DeclarationParser.parse(builder, DeclarationParser.Context.FILE); + if (decl != null) { + if (invalidElements != null) { + invalidElements.errorBefore(JavaErrorMessages.message("expected.class.or.interface"), decl); + invalidElements = null; + } + continue; + } + + if (invalidElements == null) { + invalidElements = builder.mark(); + } + builder.advanceLexer(); + } + + if (invalidElements != null) { + invalidElements.error(JavaErrorMessages.message("expected.class.or.interface")); + } + } + + @Nullable + private static PsiBuilder.Marker parsePackageStatement(final PsiBuilder builder) { + final PsiBuilder.Marker statement = builder.mark(); + + if (!expect(builder, JavaTokenType.PACKAGE_KEYWORD)) { + final PsiBuilder.Marker modList = builder.mark(); + DeclarationParser.parseAnnotations(builder); + modList.done(JavaElementType.MODIFIER_LIST); + if (!expect(builder, JavaTokenType.PACKAGE_KEYWORD)) { + statement.rollbackTo(); + return null; + } + } + + final PsiBuilder.Marker ref = ReferenceParser.parseJavaCodeReference(builder, true, false, false); + if (ref == null) { + statement.rollbackTo(); + return null; + } + + semicolon(builder); + + statement.done(JavaElementType.PACKAGE_STATEMENT); + return statement; + } + + @NotNull + private static PsiBuilder.Marker parseImportList(final PsiBuilder builder) { + final PsiBuilder.Marker list = builder.mark(); + + if (builder.getTokenType() == JavaTokenType.IMPORT_KEYWORD) { + PsiBuilder.Marker invalidElements = null; + while (!builder.eof()) { + if (IMPORT_LIST_STOPPER_SET.contains(builder.getTokenType())) break; + + final PsiBuilder.Marker statement = parseImportStatement(builder); + if (statement != null) { + if (invalidElements != null) { + invalidElements.errorBefore(JavaErrorMessages.message("unexpected.token"), statement); + invalidElements = null; + } + continue; + } + + if (invalidElements == null) { + invalidElements = builder.mark(); + } + builder.advanceLexer(); + } + + if (invalidElements != null) { + invalidElements.error(JavaErrorMessages.message("unexpected.token")); + } + } + + list.done(JavaElementType.IMPORT_LIST); + return list; + } + + @Nullable + private static PsiBuilder.Marker parseImportStatement(final PsiBuilder builder) { + if (builder.getTokenType() != JavaTokenType.IMPORT_KEYWORD) return null; + + final PsiBuilder.Marker statement = builder.mark(); + builder.advanceLexer(); + + final boolean isStatic = expect(builder, JavaTokenType.STATIC_KEYWORD); + final IElementType type = isStatic ? JavaElementType.IMPORT_STATIC_STATEMENT : JavaElementType.IMPORT_STATEMENT; + + final boolean isOk = ReferenceParser.parseImportCodeReference(builder, isStatic); + if (isOk) { + semicolon(builder); + } + + statement.done(type); + return statement; + } +} diff --git a/java/java-impl/src/com/intellij/lang/java/parser/JavaParser.java b/java/java-impl/src/com/intellij/lang/java/parser/JavaParser.java index b06bf223d085..9d1e8a49cea7 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/JavaParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/JavaParser.java @@ -37,7 +37,7 @@ public class JavaParser implements PsiParser { final PsiBuilder.Marker root = builder.mark(); setLanguageLevel(builder, myLanguageLevel); - // parse + FileParser.parse(builder); root.done(rootType); return builder.getTreeBuilt(); 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 2aa5b8da3976..6f949120096a 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 @@ -15,6 +15,7 @@ */ package com.intellij.lang.java.parser; +import com.intellij.codeInsight.daemon.JavaErrorMessages; import com.intellij.lang.*; import com.intellij.openapi.util.Key; import com.intellij.pom.java.LanguageLevel; @@ -83,6 +84,10 @@ public class JavaParserUtil { before.precede().doneBefore(type, before); } + public static void semicolon(final PsiBuilder builder) { + expectOrError(builder, JavaTokenType.SEMICOLON, JavaErrorMessages.message("expected.semicolon")); + } + public static PsiBuilder braceMatchingBuilder(final PsiBuilder builder) { return new PsiBuilderAdapter(builder) { private int braceCount = 1; diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java index 4f06c802e8e2..ba3a01062f72 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java @@ -35,6 +35,7 @@ public class ReferenceParser { public boolean isPrimitive = false; public boolean isParameterized = false; public boolean isArray = false; + public boolean hasErrors = false; public PsiBuilder.Marker marker = null; } @@ -85,7 +86,7 @@ public class ReferenceParser { typeInfo.isPrimitive = true; } else if (tokenType == JavaTokenType.IDENTIFIER) { - parseJavaCodeReference(builder, eatLastDot, true, annotationsSupported, typeInfo); + parseJavaCodeReference(builder, eatLastDot, true, annotationsSupported, false, false, typeInfo); } else if (wildcard && tokenType == JavaTokenType.QUEST) { type.drop(); @@ -137,12 +138,19 @@ public class ReferenceParser { @Nullable public static PsiBuilder.Marker parseJavaCodeReference(final PsiBuilder builder, final boolean eatLastDot, final boolean parameterList, final boolean annotations) { - return parseJavaCodeReference(builder, eatLastDot, parameterList, annotations, new TypeInfo()); + return parseJavaCodeReference(builder, eatLastDot, parameterList, annotations, false, false, new TypeInfo()); + } + + public static boolean parseImportCodeReference(final PsiBuilder builder, final boolean isStatic) { + final TypeInfo typeInfo = new TypeInfo(); + parseJavaCodeReference(builder, true, false, false, true, isStatic, typeInfo); + return !typeInfo.hasErrors; } @Nullable private static PsiBuilder.Marker parseJavaCodeReference(final PsiBuilder builder, final boolean eatLastDot, final boolean parameterList, final boolean annotations, + final boolean isImport, final boolean isStaticImport, final TypeInfo typeInfo) { PsiBuilder.Marker refElement = builder.mark(); @@ -152,6 +160,10 @@ public class ReferenceParser { if (!expect(builder, JavaTokenType.IDENTIFIER)) { refElement.rollbackTo(); + if (isImport) { + error(builder, JavaErrorMessages.message("expected.identifier")); + } + typeInfo.hasErrors = true; return null; } @@ -173,6 +185,10 @@ public class ReferenceParser { if (expect(builder, JavaTokenType.IDENTIFIER)) { hasIdentifier = true; } + else if (isImport && expect(builder, JavaTokenType.ASTERISK)) { + dotPos.drop(); + return refElement; + } else { if (!eatLastDot) { dotPos.rollbackTo(); @@ -185,20 +201,28 @@ public class ReferenceParser { refElement = refElement.precede(); if (!hasIdentifier) { - error(builder, JavaErrorMessages.message("expected.identifier")); - break; + typeInfo.hasErrors = true; + if (isImport) { + error(builder, JavaErrorMessages.message("import.statement.identifier.or.asterisk.expected.")); + refElement.drop(); + return refElement; + } + else { + error(builder, JavaErrorMessages.message("expected.identifier")); + break; + } } if (parameterList) { typeInfo.isParameterized = (builder.getTokenType() == JavaTokenType.LT); parseReferenceParameterList(builder, true); } - else { + else if (!isStaticImport || builder.getTokenType() == JavaTokenType.DOT) { emptyElement(builder, JavaElementType.REFERENCE_PARAMETER_LIST); } } - refElement.done(JavaElementType.JAVA_CODE_REFERENCE); + refElement.done(isStaticImport ? JavaElementType.IMPORT_STATIC_REFERENCE : JavaElementType.JAVA_CODE_REFERENCE); return refElement; } diff --git a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java index 9e9ba6bd6e68..a390554731f7 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java @@ -218,7 +218,7 @@ public class StatementParser { refPos.rollbackTo(); if (suspectedLT == JavaTokenType.LT) { final PsiBuilder.Marker declStatement = builder.mark(); - final PsiBuilder.Marker decl = DeclarationParser.parseDeclaration(builder, DeclarationParser.Context.CODE_BLOCK); + final PsiBuilder.Marker decl = DeclarationParser.parse(builder, DeclarationParser.Context.CODE_BLOCK); if (decl == null) { error(builder, JavaErrorMessages.message("expected.identifier")); } @@ -264,7 +264,7 @@ public class StatementParser { pos.drop(); } - final PsiBuilder.Marker decl = DeclarationParser.parseDeclaration(builder, DeclarationParser.Context.CODE_BLOCK); + final PsiBuilder.Marker decl = DeclarationParser.parse(builder, DeclarationParser.Context.CODE_BLOCK); if (decl != null) { final PsiBuilder.Marker statement = decl.precede(); statement.done(JavaElementType.DECLARATION_STATEMENT); @@ -297,10 +297,6 @@ public class StatementParser { } } - private static void semicolon(final PsiBuilder builder) { - expectOrError(builder, JavaTokenType.SEMICOLON, JavaErrorMessages.message("expected.semicolon")); - } - @NotNull private static PsiBuilder.Marker parseIfStatement(final PsiBuilder builder) { final PsiBuilder.Marker statement = builder.mark(); diff --git a/java/java-tests/testData/psi/parser-partial/files/AnnotatedPackage.txt b/java/java-tests/testData/psi/parser-partial/files/AnnotatedPackage.txt new file mode 100644 index 000000000000..e60c2eb1f41c --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/AnnotatedPackage.txt @@ -0,0 +1,31 @@ +PsiJavaFile:AnnotatedPackage.java + PsiPackageStatement:a.b.c + PsiModifierList:@Anno + PsiAnnotation + PsiJavaToken:AT('@') + PsiJavaCodeReferenceElement:Anno + PsiIdentifier:Anno('Anno') + PsiReferenceParameterList + + PsiAnnotationParameterList + + PsiWhiteSpace(' ') + PsiKeyword:package('package') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:a.b.c + PsiJavaCodeReferenceElement:a.b + PsiJavaCodeReferenceElement:a + PsiIdentifier:a('a') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:b('b') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:c('c') + PsiReferenceParameterList + + PsiJavaToken:SEMICOLON(';') + PsiImportList + \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/files/EmptyFile.txt b/java/java-tests/testData/psi/parser-partial/files/EmptyFile.txt new file mode 100644 index 000000000000..3034524d047d --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/EmptyFile.txt @@ -0,0 +1,3 @@ +PsiJavaFile:EmptyFile.java + PsiImportList + \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/files/FileWithClass.txt b/java/java-tests/testData/psi/parser-partial/files/FileWithClass.txt new file mode 100644 index 000000000000..3f71aea86d25 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/FileWithClass.txt @@ -0,0 +1,54 @@ +PsiJavaFile:FileWithClass.java + PsiPackageStatement:a + PsiKeyword:package('package') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:a + PsiIdentifier:a('a') + PsiReferenceParameterList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n') + PsiImportList + PsiImportStatement + PsiKeyword:import('import') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:b + PsiIdentifier:b('b') + PsiReferenceParameterList + + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n') + PsiClass:C + PsiModifierList:public + PsiKeyword:public('public') + PsiWhiteSpace(' ') + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:C('C') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') + PsiWhiteSpace('\n') + PsiClass:D + PsiModifierList: + + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:D('D') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiWhiteSpace(' ') + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/files/Import0.txt b/java/java-tests/testData/psi/parser-partial/files/Import0.txt new file mode 100644 index 000000000000..94334720cced --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/Import0.txt @@ -0,0 +1,17 @@ +PsiJavaFile:Import0.java + PsiImportList + PsiImportStatement + PsiKeyword:import('import') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:java.util + PsiJavaCodeReferenceElement:java + PsiIdentifier:java('java') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:util('util') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiJavaToken:ASTERISK('*') + PsiJavaToken:SEMICOLON(';') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/files/Import1.txt b/java/java-tests/testData/psi/parser-partial/files/Import1.txt new file mode 100644 index 000000000000..1831107feff3 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/Import1.txt @@ -0,0 +1,20 @@ +PsiJavaFile:Import1.java + PsiImportList + PsiImportStatement + PsiKeyword:import('import') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:java.util.Arrays + PsiJavaCodeReferenceElement:java.util + PsiJavaCodeReferenceElement:java + PsiIdentifier:java('java') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:util('util') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:Arrays('Arrays') + PsiReferenceParameterList + + PsiJavaToken:SEMICOLON(';') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/files/Package.txt b/java/java-tests/testData/psi/parser-partial/files/Package.txt new file mode 100644 index 000000000000..a3fedef93d7b --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/Package.txt @@ -0,0 +1,21 @@ +PsiJavaFile:Package.java + PsiPackageStatement:a.b.c + PsiKeyword:package('package') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:a.b.c + PsiJavaCodeReferenceElement:a.b + PsiJavaCodeReferenceElement:a + PsiIdentifier:a('a') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:b('b') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:c('c') + PsiReferenceParameterList + + PsiJavaToken:SEMICOLON(';') + PsiImportList + \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/files/StaticImport0.txt b/java/java-tests/testData/psi/parser-partial/files/StaticImport0.txt new file mode 100644 index 000000000000..114f1a9bd0f5 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/StaticImport0.txt @@ -0,0 +1,24 @@ +PsiJavaFile:StaticImport0.java + PsiImportList + PsiImportStaticStatement + PsiKeyword:import('import') + PsiWhiteSpace(' ') + PsiKeyword:static('static') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:java.util.Arrays + PsiJavaCodeReferenceElement:java.util + PsiJavaCodeReferenceElement:java + PsiIdentifier:java('java') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:util('util') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:Arrays('Arrays') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiJavaToken:ASTERISK('*') + PsiJavaToken:SEMICOLON(';') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/files/StaticImport1.txt b/java/java-tests/testData/psi/parser-partial/files/StaticImport1.txt new file mode 100644 index 000000000000..cdc45d1611c3 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/StaticImport1.txt @@ -0,0 +1,25 @@ +PsiJavaFile:StaticImport1.java + PsiImportList + PsiImportStaticStatement + PsiKeyword:import('import') + PsiWhiteSpace(' ') + PsiKeyword:static('static') + PsiWhiteSpace(' ') + PsiImportStaticReferenceElement:java.util.Arrays.sort + PsiJavaCodeReferenceElement:java.util.Arrays + PsiJavaCodeReferenceElement:java.util + PsiJavaCodeReferenceElement:java + PsiIdentifier:java('java') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:util('util') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:Arrays('Arrays') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:sort('sort') + PsiJavaToken:SEMICOLON(';') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/files/UnclosedImport0.txt b/java/java-tests/testData/psi/parser-partial/files/UnclosedImport0.txt new file mode 100644 index 000000000000..b30c120e520b --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/UnclosedImport0.txt @@ -0,0 +1,6 @@ +PsiJavaFile:UnclosedImport0.java + PsiImportList + PsiImportStatement + PsiKeyword:import('import') + PsiErrorElement:Identifier expected + \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/files/UnclosedImport1.txt b/java/java-tests/testData/psi/parser-partial/files/UnclosedImport1.txt new file mode 100644 index 000000000000..ff2b67f0efe8 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/UnclosedImport1.txt @@ -0,0 +1,18 @@ +PsiJavaFile:UnclosedImport1.java + PsiImportList + PsiImportStatement + PsiKeyword:import('import') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:java.awt + PsiJavaCodeReferenceElement:java + PsiIdentifier:java('java') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:awt('awt') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiJavaToken:ASTERISK('*') + PsiErrorElement:';' expected + \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/files/UnclosedImport2.txt b/java/java-tests/testData/psi/parser-partial/files/UnclosedImport2.txt new file mode 100644 index 000000000000..1a49c9250d9d --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/UnclosedImport2.txt @@ -0,0 +1,17 @@ +PsiJavaFile:UnclosedImport2.java + PsiImportList + PsiImportStatement + PsiKeyword:import('import') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:java.awt + PsiJavaCodeReferenceElement:java + PsiIdentifier:java('java') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiIdentifier:awt('awt') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiErrorElement:Identifier or '*' expected + \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/files/UnclosedPackage0.txt b/java/java-tests/testData/psi/parser-partial/files/UnclosedPackage0.txt new file mode 100644 index 000000000000..cc9866bf1040 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/UnclosedPackage0.txt @@ -0,0 +1,5 @@ +PsiJavaFile:UnclosedPackage0.java + PsiImportList + + PsiErrorElement:'class' or 'interface' expected + PsiKeyword:package('package') \ No newline at end of file diff --git a/java/java-tests/testData/psi/parser-partial/files/UnclosedPackage1.txt b/java/java-tests/testData/psi/parser-partial/files/UnclosedPackage1.txt new file mode 100644 index 000000000000..5f8fe8f51214 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/UnclosedPackage1.txt @@ -0,0 +1,16 @@ +PsiJavaFile:UnclosedPackage1.java + PsiPackageStatement:a. + PsiKeyword:package('package') + PsiWhiteSpace(' ') + PsiJavaCodeReferenceElement:a. + PsiJavaCodeReferenceElement:a + PsiIdentifier:a('a') + PsiReferenceParameterList + + PsiJavaToken:DOT('.') + PsiErrorElement:Identifier expected + + PsiErrorElement:';' expected + + PsiImportList + \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/FileParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/FileParserTest.java new file mode 100644 index 000000000000..cd41c779d578 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/FileParserTest.java @@ -0,0 +1,55 @@ +/* + * 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.lang.java.parser.partial; + +import com.intellij.lang.PsiBuilder; +import com.intellij.lang.java.parser.FileParser; +import com.intellij.lang.java.parser.JavaParsingTestCase; + + +public class FileParserTest extends JavaParsingTestCase { + public FileParserTest() { + super("parser-partial/files"); + } + + public void testEmptyFile() { doParserTest(""); } + + public void testPackage() { doParserTest("package a.b.c;"); } + public void testAnnotatedPackage() { doParserTest("@Anno package a.b.c;"); } + public void testUnclosedPackage0() { doParserTest("package"); } + public void testUnclosedPackage1() { doParserTest("package a."); } + + public void testImport0() { doParserTest("import java.util.*;"); } + public void testImport1() { doParserTest("import java.util.Arrays;"); } + public void testStaticImport0() { doParserTest("import static java.util.Arrays.*;"); } + public void testStaticImport1() { doParserTest("import static java.util.Arrays.sort;"); } + public void testUnclosedImport0() { doParserTest("import"); } + public void testUnclosedImport1() { doParserTest("import java.awt.*"); } + public void testUnclosedImport2() { doParserTest("import java.awt."); } + + public void testFileWithClass() { doParserTest("package a;\n" + + "import b;\n" + + "public class C { }\n" + + "class D { }"); } + + private void doParserTest(final String text) { + doParserTest(text, new Parser() { + public void parse(final PsiBuilder builder) { + FileParser.parse(builder); + } + }); + } +} From a847515b92734948fa71edfa916a538ebee1c71b Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Fri, 30 Jul 2010 20:10:33 +0400 Subject: [PATCH 10/92] New Java parser (work in progress) --- .../lang/java/parser/DeclarationParser.java | 8 +- .../lang/java/parser/ReferenceParser.java | 3 +- .../lang/java/parser/StatementParser.java | 7 +- .../psi/parser-partial/files/BindBefore0.txt | 29 +++++++ .../psi/parser-partial/files/BindBefore1.txt | 29 +++++++ .../psi/parser-partial/files/BindBefore2.txt | 29 +++++++ .../psi/parser-partial/files/BindBefore3.txt | 28 +++++++ .../psi/parser-partial/files/BindBefore4.txt | 33 ++++++++ .../testSrc/com/intellij/JavaTestUtil.java | 8 -- .../lang/java/parser/JavaParsingTestCase.java | 82 +++++++++++++++---- .../parser/partial/AnnotationParserTest.java | 2 +- .../parser/partial/DeclarationParserTest.java | 24 ++++-- .../parser/partial/ExpressionParserTest.java | 2 +- .../java/parser/partial/FileParserTest.java | 11 ++- .../parser/partial/ReferenceParserTest.java | 12 ++- .../parser/partial/StatementParserTest.java | 2 +- 16 files changed, 257 insertions(+), 52 deletions(-) create mode 100644 java/java-tests/testData/psi/parser-partial/files/BindBefore0.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/BindBefore1.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/BindBefore2.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/BindBefore3.txt create mode 100644 java/java-tests/testData/psi/parser-partial/files/BindBefore4.txt diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index e485d89ae8c3..9e153b7ff725 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -56,7 +56,7 @@ public class DeclarationParser { expectOrError(builder, JavaTokenType.RBRACE, JavaErrorMessages.message("expected.rbrace")); } - @NotNull + @Nullable private static PsiBuilder.Marker parseClassFromKeyword(final PsiBuilder builder, final PsiBuilder.Marker declaration, final boolean isAnnotation) { final IElementType keywordTokenType = builder.getTokenType(); @@ -67,7 +67,7 @@ public class DeclarationParser { if (!expect(builder, JavaTokenType.IDENTIFIER)) { error(builder, JavaErrorMessages.message("expected.identifier")); declaration.drop(); - return declaration; + return null; } ReferenceParser.parseTypeParameters(builder); @@ -195,10 +195,8 @@ public class DeclarationParser { if (context == Context.FILE) return null; } else if (tokenType instanceof ILazyParseableElementType) { - final PsiBuilder.Marker declaration = builder.mark(); - declaration.drop(); builder.advanceLexer(); - return declaration; + return null; } else if (!ElementType.MODIFIER_BIT_SET.contains(tokenType) && !ElementType.CLASS_KEYWORD_BIT_SET.contains(tokenType) && diff --git a/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java b/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java index ba3a01062f72..3097261df7fc 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/ReferenceParser.java @@ -198,6 +198,7 @@ public class ReferenceParser { } dotPos.drop(); + final PsiBuilder.Marker prevElement = refElement; refElement = refElement.precede(); if (!hasIdentifier) { @@ -205,7 +206,7 @@ public class ReferenceParser { if (isImport) { error(builder, JavaErrorMessages.message("import.statement.identifier.or.asterisk.expected.")); refElement.drop(); - return refElement; + return prevElement; } else { error(builder, JavaErrorMessages.message("expected.identifier")); diff --git a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java index a390554731f7..5590f15fd28a 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/StatementParser.java @@ -34,7 +34,7 @@ import static com.intellij.lang.java.parser.JavaParserUtil.*; public class StatementParser { - private static final boolean DEEP_PARSE_BLOCKS_IN_STATEMENTS = true; // todo: reset after testing done + private static final boolean DEEP_PARSE_BLOCKS_IN_STATEMENTS = false; private enum BraceMode { TILL_FIRST, TILL_LAST @@ -199,10 +199,8 @@ public class StatementParser { return parseBlockStatement(builder); } else if (tokenType instanceof ILazyParseableElementType) { - final PsiBuilder.Marker marker = builder.mark(); - marker.drop(); builder.advanceLexer(); - return marker; + return null; } else if (tokenType == JavaTokenType.SEMICOLON) { final PsiBuilder.Marker empty = builder.mark(); @@ -220,6 +218,7 @@ public class StatementParser { final PsiBuilder.Marker declStatement = builder.mark(); final PsiBuilder.Marker decl = DeclarationParser.parse(builder, DeclarationParser.Context.CODE_BLOCK); if (decl == null) { + ReferenceParser.parseType(builder, false, false); error(builder, JavaErrorMessages.message("expected.identifier")); } declStatement.done(JavaElementType.DECLARATION_STATEMENT); diff --git a/java/java-tests/testData/psi/parser-partial/files/BindBefore0.txt b/java/java-tests/testData/psi/parser-partial/files/BindBefore0.txt new file mode 100644 index 000000000000..734e49a84fbf --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/BindBefore0.txt @@ -0,0 +1,29 @@ +PsiJavaFile:BindBefore0.java + PsiImportList + + PsiClass:A + PsiModifierList: + + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:A('A') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiJavaToken:LBRACE('{') + PsiWhiteSpace('\n ') + PsiField:field + PsiComment(END_OF_LINE_COMMENT)('// comment') + PsiWhiteSpace('\n ') + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/files/BindBefore1.txt b/java/java-tests/testData/psi/parser-partial/files/BindBefore1.txt new file mode 100644 index 000000000000..259367010fa3 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/BindBefore1.txt @@ -0,0 +1,29 @@ +PsiJavaFile:BindBefore1.java + PsiImportList + + PsiClass:A + PsiModifierList: + + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:A('A') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiJavaToken:LBRACE('{') + PsiWhiteSpace('\n ') + PsiComment(END_OF_LINE_COMMENT)('// comment') + PsiWhiteSpace('\n\n ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/files/BindBefore2.txt b/java/java-tests/testData/psi/parser-partial/files/BindBefore2.txt new file mode 100644 index 000000000000..0b6174c8e572 --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/BindBefore2.txt @@ -0,0 +1,29 @@ +PsiJavaFile:BindBefore2.java + PsiImportList + + PsiClass:A + PsiModifierList: + + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:A('A') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiComment(END_OF_LINE_COMMENT)('// comment') + PsiWhiteSpace('\n ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/files/BindBefore3.txt b/java/java-tests/testData/psi/parser-partial/files/BindBefore3.txt new file mode 100644 index 000000000000..1de2eff0f25d --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/BindBefore3.txt @@ -0,0 +1,28 @@ +PsiJavaFile:BindBefore3.java + PsiImportList + + PsiClass:A + PsiModifierList: + + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:A('A') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiJavaToken:LBRACE('{') + PsiComment(END_OF_LINE_COMMENT)('// comment') + PsiWhiteSpace('\n ') + PsiField:field + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testData/psi/parser-partial/files/BindBefore4.txt b/java/java-tests/testData/psi/parser-partial/files/BindBefore4.txt new file mode 100644 index 000000000000..117f263a981a --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/files/BindBefore4.txt @@ -0,0 +1,33 @@ +PsiJavaFile:BindBefore4.java + PsiImportList + + PsiClass:A + PsiModifierList: + + PsiKeyword:class('class') + PsiWhiteSpace(' ') + PsiIdentifier:A('A') + PsiTypeParameterList + + PsiReferenceList + + PsiReferenceList + + PsiJavaToken:LBRACE('{') + PsiWhiteSpace('\n ') + PsiField:field + PsiComment(END_OF_LINE_COMMENT)('// comment 1') + PsiWhiteSpace('\n ') + PsiComment(END_OF_LINE_COMMENT)('// comment 2') + PsiWhiteSpace('\n ') + PsiComment(END_OF_LINE_COMMENT)('// comment 3') + PsiWhiteSpace('\n ') + PsiModifierList: + + PsiTypeElement:int + PsiKeyword:int('int') + PsiWhiteSpace(' ') + PsiIdentifier:field('field') + PsiJavaToken:SEMICOLON(';') + PsiWhiteSpace('\n') + PsiJavaToken:RBRACE('}') diff --git a/java/java-tests/testSrc/com/intellij/JavaTestUtil.java b/java/java-tests/testSrc/com/intellij/JavaTestUtil.java index 4ea7a14d3e37..08eaab0bfe36 100644 --- a/java/java-tests/testSrc/com/intellij/JavaTestUtil.java +++ b/java/java-tests/testSrc/com/intellij/JavaTestUtil.java @@ -20,12 +20,4 @@ public class JavaTestUtil { return PathManagerEx.getTestDataPath(); } - public static PsiBuilder getJavaBuilder(final CharSequence source) { - final LanguageLevel languageLevel = LanguageLevel.HIGHEST; - final JavaLexer lexer = new JavaLexer(languageLevel); - final PsiBuilder builder = PsiBuilderFactory.getInstance().createBuilder(lexer, StdLanguages.JAVA, source); - builder.setDebugMode(true); - JavaParserUtil.setLanguageLevel(builder, languageLevel); - return builder; - } } 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 15874f8244e2..57512efdd42e 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 @@ -15,17 +15,29 @@ */ package com.intellij.lang.java.parser; +import com.intellij.lang.ASTNode; import com.intellij.lang.PsiBuilder; +import com.intellij.lang.PsiBuilderFactory; +import com.intellij.lang.StdLanguages; +import com.intellij.openapi.fileTypes.StdFileTypes; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.roots.LanguageLevelProjectExtension; +import com.intellij.pom.java.LanguageLevel; +import com.intellij.psi.FileViewProvider; +import com.intellij.psi.PsiManager; +import com.intellij.psi.SingleRootFileViewProvider; import com.intellij.psi.impl.DebugUtil; -import com.intellij.psi.impl.source.tree.JavaElementType; +import com.intellij.psi.impl.source.PsiJavaFileImpl; +import com.intellij.psi.impl.source.tree.FileElement; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.tree.IFileElementType; import com.intellij.testFramework.IdeaTestCase; +import com.intellij.testFramework.LightVirtualFile; import com.intellij.testFramework.ParsingTestCase; import org.jetbrains.annotations.NonNls; import java.io.IOException; -import static com.intellij.JavaTestUtil.getJavaBuilder; - public abstract class JavaParsingTestCase extends ParsingTestCase { @SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors"}) @@ -34,33 +46,69 @@ public abstract class JavaParsingTestCase extends ParsingTestCase { IdeaTestCase.initPlatformPrefix(); } - protected interface Parser { + protected static void withLevel(final LanguageLevel level, final Runnable r) { + final LanguageLevel current = LanguageLevelProjectExtension.getInstance(getProject()).getLanguageLevel(); + try { + LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(level); + r.run(); + } + finally { + LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(current); + } + } + + protected interface TestParser { void parse(PsiBuilder builder); } - protected void doParserTest(final String text, final Parser parser) { + protected void doParserTest(final String source, final TestParser parser) { final String name = getTestName(false); - final PsiBuilder builder = getJavaBuilder(text); - final PsiBuilder.Marker root = builder.mark(); + final IElementType fileElementType = new IFileElementType("test.java.file", StdLanguages.JAVA) { + @Override + public ASTNode parseContents(final ASTNode chameleon) { + final PsiBuilder builder = createBuilder(chameleon); - parser.parse(builder); + final PsiBuilder.Marker root = builder.mark(); + parser.parse(builder); + if (!builder.eof()) { + final PsiBuilder.Marker unparsed = builder.mark(); + while (!builder.eof()) builder.advanceLexer(); + unparsed.error("Unparsed tokens"); + } + root.done(this); - if (builder.getTokenType() != null) { - final PsiBuilder.Marker unparsed = builder.mark(); - while (builder.getTokenType() != null) builder.advanceLexer(); - unparsed.error("Unparsed tokens"); - } + return builder.getTreeBuilt().getFirstChildNode(); + } + }; - root.done(JavaElementType.JAVA_FILE); + final LightVirtualFile virtualFile = new LightVirtualFile(name + '.' + myFileExt, StdFileTypes.JAVA, source, -1); + final FileViewProvider viewProvider = new SingleRootFileViewProvider(PsiManager.getInstance(getProject()), virtualFile, true); + final PsiJavaFileImpl psiFile = new PsiJavaFileImpl(viewProvider) { + @Override + protected FileElement createFileElement(final CharSequence text) { + return new FileElement(fileElementType, text); + } + }; - final String raw = DebugUtil.treeToString(builder.getTreeBuilt(), false); - final String tree = raw.replaceFirst("com.intellij.psi.util.PsiUtilBase\\S+", "PsiJavaFile:" + name + ".java"); try { - checkResult(name + ".txt", tree); + checkResult(name + ".txt", DebugUtil.psiToString(psiFile, false)); } catch (IOException e) { throw new RuntimeException(e); } } + + private static PsiBuilder createBuilder(final ASTNode chameleon) { + final Project project = chameleon.getPsi().getProject(); + final PsiBuilderFactory factory = PsiBuilderFactory.getInstance(); + final PsiBuilder builder = factory.createBuilder(project, chameleon, null, chameleon.getElementType().getLanguage(), chameleon.getChars()); + + builder.setDebugMode(true); + + final LanguageLevel level = LanguageLevelProjectExtension.getInstance(project).getLanguageLevel(); + JavaParserUtil.setLanguageLevel(builder, level); + + return builder; + } } diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/AnnotationParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/AnnotationParserTest.java index f5dcb83f898c..6b1407f35fec 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/AnnotationParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/AnnotationParserTest.java @@ -38,7 +38,7 @@ public class AnnotationParserTest extends JavaParsingTestCase { public void testFirstNameMissed() { doParserTest("@Anno(value1, param2=value2)"); } private void doParserTest(final String text) { - doParserTest(text, new Parser() { + doParserTest(text, new TestParser() { public void parse(final PsiBuilder builder) { DeclarationParser.parseAnnotations(builder); } diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java index a88fe03fbc66..2617874e8d32 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java @@ -18,6 +18,7 @@ package com.intellij.lang.java.parser.partial; import com.intellij.lang.PsiBuilder; import com.intellij.lang.java.parser.DeclarationParser; import com.intellij.lang.java.parser.JavaParsingTestCase; +import com.intellij.pom.java.LanguageLevel; // todo: fix parser and uncomment tests @@ -40,14 +41,19 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testEnumSmartTypeCompletion() { doParserTest("{ @Preliminary(A.B\n#) public class TimeTravel {}\n" + " @Preliminary(a=A.B\n#) public class TimeTravel {}\n" + " @Preliminary(a=A.B\n#, b=c) public class TimeTravel {} }", false, false); } - public void testTypeAnno() { doParserTest("{ class C<@D T extends @F Object> extends @F Object {\n" + - " @F int @F[] method() @F throws @F Exception {\n" + - " a = this instanceof @F C;\n" + - " C<@F @G C> c = new @Q C<@F C>();\n" + - " c = (@F Object)c;\n" + - " Class c = @TA String.class;\n" + - " @F C.field++;\n" + - " }\n} }", false, false); } + public void testTypeAnno() { + withLevel(LanguageLevel.JDK_1_7, + new Runnable() { public void run() { + doParserTest("{ class C<@D T extends @F Object> extends @F Object {\n" + + " @F int @F[] method() @F throws @F Exception {\n" + + " a = this instanceof @F C;\n" + + " C<@F @G C> c = new @Q C<@F C>();\n" + + " c = (@F Object)c;\n" + + " Class c = @TA String.class;\n" + + " @F C.field++;\n" + + " }\n} }", false, false); + } }); + } public void testEnumBody0() { doParserTest("{ ; }", false, true); } public void testEnumBody1() { doParserTest("{ RED, GREEN, BLUE; }", false, true); } @@ -95,7 +101,7 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testParameterizedMethod() { doParserTest("{ @Nullable T bar() {} }", false, false); } private void doParserTest(final String text, final boolean isAnnotation, final boolean isEnum) { - doParserTest(text, new Parser() { + doParserTest(text, new TestParser() { public void parse(final PsiBuilder builder) { DeclarationParser.parseClassBodyWithBraces(builder, isAnnotation, isEnum); } diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java index 340c962ad87b..ebf0ea55901f 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ExpressionParserTest.java @@ -101,7 +101,7 @@ public class ExpressionParserTest extends JavaParsingTestCase { public void testPrimitiveClassObjectAccess() { doParserTest("int.class"); } private void doParserTest(final String text) { - doParserTest(text, new Parser() { + doParserTest(text, new TestParser() { public void parse(final PsiBuilder builder) { ExpressionParser.parse(builder); } diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/FileParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/FileParserTest.java index cd41c779d578..78ec9bec02ce 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/FileParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/FileParserTest.java @@ -20,6 +20,7 @@ import com.intellij.lang.java.parser.FileParser; import com.intellij.lang.java.parser.JavaParsingTestCase; +// todo: fix comment binding, uncomment tests public class FileParserTest extends JavaParsingTestCase { public FileParserTest() { super("parser-partial/files"); @@ -45,8 +46,16 @@ public class FileParserTest extends JavaParsingTestCase { "public class C { }\n" + "class D { }"); } + //public void testBindBefore0() { doParserTest("class A{\n // comment\n int field;\n}"); } + public void testBindBefore1() { doParserTest("class A{\n // comment\n\n int field;\n}"); } + public void testBindBefore2() { doParserTest("class A{ // comment\n int field;\n}"); } + public void testBindBefore3() { doParserTest("class A{// comment\n int field;\n}"); } + //public void testBindBefore4() { doParserTest("class A{\n // comment 1\n // comment 2\n // comment 3\n int field;\n}"); } + + // todo: doc comment binding tests + private void doParserTest(final String text) { - doParserTest(text, new Parser() { + doParserTest(text, new TestParser() { public void parse(final PsiBuilder builder) { FileParser.parse(builder); } diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java index 75d1ec4e6bc7..e9f46fee618a 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/ReferenceParserTest.java @@ -18,6 +18,7 @@ package com.intellij.lang.java.parser.partial; import com.intellij.lang.PsiBuilder; import com.intellij.lang.java.parser.JavaParsingTestCase; import com.intellij.lang.java.parser.ReferenceParser; +import com.intellij.pom.java.LanguageLevel; public class ReferenceParserTest extends JavaParsingTestCase { @@ -35,7 +36,10 @@ public class ReferenceParserTest extends JavaParsingTestCase { public void testType3() { doTypeParserTest("int[][", false); } public void testType4() { doTypeParserTest("Map>", false); } public void testType5() { doTypeParserTest("Object[]...", false); } - public void testType6() { doTypeParserTest("@English String @NonEmpty []", false); } + public void testType6() { + withLevel(LanguageLevel.JDK_1_7, + new Runnable() { public void run() { doTypeParserTest("@English String @NonEmpty []", false); } }); + } public void testTypeParams0() { doTypeParamsParserTest(""); } public void testTypeParams1() { doTypeParamsParserTest(""); } @@ -47,7 +51,7 @@ public class ReferenceParserTest extends JavaParsingTestCase { public void testTypeParams7() { doTypeParamsParserTest(""); } private void doRefParserTest(final String text, final boolean incomplete) { - doParserTest(text, new Parser() { + doParserTest(text, new TestParser() { public void parse(final PsiBuilder builder) { ReferenceParser.parseJavaCodeReference(builder, incomplete, false, false); } @@ -55,7 +59,7 @@ public class ReferenceParserTest extends JavaParsingTestCase { } private void doTypeParserTest(final String text, final boolean incomplete) { - doParserTest(text, new Parser() { + doParserTest(text, new TestParser() { public void parse(final PsiBuilder builder) { ReferenceParser.parseTypeWithEllipsis(builder, incomplete, false); } @@ -63,7 +67,7 @@ public class ReferenceParserTest extends JavaParsingTestCase { } private void doTypeParamsParserTest(final String text) { - doParserTest(text, new Parser() { + doParserTest(text, new TestParser() { public void parse(final PsiBuilder builder) { ReferenceParser.parseTypeParameters(builder); } diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java index 489fc88c37ce..ea0e0c77aa8f 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/StatementParserTest.java @@ -133,7 +133,7 @@ public class StatementParserTest extends JavaParsingTestCase { public void testWhileIncomplete5() { doParserTest("{ while() foo(); }"); } private void doParserTest(final String text) { - doParserTest(text, new Parser() { + doParserTest(text, new TestParser() { public void parse(final PsiBuilder builder) { StatementParser.parseCodeBlockDeep(builder, true); } From 0ac2793995f384ddfc6307b5cae4c732b048240c Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Fri, 30 Jul 2010 21:15:07 +0400 Subject: [PATCH 11/92] fix @NotNull instrumentation for enum constructor parameters with ASM 3.3 (IDEA-56943); move NotNullVerifyingInstrumenterTest to java-tests --- .../NotNullVerifyingInstrumenter.java | 19 ++- java/java-tests/java-tests.iml | 3 + .../notNullVerification/EnumConstructor.java | 11 ++ .../notNullVerification/MultipleReturns.java | 9 ++ .../notNullVerification/SimpleReturn.java | 7 ++ .../notNullVerification/annotations.jar | Bin 0 -> 10899 bytes .../NotNullVerifyingInstrumenterTest.java | 119 ++++++++++++++++++ 7 files changed, 156 insertions(+), 12 deletions(-) create mode 100644 java/java-tests/testData/compiler/notNullVerification/EnumConstructor.java create mode 100644 java/java-tests/testData/compiler/notNullVerification/MultipleReturns.java create mode 100644 java/java-tests/testData/compiler/notNullVerification/SimpleReturn.java create mode 100644 java/java-tests/testData/compiler/notNullVerification/annotations.jar create mode 100644 java/java-tests/testSrc/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java diff --git a/java/compiler/notNull/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java b/java/compiler/notNull/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java index d6bcc68bbe76..7990ab4d73f1 100644 --- a/java/compiler/notNull/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java +++ b/java/compiler/notNull/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java @@ -93,15 +93,13 @@ public class NotNullVerifyingInstrumenter extends ClassAdapter implements Opcode av = mv.visitParameterAnnotation(parameter, anno, visible); - if (isReferenceType(args[parameter])) { - if (anno.equals(NOT_NULL_ANNO)) { - myNotNullParams.add(new Integer(parameter)); - } - else if (anno.equals("Ljava/lang/Synthetic;")) { - // See asm r1278 for what we do this, - // http://forge.objectweb.org/tracker/index.php?func=detail&aid=307392&group_id=23&atid=100023 - mySyntheticCount++; - } + if (isReferenceType(args[parameter]) && anno.equals(NOT_NULL_ANNO)) { + myNotNullParams.add(new Integer(parameter)); + } + else if (anno.equals("Ljava/lang/Synthetic;")) { + // See asm r1278 for what we do this, + // http://forge.objectweb.org/tracker/index.php?func=detail&aid=307392&group_id=23&atid=100023 + mySyntheticCount++; } return av; } @@ -197,9 +195,6 @@ public class NotNullVerifyingInstrumenter extends ClassAdapter implements Opcode private int getStartParameterIndex(final String name) { int result = 0; if (CONSTRUCTOR_NAME.equals(name)) { - if (mySuperName.equals(ENUM_CLASS_NAME)) { - result += 2; - } if (myIsNotStaticInner) { result += 1; } diff --git a/java/java-tests/java-tests.iml b/java/java-tests/java-tests.iml index d18da953d9a7..643d97843f30 100644 --- a/java/java-tests/java-tests.iml +++ b/java/java-tests/java-tests.iml @@ -15,6 +15,9 @@ + + + diff --git a/java/java-tests/testData/compiler/notNullVerification/EnumConstructor.java b/java/java-tests/testData/compiler/notNullVerification/EnumConstructor.java new file mode 100644 index 000000000000..0c78423970c1 --- /dev/null +++ b/java/java-tests/testData/compiler/notNullVerification/EnumConstructor.java @@ -0,0 +1,11 @@ +import org.jetbrains.annotations.NotNull; + +public enum EnumConstructor { + Value("label"); + + private final String label; + + EnumConstructor(@NotNull String label) { + this.label = label; + } +} diff --git a/java/java-tests/testData/compiler/notNullVerification/MultipleReturns.java b/java/java-tests/testData/compiler/notNullVerification/MultipleReturns.java new file mode 100644 index 000000000000..61446a6f654b --- /dev/null +++ b/java/java-tests/testData/compiler/notNullVerification/MultipleReturns.java @@ -0,0 +1,9 @@ +import org.jetbrains.annotations.NotNull; + +public class MultipleReturns { + @NotNull public Object test(int i) { + if (i == 0) return null; + if (i == 1) return null; + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/compiler/notNullVerification/SimpleReturn.java b/java/java-tests/testData/compiler/notNullVerification/SimpleReturn.java new file mode 100644 index 000000000000..1a85ad9407de --- /dev/null +++ b/java/java-tests/testData/compiler/notNullVerification/SimpleReturn.java @@ -0,0 +1,7 @@ +import org.jetbrains.annotations.NotNull; + +public class SimpleReturn { + @NotNull public Object test() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/compiler/notNullVerification/annotations.jar b/java/java-tests/testData/compiler/notNullVerification/annotations.jar new file mode 100644 index 0000000000000000000000000000000000000000..45860d829e912615a8c132ef86258ae97411fc91 GIT binary patch literal 10899 zcmeHNO>7&-6&})(C{bc0+45f;w`19|CEFww8%PRUwzH-vn=wTaBrV&uU1LR#q_wGC zc6Vu+Z5kjzfu0I9C{Q3h^pyTx8syLxD2f6_e-WgIwm{Ki0<`EUrvg2+MbPibXkgLatq1#-(sxL zAw34tS6>(3UI8hk38W6`qLj-lq)X+*;=-b=Dl5V;<4Zy}6iq$HpH0lhqLS*$k?L|~ zO>pF)@cG2Ev1m#cvMH+Z%T3VFCT8QyXU_2HQbXm7N=4U%9mbNRn3%542BWV)>Eyz z_l3Ev%Ssn*^HO&^Pj902Z-eCf?X5qapARucDbZ8DOjW3+vZ4q*QK`v>@z(o4`57ub z%VH50jj-Ve8ws&!No(j8v7ppMh>g0c+?OB9A$F+P0FQN%Q6DOXvRo6AGXNoWB1_ZL z8GLI*yQG&*Q|RjX5bJ*^*BWTyxjXeQ66Y6@oW@F~teao*EhDQHYTmva4_fe z)JWzuHCHphVxX>z73EQgjXS-c7b|k3hG9Y{2aLvwRo(@RSVwk}D#Q-9V6o%_3ht8J z5g}O=Cc#l7G#u|iQqXEjr3u3RR8}gLLhP`Ag|e)#3KMjb{^jYKSVyDfW?fL%q2sRR zv3B|wGU+V-hYC_rLT0)cVozoBDJgqzNh)Tf%h@#6f|SeU%ThU$&)qBEDj?_h_NI5| z0Nm9V0s#AIHUO#t3w{iFgUAcBA>{2cSkzNEyk#NkjG#`K#TY53B;H2Bvrn)wHqLg_ z>uuIA*d8`PkG=HRN00sVIM5CG*Dl_E=O*Az=@hsHk4eE+03O5x?)xHae}o;_A>yZd z=$|%fY4VF=HT|gWQFtS50soI-J+-YV+&QX4U8rs=)jk8Twm}^RriWVq9kBsQh_$e4 z0Tgg0%h4MMslp+s$=6OST8@m zl`KzRI(PeXQ+HpQntBPZQ%HBJr|uk^xkLr=qo?@n>FK$qkH-nveK2(Ae(~T zyOEhMUmaxdJcbIzbShJVp&4W+prBIu+)}z|e@{a>m(w@PgKQ?mMheADu6%DHUtE-s zgBcx|Q&h#g2)|`|W(lMztqLb=G^?m0*H~W`dRd0gMqSq`a&1Z075Z*v_L*yn5n^Y0 zC{Y*OTO$s2+T|T=S`j1c06Q3Bv)jg7i?J{4q7M2|?p*+|igx+TB}hfD_A&^ueMGu> zCBT&c-XSkVSEoFmlWR+Qq;if)3D5B)5*@0efA-E%0)2h zM=<6h822OC?IPIYM=;?c*t?nF0Ndvx*uR+onwBOUoubW5691IbfAYsH^rqFZLwJ%K z!`OcI44$OjLBI|(+GW8#$1}`$w<3S}-LL)OHFz16PJ$p9OtyLV=UzaMUX)*w(WLP z29_~-+g`^cCM>sP43sg;F@YB?;7Y?M87H4)oP6kUteX9(OzZssQ5x)EH^4K0{^~b= z0}4trNWtAMHI@ypkAf}yV1x}}JKxih?o!xMo0*N?quXeA=cC>B{;WMFItz8!JsRg8 zq!RytTA*~aH5yy&Wow3KontF85n+3=t?*c;L2P;xy%*^bmuKMGvd(Lj2JIh36&7zB z+uD!QLhiHow2^|8x+Y!0?)EUjV9NpDIRI$N-BFd#U~xq-&x2SSL&9?X$t(=rW&?tD zs`MD8#~3}}_{V6G#n_&1*nIhozoq{T$WS^S3>zy1vR&j1+Yysbz~dJi*#sUQ03K`D zpIH2S>z~Kpn}ml!=|m6wn7Kx+<^j+FTkjy8(NhI);bC-X=8>FcxAKuGt&fA(aU=*{ z)_Cn4Uu^^Bvi@Pq)urGB9kC>!uqZsSIW%EM(mbJwZZ-Au2Y>m~F+hycbTG_-G1{bk zxHyXul&xky0h%l5I~f49)_^_fNXx&S{1;3uncGf)*59lbbgeFQvw2N4Jz#?;wL8N0 zz^g)FTo;CQM0B~KRwbA87i(BRhf!n|ueVuPAi@eCYm|+5tE^vNcv$6s{2gyV zq3w7(P@wbb)En=P?nOw8Qm~Va+W-&fh;z+cugx8a$74~>U*;*T-qe-VHItv2ot=&2 z@jQP~n3t`yac(5^gqE-Waz*78ZM|MoWVIsrhGMQ+Ew%XA^6*ONRmep1n!EuAo6D=ZK$)qL=o`9X;!yZBH?$RVL)L}e>#Abl zuz0zFJ*#W~8|^CTW2#D-o#RE5S>R9k~s5;XjRx5Zci1$`TJQNjI$ z3OZLh%}cZUKi(*7Gt{Y5(-c3 z+WN{43^#sb>^+J8JGXM|3p`F$zbiacfqt*QLI<;*s;&z??#BHt+gR1D#(#lV!fj9u zx_o2*NZh~Tm1=Zug9KHj%Q*Irs*b_JbX6rhbT#dFImfC0f^U7-9Ch*XcAU<{9bPZB zcsaJ(-4DOZJ5Js2ZK}JK41Sk++ETp!$~!&#<6L*KJvXVzCRf6#n%-y_OkWHTOGDP#p)z+oh9Ig2Srq^6IU-oZzRrP_;*|| z3}EPr%XxWvqcaZAvxuz+@pQ#%yiC1&afj(Q4+b)AZ<&3&@7;GfT>Eh3wY8}3osutO vuOIF(Uc~9y){NU8)=zs39CYksZ{zLFqxAJ3|K>(# literal 0 HcmV?d00001 diff --git a/java/java-tests/testSrc/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java b/java/java-tests/testSrc/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java new file mode 100644 index 000000000000..12f5e0aa4f84 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java @@ -0,0 +1,119 @@ +package com.intellij.compiler.notNullVerification; + +import com.intellij.JavaTestUtil; +import com.intellij.compiler.PsiClassWriter; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.testFramework.IdeaTestCase; +import com.intellij.testFramework.UsefulTestCase; +import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; +import com.intellij.testFramework.fixtures.JavaTestFixtureFactory; +import com.intellij.testFramework.fixtures.TestFixtureBuilder; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * @author yole + */ +public class NotNullVerifyingInstrumenterTest extends UsefulTestCase { + private IdeaProjectTestFixture myFixture; + + @SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors"}) + public NotNullVerifyingInstrumenterTest() { + IdeaTestCase.initPlatformPrefix(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + final JavaTestFixtureFactory fixtureFactory = JavaTestFixtureFactory.getFixtureFactory(); + final TestFixtureBuilder testFixtureBuilder = fixtureFactory.createLightFixtureBuilder(); + myFixture = testFixtureBuilder.getFixture(); + myFixture.setUp(); + } + + @Override + protected void tearDown() throws Exception { + myFixture.tearDown(); + super.tearDown(); + } + + public void testSimpleReturn() throws Exception { + Class testClass = prepareTest(); + Object instance = testClass.newInstance(); + Method method = testClass.getMethod("test"); + verifyCallThrowsException("@NotNull method SimpleReturn.test must not return null", instance, method); + } + + public void testMultipleReturns() throws Exception { + Class testClass = prepareTest(); + Object instance = testClass.newInstance(); + Method method = testClass.getMethod("test", int.class); + verifyCallThrowsException("@NotNull method MultipleReturns.test must not return null", instance, method, 1); + } + + public void testEnumConstructor() throws Exception { + Class testClass = prepareTest(); + Object field = testClass.getField("Value"); + assertNotNull(field); + } + + private static void verifyCallThrowsException(final String expectedError, final Object instance, final Method method, final Object... args) throws IllegalAccessException { + String exceptionText = null; + try { + method.invoke(instance, args); + } + catch(InvocationTargetException ex) { + Throwable cause = ex.getCause(); + if (cause instanceof IllegalStateException) { + exceptionText = cause.getMessage(); + } + } + assertEquals(expectedError, exceptionText); + } + + private Class prepareTest() throws IOException { + String base = JavaTestUtil.getJavaTestDataPath() + "/compiler/notNullVerification/"; + String path = base + getTestName(false); + String javaPath = path + ".java"; + String classPath = path + ".class"; + try { + com.sun.tools.javac.Main.compile(new String[] { "-classpath", base+"annotations.jar", javaPath } ); + FileInputStream stream = new FileInputStream(classPath); + byte[] content = FileUtil.adaptiveLoadBytes(stream); + stream.close(); + + ClassReader reader = new ClassReader(content, 0, content.length); + ClassWriter writer = new PsiClassWriter(myFixture.getProject(), false); + final NotNullVerifyingInstrumenter instrumenter = new NotNullVerifyingInstrumenter(writer); + reader.accept(instrumenter, 0); + assertTrue(instrumenter.isModification()); + + MyClassLoader classLoader = new MyClassLoader(getClass().getClassLoader()); + byte[] instrumented = writer.toByteArray(); + return classLoader.doDefineClass(getTestName(false), instrumented); + } + finally { + FileUtil.delete(new File(classPath)); + } + } + + private static class MyClassLoader extends ClassLoader { + public MyClassLoader(ClassLoader parent) { + super(parent); + } + + public Class doDefineClass(String name, byte[] data) { + return defineClass(name, data, 0, data.length); + } + + public Class loadClass(String name) throws ClassNotFoundException { + return super.loadClass(name); + } + } +} From 380447cec2bc1350e470dc0353798839755155e7 Mon Sep 17 00:00:00 2001 From: Gregory Shrago Date: Fri, 30 Jul 2010 22:19:46 +0400 Subject: [PATCH 12/92] fix missing DocumentationProvider functionality --- .../lang/documentation/CompositeDocumentationProvider.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/lang-api/src/com/intellij/lang/documentation/CompositeDocumentationProvider.java b/platform/lang-api/src/com/intellij/lang/documentation/CompositeDocumentationProvider.java index 0cdf910f30da..74fcefb89c48 100644 --- a/platform/lang-api/src/com/intellij/lang/documentation/CompositeDocumentationProvider.java +++ b/platform/lang-api/src/com/intellij/lang/documentation/CompositeDocumentationProvider.java @@ -38,7 +38,9 @@ public class CompositeDocumentationProvider implements DocumentationProvider, Ex list.add(provider); } } - return list.size() == 1 ? list.get(0) : new CompositeDocumentationProvider(Collections.unmodifiableList(list)); + // CompositeDocumentationProvider should be returned anyway because it + // handles DocumentationProvider.EP as well as providers from the list + return new CompositeDocumentationProvider(Collections.unmodifiableList(list)); } private CompositeDocumentationProvider(List providers) { From 2bee573cdd8e9022dd72a8fa9ea1365ab8a54553 Mon Sep 17 00:00:00 2001 From: Gregory Shrago Date: Fri, 30 Jul 2010 22:49:17 +0400 Subject: [PATCH 13/92] more JDBC console UI tweaks --- .../console/LanguageConsoleImpl.java | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java index ef73b1c4530f..7436f0969893 100644 --- a/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java +++ b/platform/lang-impl/src/com/intellij/execution/console/LanguageConsoleImpl.java @@ -28,7 +28,6 @@ import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.actions.EditorActionUtil; import com.intellij.openapi.editor.colors.EditorColors; import com.intellij.openapi.editor.colors.EditorColorsManager; -import com.intellij.openapi.editor.colors.EditorColorsScheme; import com.intellij.openapi.editor.event.*; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.openapi.editor.ex.RangeHighlighterEx; @@ -99,7 +98,9 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider { private final AtomicBoolean myForceScrollToEnd = new AtomicBoolean(false); private final MergingUpdateQueue myUpdateQueue; private Runnable myUiUpdateRunnable; + private Editor myFullEditor; + private ActionGroup myFullEditorActions; public LanguageConsoleImpl(final Project project, String title, final Language language) { myProject = project; @@ -154,10 +155,16 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider { myPanel.add(myHistoryViewer.getComponent(), BorderLayout.CENTER); myFullEditor = fileManager.openTextEditor(new OpenFileDescriptor(getProject(), virtualFile, 0), true); assert myFullEditor != null; + configureFullEditor(); fileManager.getCurrentWindow().setFilePinned(virtualFile, true); } } + public void setFullEditorActions(ActionGroup actionGroup) { + myFullEditorActions = actionGroup; + configureFullEditor(); + } + private void setupComponents() { setupEditorDefault(myConsoleEditor); setupEditorDefault(myHistoryViewer); @@ -543,11 +550,20 @@ public class LanguageConsoleImpl implements Disposable, TypeSafeDataProvider { } editorManager.closeFile(file); myFullEditor = editorManager.openTextEditor(new OpenFileDescriptor(getProject(), newVFile, offset), focusEditor); - ((FileEditorManagerEx)editorManager).getCurrentWindow().setFilePinned(newVFile, true); + configureFullEditor(); + ((FileEditorManagerEx)editorManager).getCurrentWindow().setFilePinned(newVFile, true); } } } + private void configureFullEditor() { + if (myFullEditor == null || myFullEditorActions == null) return; + final JPanel header = new JPanel(new BorderLayout()); + header.add(ActionManager.getInstance().createActionToolbar(ActionPlaces.UNKNOWN, myFullEditorActions, true).getComponent(), BorderLayout.EAST); + myFullEditor.setHeaderComponent(header); + myFullEditor.getSettings().setLineMarkerAreaShown(false); + } + public void setInputText(final String query) { ApplicationManager.getApplication().runWriteAction(new Runnable() { public void run() { From 684f3000832129485a10eb18efe35128b7e445f2 Mon Sep 17 00:00:00 2001 From: Gregory Shrago Date: Fri, 30 Jul 2010 23:09:49 +0400 Subject: [PATCH 14/92] revert previous changes & fix CCE in CssDocumentationProvider --- .../src/com/intellij/lang/LanguageDocumentation.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/platform/lang-api/src/com/intellij/lang/LanguageDocumentation.java b/platform/lang-api/src/com/intellij/lang/LanguageDocumentation.java index 443078992067..ddd0c5cca6f0 100644 --- a/platform/lang-api/src/com/intellij/lang/LanguageDocumentation.java +++ b/platform/lang-api/src/com/intellij/lang/LanguageDocumentation.java @@ -22,6 +22,8 @@ package com.intellij.lang; import com.intellij.lang.documentation.CompositeDocumentationProvider; import com.intellij.lang.documentation.DocumentationProvider; +import java.util.List; + public class LanguageDocumentation extends LanguageExtension { public static final LanguageDocumentation INSTANCE = new LanguageDocumentation(); @@ -30,6 +32,10 @@ public class LanguageDocumentation extends LanguageExtension providers = allForLanguage(l); + if (providers.size() < 2) { + return super.forLanguage(l); + } + return CompositeDocumentationProvider.wrapProviders(providers); } } \ No newline at end of file From 6f3ef751d0373f3238589ebdb6f88b422f6bd9eb Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 30 Jul 2010 14:32:18 +0100 Subject: [PATCH 15/92] JavaFindUsagesOptions extracted from platform's FindUsagesOptions --- .../ChangeMethodSignatureFromUsageFix.java | 6 +- .../findUsages/FindClassUsagesDialog.java | 14 +- .../findUsages/FindMethodUsagesDialog.java | 8 +- .../findUsages/FindPackageUsagesDialog.java | 6 +- .../findUsages/FindThrowUsagesDialog.java | 2 +- .../findUsages/FindVariableUsagesDialog.java | 2 +- .../find/findUsages/JavaFindUsagesDialog.java | 12 +- .../findUsages/JavaFindUsagesHandler.java | 44 +++++-- .../JavaFindUsagesHandlerFactory.java | 16 +-- .../findUsages/JavaFindUsagesOptions.java | 123 ++++++++++++++++++ .../findUsages/AbstractFindUsagesDialog.java | 2 +- .../find/findUsages/FindUsagesHandler.java | 11 +- .../find/findUsages/FindUsagesManager.java | 39 +----- .../find/findUsages/FindUsagesOptions.java | 62 ++------- 14 files changed, 203 insertions(+), 144 deletions(-) create mode 100644 java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesOptions.java 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 e6dffbec02b8..42cac1dc6ea4 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 @@ -32,7 +32,7 @@ import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.find.FindManager; import com.intellij.find.findUsages.FindUsagesHandler; import com.intellij.find.findUsages.FindUsagesManager; -import com.intellij.find.findUsages.FindUsagesOptions; +import com.intellij.find.findUsages.JavaFindUsagesOptions; import com.intellij.find.impl.FindManagerImpl; import com.intellij.ide.DataManager; import com.intellij.ide.util.SuperMethodWarningUtil; @@ -144,12 +144,12 @@ public class ChangeMethodSignatureFromUsageFix implements IntentionAction { final FindUsagesHandler handler = findUsagesManager.getFindUsagesHandler(method, false); if (handler == null) return; //on failure or cancel (e.g. cancel of super methods dialog) - final FindUsagesOptions options = new FindUsagesOptions(project, editor == null? null : DataManager.getInstance().getDataContext(editor.getComponent())); + final JavaFindUsagesOptions options = new JavaFindUsagesOptions(project, editor == null? null : DataManager.getInstance().getDataContext(editor.getComponent())); options.isImplementingMethods = true; options.isMethodsUsages = true; options.isOverridingMethods = true; options.isUsages = true; - options.isSearchForTextOccurences = false; + options.isSearchForTextOccurrences = false; final int[] usagesFound = new int[1]; Runnable runnable = new Runnable() { public void run() { diff --git a/java/java-impl/src/com/intellij/find/findUsages/FindClassUsagesDialog.java b/java/java-impl/src/com/intellij/find/findUsages/FindClassUsagesDialog.java index 9b72fdd318e2..4904eda03d39 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/FindClassUsagesDialog.java +++ b/java/java-impl/src/com/intellij/find/findUsages/FindClassUsagesDialog.java @@ -43,7 +43,7 @@ public class FindClassUsagesDialog extends JavaFindUsagesDialog { return myCbUsages; } - public void calcFindUsagesOptions(FindUsagesOptions options) { + public void calcFindUsagesOptions(JavaFindUsagesOptions options) { super.calcFindUsagesOptions(options); if (isToChange(myCbUsages)){ @@ -75,19 +75,19 @@ public class FindClassUsagesDialog extends JavaFindUsagesDialog { findWhatPanel.setBorder(IdeBorderFactory.createTitledBorder(FindBundle.message("find.what.group"))); findWhatPanel.setLayout(new BoxLayout(findWhatPanel, BoxLayout.Y_AXIS)); - myCbUsages = addCheckboxToPanel(FindBundle.message("find.what.usages.checkbox"), myFindUsagesOptions.isUsages, findWhatPanel, true); + myCbUsages = addCheckboxToPanel(FindBundle.message("find.what.usages.checkbox"), getFindUsagesOptions().isUsages, findWhatPanel, true); PsiClass psiClass = (PsiClass)getPsiElement(); - myCbMethodsUsages = addCheckboxToPanel(FindBundle.message("find.what.methods.usages.checkbox"), myFindUsagesOptions.isMethodsUsages, findWhatPanel, true); + myCbMethodsUsages = addCheckboxToPanel(FindBundle.message("find.what.methods.usages.checkbox"), getFindUsagesOptions().isMethodsUsages, findWhatPanel, true); if (!psiClass.isAnnotationType()) { - myCbFieldsUsages = addCheckboxToPanel(FindBundle.message("find.what.fields.usages.checkbox"), myFindUsagesOptions.isFieldsUsages, findWhatPanel, true); + myCbFieldsUsages = addCheckboxToPanel(FindBundle.message("find.what.fields.usages.checkbox"), getFindUsagesOptions().isFieldsUsages, findWhatPanel, true); if (psiClass.isInterface()){ - myCbImplementingClasses = addCheckboxToPanel(FindBundle.message("find.what.implementing.classes.checkbox"), myFindUsagesOptions.isImplementingClasses, findWhatPanel, true); - myCbDerivedInterfaces = addCheckboxToPanel(FindBundle.message("find.what.derived.interfaces.checkbox"), myFindUsagesOptions.isDerivedInterfaces, findWhatPanel, true); + myCbImplementingClasses = addCheckboxToPanel(FindBundle.message("find.what.implementing.classes.checkbox"), getFindUsagesOptions().isImplementingClasses, findWhatPanel, true); + myCbDerivedInterfaces = addCheckboxToPanel(FindBundle.message("find.what.derived.interfaces.checkbox"), getFindUsagesOptions().isDerivedInterfaces, findWhatPanel, true); } else if (!psiClass.hasModifierProperty(PsiModifier.FINAL)){ - myCbDerivedClasses = addCheckboxToPanel(FindBundle.message("find.what.derived.classes.checkbox"), myFindUsagesOptions.isDerivedClasses, findWhatPanel, true); + myCbDerivedClasses = addCheckboxToPanel(FindBundle.message("find.what.derived.classes.checkbox"), getFindUsagesOptions().isDerivedClasses, findWhatPanel, true); } } return findWhatPanel; diff --git a/java/java-impl/src/com/intellij/find/findUsages/FindMethodUsagesDialog.java b/java/java-impl/src/com/intellij/find/findUsages/FindMethodUsagesDialog.java index 03a45a9f7d7c..8ee9356d041f 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/FindMethodUsagesDialog.java +++ b/java/java-impl/src/com/intellij/find/findUsages/FindMethodUsagesDialog.java @@ -41,7 +41,7 @@ public class FindMethodUsagesDialog extends JavaFindUsagesDialog { return myHasFindWhatPanel ? myCbUsages : null; } - public void calcFindUsagesOptions(FindUsagesOptions options) { + public void calcFindUsagesOptions(JavaFindUsagesOptions options) { super.calcFindUsagesOptions(options); options.isUsages = isSelected(myCbUsages) || !myHasFindWhatPanel; @@ -59,7 +59,7 @@ public class FindMethodUsagesDialog extends JavaFindUsagesDialog { findWhatPanel.setBorder(IdeBorderFactory.createTitledBorder(FindBundle.message("find.what.group"))); findWhatPanel.setLayout(new BoxLayout(findWhatPanel, BoxLayout.Y_AXIS)); - myCbUsages = addCheckboxToPanel(FindBundle.message("find.what.usages.checkbox"), myFindUsagesOptions.isUsages, findWhatPanel, true); + myCbUsages = addCheckboxToPanel(FindBundle.message("find.what.usages.checkbox"), getFindUsagesOptions().isUsages, findWhatPanel, true); PsiMethod method = (PsiMethod) getPsiElement(); PsiClass aClass = method.getContainingClass(); @@ -71,9 +71,9 @@ public class FindMethodUsagesDialog extends JavaFindUsagesDialog { !(aClass instanceof PsiAnonymousClass) && !aClass.hasModifierProperty(PsiModifier.FINAL)) { if (method.hasModifierProperty(PsiModifier.ABSTRACT)) { - myCbImplementingMethods = addCheckboxToPanel(FindBundle.message("find.what.implementing.methods.checkbox"), myFindUsagesOptions.isImplementingMethods, findWhatPanel, true); + myCbImplementingMethods = addCheckboxToPanel(FindBundle.message("find.what.implementing.methods.checkbox"), getFindUsagesOptions().isImplementingMethods, findWhatPanel, true); } else { - myCbOverridingMethods = addCheckboxToPanel(FindBundle.message("find.what.overriding.methods.checkbox"), myFindUsagesOptions.isOverridingMethods, findWhatPanel, true); + myCbOverridingMethods = addCheckboxToPanel(FindBundle.message("find.what.overriding.methods.checkbox"), getFindUsagesOptions().isOverridingMethods, findWhatPanel, true); } } else { myHasFindWhatPanel = false; diff --git a/java/java-impl/src/com/intellij/find/findUsages/FindPackageUsagesDialog.java b/java/java-impl/src/com/intellij/find/findUsages/FindPackageUsagesDialog.java index eb8bc8fac914..88e37433a2e7 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/FindPackageUsagesDialog.java +++ b/java/java-impl/src/com/intellij/find/findUsages/FindPackageUsagesDialog.java @@ -39,7 +39,7 @@ public class FindPackageUsagesDialog extends JavaFindUsagesDialog { return myCbUsages; } - public void calcFindUsagesOptions(FindUsagesOptions options) { + public void calcFindUsagesOptions(JavaFindUsagesOptions options) { super.calcFindUsagesOptions(options); options.isUsages = isSelected(myCbUsages); @@ -55,8 +55,8 @@ public class FindPackageUsagesDialog extends JavaFindUsagesDialog { findWhatPanel.setBorder(IdeBorderFactory.createTitledBorder(FindBundle.message("find.what.group"))); findWhatPanel.setLayout(new BoxLayout(findWhatPanel, BoxLayout.Y_AXIS)); - myCbUsages = addCheckboxToPanel(FindBundle.message("find.what.usages.checkbox"), myFindUsagesOptions.isUsages, findWhatPanel, true); - myCbClassesUsages = addCheckboxToPanel(FindBundle.message("find.what.usages.of.classes.and.interfaces"), myFindUsagesOptions.isClassesUsages, findWhatPanel, true); + myCbUsages = addCheckboxToPanel(FindBundle.message("find.what.usages.checkbox"), getFindUsagesOptions().isUsages, findWhatPanel, true); + myCbClassesUsages = addCheckboxToPanel(FindBundle.message("find.what.usages.of.classes.and.interfaces"), getFindUsagesOptions().isClassesUsages, findWhatPanel, true); return findWhatPanel; } diff --git a/java/java-impl/src/com/intellij/find/findUsages/FindThrowUsagesDialog.java b/java/java-impl/src/com/intellij/find/findUsages/FindThrowUsagesDialog.java index 06ca0877bd07..7ca78038b41c 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/FindThrowUsagesDialog.java +++ b/java/java-impl/src/com/intellij/find/findUsages/FindThrowUsagesDialog.java @@ -62,7 +62,7 @@ public class FindThrowUsagesDialog extends JavaFindUsagesDialog { return panel; } - public void calcFindUsagesOptions(final FindUsagesOptions options) { + public void calcFindUsagesOptions(final JavaFindUsagesOptions options) { super.calcFindUsagesOptions(options); options.isUsages = isSelected(myCbUsages) || !myHasFindWhatPanel; options.isThrowUsages = true; diff --git a/java/java-impl/src/com/intellij/find/findUsages/FindVariableUsagesDialog.java b/java/java-impl/src/com/intellij/find/findUsages/FindVariableUsagesDialog.java index bb694bc87302..26ceefe818d7 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/FindVariableUsagesDialog.java +++ b/java/java-impl/src/com/intellij/find/findUsages/FindVariableUsagesDialog.java @@ -32,7 +32,7 @@ public class FindVariableUsagesDialog extends JavaFindUsagesDialog { return myCbToSkipResultsWhenOneUsage; } - public void calcFindUsagesOptions(FindUsagesOptions options) { + public void calcFindUsagesOptions(JavaFindUsagesOptions options) { super.calcFindUsagesOptions(options); options.isReadAccess = true; diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesDialog.java b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesDialog.java index 07f23744c02f..8934bf3865f9 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesDialog.java +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesDialog.java @@ -48,12 +48,16 @@ public abstract class JavaFindUsagesDialog extends AbstractFindUsagesDialog { return FindUsagesUtil.isSearchForTextOccurencesAvailable(element, isSingleFile, handler); } - public void calcFindUsagesOptions(FindUsagesOptions options) { - super.calcFindUsagesOptions(options); + public void calcFindUsagesOptions(JavaFindUsagesOptions options) { options.isIncludeOverloadUsages = myIncludeOverloadedMethodsAvailable && isToChange(myCbIncludeOverloadedMethods) && myCbIncludeOverloadedMethods.isSelected(); } + public void calcFindUsagesOptions(FindUsagesOptions options) { + super.calcFindUsagesOptions(options); + calcFindUsagesOptions((JavaFindUsagesOptions)options); + } + protected void doOKAction() { if (shouldDoOkAction()) { if (myIncludeOverloadedMethodsAvailable) { @@ -95,4 +99,8 @@ public abstract class JavaFindUsagesDialog extends AbstractFindUsagesDialog { protected void doHelpAction() { HelpManager.getInstance().invokeHelp(FindUsagesManager.getHelpID(myPsiElement)); } + + protected JavaFindUsagesOptions getFindUsagesOptions() { + return (JavaFindUsagesOptions)myFindUsagesOptions; + } } diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java index bbc0535caf89..7c1b2b733cc8 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java @@ -23,6 +23,7 @@ import com.intellij.openapi.application.ReadActionProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Comparing; @@ -94,6 +95,20 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ myFindVariableOptions = findVariableOptions; } + @SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass"}) + public static JavaFindUsagesOptions createFindUsagesOptions(final Project project) { + JavaFindUsagesOptions findUsagesOptions = new JavaFindUsagesOptions(project, null); + findUsagesOptions.isUsages = true; + findUsagesOptions.isIncludeOverloadUsages = false; + findUsagesOptions.isIncludeSubpackages = true; + findUsagesOptions.isReadAccess = true; + findUsagesOptions.isWriteAccess = true; + findUsagesOptions.isCheckDeepInheritance = true; + findUsagesOptions.isSearchForTextOccurrences = true; + return findUsagesOptions; + } + + @NotNull public AbstractFindUsagesDialog getFindUsagesDialog(boolean isSingleFile, boolean toShowInNewTab, boolean mustOpenInNewTab) { PsiElement element = getPsiElement(); @@ -263,7 +278,8 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } @Override - public void processElementUsages(@NotNull final PsiElement element, @NotNull final Processor processor, @NotNull final FindUsagesOptions options) { + public void processElementUsages(@NotNull final PsiElement element, @NotNull final Processor processor, @NotNull final FindUsagesOptions _options) { + final JavaFindUsagesOptions options = (JavaFindUsagesOptions) _options; if (element instanceof PsiVariable){ if (options.isReadAccess || options.isWriteAccess){ if (options.isReadAccess && options.isWriteAccess){ @@ -348,13 +364,13 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } } - if (!ThrowSearchUtil.isSearchable(element) && options.isSearchForTextOccurences && options.searchScope instanceof GlobalSearchScope) { + if (!ThrowSearchUtil.isSearchable(element) && options.isSearchForTextOccurrences && options.searchScope instanceof GlobalSearchScope) { // todo add to fastTrack processUsagesInText(element, processor, (GlobalSearchScope)options.searchScope); } } - private static void processOverridingMethods(PsiMethod psiMethod, final Processor processor, final FindUsagesOptions options) { + private static void processOverridingMethods(PsiMethod psiMethod, final Processor processor, final JavaFindUsagesOptions options) { OverridingMethodsSearch.search(psiMethod, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter( new PsiElementProcessor() { public boolean execute(PsiMethod element) { @@ -365,7 +381,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } - private static void addClassesUsages(PsiPackage aPackage, final Processor results, final FindUsagesOptions options) { + private static void addClassesUsages(PsiPackage aPackage, final Processor results, final JavaFindUsagesOptions options) { final HashSet filesSet = new HashSet(); final ArrayList files = new ArrayList(); ReferencesSearch.search(new ReferencesSearch.SearchParameters(aPackage, options.searchScope, false, options.fastTrack)).forEach(new ReadActionProcessor() { @@ -429,7 +445,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ }); } - private static void addMethodsUsages(final PsiClass aClass, final Processor results, final FindUsagesOptions options) { + private static void addMethodsUsages(final PsiClass aClass, final Processor results, final JavaFindUsagesOptions options) { if (options.isIncludeInherited) { final PsiManager manager = aClass.getManager(); PsiMethod[] methods = aClass.getAllMethods(); @@ -463,7 +479,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } } - private static void addFieldsUsages(final PsiClass aClass, final Processor results, final FindUsagesOptions options) { + private static void addFieldsUsages(final PsiClass aClass, final Processor results, final JavaFindUsagesOptions options) { if (options.isIncludeInherited) { final PsiManager manager = aClass.getManager(); PsiField[] fields = aClass.getAllFields(); @@ -528,7 +544,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ return null; } - private static void addInheritors(final PsiClass aClass, final Processor results, final FindUsagesOptions options) { + private static void addInheritors(final PsiClass aClass, final Processor results, final JavaFindUsagesOptions options) { ClassInheritorsSearch.search(aClass, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter( new PsiElementProcessor() { public boolean execute(PsiClass element) { @@ -539,7 +555,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ })); } - private static void addDerivedInterfaces(PsiClass anInterface, final Processor results, final FindUsagesOptions options) { + private static void addDerivedInterfaces(PsiClass anInterface, final Processor results, final JavaFindUsagesOptions options) { ClassInheritorsSearch.search(anInterface, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter( new PsiElementProcessor() { public boolean execute(PsiClass inheritor) { @@ -552,7 +568,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ })); } - private static void addImplementingClasses(PsiClass anInterface, final Processor results, final FindUsagesOptions options) { + private static void addImplementingClasses(PsiClass anInterface, final Processor results, final JavaFindUsagesOptions options) { ClassInheritorsSearch.search(anInterface, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter( new PsiElementProcessor() { public boolean execute(PsiClass inheritor) { @@ -577,13 +593,13 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ PsiClass usedClass = getFieldOrMethodAccessedClass((PsiReferenceExpression)refElement, methodClass); if (usedClass != null) { if (manager.areElementsEquivalent(usedClass, aClass) || usedClass.isInheritor(aClass, true)) { - addResult(results, refElement, options, element); + addResult(results, refElement, (JavaFindUsagesOptions)options, element); } } } } - public static void addElementUsages(final PsiElement element, final Processor result, final FindUsagesOptions options) { + public static void addElementUsages(final PsiElement element, final Processor result, final JavaFindUsagesOptions options) { final SearchScope searchScope = options.searchScope; if (element instanceof PsiMethod && ((PsiMethod)element).isConstructor()){ PsiMethod method = (PsiMethod)element; @@ -613,13 +629,13 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } } - public static void addResult(Processor total, PsiElement element, FindUsagesOptions options, PsiElement refElement) { + public static void addResult(Processor total, PsiElement element, JavaFindUsagesOptions options, PsiElement refElement) { if (filterUsage(element, options, refElement)){ total.process(new UsageInfo(element)); } } - public static boolean addResult(Processor results, PsiReference ref, FindUsagesOptions options, PsiElement refElement) { + public static boolean addResult(Processor results, PsiReference ref, JavaFindUsagesOptions options, PsiElement refElement) { if (filterUsage(ref.getElement(), options, refElement)){ TextRange rangeInElement = ref.getRangeInElement(); return results.process(new UsageInfo(ref.getElement(), rangeInElement.getStartOffset(), rangeInElement.getEndOffset(), false)); @@ -627,7 +643,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ return true; } - private static boolean filterUsage(PsiElement usage, FindUsagesOptions options, PsiElement refElement) { + private static boolean filterUsage(PsiElement usage, JavaFindUsagesOptions options, PsiElement refElement) { if (!(usage instanceof PsiJavaCodeReferenceElement)) { return true; } diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandlerFactory.java b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandlerFactory.java index f4b2294a3c58..540a46200b8c 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandlerFactory.java +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandlerFactory.java @@ -38,21 +38,21 @@ public class JavaFindUsagesHandlerFactory extends FindUsagesHandlerFactory { } public JavaFindUsagesHandlerFactory(Project project) { - final FindUsagesOptions findClassOptions = FindUsagesHandler.createFindUsagesOptions(project); - final FindUsagesOptions findMethodOptions = FindUsagesHandler.createFindUsagesOptions(project); + final JavaFindUsagesOptions findClassOptions = JavaFindUsagesHandler.createFindUsagesOptions(project); + final JavaFindUsagesOptions findMethodOptions = JavaFindUsagesHandler.createFindUsagesOptions(project); findMethodOptions.isCheckDeepInheritance = false; findMethodOptions.isIncludeSubpackages = false; - findMethodOptions.isSearchForTextOccurences = false; - final FindUsagesOptions findPackageOptions = FindUsagesHandler.createFindUsagesOptions(project); + findMethodOptions.isSearchForTextOccurrences = false; + final FindUsagesOptions findPackageOptions = JavaFindUsagesHandler.createFindUsagesOptions(project); - final FindUsagesOptions findThrowOptions = FindUsagesHandler.createFindUsagesOptions(project); - findThrowOptions.isSearchForTextOccurences = false; + final JavaFindUsagesOptions findThrowOptions = JavaFindUsagesHandler.createFindUsagesOptions(project); + findThrowOptions.isSearchForTextOccurrences = false; findThrowOptions.isThrowUsages = true; - final FindUsagesOptions findVariableOptions = FindUsagesHandler.createFindUsagesOptions(project); + final JavaFindUsagesOptions findVariableOptions = JavaFindUsagesHandler.createFindUsagesOptions(project); findVariableOptions.isCheckDeepInheritance = false; findVariableOptions.isIncludeSubpackages = false; - findVariableOptions.isSearchForTextOccurences = false; + findVariableOptions.isSearchForTextOccurrences = false; myFindClassOptions = findClassOptions; myFindMethodOptions = findMethodOptions; diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesOptions.java b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesOptions.java new file mode 100644 index 000000000000..723ab9ef8a1b --- /dev/null +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesOptions.java @@ -0,0 +1,123 @@ +package com.intellij.find.findUsages; + +import com.intellij.find.FindBundle; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; + +/** + * @author peter + */ +public class JavaFindUsagesOptions extends FindUsagesOptions { + public boolean isClassesUsages = false; + public boolean isMethodsUsages = false; + public boolean isFieldsUsages = false; + public boolean isDerivedClasses = false; + public boolean isImplementingClasses = false; + public boolean isDerivedInterfaces = false; + public boolean isOverridingMethods = false; + public boolean isImplementingMethods = false; + public boolean isIncludeSubpackages = true; + public boolean isSkipImportStatements = false; + public boolean isSkipPackageStatements = false; + public boolean isCheckDeepInheritance = true; + public boolean isIncludeInherited = false; + public boolean isReadAccess = false; + public boolean isWriteAccess = false; + public boolean isIncludeOverloadUsages = false; + public boolean isThrowUsages = false; + + public JavaFindUsagesOptions(@NotNull Project project, @Nullable DataContext dataContext) { + super(project, dataContext); + } + + public boolean equals(final Object o) { + if (this == o) return true; + if (!super.equals(this)) return false; + if (o == null || getClass() != o.getClass()) return false; + + final JavaFindUsagesOptions that = (JavaFindUsagesOptions)o; + + if (isCheckDeepInheritance != that.isCheckDeepInheritance) return false; + if (isClassesUsages != that.isClassesUsages) return false; + if (isDerivedClasses != that.isDerivedClasses) return false; + if (isDerivedInterfaces != that.isDerivedInterfaces) return false; + if (isFieldsUsages != that.isFieldsUsages) return false; + if (isImplementingClasses != that.isImplementingClasses) return false; + if (isImplementingMethods != that.isImplementingMethods) return false; + if (isIncludeInherited != that.isIncludeInherited) return false; + if (isIncludeOverloadUsages != that.isIncludeOverloadUsages) return false; + if (isIncludeSubpackages != that.isIncludeSubpackages) return false; + if (isMethodsUsages != that.isMethodsUsages) return false; + if (isOverridingMethods != that.isOverridingMethods) return false; + if (isReadAccess != that.isReadAccess) return false; + if (isSearchForTextOccurrences != that.isSearchForTextOccurrences) return false; + if (isSkipImportStatements != that.isSkipImportStatements) return false; + if (isSkipPackageStatements != that.isSkipPackageStatements) return false; + if (isThrowUsages != that.isThrowUsages) return false; + if (isUsages != that.isUsages) return false; + if (isWriteAccess != that.isWriteAccess) return false; + if (searchScope != null ? !searchScope.equals(that.searchScope) : that.searchScope != null) return false; + + return true; + } + + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (isClassesUsages ? 1 : 0); + result = 31 * result + (isMethodsUsages ? 1 : 0); + result = 31 * result + (isFieldsUsages ? 1 : 0); + result = 31 * result + (isDerivedClasses ? 1 : 0); + result = 31 * result + (isImplementingClasses ? 1 : 0); + result = 31 * result + (isDerivedInterfaces ? 1 : 0); + result = 31 * result + (isOverridingMethods ? 1 : 0); + result = 31 * result + (isImplementingMethods ? 1 : 0); + result = 31 * result + (isIncludeSubpackages ? 1 : 0); + result = 31 * result + (isSkipImportStatements ? 1 : 0); + result = 31 * result + (isSkipPackageStatements ? 1 : 0); + result = 31 * result + (isCheckDeepInheritance ? 1 : 0); + result = 31 * result + (isIncludeInherited ? 1 : 0); + result = 31 * result + (isReadAccess ? 1 : 0); + result = 31 * result + (isWriteAccess ? 1 : 0); + result = 31 * result + (isIncludeOverloadUsages ? 1 : 0); + result = 31 * result + (isThrowUsages ? 1 : 0); + return result; + } + + @Override + public String generateUsagesString() { + String suffix = " " + FindBundle.message("find.usages.panel.title.separator") + " "; + ArrayList strings = new ArrayList(); + if (this.isUsages || this.isClassesUsages || this.isMethodsUsages || this.isFieldsUsages) { + strings.add(FindBundle.message("find.usages.panel.title.usages")); + } + if (this.isIncludeOverloadUsages) { + strings.add(FindBundle.message("find.usages.panel.title.overloaded.methods.usages")); + } + if (this.isDerivedClasses) { + strings.add(FindBundle.message("find.usages.panel.title.derived.classes")); + } + if (this.isDerivedInterfaces) { + strings.add(FindBundle.message("find.usages.panel.title.derived.interfaces")); + } + if (this.isImplementingClasses) { + strings.add(FindBundle.message("find.usages.panel.title.implementing.classes")); + } + if (this.isImplementingMethods) { + strings.add(FindBundle.message("find.usages.panel.title.implementing.methods")); + } + if (this.isOverridingMethods) { + strings.add(FindBundle.message("find.usages.panel.title.overriding.methods")); + } + if (strings.isEmpty()) { + strings.add(FindBundle.message("find.usages.panel.title.usages")); + } + return StringUtil.join(strings, suffix); + } + + +} diff --git a/platform/lang-impl/src/com/intellij/find/findUsages/AbstractFindUsagesDialog.java b/platform/lang-impl/src/com/intellij/find/findUsages/AbstractFindUsagesDialog.java index 3c241d045748..0a7b6f42401e 100644 --- a/platform/lang-impl/src/com/intellij/find/findUsages/AbstractFindUsagesDialog.java +++ b/platform/lang-impl/src/com/intellij/find/findUsages/AbstractFindUsagesDialog.java @@ -148,7 +148,7 @@ public abstract class AbstractFindUsagesDialog extends DialogWrapper { public void calcFindUsagesOptions(FindUsagesOptions options) { options.searchScope = myScopeCombo == null ? GlobalSearchScope.allScope(myProject) : myScopeCombo.getSelectedScope(); - options.isSearchForTextOccurences = isToChange(myCbToSearchForTextOccurences) && isSelected(myCbToSearchForTextOccurences); + options.isSearchForTextOccurrences = isToChange(myCbToSearchForTextOccurences) && isSelected(myCbToSearchForTextOccurences); } protected void update() { diff --git a/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesHandler.java b/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesHandler.java index 049ec3ed8c7e..866f16c2c0ab 100644 --- a/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesHandler.java +++ b/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesHandler.java @@ -73,19 +73,14 @@ public abstract class FindUsagesHandler { public static FindUsagesOptions createFindUsagesOptions(final Project project) { FindUsagesOptions findUsagesOptions = new FindUsagesOptions(project, null); findUsagesOptions.isUsages = true; - findUsagesOptions.isIncludeOverloadUsages = false; - findUsagesOptions.isIncludeSubpackages = true; - findUsagesOptions.isReadAccess = true; - findUsagesOptions.isWriteAccess = true; - findUsagesOptions.isCheckDeepInheritance = true; - findUsagesOptions.isSearchForTextOccurences = true; + findUsagesOptions.isSearchForTextOccurrences = true; return findUsagesOptions; } @NotNull public FindUsagesOptions getFindUsagesOptions() { FindUsagesOptions options = createFindUsagesOptions(getProject()); - options.isSearchForTextOccurences &= isSearchForTextOccurencesAvailable(getPsiElement(), false); + options.isSearchForTextOccurrences &= isSearchForTextOccurencesAvailable(getPsiElement(), false); return options; } @@ -100,7 +95,7 @@ public abstract class FindUsagesHandler { final SearchScope scope = options.searchScope; - final boolean searchText = options.isSearchForTextOccurences && scope instanceof GlobalSearchScope; + final boolean searchText = options.isSearchForTextOccurrences && scope instanceof GlobalSearchScope; if (options.isUsages) { ReferencesSearch.search(new ReferencesSearch.SearchParameters(element, scope, false, options.fastTrack)).forEach(refProcessor); diff --git a/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesManager.java b/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesManager.java index a924b846a51e..1e4d9aa89f21 100644 --- a/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesManager.java +++ b/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesManager.java @@ -257,9 +257,6 @@ public class FindUsagesManager implements JDOMExternalizable { new UsageInfoToUsageConverter.TargetElementsDescriptor(handler.getPrimaryElements(), handler.getSecondaryElements()); if (singleFile) { findUsagesOptions = (FindUsagesOptions)findUsagesOptions.clone(); - findUsagesOptions.isDerivedClasses = false; - findUsagesOptions.isDerivedInterfaces = false; - findUsagesOptions.isImplementingClasses = false; editor.putUserData(KEY_START_USAGE_AGAIN, null); findUsagesInEditor(descriptor, handler, scopeFile, FileSearchScope.FROM_START, findUsagesOptions, editor); } @@ -571,41 +568,7 @@ public class FindUsagesManager implements JDOMExternalizable { } private static String generateUsagesString(final FindUsagesOptions selectedOptions) { - String suffix = " " + FindBundle.message("find.usages.panel.title.separator") + " "; - ArrayList strings = new ArrayList(); - if (selectedOptions.isUsages - || selectedOptions.isClassesUsages || - selectedOptions.isMethodsUsages || - selectedOptions.isFieldsUsages) { - strings.add(FindBundle.message("find.usages.panel.title.usages")); - } - if (selectedOptions.isIncludeOverloadUsages) { - strings.add(FindBundle.message("find.usages.panel.title.overloaded.methods.usages")); - } - if (selectedOptions.isDerivedClasses) { - strings.add(FindBundle.message("find.usages.panel.title.derived.classes")); - } - if (selectedOptions.isDerivedInterfaces) { - strings.add(FindBundle.message("find.usages.panel.title.derived.interfaces")); - } - if (selectedOptions.isImplementingClasses) { - strings.add(FindBundle.message("find.usages.panel.title.implementing.classes")); - } - if (selectedOptions.isImplementingMethods) { - strings.add(FindBundle.message("find.usages.panel.title.implementing.methods")); - } - if (selectedOptions.isOverridingMethods) { - strings.add(FindBundle.message("find.usages.panel.title.overriding.methods")); - } - if (strings.isEmpty()) { - strings.add(FindBundle.message("find.usages.panel.title.usages")); - } - String usagesString = ""; - for (int i = 0; i < strings.size(); i++) { - String s = strings.get(i); - usagesString += i == strings.size() - 1 ? s : s + suffix; - } - return usagesString; + return selectedOptions.generateUsagesString(); } private static void showEditorHint(String message, final Editor editor) { diff --git a/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesOptions.java b/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesOptions.java index 376eaf6eb95f..1591efd3c988 100644 --- a/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesOptions.java +++ b/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesOptions.java @@ -17,6 +17,7 @@ package com.intellij.find.findUsages; +import com.intellij.find.FindBundle; import com.intellij.find.FindSettings; import com.intellij.ide.util.scopeChooser.ScopeChooserCombo; import com.intellij.openapi.actionSystem.DataContext; @@ -36,26 +37,9 @@ import java.util.List; public class FindUsagesOptions extends UserDataHolderBase implements Cloneable { public SearchScope searchScope; - public boolean isSearchForTextOccurences = true; + public boolean isSearchForTextOccurrences = true; public boolean isUsages = false; - public boolean isClassesUsages = false; - public boolean isMethodsUsages = false; - public boolean isFieldsUsages = false; - public boolean isDerivedClasses = false; - public boolean isImplementingClasses = false; - public boolean isDerivedInterfaces = false; - public boolean isOverridingMethods = false; - public boolean isImplementingMethods = false; - public boolean isIncludeSubpackages = true; - public boolean isSkipImportStatements = false; - public boolean isSkipPackageStatements = false; - public boolean isCheckDeepInheritance = true; - public boolean isIncludeInherited = false; - public boolean isReadAccess = false; - public boolean isWriteAccess = false; - public boolean isIncludeOverloadUsages = false; - public boolean isThrowUsages = false; public SearchRequestCollector fastTrack = null; public FindUsagesOptions(@NotNull Project project, @Nullable final DataContext dataContext) { @@ -86,25 +70,8 @@ public class FindUsagesOptions extends UserDataHolderBase implements Cloneable { final FindUsagesOptions that = (FindUsagesOptions)o; - if (isCheckDeepInheritance != that.isCheckDeepInheritance) return false; - if (isClassesUsages != that.isClassesUsages) return false; - if (isDerivedClasses != that.isDerivedClasses) return false; - if (isDerivedInterfaces != that.isDerivedInterfaces) return false; - if (isFieldsUsages != that.isFieldsUsages) return false; - if (isImplementingClasses != that.isImplementingClasses) return false; - if (isImplementingMethods != that.isImplementingMethods) return false; - if (isIncludeInherited != that.isIncludeInherited) return false; - if (isIncludeOverloadUsages != that.isIncludeOverloadUsages) return false; - if (isIncludeSubpackages != that.isIncludeSubpackages) return false; - if (isMethodsUsages != that.isMethodsUsages) return false; - if (isOverridingMethods != that.isOverridingMethods) return false; - if (isReadAccess != that.isReadAccess) return false; - if (isSearchForTextOccurences != that.isSearchForTextOccurences) return false; - if (isSkipImportStatements != that.isSkipImportStatements) return false; - if (isSkipPackageStatements != that.isSkipPackageStatements) return false; - if (isThrowUsages != that.isThrowUsages) return false; + if (isSearchForTextOccurrences != that.isSearchForTextOccurrences) return false; if (isUsages != that.isUsages) return false; - if (isWriteAccess != that.isWriteAccess) return false; if (searchScope != null ? !searchScope.equals(that.searchScope) : that.searchScope != null) return false; return true; @@ -113,25 +80,12 @@ public class FindUsagesOptions extends UserDataHolderBase implements Cloneable { public int hashCode() { int result; result = (searchScope != null ? searchScope.hashCode() : 0); - result = 31 * result + (isSearchForTextOccurences ? 1 : 0); + result = 31 * result + (isSearchForTextOccurrences ? 1 : 0); result = 31 * result + (isUsages ? 1 : 0); - result = 31 * result + (isClassesUsages ? 1 : 0); - result = 31 * result + (isMethodsUsages ? 1 : 0); - result = 31 * result + (isFieldsUsages ? 1 : 0); - result = 31 * result + (isDerivedClasses ? 1 : 0); - result = 31 * result + (isImplementingClasses ? 1 : 0); - result = 31 * result + (isDerivedInterfaces ? 1 : 0); - result = 31 * result + (isOverridingMethods ? 1 : 0); - result = 31 * result + (isImplementingMethods ? 1 : 0); - result = 31 * result + (isIncludeSubpackages ? 1 : 0); - result = 31 * result + (isSkipImportStatements ? 1 : 0); - result = 31 * result + (isSkipPackageStatements ? 1 : 0); - result = 31 * result + (isCheckDeepInheritance ? 1 : 0); - result = 31 * result + (isIncludeInherited ? 1 : 0); - result = 31 * result + (isReadAccess ? 1 : 0); - result = 31 * result + (isWriteAccess ? 1 : 0); - result = 31 * result + (isIncludeOverloadUsages ? 1 : 0); - result = 31 * result + (isThrowUsages ? 1 : 0); return result; } + + public String generateUsagesString() { + return FindBundle.message("find.usages.panel.title.usages"); + } } From d3c1b56596eca9d38fb90da2c6be0459ee5b7667 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 30 Jul 2010 17:49:59 +0100 Subject: [PATCH 16/92] JavaFindUsagesOptions broken into more fine options classes for different targets --- .../ChangeMethodSignatureFromUsageFix.java | 6 +- .../findUsages/FindClassUsagesDialog.java | 4 +- .../findUsages/FindMethodUsagesDialog.java | 4 +- .../findUsages/FindPackageUsagesDialog.java | 4 +- .../findUsages/FindThrowUsagesDialog.java | 5 +- .../findUsages/FindVariableUsagesDialog.java | 4 +- .../JavaClassFindUsagesOptions.java | 70 +++++++ .../find/findUsages/JavaFindUsagesDialog.java | 16 +- .../findUsages/JavaFindUsagesHandler.java | 192 +++++++----------- .../JavaFindUsagesHandlerFactory.java | 60 ++---- .../findUsages/JavaFindUsagesOptions.java | 101 ++------- .../JavaMethodFindUsagesOptions.java | 64 ++++++ .../JavaPackageFindUsagesOptions.java | 49 +++++ .../JavaThrowFindUsagesOptions.java | 16 ++ .../JavaVariableFindUsagesOptions.java | 38 ++++ .../find/findUsages/FindUsagesOptions.java | 4 + .../GroovyFindUsagesHandlerFactory.java | 3 +- 17 files changed, 377 insertions(+), 263 deletions(-) create mode 100644 java/java-impl/src/com/intellij/find/findUsages/JavaClassFindUsagesOptions.java create mode 100644 java/java-impl/src/com/intellij/find/findUsages/JavaMethodFindUsagesOptions.java create mode 100644 java/java-impl/src/com/intellij/find/findUsages/JavaPackageFindUsagesOptions.java create mode 100644 java/java-impl/src/com/intellij/find/findUsages/JavaThrowFindUsagesOptions.java create mode 100644 java/java-impl/src/com/intellij/find/findUsages/JavaVariableFindUsagesOptions.java 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 42cac1dc6ea4..0cdd3e93ef25 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 @@ -32,9 +32,8 @@ import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.find.FindManager; import com.intellij.find.findUsages.FindUsagesHandler; import com.intellij.find.findUsages.FindUsagesManager; -import com.intellij.find.findUsages.JavaFindUsagesOptions; +import com.intellij.find.findUsages.JavaMethodFindUsagesOptions; import com.intellij.find.impl.FindManagerImpl; -import com.intellij.ide.DataManager; import com.intellij.ide.util.SuperMethodWarningUtil; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.undo.UndoUtil; @@ -144,9 +143,8 @@ public class ChangeMethodSignatureFromUsageFix implements IntentionAction { final FindUsagesHandler handler = findUsagesManager.getFindUsagesHandler(method, false); if (handler == null) return; //on failure or cancel (e.g. cancel of super methods dialog) - final JavaFindUsagesOptions options = new JavaFindUsagesOptions(project, editor == null? null : DataManager.getInstance().getDataContext(editor.getComponent())); + final JavaMethodFindUsagesOptions options = new JavaMethodFindUsagesOptions(project); options.isImplementingMethods = true; - options.isMethodsUsages = true; options.isOverridingMethods = true; options.isUsages = true; options.isSearchForTextOccurrences = false; diff --git a/java/java-impl/src/com/intellij/find/findUsages/FindClassUsagesDialog.java b/java/java-impl/src/com/intellij/find/findUsages/FindClassUsagesDialog.java index 4904eda03d39..2e21cbd786c1 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/FindClassUsagesDialog.java +++ b/java/java-impl/src/com/intellij/find/findUsages/FindClassUsagesDialog.java @@ -25,7 +25,7 @@ import com.intellij.ui.StateRestoringCheckBox; import javax.swing.*; -public class FindClassUsagesDialog extends JavaFindUsagesDialog { +public class FindClassUsagesDialog extends JavaFindUsagesDialog { private StateRestoringCheckBox myCbUsages; private StateRestoringCheckBox myCbMethodsUsages; private StateRestoringCheckBox myCbFieldsUsages; @@ -43,7 +43,7 @@ public class FindClassUsagesDialog extends JavaFindUsagesDialog { return myCbUsages; } - public void calcFindUsagesOptions(JavaFindUsagesOptions options) { + public void calcFindUsagesOptions(JavaClassFindUsagesOptions options) { super.calcFindUsagesOptions(options); if (isToChange(myCbUsages)){ diff --git a/java/java-impl/src/com/intellij/find/findUsages/FindMethodUsagesDialog.java b/java/java-impl/src/com/intellij/find/findUsages/FindMethodUsagesDialog.java index 8ee9356d041f..32d01d0985e6 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/FindMethodUsagesDialog.java +++ b/java/java-impl/src/com/intellij/find/findUsages/FindMethodUsagesDialog.java @@ -24,7 +24,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; -public class FindMethodUsagesDialog extends JavaFindUsagesDialog { +public class FindMethodUsagesDialog extends JavaFindUsagesDialog { private StateRestoringCheckBox myCbUsages; private StateRestoringCheckBox myCbImplementingMethods; private StateRestoringCheckBox myCbOverridingMethods; @@ -41,7 +41,7 @@ public class FindMethodUsagesDialog extends JavaFindUsagesDialog { return myHasFindWhatPanel ? myCbUsages : null; } - public void calcFindUsagesOptions(JavaFindUsagesOptions options) { + public void calcFindUsagesOptions(JavaMethodFindUsagesOptions options) { super.calcFindUsagesOptions(options); options.isUsages = isSelected(myCbUsages) || !myHasFindWhatPanel; diff --git a/java/java-impl/src/com/intellij/find/findUsages/FindPackageUsagesDialog.java b/java/java-impl/src/com/intellij/find/findUsages/FindPackageUsagesDialog.java index 88e37433a2e7..7cc36e7df3da 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/FindPackageUsagesDialog.java +++ b/java/java-impl/src/com/intellij/find/findUsages/FindPackageUsagesDialog.java @@ -23,7 +23,7 @@ import com.intellij.ui.StateRestoringCheckBox; import javax.swing.*; -public class FindPackageUsagesDialog extends JavaFindUsagesDialog { +public class FindPackageUsagesDialog extends JavaFindUsagesDialog { private StateRestoringCheckBox myCbUsages; private StateRestoringCheckBox myCbClassesUsages; @@ -39,7 +39,7 @@ public class FindPackageUsagesDialog extends JavaFindUsagesDialog { return myCbUsages; } - public void calcFindUsagesOptions(JavaFindUsagesOptions options) { + public void calcFindUsagesOptions(JavaPackageFindUsagesOptions options) { super.calcFindUsagesOptions(options); options.isUsages = isSelected(myCbUsages); diff --git a/java/java-impl/src/com/intellij/find/findUsages/FindThrowUsagesDialog.java b/java/java-impl/src/com/intellij/find/findUsages/FindThrowUsagesDialog.java index 7ca78038b41c..d8791a22dfe0 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/FindThrowUsagesDialog.java +++ b/java/java-impl/src/com/intellij/find/findUsages/FindThrowUsagesDialog.java @@ -25,7 +25,7 @@ import com.intellij.ui.StateRestoringCheckBox; import javax.swing.*; import java.awt.*; -public class FindThrowUsagesDialog extends JavaFindUsagesDialog { +public class FindThrowUsagesDialog extends JavaFindUsagesDialog { private StateRestoringCheckBox myCbUsages; private JComboBox myCbExns; private boolean myHasFindWhatPanel; @@ -62,10 +62,9 @@ public class FindThrowUsagesDialog extends JavaFindUsagesDialog { return panel; } - public void calcFindUsagesOptions(final JavaFindUsagesOptions options) { + public void calcFindUsagesOptions(final JavaThrowFindUsagesOptions options) { super.calcFindUsagesOptions(options); options.isUsages = isSelected(myCbUsages) || !myHasFindWhatPanel; - options.isThrowUsages = true; } protected JPanel createFindWhatPanel() { diff --git a/java/java-impl/src/com/intellij/find/findUsages/FindVariableUsagesDialog.java b/java/java-impl/src/com/intellij/find/findUsages/FindVariableUsagesDialog.java index 26ceefe818d7..cc33f9c6e069 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/FindVariableUsagesDialog.java +++ b/java/java-impl/src/com/intellij/find/findUsages/FindVariableUsagesDialog.java @@ -21,7 +21,7 @@ import com.intellij.psi.PsiField; import javax.swing.*; -public class FindVariableUsagesDialog extends JavaFindUsagesDialog { +public class FindVariableUsagesDialog extends JavaFindUsagesDialog { public FindVariableUsagesDialog(PsiElement element, Project project, FindUsagesOptions findUsagesOptions, boolean toShowInNewTab, boolean mustOpenInNewTab, boolean isSingleFile, FindUsagesHandler handler){ @@ -32,7 +32,7 @@ public class FindVariableUsagesDialog extends JavaFindUsagesDialog { return myCbToSkipResultsWhenOneUsage; } - public void calcFindUsagesOptions(JavaFindUsagesOptions options) { + public void calcFindUsagesOptions(JavaVariableFindUsagesOptions options) { super.calcFindUsagesOptions(options); options.isReadAccess = true; diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaClassFindUsagesOptions.java b/java/java-impl/src/com/intellij/find/findUsages/JavaClassFindUsagesOptions.java new file mode 100644 index 000000000000..a3e72c680e43 --- /dev/null +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaClassFindUsagesOptions.java @@ -0,0 +1,70 @@ +package com.intellij.find.findUsages; + +import com.intellij.find.FindBundle; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; + +import java.util.LinkedHashSet; + +/** + * @author peter + */ +public class JavaClassFindUsagesOptions extends JavaFindUsagesOptions { + public boolean isMethodsUsages = false; + public boolean isFieldsUsages = false; + public boolean isDerivedClasses = false; + public boolean isImplementingClasses = false; + public boolean isDerivedInterfaces = false; + public boolean isCheckDeepInheritance = true; + public boolean isIncludeInherited = false; + + public JavaClassFindUsagesOptions(@NotNull Project project) { + super(project); + } + + protected void addUsageTypes(LinkedHashSet strings) { + if (this.isUsages || this.isMethodsUsages || this.isFieldsUsages) { + strings.add(FindBundle.message("find.usages.panel.title.usages")); + } + if (this.isDerivedClasses) { + strings.add(FindBundle.message("find.usages.panel.title.derived.classes")); + } + if (this.isImplementingClasses) { + strings.add(FindBundle.message("find.usages.panel.title.implementing.classes")); + } + if (this.isDerivedInterfaces) { + strings.add(FindBundle.message("find.usages.panel.title.derived.interfaces")); + } + } + + public boolean equals(final Object o) { + if (this == o) return true; + if (!super.equals(this)) return false; + if (o == null || getClass() != o.getClass()) return false; + + final JavaClassFindUsagesOptions that = (JavaClassFindUsagesOptions)o; + + if (isCheckDeepInheritance != that.isCheckDeepInheritance) return false; + if (isDerivedClasses != that.isDerivedClasses) return false; + if (isDerivedInterfaces != that.isDerivedInterfaces) return false; + if (isFieldsUsages != that.isFieldsUsages) return false; + if (isImplementingClasses != that.isImplementingClasses) return false; + if (isIncludeInherited != that.isIncludeInherited) return false; + if (isMethodsUsages != that.isMethodsUsages) return false; + + return true; + } + + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (isMethodsUsages ? 1 : 0); + result = 31 * result + (isFieldsUsages ? 1 : 0); + result = 31 * result + (isDerivedClasses ? 1 : 0); + result = 31 * result + (isImplementingClasses ? 1 : 0); + result = 31 * result + (isDerivedInterfaces ? 1 : 0); + result = 31 * result + (isCheckDeepInheritance ? 1 : 0); + result = 31 * result + (isIncludeInherited ? 1 : 0); + return result; + } + +} diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesDialog.java b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesDialog.java index 8934bf3865f9..c56ee406e735 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesDialog.java +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesDialog.java @@ -30,7 +30,7 @@ import com.intellij.usageView.UsageViewUtil; import javax.swing.*; -public abstract class JavaFindUsagesDialog extends AbstractFindUsagesDialog { +public abstract class JavaFindUsagesDialog extends AbstractFindUsagesDialog { protected final PsiElement myPsiElement; private StateRestoringCheckBox myCbIncludeOverloadedMethods; private boolean myIncludeOverloadedMethodsAvailable = false; @@ -48,14 +48,16 @@ public abstract class JavaFindUsagesDialog extends AbstractFindUsagesDialog { return FindUsagesUtil.isSearchForTextOccurencesAvailable(element, isSingleFile, handler); } - public void calcFindUsagesOptions(JavaFindUsagesOptions options) { - options.isIncludeOverloadUsages = - myIncludeOverloadedMethodsAvailable && isToChange(myCbIncludeOverloadedMethods) && myCbIncludeOverloadedMethods.isSelected(); + public void calcFindUsagesOptions(T options) { + if (options instanceof JavaMethodFindUsagesOptions) { + ((JavaMethodFindUsagesOptions)options).isIncludeOverloadUsages = + myIncludeOverloadedMethodsAvailable && isToChange(myCbIncludeOverloadedMethods) && myCbIncludeOverloadedMethods.isSelected(); + } } public void calcFindUsagesOptions(FindUsagesOptions options) { super.calcFindUsagesOptions(options); - calcFindUsagesOptions((JavaFindUsagesOptions)options); + calcFindUsagesOptions((T)options); } protected void doOKAction() { @@ -100,7 +102,7 @@ public abstract class JavaFindUsagesDialog extends AbstractFindUsagesDialog { HelpManager.getInstance().invokeHelp(FindUsagesManager.getHelpID(myPsiElement)); } - protected JavaFindUsagesOptions getFindUsagesOptions() { - return (JavaFindUsagesOptions)myFindUsagesOptions; + protected T getFindUsagesOptions() { + return (T)myFindUsagesOptions; } } diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java index 7c1b2b733cc8..d284cb65d533 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandler.java @@ -23,7 +23,6 @@ import com.intellij.openapi.application.ReadActionProcessor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Comparing; @@ -63,69 +62,36 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ public static final String ACTION_STRING = FindBundle.message("find.super.method.warning.action.verb"); private final PsiElement[] myElementsToSearch; - private final FindUsagesOptions myFindPackageOptions; - private final FindUsagesOptions myFindClassOptions; - private final FindUsagesOptions myFindMethodOptions; - private final FindUsagesOptions myFindVariableOptions; - private final FindUsagesOptions myFindThrowOptions; + private final JavaFindUsagesHandlerFactory myFactory; - public JavaFindUsagesHandler(@NotNull PsiElement psiElement, - @NotNull FindUsagesOptions findClassOptions, - @NotNull FindUsagesOptions findMethodOptions, - @NotNull FindUsagesOptions findPackageOptions, - @NotNull FindUsagesOptions findThrowOptions, - @NotNull FindUsagesOptions findVariableOptions) { - this(psiElement, PsiElement.EMPTY_ARRAY, findClassOptions, findMethodOptions, findPackageOptions, findThrowOptions, findVariableOptions); + public JavaFindUsagesHandler(@NotNull PsiElement psiElement, JavaFindUsagesHandlerFactory factory) { + this(psiElement, PsiElement.EMPTY_ARRAY, factory); } - public JavaFindUsagesHandler(@NotNull PsiElement psiElement, - @NotNull PsiElement[] elementsToSearch, - @NotNull FindUsagesOptions findClassOptions, - @NotNull FindUsagesOptions findMethodOptions, - @NotNull FindUsagesOptions findPackageOptions, - @NotNull FindUsagesOptions findThrowOptions, - @NotNull FindUsagesOptions findVariableOptions) { + public JavaFindUsagesHandler(@NotNull PsiElement psiElement, @NotNull PsiElement[] elementsToSearch, JavaFindUsagesHandlerFactory factory) { super(psiElement); myElementsToSearch = elementsToSearch; - myFindClassOptions = findClassOptions; - myFindMethodOptions = findMethodOptions; - myFindPackageOptions = findPackageOptions; - myFindThrowOptions = findThrowOptions; - myFindVariableOptions = findVariableOptions; + myFactory = factory; } - @SuppressWarnings({"MethodOverridesStaticMethodOfSuperclass"}) - public static JavaFindUsagesOptions createFindUsagesOptions(final Project project) { - JavaFindUsagesOptions findUsagesOptions = new JavaFindUsagesOptions(project, null); - findUsagesOptions.isUsages = true; - findUsagesOptions.isIncludeOverloadUsages = false; - findUsagesOptions.isIncludeSubpackages = true; - findUsagesOptions.isReadAccess = true; - findUsagesOptions.isWriteAccess = true; - findUsagesOptions.isCheckDeepInheritance = true; - findUsagesOptions.isSearchForTextOccurrences = true; - return findUsagesOptions; - } - - @NotNull public AbstractFindUsagesDialog getFindUsagesDialog(boolean isSingleFile, boolean toShowInNewTab, boolean mustOpenInNewTab) { PsiElement element = getPsiElement(); if (element instanceof PsiPackage) { - return new FindPackageUsagesDialog(element, getProject(), myFindPackageOptions, toShowInNewTab, mustOpenInNewTab, isSingleFile, this); + return new FindPackageUsagesDialog(element, getProject(), myFactory.getFindPackageOptions(), toShowInNewTab, mustOpenInNewTab, isSingleFile, this); } if (element instanceof PsiClass) { - return new FindClassUsagesDialog(element, getProject(), myFindClassOptions, toShowInNewTab, mustOpenInNewTab, isSingleFile, this); + return new FindClassUsagesDialog(element, getProject(), myFactory.getFindClassOptions(), toShowInNewTab, mustOpenInNewTab, isSingleFile, this); } if (element instanceof PsiMethod) { - return new FindMethodUsagesDialog(element, getProject(), myFindMethodOptions, toShowInNewTab, mustOpenInNewTab, isSingleFile, this); + return new FindMethodUsagesDialog(element, getProject(), myFactory.getFindMethodOptions(), toShowInNewTab, mustOpenInNewTab, isSingleFile, this); } if (element instanceof PsiVariable) { - return new FindVariableUsagesDialog(element, getProject(), myFindVariableOptions, toShowInNewTab, mustOpenInNewTab, isSingleFile, this); + return new FindVariableUsagesDialog(element, getProject(), myFactory.getFindVariableOptions(), toShowInNewTab, mustOpenInNewTab, isSingleFile, this); } if (ThrowSearchUtil.isSearchable(element)) { - return new FindThrowUsagesDialog(element, getProject(), myFindThrowOptions, toShowInNewTab, mustOpenInNewTab, isSingleFile, this); + return new FindThrowUsagesDialog(element, getProject(), myFactory.getFindThrowOptions(), toShowInNewTab, mustOpenInNewTab, isSingleFile, this); } return super.getFindUsagesDialog(isSingleFile, toShowInNewTab, mustOpenInNewTab); } @@ -221,19 +187,19 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ public FindUsagesOptions getFindUsagesOptions() { PsiElement element = getPsiElement(); if (element instanceof PsiPackage) { - return myFindPackageOptions; + return myFactory.getFindPackageOptions(); } if (element instanceof PsiClass) { - return myFindClassOptions; + return myFactory.getFindClassOptions(); } if (element instanceof PsiMethod) { - return myFindMethodOptions; + return myFactory.getFindMethodOptions(); } if (element instanceof PsiVariable) { - return myFindVariableOptions; + return myFactory.getFindVariableOptions(); } if (ThrowSearchUtil.isSearchable(element)) { - return myFindThrowOptions; + return myFactory.getFindThrowOptions(); } return super.getFindUsagesOptions(); } @@ -278,12 +244,11 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } @Override - public void processElementUsages(@NotNull final PsiElement element, @NotNull final Processor processor, @NotNull final FindUsagesOptions _options) { - final JavaFindUsagesOptions options = (JavaFindUsagesOptions) _options; - if (element instanceof PsiVariable){ - if (options.isReadAccess || options.isWriteAccess){ - if (options.isReadAccess && options.isWriteAccess){ - //todo[myakovlev] this also shows param in javadoc (PsiDocParamRef), but should not + public void processElementUsages(@NotNull final PsiElement element, @NotNull final Processor processor, @NotNull final FindUsagesOptions options) { + if (options instanceof JavaVariableFindUsagesOptions) { + final JavaVariableFindUsagesOptions varOptions = (JavaVariableFindUsagesOptions) options; + if (varOptions.isReadAccess || varOptions.isWriteAccess){ + if (varOptions.isReadAccess && varOptions.isWriteAccess){ addElementUsages(element, processor, options); } else{ @@ -291,12 +256,12 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ public boolean process(UsageInfo info) { final PsiElement element = info.getElement(); boolean isWrite = element instanceof PsiExpression && PsiUtil.isAccessedForWriting((PsiExpression)element); - if (isWrite == options.isWriteAccess) { + if (isWrite == varOptions.isWriteAccess) { if (!processor.process(info)) return false; } return true; } - }, options); + }, varOptions); } } } @@ -306,7 +271,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ ApplicationManager.getApplication().runReadAction(new Runnable() { public void run() { - if (ThrowSearchUtil.isSearchable (element) && options.isThrowUsages) { + if (ThrowSearchUtil.isSearchable (element) && options instanceof JavaThrowFindUsagesOptions && options.isUsages) { ThrowSearchUtil.Root root = options.getUserData(ThrowSearchUtil.THROW_SEARCH_ROOT_KEY); if (root == null) { final ThrowSearchUtil.Root[] roots = ThrowSearchUtil.getSearchRoots(element); @@ -321,46 +286,49 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } }); - if (element instanceof PsiPackage && options.isClassesUsages){ - addClassesUsages((PsiPackage)element, processor, options); + if (options instanceof JavaPackageFindUsagesOptions && ((JavaPackageFindUsagesOptions)options).isClassesUsages){ + addClassesUsages((PsiPackage)element, processor, (JavaPackageFindUsagesOptions)options); } - if (element instanceof PsiClass && options.isMethodsUsages){ - addMethodsUsages((PsiClass)element, processor, options); - } - - if (element instanceof PsiClass && options.isFieldsUsages){ - addFieldsUsages((PsiClass)element, processor, options); - } - - if (element instanceof PsiClass){ - if (((PsiClass)element).isInterface()) { - if (options.isDerivedInterfaces){ - if (options.isImplementingClasses){ - addInheritors((PsiClass)element, processor, options); + if (options instanceof JavaClassFindUsagesOptions) { + final JavaClassFindUsagesOptions classOptions = (JavaClassFindUsagesOptions)options; + final PsiClass psiClass = (PsiClass)element; + if (classOptions.isMethodsUsages){ + addMethodsUsages(psiClass, processor, classOptions); + } + if (classOptions.isFieldsUsages){ + addFieldsUsages(psiClass, processor, classOptions); + } + if (psiClass.isInterface()) { + if (classOptions.isDerivedInterfaces){ + if (classOptions.isImplementingClasses){ + addInheritors(psiClass, processor, classOptions); } else{ - addDerivedInterfaces((PsiClass)element, processor, options); + addDerivedInterfaces(psiClass, processor, classOptions); } } - else if (options.isImplementingClasses){ - addImplementingClasses((PsiClass)element, processor, options); + else if (classOptions.isImplementingClasses){ + addImplementingClasses(psiClass, processor, classOptions); } } - else if (options.isDerivedClasses) { - addInheritors((PsiClass)element, processor, options); + else if (classOptions.isDerivedClasses) { + addInheritors(psiClass, processor, classOptions); } } - if (element instanceof PsiMethod){ + + + if (options instanceof JavaMethodFindUsagesOptions){ final PsiMethod psiMethod = (PsiMethod)element; boolean isAbstract = ApplicationManager.getApplication().runReadAction(new Computable() { public Boolean compute() { return psiMethod.hasModifierProperty(PsiModifier.ABSTRACT); } }); - if (isAbstract && options.isImplementingMethods || options.isOverridingMethods) { - processOverridingMethods(psiMethod, processor, options); + final JavaMethodFindUsagesOptions methodOptions = (JavaMethodFindUsagesOptions)options; + if (isAbstract && methodOptions.isImplementingMethods || methodOptions.isOverridingMethods) { + processOverridingMethods(psiMethod, processor, methodOptions); } } @@ -370,18 +338,18 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } } - private static void processOverridingMethods(PsiMethod psiMethod, final Processor processor, final JavaFindUsagesOptions options) { + private static void processOverridingMethods(PsiMethod psiMethod, final Processor processor, final JavaMethodFindUsagesOptions options) { OverridingMethodsSearch.search(psiMethod, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter( new PsiElementProcessor() { public boolean execute(PsiMethod element) { - addResult(processor, element.getNavigationElement(), options, null); + addResult(processor, element.getNavigationElement(), options); return true; } })); } - private static void addClassesUsages(PsiPackage aPackage, final Processor results, final JavaFindUsagesOptions options) { + private static void addClassesUsages(PsiPackage aPackage, final Processor results, final JavaPackageFindUsagesOptions options) { final HashSet filesSet = new HashSet(); final ArrayList files = new ArrayList(); ReferencesSearch.search(new ReferencesSearch.SearchParameters(aPackage, options.searchScope, false, options.fastTrack)).forEach(new ReadActionProcessor() { @@ -412,7 +380,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } ReferencesSearch.search(new ReferencesSearch.SearchParameters(aClass, new LocalSearchScope(file), false, options.fastTrack)).forEach(new ReadActionProcessor() { public boolean processInReadAction(final PsiReference psiReference) { - return addResult(results, psiReference, options, aClass); + return addResult(results, psiReference, options); } }); } @@ -445,7 +413,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ }); } - private static void addMethodsUsages(final PsiClass aClass, final Processor results, final JavaFindUsagesOptions options) { + private static void addMethodsUsages(final PsiClass aClass, final Processor results, final JavaClassFindUsagesOptions options) { if (options.isIncludeInherited) { final PsiManager manager = aClass.getManager(); PsiMethod[] methods = aClass.getAllMethods(); @@ -462,10 +430,9 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ addElementUsages(methods[i], results, options); } else{ - boolean strictSignatureSearch = !options.isIncludeOverloadUsages; - MethodReferencesSearch.search(new MethodReferencesSearch.SearchParameters(method, options.searchScope, strictSignatureSearch, options.fastTrack)).forEach(new PsiReferenceProcessorAdapter(new PsiReferenceProcessor() { + MethodReferencesSearch.search(new MethodReferencesSearch.SearchParameters(method, options.searchScope, true, options.fastTrack)).forEach(new PsiReferenceProcessorAdapter(new PsiReferenceProcessor() { public boolean execute(PsiReference reference) { - addResultFromReference(reference, methodClass, manager, aClass, results, options, method); + addResultFromReference(reference, methodClass, manager, aClass, results, options); return true; } })); @@ -479,7 +446,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } } - private static void addFieldsUsages(final PsiClass aClass, final Processor results, final JavaFindUsagesOptions options) { + private static void addFieldsUsages(final PsiClass aClass, final Processor results, final JavaClassFindUsagesOptions options) { if (options.isIncludeInherited) { final PsiManager manager = aClass.getManager(); PsiField[] fields = aClass.getAllFields(); @@ -497,7 +464,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ else { ReferencesSearch.search(new ReferencesSearch.SearchParameters(field, options.searchScope, false, options.fastTrack)).forEach(new ReadActionProcessor() { public boolean processInReadAction(final PsiReference reference) { - addResultFromReference(reference, fieldClass, manager, aClass, results, options, field); + addResultFromReference(reference, fieldClass, manager, aClass, results, options); return true; } }); @@ -544,23 +511,23 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ return null; } - private static void addInheritors(final PsiClass aClass, final Processor results, final JavaFindUsagesOptions options) { + private static void addInheritors(final PsiClass aClass, final Processor results, final JavaClassFindUsagesOptions options) { ClassInheritorsSearch.search(aClass, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter( new PsiElementProcessor() { public boolean execute(PsiClass element) { - addResult(results, element, options, null); + addResult(results, element, options); return true; } })); } - private static void addDerivedInterfaces(PsiClass anInterface, final Processor results, final JavaFindUsagesOptions options) { + private static void addDerivedInterfaces(PsiClass anInterface, final Processor results, final JavaClassFindUsagesOptions options) { ClassInheritorsSearch.search(anInterface, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter( new PsiElementProcessor() { public boolean execute(PsiClass inheritor) { if (inheritor.isInterface()) { - addResult(results, inheritor, options, null); + addResult(results, inheritor, options); } return true; } @@ -568,12 +535,12 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ })); } - private static void addImplementingClasses(PsiClass anInterface, final Processor results, final JavaFindUsagesOptions options) { + private static void addImplementingClasses(PsiClass anInterface, final Processor results, final JavaClassFindUsagesOptions options) { ClassInheritorsSearch.search(anInterface, options.searchScope, options.isCheckDeepInheritance).forEach(new PsiElementProcessorAdapter( new PsiElementProcessor() { public boolean execute(PsiClass inheritor) { if (!inheritor.isInterface()) { - addResult(results, inheritor, options, null); + addResult(results, inheritor, options); } return true; } @@ -586,29 +553,28 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ final PsiManager manager, final PsiClass aClass, final Processor results, - final FindUsagesOptions options, - final PsiElement element) { + final FindUsagesOptions options) { PsiElement refElement = reference.getElement(); if (refElement instanceof PsiReferenceExpression) { PsiClass usedClass = getFieldOrMethodAccessedClass((PsiReferenceExpression)refElement, methodClass); if (usedClass != null) { if (manager.areElementsEquivalent(usedClass, aClass) || usedClass.isInheritor(aClass, true)) { - addResult(results, refElement, (JavaFindUsagesOptions)options, element); + addResult(results, refElement, options); } } } } - public static void addElementUsages(final PsiElement element, final Processor result, final JavaFindUsagesOptions options) { + public static void addElementUsages(final PsiElement element, final Processor result, final FindUsagesOptions options) { final SearchScope searchScope = options.searchScope; if (element instanceof PsiMethod && ((PsiMethod)element).isConstructor()){ PsiMethod method = (PsiMethod)element; final PsiClass parentClass = method.getContainingClass(); if (parentClass != null) { - MethodReferencesSearch.search(new MethodReferencesSearch.SearchParameters(method, searchScope, !options.isIncludeOverloadUsages, options.fastTrack)).forEach(new ReadActionProcessor() { + MethodReferencesSearch.search(new MethodReferencesSearch.SearchParameters(method, searchScope, !((JavaMethodFindUsagesOptions)options).isIncludeOverloadUsages, options.fastTrack)).forEach(new ReadActionProcessor() { public boolean processInReadAction(final PsiReference ref) { - return addResult(result, ref, options, parentClass); + return addResult(result, ref, options); } }); } @@ -617,37 +583,37 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ final ReadActionProcessor consumer = new ReadActionProcessor() { public boolean processInReadAction(final PsiReference ref) { - return addResult(result, ref, options, element); + return addResult(result, ref, options); } }; if (element instanceof PsiMethod) { - final boolean strictSignatureSearch = !options.isIncludeOverloadUsages; + final boolean strictSignatureSearch = !((JavaMethodFindUsagesOptions)options).isIncludeOverloadUsages; MethodReferencesSearch.search(new MethodReferencesSearch.SearchParameters((PsiMethod)element, searchScope, strictSignatureSearch, options.fastTrack)).forEach(consumer); } else { ReferencesSearch.search(new ReferencesSearch.SearchParameters(element, searchScope, false, options.fastTrack)).forEach(consumer); } } - public static void addResult(Processor total, PsiElement element, JavaFindUsagesOptions options, PsiElement refElement) { - if (filterUsage(element, options, refElement)){ + public static void addResult(Processor total, PsiElement element, FindUsagesOptions options) { + if (filterUsage(element, options)){ total.process(new UsageInfo(element)); } } - public static boolean addResult(Processor results, PsiReference ref, JavaFindUsagesOptions options, PsiElement refElement) { - if (filterUsage(ref.getElement(), options, refElement)){ + public static boolean addResult(Processor results, PsiReference ref, FindUsagesOptions options) { + if (filterUsage(ref.getElement(), options)){ TextRange rangeInElement = ref.getRangeInElement(); return results.process(new UsageInfo(ref.getElement(), rangeInElement.getStartOffset(), rangeInElement.getEndOffset(), false)); } return true; } - private static boolean filterUsage(PsiElement usage, JavaFindUsagesOptions options, PsiElement refElement) { + private static boolean filterUsage(PsiElement usage, FindUsagesOptions options) { if (!(usage instanceof PsiJavaCodeReferenceElement)) { return true; } - if (refElement instanceof PsiPackage && !options.isIncludeSubpackages && + if (options instanceof JavaPackageFindUsagesOptions && !((JavaPackageFindUsagesOptions)options).isIncludeSubpackages && ((PsiReference)usage).resolve() instanceof PsiPackage) { PsiElement parent = usage.getParent(); if (parent instanceof PsiJavaCodeReferenceElement && ((PsiJavaCodeReferenceElement)parent).resolve() instanceof PsiPackage) { @@ -656,7 +622,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } if (!(usage instanceof PsiReferenceExpression)){ - if (options.isSkipImportStatements){ + if (options instanceof JavaFindUsagesOptions && ((JavaFindUsagesOptions)options).isSkipImportStatements){ PsiElement parent = usage.getParent(); while(parent instanceof PsiJavaCodeReferenceElement){ parent = parent.getParent(); @@ -666,7 +632,7 @@ public class JavaFindUsagesHandler extends FindUsagesHandler{ } } - if (options.isSkipPackageStatements){ + if (options instanceof JavaPackageFindUsagesOptions && ((JavaPackageFindUsagesOptions)options).isSkipPackageStatements){ PsiElement parent = usage.getParent(); while(parent instanceof PsiJavaCodeReferenceElement){ parent = parent.getParent(); diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandlerFactory.java b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandlerFactory.java index 540a46200b8c..195c32909870 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandlerFactory.java +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesHandlerFactory.java @@ -27,38 +27,22 @@ import org.jetbrains.annotations.NotNull; * @author peter */ public class JavaFindUsagesHandlerFactory extends FindUsagesHandlerFactory { - private final FindUsagesOptions myFindClassOptions; - private final FindUsagesOptions myFindMethodOptions; - private final FindUsagesOptions myFindPackageOptions; - private final FindUsagesOptions myFindThrowOptions; - private final FindUsagesOptions myFindVariableOptions; + private final JavaClassFindUsagesOptions myFindClassOptions; + private final JavaMethodFindUsagesOptions myFindMethodOptions; + private final JavaPackageFindUsagesOptions myFindPackageOptions; + private final JavaThrowFindUsagesOptions myFindThrowOptions; + private final JavaVariableFindUsagesOptions myFindVariableOptions; public static JavaFindUsagesHandlerFactory getInstance(@NotNull Project project) { return ContainerUtil.findInstance(Extensions.getExtensions(EP_NAME, project), JavaFindUsagesHandlerFactory.class); } public JavaFindUsagesHandlerFactory(Project project) { - final JavaFindUsagesOptions findClassOptions = JavaFindUsagesHandler.createFindUsagesOptions(project); - final JavaFindUsagesOptions findMethodOptions = JavaFindUsagesHandler.createFindUsagesOptions(project); - findMethodOptions.isCheckDeepInheritance = false; - findMethodOptions.isIncludeSubpackages = false; - findMethodOptions.isSearchForTextOccurrences = false; - final FindUsagesOptions findPackageOptions = JavaFindUsagesHandler.createFindUsagesOptions(project); - - final JavaFindUsagesOptions findThrowOptions = JavaFindUsagesHandler.createFindUsagesOptions(project); - findThrowOptions.isSearchForTextOccurrences = false; - findThrowOptions.isThrowUsages = true; - - final JavaFindUsagesOptions findVariableOptions = JavaFindUsagesHandler.createFindUsagesOptions(project); - findVariableOptions.isCheckDeepInheritance = false; - findVariableOptions.isIncludeSubpackages = false; - findVariableOptions.isSearchForTextOccurrences = false; - - myFindClassOptions = findClassOptions; - myFindMethodOptions = findMethodOptions; - myFindPackageOptions = findPackageOptions; - myFindThrowOptions = findThrowOptions; - myFindVariableOptions = findVariableOptions; + myFindClassOptions = new JavaClassFindUsagesOptions(project); + myFindMethodOptions = new JavaMethodFindUsagesOptions(project); + myFindPackageOptions = new JavaPackageFindUsagesOptions(project); + myFindThrowOptions = new JavaThrowFindUsagesOptions(project); + myFindVariableOptions = new JavaVariableFindUsagesOptions(project); } public boolean canFindUsages(@NotNull final PsiElement element) { @@ -68,46 +52,40 @@ public class JavaFindUsagesHandlerFactory extends FindUsagesHandlerFactory { public FindUsagesHandler createFindUsagesHandler(@NotNull final PsiElement element, final boolean forHighlightUsages) { if (element instanceof PsiDirectory) { final PsiPackage psiPackage = JavaDirectoryService.getInstance().getPackage((PsiDirectory)element); - return psiPackage == null - ? null - : new JavaFindUsagesHandler(psiPackage, myFindClassOptions, myFindMethodOptions, myFindPackageOptions, myFindThrowOptions, - myFindVariableOptions); + return psiPackage == null ? null : new JavaFindUsagesHandler(psiPackage, this); } if (element instanceof PsiMethod && !forHighlightUsages) { final PsiMethod[] methods = SuperMethodWarningUtil.checkSuperMethods((PsiMethod)element, JavaFindUsagesHandler.ACTION_STRING); if (methods.length > 1) { - return new JavaFindUsagesHandler(element, methods, myFindClassOptions, myFindMethodOptions, myFindPackageOptions, - myFindThrowOptions, myFindVariableOptions); + return new JavaFindUsagesHandler(element, methods, this); } if (methods.length == 1) { - return new JavaFindUsagesHandler(methods[0], myFindClassOptions, myFindMethodOptions, myFindPackageOptions, myFindThrowOptions, - myFindVariableOptions); + return new JavaFindUsagesHandler(methods[0], this); } return FindUsagesHandler.NULL_HANDLER; } - return new JavaFindUsagesHandler(element, myFindClassOptions, myFindMethodOptions, myFindPackageOptions, myFindThrowOptions, - myFindVariableOptions); + return new JavaFindUsagesHandler(element, this); } - public FindUsagesOptions getFindClassOptions() { + public JavaClassFindUsagesOptions getFindClassOptions() { return myFindClassOptions; } - public FindUsagesOptions getFindMethodOptions() { + public JavaMethodFindUsagesOptions getFindMethodOptions() { return myFindMethodOptions; } - public FindUsagesOptions getFindPackageOptions() { + public JavaPackageFindUsagesOptions getFindPackageOptions() { return myFindPackageOptions; } - public FindUsagesOptions getFindThrowOptions() { + public JavaThrowFindUsagesOptions getFindThrowOptions() { return myFindThrowOptions; } - public FindUsagesOptions getFindVariableOptions() { + public JavaVariableFindUsagesOptions getFindVariableOptions() { return myFindVariableOptions; } } diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesOptions.java b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesOptions.java index 723ab9ef8a1b..7aa91305d380 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesOptions.java +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaFindUsagesOptions.java @@ -1,38 +1,22 @@ package com.intellij.find.findUsages; import com.intellij.find.FindBundle; -import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; +import java.util.LinkedHashSet; /** * @author peter */ -public class JavaFindUsagesOptions extends FindUsagesOptions { - public boolean isClassesUsages = false; - public boolean isMethodsUsages = false; - public boolean isFieldsUsages = false; - public boolean isDerivedClasses = false; - public boolean isImplementingClasses = false; - public boolean isDerivedInterfaces = false; - public boolean isOverridingMethods = false; - public boolean isImplementingMethods = false; - public boolean isIncludeSubpackages = true; +public abstract class JavaFindUsagesOptions extends FindUsagesOptions { public boolean isSkipImportStatements = false; - public boolean isSkipPackageStatements = false; - public boolean isCheckDeepInheritance = true; - public boolean isIncludeInherited = false; - public boolean isReadAccess = false; - public boolean isWriteAccess = false; - public boolean isIncludeOverloadUsages = false; - public boolean isThrowUsages = false; - public JavaFindUsagesOptions(@NotNull Project project, @Nullable DataContext dataContext) { - super(project, dataContext); + public JavaFindUsagesOptions(@NotNull Project project) { + super(project, null); + + isUsages = true; } public boolean equals(final Object o) { @@ -40,79 +24,26 @@ public class JavaFindUsagesOptions extends FindUsagesOptions { if (!super.equals(this)) return false; if (o == null || getClass() != o.getClass()) return false; - final JavaFindUsagesOptions that = (JavaFindUsagesOptions)o; - - if (isCheckDeepInheritance != that.isCheckDeepInheritance) return false; - if (isClassesUsages != that.isClassesUsages) return false; - if (isDerivedClasses != that.isDerivedClasses) return false; - if (isDerivedInterfaces != that.isDerivedInterfaces) return false; - if (isFieldsUsages != that.isFieldsUsages) return false; - if (isImplementingClasses != that.isImplementingClasses) return false; - if (isImplementingMethods != that.isImplementingMethods) return false; - if (isIncludeInherited != that.isIncludeInherited) return false; - if (isIncludeOverloadUsages != that.isIncludeOverloadUsages) return false; - if (isIncludeSubpackages != that.isIncludeSubpackages) return false; - if (isMethodsUsages != that.isMethodsUsages) return false; - if (isOverridingMethods != that.isOverridingMethods) return false; - if (isReadAccess != that.isReadAccess) return false; - if (isSearchForTextOccurrences != that.isSearchForTextOccurrences) return false; - if (isSkipImportStatements != that.isSkipImportStatements) return false; - if (isSkipPackageStatements != that.isSkipPackageStatements) return false; - if (isThrowUsages != that.isThrowUsages) return false; - if (isUsages != that.isUsages) return false; - if (isWriteAccess != that.isWriteAccess) return false; - if (searchScope != null ? !searchScope.equals(that.searchScope) : that.searchScope != null) return false; - - return true; + return isSkipImportStatements == ((JavaFindUsagesOptions)o).isSkipImportStatements; } public int hashCode() { int result = super.hashCode(); - result = 31 * result + (isClassesUsages ? 1 : 0); - result = 31 * result + (isMethodsUsages ? 1 : 0); - result = 31 * result + (isFieldsUsages ? 1 : 0); - result = 31 * result + (isDerivedClasses ? 1 : 0); - result = 31 * result + (isImplementingClasses ? 1 : 0); - result = 31 * result + (isDerivedInterfaces ? 1 : 0); - result = 31 * result + (isOverridingMethods ? 1 : 0); - result = 31 * result + (isImplementingMethods ? 1 : 0); - result = 31 * result + (isIncludeSubpackages ? 1 : 0); result = 31 * result + (isSkipImportStatements ? 1 : 0); - result = 31 * result + (isSkipPackageStatements ? 1 : 0); - result = 31 * result + (isCheckDeepInheritance ? 1 : 0); - result = 31 * result + (isIncludeInherited ? 1 : 0); - result = 31 * result + (isReadAccess ? 1 : 0); - result = 31 * result + (isWriteAccess ? 1 : 0); - result = 31 * result + (isIncludeOverloadUsages ? 1 : 0); - result = 31 * result + (isThrowUsages ? 1 : 0); return result; } + protected void addUsageTypes(LinkedHashSet to) { + if (this.isUsages) { + to.add(FindBundle.message("find.usages.panel.title.usages")); + } + } + @Override - public String generateUsagesString() { + public final String generateUsagesString() { String suffix = " " + FindBundle.message("find.usages.panel.title.separator") + " "; - ArrayList strings = new ArrayList(); - if (this.isUsages || this.isClassesUsages || this.isMethodsUsages || this.isFieldsUsages) { - strings.add(FindBundle.message("find.usages.panel.title.usages")); - } - if (this.isIncludeOverloadUsages) { - strings.add(FindBundle.message("find.usages.panel.title.overloaded.methods.usages")); - } - if (this.isDerivedClasses) { - strings.add(FindBundle.message("find.usages.panel.title.derived.classes")); - } - if (this.isDerivedInterfaces) { - strings.add(FindBundle.message("find.usages.panel.title.derived.interfaces")); - } - if (this.isImplementingClasses) { - strings.add(FindBundle.message("find.usages.panel.title.implementing.classes")); - } - if (this.isImplementingMethods) { - strings.add(FindBundle.message("find.usages.panel.title.implementing.methods")); - } - if (this.isOverridingMethods) { - strings.add(FindBundle.message("find.usages.panel.title.overriding.methods")); - } + LinkedHashSet strings = new LinkedHashSet(); + addUsageTypes(strings); if (strings.isEmpty()) { strings.add(FindBundle.message("find.usages.panel.title.usages")); } diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaMethodFindUsagesOptions.java b/java/java-impl/src/com/intellij/find/findUsages/JavaMethodFindUsagesOptions.java new file mode 100644 index 000000000000..6efafe155922 --- /dev/null +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaMethodFindUsagesOptions.java @@ -0,0 +1,64 @@ +package com.intellij.find.findUsages; + +import com.intellij.find.FindBundle; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; + +import java.util.LinkedHashSet; + +/** + * @author peter + */ +public class JavaMethodFindUsagesOptions extends JavaFindUsagesOptions { + public boolean isOverridingMethods = false; + public boolean isImplementingMethods = false; + public boolean isCheckDeepInheritance = true; + public boolean isIncludeInherited = false; + public boolean isIncludeOverloadUsages = false; + + public JavaMethodFindUsagesOptions(@NotNull Project project) { + super(project); + isSearchForTextOccurrences = false; + } + + public boolean equals(final Object o) { + if (this == o) return true; + if (!super.equals(this)) return false; + if (o == null || getClass() != o.getClass()) return false; + + final JavaMethodFindUsagesOptions that = (JavaMethodFindUsagesOptions)o; + + if (isCheckDeepInheritance != that.isCheckDeepInheritance) return false; + if (isImplementingMethods != that.isImplementingMethods) return false; + if (isIncludeInherited != that.isIncludeInherited) return false; + if (isIncludeOverloadUsages != that.isIncludeOverloadUsages) return false; + if (isOverridingMethods != that.isOverridingMethods) return false; + + return true; + } + + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (isOverridingMethods ? 1 : 0); + result = 31 * result + (isImplementingMethods ? 1 : 0); + result = 31 * result + (isCheckDeepInheritance ? 1 : 0); + result = 31 * result + (isIncludeInherited ? 1 : 0); + result = 31 * result + (isIncludeOverloadUsages ? 1 : 0); + return result; + } + + @Override + protected void addUsageTypes(LinkedHashSet strings) { + super.addUsageTypes(strings); + if (this.isIncludeOverloadUsages) { + strings.add(FindBundle.message("find.usages.panel.title.overloaded.methods.usages")); + } + if (this.isImplementingMethods) { + strings.add(FindBundle.message("find.usages.panel.title.implementing.methods")); + } + if (this.isOverridingMethods) { + strings.add(FindBundle.message("find.usages.panel.title.overriding.methods")); + } + + } +} diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaPackageFindUsagesOptions.java b/java/java-impl/src/com/intellij/find/findUsages/JavaPackageFindUsagesOptions.java new file mode 100644 index 000000000000..846bf2c1b0d9 --- /dev/null +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaPackageFindUsagesOptions.java @@ -0,0 +1,49 @@ +package com.intellij.find.findUsages; + +import com.intellij.find.FindBundle; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; + +import java.util.LinkedHashSet; + +/** + * @author peter + */ +public class JavaPackageFindUsagesOptions extends JavaFindUsagesOptions { + public boolean isClassesUsages = false; + public boolean isIncludeSubpackages = true; + public boolean isSkipPackageStatements = false; + + public JavaPackageFindUsagesOptions(@NotNull Project project) { + super(project); + } + + protected void addUsageTypes(LinkedHashSet to) { + if (this.isUsages || this.isClassesUsages) { + to.add(FindBundle.message("find.usages.panel.title.usages")); + } + } + + public boolean equals(final Object o) { + if (this == o) return true; + if (!super.equals(this)) return false; + if (o == null || getClass() != o.getClass()) return false; + + final JavaPackageFindUsagesOptions that = (JavaPackageFindUsagesOptions)o; + + if (isClassesUsages != that.isClassesUsages) return false; + if (isIncludeSubpackages != that.isIncludeSubpackages) return false; + if (isSkipPackageStatements != that.isSkipPackageStatements) return false; + + return true; + } + + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (isClassesUsages ? 1 : 0); + result = 31 * result + (isIncludeSubpackages ? 1 : 0); + result = 31 * result + (isSkipPackageStatements ? 1 : 0); + return result; + } + +} diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaThrowFindUsagesOptions.java b/java/java-impl/src/com/intellij/find/findUsages/JavaThrowFindUsagesOptions.java new file mode 100644 index 000000000000..c90c6a675dee --- /dev/null +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaThrowFindUsagesOptions.java @@ -0,0 +1,16 @@ +package com.intellij.find.findUsages; + +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; + +/** + * @author peter + */ +public class JavaThrowFindUsagesOptions extends JavaFindUsagesOptions { + + public JavaThrowFindUsagesOptions(@NotNull Project project) { + super(project); + isSearchForTextOccurrences = false; + } + +} diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaVariableFindUsagesOptions.java b/java/java-impl/src/com/intellij/find/findUsages/JavaVariableFindUsagesOptions.java new file mode 100644 index 000000000000..c39b664c6b7c --- /dev/null +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaVariableFindUsagesOptions.java @@ -0,0 +1,38 @@ +package com.intellij.find.findUsages; + +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; + +/** + * @author peter + */ +public class JavaVariableFindUsagesOptions extends JavaFindUsagesOptions { + public boolean isReadAccess = false; + public boolean isWriteAccess = false; + + public JavaVariableFindUsagesOptions(@NotNull Project project) { + super(project); + isSearchForTextOccurrences = false; + } + + public boolean equals(final Object o) { + if (this == o) return true; + if (!super.equals(this)) return false; + if (o == null || getClass() != o.getClass()) return false; + + final JavaVariableFindUsagesOptions that = (JavaVariableFindUsagesOptions)o; + + if (isReadAccess != that.isReadAccess) return false; + if (isWriteAccess != that.isWriteAccess) return false; + + return true; + } + + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + (isReadAccess ? 1 : 0); + result = 31 * result + (isWriteAccess ? 1 : 0); + return result; + } + +} diff --git a/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesOptions.java b/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesOptions.java index 1591efd3c988..1c01130562e6 100644 --- a/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesOptions.java +++ b/platform/lang-impl/src/com/intellij/find/findUsages/FindUsagesOptions.java @@ -42,6 +42,10 @@ public class FindUsagesOptions extends UserDataHolderBase implements Cloneable { public boolean isUsages = false; public SearchRequestCollector fastTrack = null; + public FindUsagesOptions(@NotNull Project project) { + this(project, null); + } + public FindUsagesOptions(@NotNull Project project, @Nullable final DataContext dataContext) { String defaultScopeName = FindSettings.getInstance().getDefaultScopeName(); List predefined = ScopeChooserCombo.getPredefinedScopes(project, dataContext, true, false, false, false); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyFindUsagesHandlerFactory.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyFindUsagesHandlerFactory.java index 9cfce6d91006..d3cbc97a606c 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyFindUsagesHandlerFactory.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GroovyFindUsagesHandlerFactory.java @@ -52,8 +52,7 @@ public class GroovyFindUsagesHandlerFactory extends JavaFindUsagesHandlerFactory @Override public FindUsagesHandler createFindUsagesHandler(@NotNull PsiElement element, boolean forHighlightUsages) { - return new JavaFindUsagesHandler(element, getFindClassOptions(), getFindMethodOptions(), getFindPackageOptions(), getFindThrowOptions(), - getFindVariableOptions()) { + return new JavaFindUsagesHandler(element, this) { @NotNull @Override public PsiElement[] getSecondaryElements() { From 801dba3cab3d70c9e2c4799be2811a60f20ea3b9 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 30 Jul 2010 18:15:59 +0100 Subject: [PATCH 17/92] default variable search options are both read/write --- .../find/findUsages/JavaVariableFindUsagesOptions.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/java-impl/src/com/intellij/find/findUsages/JavaVariableFindUsagesOptions.java b/java/java-impl/src/com/intellij/find/findUsages/JavaVariableFindUsagesOptions.java index c39b664c6b7c..c7fc44babc9c 100644 --- a/java/java-impl/src/com/intellij/find/findUsages/JavaVariableFindUsagesOptions.java +++ b/java/java-impl/src/com/intellij/find/findUsages/JavaVariableFindUsagesOptions.java @@ -7,8 +7,8 @@ import org.jetbrains.annotations.NotNull; * @author peter */ public class JavaVariableFindUsagesOptions extends JavaFindUsagesOptions { - public boolean isReadAccess = false; - public boolean isWriteAccess = false; + public boolean isReadAccess = true; + public boolean isWriteAccess = true; public JavaVariableFindUsagesOptions(@NotNull Project project) { super(project); From 24488c204b1bf274041199a8a9be59148bb0f59a Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 30 Jul 2010 18:20:58 +0100 Subject: [PATCH 18/92] don't insert . after completing super in java --- .../intellij/codeInsight/completion/JavaCompletionData.java | 3 --- .../codeInsight/completion/normal/SuperInMethod_after.java | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java index 716b8b940dc9..3cf1832683c9 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionData.java @@ -580,9 +580,6 @@ public class JavaCompletionData extends JavaAwareCompletionData{ } }); } - else if (!AFTER_DOT.accepts(position)) { - superItem.setTailType(TailType.DOT); - } result.addElement(superItem); } diff --git a/java/java-tests/testData/codeInsight/completion/normal/SuperInMethod_after.java b/java/java-tests/testData/codeInsight/completion/normal/SuperInMethod_after.java index c591d1c16f9f..de4c112bfea8 100644 --- a/java/java-tests/testData/codeInsight/completion/normal/SuperInMethod_after.java +++ b/java/java-tests/testData/codeInsight/completion/normal/SuperInMethod_after.java @@ -1,7 +1,7 @@ public class Xxx { void foo() { - super. + super } } From a1e5e5638b1e355d801f110a8f88c5fbde004bf5 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 30 Jul 2010 20:05:36 +0100 Subject: [PATCH 19/92] groovy: delegate basic completion to class name as in java, don't swallow the completion character --- .../completion/AllClassesGetter.java | 1 - .../JavaClassNameInsertHandler.java | 1 + plugins/groovy/src/META-INF/plugin.xml | 3 +- .../GroovyBasicToClassNameDelegator.java | 40 +++++++++++++++++++ ...a => GroovyClassNameCompletionTest.groovy} | 22 ++++++++-- 5 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyBasicToClassNameDelegator.java rename plugins/groovy/test/org/jetbrains/plugins/groovy/{GroovyClassNameCompletionTest.java => GroovyClassNameCompletionTest.groovy} (84%) diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java b/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java index fa190b5dad65..b4a3e738a47c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/AllClassesGetter.java @@ -134,7 +134,6 @@ public class AllClassesGetter { } public void handleInsert(final InsertionContext context, final JavaPsiClassReferenceElement item) { - context.setAddCompletionChar(false); _handleInsert(context, item); item.getTailType().processTail(context.getEditor(), context.getEditor().getCaretModel().getOffset()); } diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java index da9605457ab0..9cd67d4c1ba5 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaClassNameInsertHandler.java @@ -27,6 +27,7 @@ import com.intellij.psi.util.PsiTreeUtil; public class JavaClassNameInsertHandler implements AllClassesGetter.ClassNameInsertHandler { public AllClassesGetter.ClassNameInsertHandlerResult handleInsert(InsertionContext context, JavaPsiClassReferenceElement item) { + context.setAddCompletionChar(false); Editor editor = context.getEditor(); PsiFile file = context.getFile(); int endOffset = editor.getCaretModel().getOffset(); diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 2f285a10915c..51b84ec6fcc9 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -168,7 +168,8 @@ implementationClass="org.jetbrains.plugins.groovy.codeInsight.GroovyLineMarkerProvider"/> - + + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyBasicToClassNameDelegator.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyBasicToClassNameDelegator.java new file mode 100644 index 000000000000..da9a48e0096f --- /dev/null +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/completion/GroovyBasicToClassNameDelegator.java @@ -0,0 +1,40 @@ +package org.jetbrains.plugins.groovy.lang.completion; + +import com.intellij.codeInsight.completion.AbstractBasicToClassNameDelegator; +import com.intellij.codeInsight.completion.CompletionResultSet; +import com.intellij.codeInsight.completion.JavaPsiClassReferenceElement; +import com.intellij.codeInsight.lookup.AutoCompletionPolicy; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase; +import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement; + +/** + * @author peter + */ +public class GroovyBasicToClassNameDelegator extends AbstractBasicToClassNameDelegator{ + + @Override + protected boolean isClassNameCompletionSupported(CompletionResultSet result, PsiFile file, PsiElement position) { + if (!(file instanceof GroovyFileBase)) { + return false; + } + + if (!(position.getParent() instanceof GrCodeReferenceElement)) return false; + if (((GrCodeReferenceElement)position.getParent()).getQualifier() != null) return false; + + final String s = result.getPrefixMatcher().getPrefix(); + if (StringUtil.isEmpty(s) || !Character.isUpperCase(s.charAt(0))) return false; + return true; + } + + @Override + protected void updateProperties(LookupElement lookupElement) { + if (lookupElement instanceof JavaPsiClassReferenceElement) { + ((JavaPsiClassReferenceElement)lookupElement).setAutoCompletionPolicy(AutoCompletionPolicy.NEVER_AUTOCOMPLETE); + } + } + +} diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyClassNameCompletionTest.java b/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyClassNameCompletionTest.groovy similarity index 84% rename from plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyClassNameCompletionTest.java rename to plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyClassNameCompletionTest.groovy index 7a507fa4a938..a4b125942907 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyClassNameCompletionTest.java +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/GroovyClassNameCompletionTest.groovy @@ -26,8 +26,6 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.util.TestUtils; -import java.io.IOException; - /** * @author Maxim.Medvedev */ @@ -52,11 +50,11 @@ public class GroovyClassNameCompletionTest extends LightCodeInsightFixtureTestCa super.tearDown(); } - public void doTest(boolean forceCompletion) throws Exception { + public void doTest(boolean force) throws Exception { addClassToProject("a", "FooBar"); myFixture.configureByFile(getTestName(false) + ".groovy"); myFixture.complete(CompletionType.CLASS_NAME); - if (forceCompletion) forceCompletion(); + if (force) forceCompletion(); myFixture.checkResultByFile(getTestName(false) + "_after.groovy"); } @@ -86,4 +84,20 @@ public class GroovyClassNameCompletionTest extends LightCodeInsightFixtureTestCa public void testInTypeElementPlace() throws Exception {doTest(false);} public void testWhenImportExists() throws Exception{doTest(false);} + public void testFinishByDot() throws Exception{ + addClassToProject("a", "FooBar"); + myFixture.configureByText("a.groovy", "FBa") + myFixture.complete(CompletionType.CLASS_NAME) + myFixture.type '.'.charAt(0) + myFixture.checkResult "a.FooBar.a" + } + + public void testDelegateBasicToClassName() throws Exception{ + addClassToProject("a", "FooBarGooDoo"); + myFixture.configureByText("a.groovy", "FBGDa") + myFixture.completeBasic() + myFixture.type '.'.charAt(0) + myFixture.checkResult "a.FooBarGooDoo.a" + } + } From 1c07d168ad101ce2c2aebcea03e3ac1372418719 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 30 Jul 2010 20:11:04 +0100 Subject: [PATCH 20/92] optimize newifyTransform.gdsl so that it doesn't run on every reference's resolve --- plugins/groovy/resources/standardDsls/metaDsl.gdsl | 6 +++--- plugins/groovy/resources/standardDsls/newifyTransform.gdsl | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/groovy/resources/standardDsls/metaDsl.gdsl b/plugins/groovy/resources/standardDsls/metaDsl.gdsl index e5edaed46399..d11a89b40343 100644 --- a/plugins/groovy/resources/standardDsls/metaDsl.gdsl +++ b/plugins/groovy/resources/standardDsls/metaDsl.gdsl @@ -10,9 +10,9 @@ contributor([gdslScriptContext]) { method name: "supportsVersion", params: [version: "java.lang.String"], type: boolean // scopes - property name: "closureScope", params: [contexts: "java.util.Map"], type: {} - property name: "scriptScope", params: [contexts: "java.util.Map"], type: {} - property name: "annotatedScope", params: [contexts: "java.util.Map"], type: {} + method name: "closureScope", params: [contexts: "java.util.Map"], type: {} + method name: "scriptScope", params: [contexts: "java.util.Map"], type: {} + method name: "annotatedScope", params: [contexts: "java.util.Map"], type: {} method name: "hasAnnotation", params:[fqn: "java.lang.String"], type: "java.lang.Object" method name: "hasMethod", params:[memberPattern: "java.lang.Object"], type: "java.lang.Object" diff --git a/plugins/groovy/resources/standardDsls/newifyTransform.gdsl b/plugins/groovy/resources/standardDsls/newifyTransform.gdsl index 303c5ea1bb61..acfdb6864857 100644 --- a/plugins/groovy/resources/standardDsls/newifyTransform.gdsl +++ b/plugins/groovy/resources/standardDsls/newifyTransform.gdsl @@ -1,4 +1,4 @@ -contributor(context()) { +contributor(context(scope:annotatedScope(ctype: "groovy.lang.Newify"))) { // For methods def memb = enclosingMember() if (memb) { From 6f54136fcb2d0d931900bbc29e440022767c1e7a Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 30 Jul 2010 20:33:36 +0100 Subject: [PATCH 21/92] minor optimizations --- .../psi/impl/auxiliary/modifiers/GrModifierListImpl.java | 2 +- .../psi/impl/statements/GrVariableDeclarationImpl.java | 9 ++++++++- .../impl/statements/arguments/GrArgumentListImpl.java | 9 ++++++++- .../expressions/path/GrMethodCallExpressionImpl.java | 5 ++++- .../impl/statements/typedef/GrTypeDefinitionImpl.java | 2 +- 5 files changed, 22 insertions(+), 5 deletions(-) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/modifiers/GrModifierListImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/modifiers/GrModifierListImpl.java index 912e40d88d24..2dc9da7a349d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/modifiers/GrModifierListImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/auxiliary/modifiers/GrModifierListImpl.java @@ -215,7 +215,7 @@ public class GrModifierListImpl extends GroovyBaseElementImpl result = new ArrayList(); + for (PsiElement cur = this.getFirstChild(); cur != null; cur = cur.getNextSibling()) { + if (cur instanceof GrVariable) result.add((GrVariable)cur); + } + return result.toArray(new GrVariable[result.size()]); } public void setType(@Nullable PsiType type) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentListImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentListImpl.java index 9def31570dff..c25657eacf9f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentListImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentListImpl.java @@ -32,6 +32,9 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpres import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiElementImpl; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; +import java.util.ArrayList; +import java.util.List; + import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mCOMMA; /** @@ -58,7 +61,11 @@ public class GrArgumentListImpl extends GroovyPsiElementImpl implements GrArgume @NotNull public GrExpression[] getExpressionArguments() { - return findChildrenByClass(GrExpression.class); + List result = new ArrayList(); + for (PsiElement cur = this.getFirstChild(); cur != null; cur = cur.getNextSibling()) { + if (cur instanceof GrExpression) result.add((GrExpression)cur); + } + return result.toArray(new GrExpression[result.size()]); } public GrArgumentList replaceWithArgumentList(GrArgumentList newArgList) throws IncorrectOperationException { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrMethodCallExpressionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrMethodCallExpressionImpl.java index 240808ac9b5b..1a2953f18d35 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrMethodCallExpressionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/path/GrMethodCallExpressionImpl.java @@ -145,7 +145,10 @@ public class GrMethodCallExpressionImpl extends GrCallExpressionImpl implements @Nullable public GrExpression getInvokedExpression() { - return findChildByClass(GrExpression.class); + for (PsiElement cur = this.getFirstChild(); cur != null; cur = cur.getNextSibling()) { + if (cur instanceof GrExpression) return (GrExpression)cur; + } + return null; } public GrExpression replaceClosureArgument(@NotNull GrClosableBlock closure, @NotNull GrExpression newExpr) throws IncorrectOperationException { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionImpl.java index b21d9252909f..1d145e74bb0a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/typedef/GrTypeDefinitionImpl.java @@ -563,7 +563,7 @@ public abstract class GrTypeDefinitionImpl extends GroovyBaseElementImpl Date: Fri, 30 Jul 2010 20:45:34 +0100 Subject: [PATCH 22/92] minor optimization --- plugins/groovy/src/META-INF/plugin.xml | 3 +- .../plugins/groovy/gpp/GppStdLibSupport.java | 30 ++++++++--------- .../plugins/groovy/gpp/GppTypeConverter.java | 5 ++- .../GrConstructorInvocationImpl.java | 4 --- .../plugins/groovy/lang/psi/util/PsiUtil.java | 6 ---- .../lang/resolve/NonCodeMembersProcessor.java | 32 ------------------- .../groovy/lang/resolve/ResolveUtil.java | 5 --- 7 files changed, 17 insertions(+), 68 deletions(-) delete mode 100644 plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/NonCodeMembersProcessor.java diff --git a/plugins/groovy/src/META-INF/plugin.xml b/plugins/groovy/src/META-INF/plugin.xml index 51b84ec6fcc9..87e23ebaf85c 100644 --- a/plugins/groovy/src/META-INF/plugin.xml +++ b/plugins/groovy/src/META-INF/plugin.xml @@ -30,7 +30,6 @@ - @@ -79,7 +78,7 @@ - + diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppStdLibSupport.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppStdLibSupport.java index a1163e37884a..0d97c2e8608a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppStdLibSupport.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppStdLibSupport.java @@ -8,7 +8,6 @@ import com.intellij.psi.scope.PsiScopeProcessor; import com.intellij.psi.util.CachedValue; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; -import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.NotNullFunction; import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElement; @@ -18,7 +17,7 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyPsiManager; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GrGdkMethodImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil; import org.jetbrains.plugins.groovy.lang.resolve.DominanceAwareMethod; -import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersProcessor; +import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; import java.util.HashMap; @@ -28,7 +27,7 @@ import java.util.Map; /** * @author peter */ -public class GppStdLibSupport implements NonCodeMembersProcessor { +public class GppStdLibSupport extends NonCodeMembersContributor { private static final Key>>> CACHED_STDLIB = Key.create("GppStdLib"); private static final String[] STDLIB_CLASSES = { "groovy.util.Conversions", @@ -40,16 +39,15 @@ public class GppStdLibSupport implements NonCodeMembersProcessor { "org.mbte.groovypp.runtime.ArraysMethods", "org.mbte.groovypp.runtime.DefaultGroovyPPMethods"}; - public boolean processNonCodeMembers(PsiType type, PsiScopeProcessor processor, PsiElement place, boolean forCompletion) { + @Override + public void processDynamicElements(@NotNull PsiType type, PsiScopeProcessor processor, PsiElement place, ResolveState state) { if (!(type instanceof PsiClassType)) { - return true; + return; } if (!GppTypeConverter.hasTypedContext(place)) { - return true; + return; } - final String className = TypeConversionUtil.erasure(type).getCanonicalText(); - final Project project = place.getProject(); final Map> map = CachedValuesManager.getManager(project).getCachedValue(project, CACHED_STDLIB, new CachedValueProvider>>() { public Result>> compute() { @@ -76,17 +74,17 @@ public class GppStdLibSupport implements NonCodeMembersProcessor { return Result.create(result, ProjectRootManager.getInstance(project)); } }, false); - final List methods = map.get(className); - if (methods == null) { - return true; - } - for (PsiMethod method : methods) { - if (!ResolveUtil.processElement(processor, method)) { - return false; + for (String className : ResolveUtil.getAllSuperTypes(type, place).keySet()) { + final List methods = map.get(className); + if (methods != null) { + for (PsiMethod method : methods) { + if (!ResolveUtil.processElement(processor, method, state)) { + return; + } + } } } - return true; } private static class GppGdkMethod extends GrGdkMethodImpl implements DominanceAwareMethod { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java index e65c3e889a84..0cb82bdee163 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppTypeConverter.java @@ -1,6 +1,6 @@ package org.jetbrains.plugins.groovy.gpp; -import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; @@ -30,8 +30,7 @@ public class GppTypeConverter extends GrTypeConverter { return true; } - final VirtualFile vfile = context.getContainingFile().getOriginalFile().getVirtualFile(); - if (vfile != null && isGppExtension(vfile.getExtension())) { + if (isGppExtension(StringUtil.getShortName(context.getContainingFile().getName()))) { return true; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java index 707c0012b9b4..c6774d67d833 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/GrConstructorInvocationImpl.java @@ -38,7 +38,6 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyResolveResultImpl; import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil; import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil; import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor; -import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersProcessor; import org.jetbrains.plugins.groovy.lang.resolve.processors.MethodResolverProcessor; /** @@ -104,9 +103,6 @@ public class GrConstructorInvocationImpl extends GroovyPsiElementImpl implements MethodResolverProcessor processor = new MethodResolverProcessor(clazz.getName(), this, true, thisType, argTypes, PsiType.EMPTY_ARRAY); clazz.processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, substitutor), null, this); - for (NonCodeMembersProcessor membersProcessor : NonCodeMembersProcessor.EP_NAME.getExtensions()) { - if (!membersProcessor.processNonCodeMembers(thisType, processor, this, true)) break; - } NonCodeMembersContributor.runContributors(thisType, processor, this, ResolveState.initial()); return processor.getCandidates(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java index 6e654b3db96f..8c2d50a2ce79 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java @@ -18,9 +18,7 @@ package org.jetbrains.plugins.groovy.lang.psi.util; import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.lang.ASTNode; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; @@ -72,7 +70,6 @@ import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass; import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.JavaIdentifier; import org.jetbrains.plugins.groovy.lang.psi.impl.types.GrClosureSignatureUtil; import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor; -import org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersProcessor; import org.jetbrains.plugins.groovy.lang.resolve.ResolveUtil; import org.jetbrains.plugins.groovy.lang.resolve.processors.MethodResolverProcessor; import org.jetbrains.plugins.groovy.lang.resolve.processors.ResolverProcessor; @@ -877,9 +874,6 @@ public class PsiUtil { ResolveState.initial().put(PsiSubstitutor.KEY, substitutor).put(ResolverProcessor.RESOLVE_CONTEXT, context); final boolean toBreak = element.processDeclarations(processor, state, null, place); - for (NonCodeMembersProcessor membersProcessor : NonCodeMembersProcessor.EP_NAME.getExtensions()) { - if (!membersProcessor.processNonCodeMembers(thisType, processor, place, true)) break; - } NonCodeMembersContributor.runContributors(thisType, processor, place, state); ContainerUtil.addAll(constructorResults, processor.getCandidates()); if (!toBreak) break; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/NonCodeMembersProcessor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/NonCodeMembersProcessor.java deleted file mode 100644 index 655cb3dd74fc..000000000000 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/NonCodeMembersProcessor.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2000-2009 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.lang.resolve; - -import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiType; -import com.intellij.psi.scope.PsiScopeProcessor; - -/** - * @deprecated use {@link org.jetbrains.plugins.groovy.lang.resolve.NonCodeMembersContributor} - * @author Maxim.Medvedev - */ -public interface NonCodeMembersProcessor { - ExtensionPointName EP_NAME = ExtensionPointName.create("org.intellij.groovy.nonCodeProcessor"); - - boolean processNonCodeMembers(PsiType type, PsiScopeProcessor processor, PsiElement place, boolean forCompletion); -} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java index 47905ecaec05..09547a8f973a 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java @@ -153,11 +153,6 @@ public class ResolveUtil { } } - for (PsiType psiType : getAllSuperTypes(type, place).values()) { - for (NonCodeMembersProcessor membersProcessor : NonCodeMembersProcessor.EP_NAME.getExtensions()) { - if (!membersProcessor.processNonCodeMembers(psiType, processor, place, forCompletion)) return false; - } - } return true; } From 10e6ebe4a380af89d1112a6e691664a92ad7f181 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 30 Jul 2010 20:47:35 +0100 Subject: [PATCH 23/92] cleanup --- .../psi/impl/statements/arguments/GrArgumentLabelImpl.java | 2 +- .../psi/impl/statements/blocks/GrClosableBlockImpl.java | 2 +- .../expressions/CompleteReferenceExpression.java | 4 ++-- .../statements/expressions/GrReferenceExpressionImpl.java | 4 ++-- .../lang/psi/impl/statements/expressions/TypesUtil.java | 2 +- .../jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java | 2 +- .../jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java | 7 +++---- 7 files changed, 11 insertions(+), 12 deletions(-) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java index 7109cb41489b..d75228c0e022 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/arguments/GrArgumentLabelImpl.java @@ -154,7 +154,7 @@ public class GrArgumentLabelImpl extends GroovyPsiElementImpl implements GrArgum if (field != null) return field; final PropertyResolverProcessor processor = new PropertyResolverProcessor(propName, this); ResolveUtil - .processNonCodeMethods(JavaPsiFacade.getElementFactory(getProject()).createType(clazz), processor, this, false); + .processNonCodeMethods(JavaPsiFacade.getElementFactory(getProject()).createType(clazz), processor, this); final GroovyResolveResult[] candidates = processor.getCandidates(); if (candidates.length == 0) return null; return candidates[0].getElement(); diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java index 0aa8f7ae378e..8330054c7a3f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/blocks/GrClosableBlockImpl.java @@ -86,7 +86,7 @@ public class GrClosableBlockImpl extends GrBlockImpl implements GrClosableBlock // Process non-code in closures PsiType clType = JavaPsiFacade.getInstance(getProject()).getElementFactory().createType(closureClass, PsiSubstitutor.EMPTY); - if (!ResolveUtil.processNonCodeMethods(clType, processor, place, true)) return false; + if (!ResolveUtil.processNonCodeMethods(clType, processor, place)) return false; } return true; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java index 142f3e8e02ce..e626c70df3f4 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/CompleteReferenceExpression.java @@ -299,7 +299,7 @@ public class CompleteReferenceExpression { javaLangClass.processDeclarations(processor, ResolveState.initial(), null, refExpr); PsiType javaLangClassType = JavaPsiFacade.getInstance(refExpr.getProject()).getElementFactory().createType(javaLangClass, substitutor); - ResolveUtil.processNonCodeMethods(javaLangClassType, processor, refExpr, true); + ResolveUtil.processNonCodeMethods(javaLangClassType, processor, refExpr); } } } @@ -367,6 +367,6 @@ public class CompleteReferenceExpression { return; } - ResolveUtil.processNonCodeMethods(qualifierType, processor, refExpr, true); + ResolveUtil.processNonCodeMethods(qualifierType, processor, refExpr); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java index 947db67a586f..5e3d6147aefb 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java @@ -641,7 +641,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements } if (!javaLangClass.processDeclarations(processor, state, null, this)) return; PsiType javaLangClassType = JavaPsiFacade.getInstance(getProject()).getElementFactory().createType(javaLangClass, substitutor); - ResolveUtil.processNonCodeMethods(javaLangClassType, processor, this, false); + ResolveUtil.processNonCodeMethods(javaLangClassType, processor, this); } } } @@ -670,7 +670,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements return; } - ResolveUtil.processNonCodeMethods(qualifierType, processor, this, false); + ResolveUtil.processNonCodeMethods(qualifierType, processor, this); } @Nullable diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java index 9b1707f9aa02..f29b2b3fe11d 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/TypesUtil.java @@ -135,7 +135,7 @@ public class TypesUtil { } } - ResolveUtil.processNonCodeMethods(thisType, processor, place, false); + ResolveUtil.processNonCodeMethods(thisType, processor, place); return processor.getCandidates(); } return GroovyResolveResult.EMPTY_ARRAY; diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java index 8c2d50a2ce79..edf3a6d9cc22 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/util/PsiUtil.java @@ -677,7 +677,7 @@ public class PsiUtil { qClass.processDeclarations(processor, ResolveState.initial().put(PsiSubstitutor.KEY, PsiSubstitutor.EMPTY), null, expr); } - ResolveUtil.processNonCodeMethods(qualifierType, processor, expr, false); + ResolveUtil.processNonCodeMethods(qualifierType, processor, expr); final GroovyResolveResult[] candidates = processor.getCandidates(); PsiType type = null; if (candidates.length == 1) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java index 09547a8f973a..8ef7a0c245d8 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java @@ -75,12 +75,12 @@ public class ResolveUtil { if (!run.processDeclarations(processor, ResolveState.initial(), lastParent, place)) return false; if (processNonCodeMethods) { if (run instanceof GrTypeDefinition) { - processNonCodeMethods(factory.createType(((GrTypeDefinition)run)), processor, place, false); + processNonCodeMethods(factory.createType(((GrTypeDefinition)run)), processor, place); } else if ((run instanceof GroovyFileBase) && ((GroovyFileBase)run).isScript()) { final PsiClass psiClass = ((GroovyFileBase)run).getScriptClass(); if (psiClass != null) { - processNonCodeMethods(factory.createType(psiClass), processor, place, false); + processNonCodeMethods(factory.createType(psiClass), processor, place); } } } @@ -137,8 +137,7 @@ public class ResolveUtil { public static boolean processNonCodeMethods(PsiType type, PsiScopeProcessor processor, - PsiElement place, - boolean forCompletion) { + PsiElement place) { if (!NonCodeMembersContributor.runContributors(type, processor, place, ResolveState.initial())) { return false; } From 6fd80e142dd349c0311b20221c1a3859f352ef26 Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 30 Jul 2010 20:51:02 +0100 Subject: [PATCH 24/92] cleanup --- .../dynamic/DynamicMembersContributor.java | 4 ++-- .../groovy/dsl/holders/NonCodeMembersHolder.java | 2 +- .../plugins/groovy/gant/GantMemberContributor.java | 12 ++++++------ .../plugins/groovy/lang/resolve/ResolveUtil.java | 8 -------- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/dynamic/DynamicMembersContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/dynamic/DynamicMembersContributor.java index 8ed517620788..cb98b758fe9f 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/dynamic/DynamicMembersContributor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/annotator/intentions/dynamic/DynamicMembersContributor.java @@ -18,11 +18,11 @@ public class DynamicMembersContributor extends NonCodeMembersContributor { final DynamicManager manager = DynamicManager.getInstance(place.getProject()); for (String qName : ResolveUtil.getAllSuperTypes(qualifierType, place).keySet()) { for (PsiMethod method : manager.getMethods(qName)) { - if (!ResolveUtil.processElement(processor, method)) return; + if (!ResolveUtil.processElement(processor, method, state)) return; } for (PsiVariable var : manager.getProperties(qName)) { - if (!ResolveUtil.processElement(processor, var)) return; + if (!ResolveUtil.processElement(processor, var, state)) return; } } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/holders/NonCodeMembersHolder.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/holders/NonCodeMembersHolder.java index 8fb9d19e7068..3ff969ec90c1 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/holders/NonCodeMembersHolder.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/dsl/holders/NonCodeMembersHolder.java @@ -80,7 +80,7 @@ public class NonCodeMembersHolder implements CustomMembersHolder { public boolean processMembers(PsiScopeProcessor processor) { for (PsiMethod method : myMethods) { - if (!ResolveUtil.processElement(processor, method)) { + if (!ResolveUtil.processElement(processor, method, ResolveState.initial())) { return false; } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/GantMemberContributor.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/GantMemberContributor.java index f73772850609..3afc1109b6ab 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/GantMemberContributor.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/gant/GantMemberContributor.java @@ -39,7 +39,7 @@ public class GantMemberContributor extends NonCodeMembersContributor { PsiElement place, ResolveState state) { if (ResolveUtil.isInheritor(qualifierType, "groovy.util.AntBuilder", place)) { - processAntTasks(processor, place); + processAntTasks(processor, place, state); return; } @@ -59,7 +59,7 @@ public class GantMemberContributor extends NonCodeMembersContributor { final PsiMethod method = ((GrMethodCall)parent).resolveMethod(); if (method instanceof AntBuilderMethod) { antTasksProcessed = true; - if (!processAntTasks(processor, place)) { + if (!processAntTasks(processor, place, state)) { return; } if (!((AntBuilderMethod)method).processNestedElements(processor)) { @@ -84,25 +84,25 @@ public class GantMemberContributor extends NonCodeMembersContributor { if (targetName != null) { final PsiNamedElement variable = new LightVariableBuilder(targetName, GrClosableBlock.GROOVY_LANG_CLOSURE, label). setBaseIcon(GantIcons.GANT_TARGET); - if (!ResolveUtil.processElement(processor, variable)) { + if (!ResolveUtil.processElement(processor, variable, state)) { return; } } } if (!antTasksProcessed) { - processAntTasks(processor, place); + processAntTasks(processor, place, state); } } - private static boolean processAntTasks(PsiScopeProcessor processor, PsiElement place) { + private static boolean processAntTasks(PsiScopeProcessor processor, PsiElement place, ResolveState state) { if (!AntTasksProvider.antAvailable) { return true; } for (LightMethodBuilder task : AntTasksProvider.getAntTasks(place)) { - if (!ResolveUtil.processElement(processor, task)) { + if (!ResolveUtil.processElement(processor, task, state)) { return false; } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java index 8ef7a0c245d8..6c9ce65238a3 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/resolve/ResolveUtil.java @@ -115,14 +115,6 @@ public class ResolveUtil { return nameHint.getName(ResolveState.initial()); } - - /** - * @deprecated - * use {@link #processElement(PsiScopeProcessor, PsiNamedElement, ResolveState)} instead - */ - public static boolean processElement(PsiScopeProcessor processor, PsiNamedElement namedElement) { - return processElement(processor, namedElement, ResolveState.initial()); - } public static boolean processElement(PsiScopeProcessor processor, PsiNamedElement namedElement, ResolveState state) { NameHint nameHint = processor.getHint(NameHint.KEY); From 76ecd2e0e125f5f8364acdbaa8126aff3417daee Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 30 Jul 2010 20:58:31 +0100 Subject: [PATCH 25/92] support g++ stdlib methods on arrays --- .../jetbrains/plugins/groovy/gpp/GppStdLibSupport.java | 2 +- .../plugins/groovy/lang/GppFunctionalTest.groovy | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppStdLibSupport.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppStdLibSupport.java index 0d97c2e8608a..95bc08d8eafb 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppStdLibSupport.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/gpp/GppStdLibSupport.java @@ -41,7 +41,7 @@ public class GppStdLibSupport extends NonCodeMembersContributor { @Override public void processDynamicElements(@NotNull PsiType type, PsiScopeProcessor processor, PsiElement place, ResolveState state) { - if (!(type instanceof PsiClassType)) { + if (!(type instanceof PsiClassType) && !(type instanceof PsiArrayType)) { return; } if (!GppTypeConverter.hasTypedContext(place)) { diff --git a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy index 57b08e4f9ec9..0b6592de7fb8 100644 --- a/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy +++ b/plugins/groovy/test/org/jetbrains/plugins/groovy/lang/GppFunctionalTest.groovy @@ -322,6 +322,16 @@ class BarImpl extends Bar {} assertEquals "groovy.util.Iterations", method.containingClass.qualifiedName } + public void testResolveToStdLibWithArrayQualifier() throws Exception { + configureGppScript """ +Integer[] a = [] +a.foldLeft(2, { a, b -> a+b }) +""" + PsiMethod method = resolveReference().navigationElement + assertEquals "foldLeft", method.name + assertEquals "groovy.util.Iterations", method.containingClass.qualifiedName + } + private PsiElement resolveReference() { return myFixture.file.findReferenceAt(myFixture.editor.caretModel.offset).resolve() } From 821b331c00be5334b12cb7b3a46563042c3fe4bb Mon Sep 17 00:00:00 2001 From: peter Date: Fri, 30 Jul 2010 21:36:17 +0100 Subject: [PATCH 26/92] minor optimizations --- .../openapi/roots/impl/ModuleFileIndexImpl.java | 17 +++++++++++++---- .../lang/psi/impl/GrReferenceElementImpl.java | 3 ++- .../expressions/GrReferenceExpressionImpl.java | 3 ++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java index 579f31d183cb..f57450236da0 100644 --- a/platform/lang-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java +++ b/platform/lang-impl/src/com/intellij/openapi/roots/impl/ModuleFileIndexImpl.java @@ -104,12 +104,21 @@ public class ModuleFileIndexImpl implements ModuleFileIndex { final List orderEntries = info.getOrderEntries(); if (orderEntries.isEmpty()) return Collections.emptyList(); - SmartList answer = new SmartList(); - for (OrderEntry entry : orderEntries) { - if (entry.getOwnerModule() == myModule) answer.add(entry); + final Module module = myModule; + + SmartList answer = null; + final int size = orderEntries.size(); + for (int i = 0; i < size; i++) { + OrderEntry entry = orderEntries.get(i); + if (entry.getOwnerModule() == module) { + if (answer == null) { + answer = new SmartList(); + } + answer.add(entry); + } } - return answer; + return answer == null ? Collections.emptyList() : answer; } public OrderEntry getOrderEntryForFile(@NotNull VirtualFile fileOrDir) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrReferenceElementImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrReferenceElementImpl.java index 06567f87b933..fa675a636518 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrReferenceElementImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/GrReferenceElementImpl.java @@ -22,6 +22,7 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes; +import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes; import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement; import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrTypeArgumentList; @@ -129,6 +130,6 @@ public abstract class GrReferenceElementImpl extends GroovyPsiElementImpl implem @Nullable public GrTypeArgumentList getTypeArgumentList() { - return findChildByClass(GrTypeArgumentList.class); + return (GrTypeArgumentList)findChildByType(GroovyElementTypes.TYPE_ARGUMENTS); } } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java index 5e3d6147aefb..1de00521bb23 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/statements/expressions/GrReferenceExpressionImpl.java @@ -72,6 +72,7 @@ import java.util.List; */ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements GrReferenceExpression { public static final Key IS_RESOLVED_TO_GETTER = new Key("Is resolved to getter"); + private static final IElementType[] REFERENCE_NAME_TYPES = TokenSets.REFERENCE_NAMES.getTypes(); public GrReferenceExpressionImpl(@NotNull ASTNode node) { super(node); @@ -199,7 +200,7 @@ public class GrReferenceExpressionImpl extends GrReferenceElementImpl implements public PsiElement getReferenceNameElement() { final ASTNode lastChild = getNode().getLastChildNode(); if (lastChild == null) return null; - for (IElementType elementType : TokenSets.REFERENCE_NAMES.getTypes()) { + for (IElementType elementType : REFERENCE_NAME_TYPES) { if (lastChild.getElementType() == elementType) return lastChild.getPsi(); } From 09b521f3c797c50a183ea90718c9bab4841ae97d Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 00:47:31 +0100 Subject: [PATCH 27/92] some utilities --- .../com/intellij/util/containers/CollectionFactory.java | 8 ++++++++ .../util/src/com/intellij/util/containers/MultiMap.java | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/platform/util/src/com/intellij/util/containers/CollectionFactory.java b/platform/util/src/com/intellij/util/containers/CollectionFactory.java index ff443bee65b5..88db418d9d90 100644 --- a/platform/util/src/com/intellij/util/containers/CollectionFactory.java +++ b/platform/util/src/com/intellij/util/containers/CollectionFactory.java @@ -52,6 +52,10 @@ public class CollectionFactory { return new ArrayList(); } + public static ArrayList arrayList(T... elements) { + return new ArrayList(Arrays.asList(elements)); + } + public static Stack stack() { return new Stack(); } @@ -67,4 +71,8 @@ public class CollectionFactory { public static LinkedHashMap linkedMap() { return new LinkedHashMap(); } + + public static LinkedHashSet linkedHashSet() { + return new LinkedHashSet(); + } } diff --git a/platform/util/src/com/intellij/util/containers/MultiMap.java b/platform/util/src/com/intellij/util/containers/MultiMap.java index 57af35d547d0..fb5a7d93e53b 100644 --- a/platform/util/src/com/intellij/util/containers/MultiMap.java +++ b/platform/util/src/com/intellij/util/containers/MultiMap.java @@ -105,6 +105,15 @@ public class MultiMap { return collection == null ? createEmptyCollection() : collection; } + @NotNull + public Collection getModifiable(final K key) { + Collection collection = myMap.get(key); + if (collection == null) { + myMap.put(key, collection = createCollection()); + } + return collection; + } + public Set keySet() { return myMap.keySet(); } From b98e70c1e51733dc0ab451eacfa0b94bada4e2c0 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 00:49:51 +0100 Subject: [PATCH 28/92] debug info, cleanup --- .../intellij/psi/search/SearchRequestCollector.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java b/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java index 596282772625..26427cb71818 100644 --- a/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java +++ b/platform/lang-api/src/com/intellij/psi/search/SearchRequestCollector.java @@ -50,12 +50,6 @@ public class SearchRequestCollector { } } - public boolean hasRequests() { - synchronized (lock) { - return !myWordRequests.isEmpty() || !myCustomSearchActions.isEmpty() || !myQueryRequests.isEmpty(); - } - } - public List takeQueryRequests() { return takeRequests(myQueryRequests); } @@ -76,4 +70,9 @@ public class SearchRequestCollector { public List>> takeCustomSearchActions() { return takeRequests(myCustomSearchActions); } + + @Override + public String toString() { + return myWordRequests.toString().replace(',', '\n'); + } } From ddcb0c14c81b5ab435b10cf5316c020c20107771 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 01:10:55 +0100 Subject: [PATCH 29/92] merge identical search queries => need equals/hashCode for scopes and processors --- .../search/MethodTextOccurrenceProcessor.java | 5 +- .../search/DelegatingGlobalSearchScope.java | 31 +++++++ .../psi/search/GlobalSearchScope.java | 67 ++++++++++++++- .../intellij/psi/search/PsiSearchRequest.java | 28 +++++- .../psi/search/RequestResultProcessor.java | 25 ++++++ .../SingleTargetRequestResultProcessor.java | 7 +- .../psi/impl/search/PsiSearchHelperImpl.java | 86 ++++++++++++++----- .../GrAliasedImportedElementSearcher.java | 1 + .../GroovyConstructorUsagesSearcher.java | 2 +- .../MethodLateBoundReferencesSearcher.java | 2 +- .../psi/impl/search/GrSourceFilterScope.java | 2 +- 11 files changed, 225 insertions(+), 31 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/impl/search/MethodTextOccurrenceProcessor.java b/java/java-impl/src/com/intellij/psi/impl/search/MethodTextOccurrenceProcessor.java index 6296f4f0e8d7..5429f37824b1 100644 --- a/java/java-impl/src/com/intellij/psi/impl/search/MethodTextOccurrenceProcessor.java +++ b/java/java-impl/src/com/intellij/psi/impl/search/MethodTextOccurrenceProcessor.java @@ -23,16 +23,19 @@ import com.intellij.psi.util.TypeConversionUtil; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; +import java.util.Arrays; + /** * @author peter */ -public class MethodTextOccurrenceProcessor extends RequestResultProcessor { +public final class MethodTextOccurrenceProcessor extends RequestResultProcessor { private static final PsiReferenceService ourReferenceService = PsiReferenceService.getService(); private final PsiMethod[] myMethods; private final PsiClass myContainingClass; private final boolean myStrictSignatureSearch; public MethodTextOccurrenceProcessor(@NotNull final PsiClass aClass, final boolean strictSignatureSearch, final PsiMethod... methods) { + super(strictSignatureSearch, Arrays.asList(methods)); myMethods = methods; myContainingClass = aClass; myStrictSignatureSearch = strictSignatureSearch; diff --git a/platform/lang-api/src/com/intellij/psi/search/DelegatingGlobalSearchScope.java b/platform/lang-api/src/com/intellij/psi/search/DelegatingGlobalSearchScope.java index a2a746720e52..affe151105cb 100644 --- a/platform/lang-api/src/com/intellij/psi/search/DelegatingGlobalSearchScope.java +++ b/platform/lang-api/src/com/intellij/psi/search/DelegatingGlobalSearchScope.java @@ -4,15 +4,25 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; +import java.util.Arrays; + /** * @author peter */ public class DelegatingGlobalSearchScope extends GlobalSearchScope { protected final GlobalSearchScope myBaseScope; + private final Object myEquality; public DelegatingGlobalSearchScope(@NotNull GlobalSearchScope baseScope) { super(baseScope.getProject()); myBaseScope = baseScope; + myEquality = new Object(); + } + + public DelegatingGlobalSearchScope(GlobalSearchScope baseScope, Object... equality) { + super(baseScope.getProject()); + myBaseScope = baseScope; + myEquality = Arrays.asList(equality); } @Override @@ -88,4 +98,25 @@ public class DelegatingGlobalSearchScope extends GlobalSearchScope { public String getDisplayName() { return myBaseScope.getDisplayName(); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + DelegatingGlobalSearchScope that = (DelegatingGlobalSearchScope)o; + + if (!myBaseScope.equals(that.myBaseScope)) return false; + if (!myEquality.equals(that.myEquality)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + myBaseScope.hashCode(); + result = 31 * result + myEquality.hashCode(); + return result; + } } diff --git a/platform/lang-api/src/com/intellij/psi/search/GlobalSearchScope.java b/platform/lang-api/src/com/intellij/psi/search/GlobalSearchScope.java index fc9ef4a1630e..b2faf5df7f0f 100644 --- a/platform/lang-api/src/com/intellij/psi/search/GlobalSearchScope.java +++ b/platform/lang-api/src/com/intellij/psi/search/GlobalSearchScope.java @@ -31,11 +31,13 @@ import com.intellij.psi.search.scope.packageSet.NamedScopeManager; import com.intellij.psi.search.scope.packageSet.NamedScopesHolder; import com.intellij.psi.search.scope.packageSet.PackageSet; import com.intellij.util.ArrayUtil; -import com.intellij.util.containers.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; public abstract class GlobalSearchScope extends SearchScope implements ProjectAwareFileFilter { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.search.GlobalSearchScope"); @@ -313,6 +315,27 @@ public abstract class GlobalSearchScope extends SearchScope implements ProjectAw public boolean isSearchOutsideRootModel() { return myScope1.isSearchOutsideRootModel() && myScope2.isSearchOutsideRootModel(); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof IntersectionScope)) return false; + + IntersectionScope that = (IntersectionScope)o; + + if (!myScope1.equals(that.myScope1)) return false; + if (!myScope2.equals(that.myScope2)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + myScope1.hashCode(); + result = 31 * result + myScope2.hashCode(); + return result; + } } private static class UnionScope extends GlobalSearchScope { private final GlobalSearchScope myScope1; @@ -367,6 +390,27 @@ public abstract class GlobalSearchScope extends SearchScope implements ProjectAw public boolean isSearchInLibraries() { return myScope1.isSearchInLibraries() || myScope2.isSearchInLibraries(); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof UnionScope)) return false; + + UnionScope that = (UnionScope)o; + + if (!myScope1.equals(that.myScope1)) return false; + if (!myScope2.equals(that.myScope2)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + myScope1.hashCode(); + result = 31 * result + myScope2.hashCode(); + return result; + } } private static class ProductionScopeFilter extends GlobalSearchScope { @@ -605,6 +649,25 @@ public abstract class GlobalSearchScope extends SearchScope implements ProjectAw } return super.uniteWith(scope); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof FileTypeRestrictionScope)) return false; + + FileTypeRestrictionScope that = (FileTypeRestrictionScope)o; + + if (!Arrays.equals(myFileTypes, that.myFileTypes)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + Arrays.hashCode(myFileTypes); + return result; + } } private static class EmptyScope extends GlobalSearchScope { diff --git a/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java b/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java index b2a61d521ae7..16ccdef9514f 100644 --- a/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java +++ b/platform/lang-api/src/com/intellij/psi/search/PsiSearchRequest.java @@ -27,6 +27,32 @@ public class PsiSearchRequest { @Override public String toString() { - return "PsiSearchRequest: " + word + "; " + processor; + return word + " -> " + processor; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PsiSearchRequest)) return false; + + PsiSearchRequest that = (PsiSearchRequest)o; + + if (caseSensitive != that.caseSensitive) return false; + if (searchContext != that.searchContext) return false; + if (processor != null ? !processor.equals(that.processor) : that.processor != null) return false; + if (searchScope != null ? !searchScope.equals(that.searchScope) : that.searchScope != null) return false; + if (word != null ? !word.equals(that.word) : that.word != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = searchScope != null ? searchScope.hashCode() : 0; + result = 31 * result + (word != null ? word.hashCode() : 0); + result = 31 * result + (int)searchContext; + result = 31 * result + (caseSensitive ? 1 : 0); + result = 31 * result + (processor != null ? processor.hashCode() : 0); + return result; } } diff --git a/platform/lang-api/src/com/intellij/psi/search/RequestResultProcessor.java b/platform/lang-api/src/com/intellij/psi/search/RequestResultProcessor.java index 187018fe3021..e2a1d95c6a06 100644 --- a/platform/lang-api/src/com/intellij/psi/search/RequestResultProcessor.java +++ b/platform/lang-api/src/com/intellij/psi/search/RequestResultProcessor.java @@ -3,11 +3,36 @@ package com.intellij.psi.search; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiReference; import com.intellij.util.Processor; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; /** * @author peter */ public abstract class RequestResultProcessor { + private final Object myEquality; + + protected RequestResultProcessor(@NotNull Object... equality) { + myEquality = Arrays.asList(equality); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + RequestResultProcessor that = (RequestResultProcessor)o; + + if (!myEquality.equals(that.myEquality)) return false; + + return true; + } + + @Override + public int hashCode() { + return myEquality.hashCode(); + } public abstract boolean processTextOccurrence(PsiElement element, int offsetInElement, final Processor consumer); diff --git a/platform/lang-api/src/com/intellij/psi/search/SingleTargetRequestResultProcessor.java b/platform/lang-api/src/com/intellij/psi/search/SingleTargetRequestResultProcessor.java index 49db682184d2..9c9628ec91da 100644 --- a/platform/lang-api/src/com/intellij/psi/search/SingleTargetRequestResultProcessor.java +++ b/platform/lang-api/src/com/intellij/psi/search/SingleTargetRequestResultProcessor.java @@ -10,11 +10,12 @@ import org.jetbrains.annotations.NotNull; /** * @author peter */ -public class SingleTargetRequestResultProcessor extends RequestResultProcessor { +public final class SingleTargetRequestResultProcessor extends RequestResultProcessor { private static final PsiReferenceService ourReferenceService = PsiReferenceService.getService(); private final PsiElement myTarget; public SingleTargetRequestResultProcessor(@NotNull PsiElement target) { + super(target); myTarget = target; } @@ -31,4 +32,8 @@ public class SingleTargetRequestResultProcessor extends RequestResultProcessor { return true; } + @Override + public String toString() { + return "SingleTarget: " + myTarget; + } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java index 878523e34bd8..185908c946bb 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/search/PsiSearchHelperImpl.java @@ -31,7 +31,6 @@ import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Computable; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.util.text.StringUtil; @@ -45,6 +44,7 @@ import com.intellij.psi.search.*; import com.intellij.psi.search.searches.IndexPatternSearch; import com.intellij.util.CommonProcessors; import com.intellij.util.Processor; +import com.intellij.util.containers.CollectionFactory; import com.intellij.util.containers.MultiMap; import com.intellij.util.indexing.FileBasedIndex; import com.intellij.util.text.StringSearcher; @@ -488,27 +488,60 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { myManager.getCacheManager().processFilesWithWord(processor, word, UsageSearchContext.IN_STRINGS, scope, true); } - private static class RequestWithProcessor extends Pair> { + private static class RequestWithProcessor { + final PsiSearchRequest request; + Processor refProcessor; + private RequestWithProcessor(PsiSearchRequest first, Processor second) { - super(first, second); + request = first; + refProcessor = second; + } + + boolean uniteWith(final RequestWithProcessor another) { + if (request.equals(another.request)) { + final Processor myProcessor = refProcessor; + if (myProcessor != another.refProcessor) { + refProcessor = new Processor() { + @Override + public boolean process(PsiReference psiReference) { + if (!myProcessor.process(psiReference)) return false; + return another.refProcessor.process(psiReference); + } + }; + } + return true; + } + return false; + } + + @Override + public String toString() { + return request.toString(); } } public boolean processRequests(@NotNull SearchRequestCollector collector, Processor processor) { - Map> collectors = new HashMap>(); + Map> collectors = CollectionFactory.hashMap(); collectors.put(collector, processor); appendCollectorsFromQueryRequests(collectors); do { - final MultiMap, RequestWithProcessor> singles = new MultiMap, RequestWithProcessor>(); - final List> customs = new ArrayList>(); - distributePrimitives(collectors, singles, customs); + final MultiMap, RequestWithProcessor> globals = new MultiMap, RequestWithProcessor>(); + final List> customs = CollectionFactory.arrayList(); + final LinkedHashSet locals = CollectionFactory.linkedHashSet(); + distributePrimitives(collectors, locals, globals, customs); - if (!processRequestsOptimized(singles)) { + if (!processGlobalRequestsOptimized(globals)) { return false; } + for (RequestWithProcessor local : locals) { + if (!processSingleRequest(local.request, local.refProcessor)) { + return false; + } + } + for (Computable custom : customs) { if (!custom.compute()) { return false; @@ -534,7 +567,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { return changed; } - private boolean processRequestsOptimized(MultiMap, RequestWithProcessor> singles) { + private boolean processGlobalRequestsOptimized(MultiMap, RequestWithProcessor> singles) { if (singles.isEmpty()) { return true; } @@ -543,7 +576,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { final Collection requests = singles.get(singles.keySet().iterator().next()); if (requests.size() == 1) { final RequestWithProcessor theOnly = requests.iterator().next(); - return processSingleRequest(theOnly.first, theOnly.second); + return processSingleRequest(theOnly.request, theOnly.refProcessor); } } @@ -562,8 +595,8 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { final Map searchers = new HashMap(); final Set allWords = new TreeSet(); for (RequestWithProcessor singleRequest : candidateFiles.values()) { - searchers.put(singleRequest, new StringSearcher(singleRequest.first.word, singleRequest.first.caseSensitive, true)); - allWords.add(singleRequest.first.word); + searchers.put(singleRequest, new StringSearcher(singleRequest.request.word, singleRequest.request.caseSensitive, true)); + allWords.add(singleRequest.request.word); } if (progress != null) { @@ -575,7 +608,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { final VirtualFile vfile = psiRoot.getContainingFile().getVirtualFile(); for (final RequestWithProcessor singleRequest : candidateFiles.get(vfile)) { StringSearcher searcher = searchers.get(singleRequest); - if (!LowLevelSearchUtil.processElementsContainingWordInElement(adaptProcessor(singleRequest.first, singleRequest.second), psiRoot, searcher, false, progress)) { + if (!LowLevelSearchUtil.processElementsContainingWordInElement(adaptProcessor(singleRequest.request, singleRequest.refProcessor), psiRoot, searcher, false, progress)) { return false; } } @@ -629,7 +662,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { private static GlobalSearchScope uniteScopes(Collection requests) { GlobalSearchScope commonScope = null; for (RequestWithProcessor r : requests) { - final GlobalSearchScope scope = (GlobalSearchScope)r.first.searchScope; + final GlobalSearchScope scope = (GlobalSearchScope)r.request.searchScope; commonScope = commonScope == null ? scope : commonScope.uniteWith(scope); } assert commonScope != null; @@ -652,7 +685,7 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { } int mask = value.intValue(); for (RequestWithProcessor single : data) { - final PsiSearchRequest request = single.first; + final PsiSearchRequest request = single.request; if ((mask & request.searchContext) != 0 && ((GlobalSearchScope)request.searchScope).contains(file)) { local.putValue(file, single); } @@ -666,7 +699,8 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { return local; } - private void distributePrimitives(final Map> collectors, + private static void distributePrimitives(final Map> collectors, + LinkedHashSet locals, MultiMap, RequestWithProcessor> singles, List> customs) { for (final SearchRequestCollector collector : collectors.keySet()) { @@ -674,19 +708,14 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { for (final PsiSearchRequest primitive : collector.takeSearchRequests()) { final SearchScope scope = primitive.searchScope; if (scope instanceof LocalSearchScope) { - customs.add(new Computable() { - @Override - public Boolean compute() { - return processSingleRequest(primitive, processor); - } - }); + registerRequest(locals, primitive, processor); } else { final List words = StringUtil.getWordsIn(primitive.word); final Set key = new HashSet(words.size() * 2); for (String word : words) { key.add(new IdIndexEntry(word, primitive.caseSensitive)); } - singles.putValue(key, new RequestWithProcessor(primitive, processor)); + registerRequest(singles.getModifiable(key), primitive, processor); } } for (final Processor> customAction : collector.takeCustomSearchActions()) { @@ -700,6 +729,17 @@ public class PsiSearchHelperImpl implements PsiSearchHelper { } } + private static void registerRequest(Collection collection, + PsiSearchRequest primitive, Processor processor) { + final RequestWithProcessor newValue = new RequestWithProcessor(primitive, processor); + for (RequestWithProcessor existing : collection) { + if (existing.uniteWith(newValue)) { + return; + } + } + collection.add(newValue); + } + private boolean processSingleRequest(PsiSearchRequest single, Processor consumer) { return processElementsWithWord(adaptProcessor(single, consumer), single.searchScope, single.word, single.searchContext, single.caseSensitive); } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GrAliasedImportedElementSearcher.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GrAliasedImportedElementSearcher.java index 4712e91b1070..80adef8ecd13 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GrAliasedImportedElementSearcher.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/findUsages/GrAliasedImportedElementSearcher.java @@ -68,6 +68,7 @@ public class GrAliasedImportedElementSearcher extends QueryExecutorBase consumer) { if (!(element instanceof GrReferenceExpression)) { diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/search/GrSourceFilterScope.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/search/GrSourceFilterScope.java index b7b606ad84bb..244204bb57be 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/search/GrSourceFilterScope.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/impl/search/GrSourceFilterScope.java @@ -30,7 +30,7 @@ public class GrSourceFilterScope extends DelegatingGlobalSearchScope { private final ProjectFileIndex myIndex; public GrSourceFilterScope(@NotNull final GlobalSearchScope delegate) { - super(delegate); + super(delegate, "groovy.sourceFilter"); myIndex = ProjectRootManager.getInstance(getProject()).getFileIndex(); } From 1e59665ef324076b03afa35ad03ab8b54aee2d81 Mon Sep 17 00:00:00 2001 From: Constantine Plotnikov Date: Mon, 19 Jul 2010 18:26:32 +0400 Subject: [PATCH 30/92] git4idea: Fixed ISE --- plugins/git4idea/src/git4idea/GitVcs.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/git4idea/src/git4idea/GitVcs.java b/plugins/git4idea/src/git4idea/GitVcs.java index e4043409ac1a..c32aac887549 100644 --- a/plugins/git4idea/src/git4idea/GitVcs.java +++ b/plugins/git4idea/src/git4idea/GitVcs.java @@ -43,6 +43,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.EventDispatcher; import com.intellij.util.containers.ComparatorDelegate; import com.intellij.util.containers.Convertor; +import com.intellij.util.ui.UIUtil; import git4idea.annotate.GitAnnotationProvider; import git4idea.changes.GitChangeProvider; import git4idea.changes.GitChangeUtils; @@ -541,8 +542,13 @@ public class GitVcs extends AbstractVcs { buffer.append("\n"); buffer.append(exception.getMessage()); } - String msg = buffer.toString(); - Messages.showErrorDialog(myProject, msg, GitBundle.getString("error.dialog.title")); + final String msg = buffer.toString(); + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + Messages.showErrorDialog(myProject, msg, GitBundle.getString("error.dialog.title")); + } + }); } } From 78cfed0edd38e3c6bbd77075c69ddd850304d3b0 Mon Sep 17 00:00:00 2001 From: Constantine Plotnikov Date: Mon, 19 Jul 2010 18:28:01 +0400 Subject: [PATCH 31/92] git4idea: fixed IAE --- .../impl/providers/masterKey/windows/WindowsCryptUtils.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/windows/WindowsCryptUtils.java b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/windows/WindowsCryptUtils.java index 67dda0ab82c1..17a8e5b57bff 100644 --- a/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/windows/WindowsCryptUtils.java +++ b/platform/platform-impl/src/com/intellij/ide/passwordSafe/impl/providers/masterKey/windows/WindowsCryptUtils.java @@ -58,6 +58,9 @@ public class WindowsCryptUtils { * @return the the protected form the data */ public static byte[] protect(byte[] data) throws MasterPasswordUnavailableException { + if(data.length == 0) { + return data; + } Memory input = new Memory(data.length); input.write(0, data, 0, data.length); Crypt32.DATA_BLOB in = new Crypt32.DATA_BLOB(); @@ -87,6 +90,9 @@ public class WindowsCryptUtils { * @return the the protected form the data */ public static byte[] unprotect(byte[] data) throws MasterPasswordUnavailableException { + if(data.length == 0) { + return data; + } Memory input = new Memory(data.length); input.write(0, data, 0, data.length); Crypt32.DATA_BLOB in = new Crypt32.DATA_BLOB(); From 39c5fda17b2fb23639066a4a2458cf1074fc0e53 Mon Sep 17 00:00:00 2001 From: Constantine Plotnikov Date: Tue, 20 Jul 2010 19:07:57 +0400 Subject: [PATCH 32/92] git4idea: Refactored basics shelve/unshelve functionality to separate utility class --- .../git4idea/update/GitBaseRebaseProcess.java | 82 +----------- .../src/git4idea/update/GitStashUtils.java | 124 ++++++++++++++++++ 2 files changed, 128 insertions(+), 78 deletions(-) diff --git a/plugins/git4idea/src/git4idea/update/GitBaseRebaseProcess.java b/plugins/git4idea/src/git4idea/update/GitBaseRebaseProcess.java index b4ac25b205f3..06ccc3a15159 100644 --- a/plugins/git4idea/src/git4idea/update/GitBaseRebaseProcess.java +++ b/plugins/git4idea/src/git4idea/update/GitBaseRebaseProcess.java @@ -24,18 +24,13 @@ import com.intellij.openapi.ui.Messages; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vcs.AbstractVcsHelper; -import com.intellij.openapi.vcs.FilePath; -import com.intellij.openapi.vcs.FileStatus; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.*; import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager; -import com.intellij.openapi.vcs.changes.shelf.ShelvedBinaryFile; -import com.intellij.openapi.vcs.changes.shelf.ShelvedChange; import com.intellij.openapi.vcs.changes.shelf.ShelvedChangeList; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.ui.UIUtil; -import com.intellij.vcsUtil.VcsUtil; import git4idea.GitBranch; import git4idea.GitUtil; import git4idea.GitVcs; @@ -46,11 +41,8 @@ import git4idea.i18n.GitBundle; import git4idea.rebase.GitRebaseUtils; import git4idea.ui.GitConvertFilesDialog; import git4idea.ui.GitUIUtil; -import git4idea.vfs.GitVFSListener; -import javax.swing.event.ChangeEvent; import java.io.File; -import java.io.IOException; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; @@ -231,65 +223,8 @@ public abstract class GitBaseRebaseProcess { } if (getUpdatePolicy() == GitVcsSettings.UpdateChangesPolicy.SHELVE) { if (myShelvedChangeList != null) { - // The changes are temporary copied to the first local change list, the next operation will restore them back myProgressIndicator.setText(GitBundle.getString("update.unshelving.changes")); - VirtualFile baseDir = myProject.getBaseDir(); - assert baseDir != null; - String projectPath = baseDir.getPath() + "/"; - // Refresh files that might be affected by unshelve - HashSet filesToRefresh = new HashSet(); - for (ShelvedChange c : myShelvedChangeList.getChanges()) { - if (c.getBeforePath() != null) { - filesToRefresh.add(new File(projectPath + c.getBeforePath())); - } - if (c.getAfterPath() != null) { - filesToRefresh.add(new File(projectPath + c.getAfterPath())); - } - } - for (ShelvedBinaryFile f : myShelvedChangeList.getBinaryFiles()) { - if (f.BEFORE_PATH != null) { - filesToRefresh.add(new File(projectPath + f.BEFORE_PATH)); - } - if (f.AFTER_PATH != null) { - filesToRefresh.add(new File(projectPath + f.BEFORE_PATH)); - } - } - LocalFileSystem.getInstance().refreshIoFiles(filesToRefresh); - // Do unshevle - UIUtil.invokeAndWaitIfNeeded(new Runnable() { - public void run() { - GitVFSListener l = GitVcs.getInstance(myProject).getVFSListener(); - l.setEventsSuppressed(true); - try { - myShelveManager - .unshelveChangeList(myShelvedChangeList, myShelvedChangeList.getChanges(), myShelvedChangeList.getBinaryFiles(), - myChangeManager.getDefaultChangeList(), false); - } - finally { - l.setEventsSuppressed(false); - } - } - }); - Collection paths = new ArrayList(); - for (ShelvedChange c : myShelvedChangeList.getChanges()) { - if (c.getBeforePath() == null || !c.getBeforePath().equals(c.getAfterPath()) || c.getFileStatus() == FileStatus.ADDED) { - paths.add(VcsUtil.getFilePath(projectPath + c.getAfterPath())); - } - } - for (ShelvedBinaryFile f : myShelvedChangeList.getBinaryFiles()) { - if (f.BEFORE_PATH == null || !f.BEFORE_PATH.equals(f.AFTER_PATH) || f.getFileStatus() == FileStatus.ADDED) { - paths.add(VcsUtil.getFilePath(projectPath + f.AFTER_PATH)); - } - } - Map> map = GitUtil.sortGitFilePathsByGitRoot(paths); - for (Map.Entry> e : map.entrySet()) { - try { - GitFileUtils.addPaths(myProject, e.getKey(), e.getValue()); - } - catch (VcsException e1) { - myExceptions.add(e1); - } - } + GitStashUtils.doSystemUnshelve(myProject, myShelvedChangeList, myShelveManager, myChangeManager, myExceptions); } } // Move files back to theirs change lists @@ -440,18 +375,9 @@ public abstract class GitBaseRebaseProcess { changes.addAll(l.getChanges()); } if (changes.size() > 0) { - try { - myProgressIndicator.setText(GitBundle.getString("update.shelving.changes")); - myShelvedChangeList = myShelveManager.shelveChanges(changes, myStashMessage); - myProject.getMessageBus().syncPublisher(ShelveChangesManager.SHELF_TOPIC).stateChanged(new ChangeEvent(this)); - } - catch (IOException e) { - //noinspection ThrowableInstanceNeverThrown - myExceptions.add(new VcsException("Shelving changes failed.", e)); - return false; - } - catch (VcsException e) { - myExceptions.add(e); + myProgressIndicator.setText(GitBundle.getString("update.shelving.changes")); + myShelvedChangeList = GitStashUtils.shelveChanges(myProject, myShelveManager, changes, myStashMessage, myExceptions); + if(myShelvedChangeList == null) { return false; } } diff --git a/plugins/git4idea/src/git4idea/update/GitStashUtils.java b/plugins/git4idea/src/git4idea/update/GitStashUtils.java index 6790ec20e799..856af6a7c8a6 100644 --- a/plugins/git4idea/src/git4idea/update/GitStashUtils.java +++ b/plugins/git4idea/src/git4idea/update/GitStashUtils.java @@ -16,13 +16,33 @@ package git4idea.update; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.FilePath; +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.ChangeListManagerEx; +import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager; +import com.intellij.openapi.vcs.changes.shelf.ShelvedBinaryFile; +import com.intellij.openapi.vcs.changes.shelf.ShelvedChange; +import com.intellij.openapi.vcs.changes.shelf.ShelvedChangeList; +import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ui.UIUtil; +import com.intellij.vcsUtil.VcsUtil; +import git4idea.GitUtil; import git4idea.GitVcs; import git4idea.commands.GitCommand; +import git4idea.commands.GitFileUtils; import git4idea.commands.GitSimpleHandler; import git4idea.config.GitVersion; +import git4idea.vfs.GitVFSListener; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.event.ChangeEvent; +import java.io.File; +import java.io.IOException; +import java.util.*; /** * The class contains utilities for creating and removing stashes. @@ -68,4 +88,108 @@ public class GitStashUtils { } handler.run(); } + + /** + * Perform system level unshelve operation + * + * @param project the project + * @param shelvedChangeList the shelved change list + * @param shelveManager the shelve manager + * @param changeManager the change manager + * @param exceptions the collected exceptions + */ + public static void doSystemUnshelve(final Project project, + final ShelvedChangeList shelvedChangeList, + final ShelveChangesManager shelveManager, + final ChangeListManagerEx changeManager, + List exceptions) { + // The changes are temporary copied to the first local change list, the next operation will restore them back + VirtualFile baseDir = project.getBaseDir(); + assert baseDir != null; + String projectPath = baseDir.getPath() + "/"; + // Refresh files that might be affected by unshelve + HashSet filesToRefresh = new HashSet(); + for (ShelvedChange c : shelvedChangeList.getChanges()) { + if (c.getBeforePath() != null) { + filesToRefresh.add(new File(projectPath + c.getBeforePath())); + } + if (c.getAfterPath() != null) { + filesToRefresh.add(new File(projectPath + c.getAfterPath())); + } + } + for (ShelvedBinaryFile f : shelvedChangeList.getBinaryFiles()) { + if (f.BEFORE_PATH != null) { + filesToRefresh.add(new File(projectPath + f.BEFORE_PATH)); + } + if (f.AFTER_PATH != null) { + filesToRefresh.add(new File(projectPath + f.BEFORE_PATH)); + } + } + LocalFileSystem.getInstance().refreshIoFiles(filesToRefresh); + // Do unshevle + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + public void run() { + GitVFSListener l = GitVcs.getInstance(project).getVFSListener(); + l.setEventsSuppressed(true); + try { + shelveManager + .unshelveChangeList(shelvedChangeList, shelvedChangeList.getChanges(), shelvedChangeList.getBinaryFiles(), + changeManager.getDefaultChangeList(), false); + } + finally { + l.setEventsSuppressed(false); + } + } + }); + Collection paths = new ArrayList(); + for (ShelvedChange c : shelvedChangeList.getChanges()) { + if (c.getBeforePath() == null || !c.getBeforePath().equals(c.getAfterPath()) || c.getFileStatus() == FileStatus.ADDED) { + paths.add(VcsUtil.getFilePath(projectPath + c.getAfterPath())); + } + } + for (ShelvedBinaryFile f : shelvedChangeList.getBinaryFiles()) { + if (f.BEFORE_PATH == null || !f.BEFORE_PATH.equals(f.AFTER_PATH) || f.getFileStatus() == FileStatus.ADDED) { + paths.add(VcsUtil.getFilePath(projectPath + f.AFTER_PATH)); + } + } + Map> map = GitUtil.sortGitFilePathsByGitRoot(paths); + for (Map.Entry> e : map.entrySet()) { + try { + GitFileUtils.addPaths(project, e.getKey(), e.getValue()); + } + catch (VcsException e1) { + exceptions.add(e1); + } + } + } + + /** + * Shelve changes + * + * @param project the context project + * @param shelveManager the shelve manager + * @param changes the changes to process + * @param description the description of for the shelve + * @param exceptions the generated exceptions + * @return created shelved change list or null in case failure + */ + @Nullable + public static ShelvedChangeList shelveChanges(final Project project, final ShelveChangesManager shelveManager, Collection changes, + final String description, + final List exceptions) { + try { + ShelvedChangeList shelve = shelveManager.shelveChanges(changes, description); + project.getMessageBus().syncPublisher(ShelveChangesManager.SHELF_TOPIC).stateChanged(new ChangeEvent(GitStashUtils.class)); + return shelve; + } + catch (IOException e) { + //noinspection ThrowableInstanceNeverThrown + exceptions.add(new VcsException("Shelving changes failed: " + description, e)); + return null; + } + catch (VcsException e) { + exceptions.add(e); + return null; + } + } } From 576be812210871cb9ff8a572fd82b4ff62ca91f1 Mon Sep 17 00:00:00 2001 From: Constantine Plotnikov Date: Tue, 20 Jul 2010 19:11:09 +0400 Subject: [PATCH 33/92] git4idea: added possiblity to detect that there is a merge in progress --- .../src/git4idea/merge/GitMergeUtil.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/plugins/git4idea/src/git4idea/merge/GitMergeUtil.java b/plugins/git4idea/src/git4idea/merge/GitMergeUtil.java index 2a2be19b9b37..dd1909a8d4a2 100644 --- a/plugins/git4idea/src/git4idea/merge/GitMergeUtil.java +++ b/plugins/git4idea/src/git4idea/merge/GitMergeUtil.java @@ -29,16 +29,19 @@ import com.intellij.openapi.vcs.update.FileGroup; import com.intellij.openapi.vcs.update.UpdateInfoTree; import com.intellij.openapi.vcs.update.UpdatedFiles; import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import git4idea.GitRevisionNumber; import git4idea.GitVcs; import git4idea.actions.GitRepositoryAction; import git4idea.i18n.GitBundle; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -186,4 +189,29 @@ public class GitMergeUtil { }); } } + + + /** + * @param root the vcs root + * @return the path to merge head file + */ + @Nullable + private static File getMergeHead(VirtualFile root) { + File gitDir = new File(VfsUtil.virtualToIoFile(root), ".git"); + File f = new File(gitDir, "MERGE_HEAD"); + if (f.exists()) { + return f; + } + return null; + } + + /** + * Checks if the merge is in the progress for the specified git root + * + * @param root the git root + * @return true if the merge_head file presents in the root + */ + public static boolean isMergeInTheProgress(VirtualFile root) { + return getMergeHead(root) != null; + } } From d1bb8cbe8b37d8b1366f28c61f0333a0e2d0ed87 Mon Sep 17 00:00:00 2001 From: Constantine Plotnikov Date: Tue, 20 Jul 2010 19:17:00 +0400 Subject: [PATCH 34/92] git4idea: added support for the case of detecting the branch right after git init --- plugins/git4idea/src/git4idea/GitBranch.java | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/plugins/git4idea/src/git4idea/GitBranch.java b/plugins/git4idea/src/git4idea/GitBranch.java index ed16d4ef8070..5c4d36f6c3de 100644 --- a/plugins/git4idea/src/git4idea/GitBranch.java +++ b/plugins/git4idea/src/git4idea/GitBranch.java @@ -16,6 +16,7 @@ package git4idea; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import git4idea.commands.GitCommand; @@ -26,6 +27,8 @@ import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.StringTokenizer; @@ -96,7 +99,8 @@ public class GitBranch extends GitReference { h.setNoSSH(true); h.setSilent(true); h.addParameters("--no-color"); - for (StringTokenizer lines = new StringTokenizer(h.run(), "\n"); lines.hasMoreTokens();) { + String output = h.run(); + for (StringTokenizer lines = new StringTokenizer(output, "\n"); lines.hasMoreTokens();) { String line = lines.nextToken(); if (line != null && line.startsWith("*")) { //noinspection HardCodedStringLiteral @@ -108,6 +112,20 @@ public class GitBranch extends GitReference { } } } + if (output.trim().length() == 0) { + // the case for situation after git init and before first commit + String text; + try { + text = new String(FileUtil.loadFileText(new File(root.getPath(), ".git/HEAD"), GitUtil.UTF8_ENCODING)).trim(); + } + catch (IOException e) { + return null; + } + String prefix = "ref: refs/heads/"; + if (text.startsWith(prefix)) { + return new GitBranch(text.substring(prefix.length()), true, false); + } + } return null; } From 47fd1856247488cc47e46dd25614a6f670fd6efb Mon Sep 17 00:00:00 2001 From: Constantine Plotnikov Date: Tue, 20 Jul 2010 19:20:18 +0400 Subject: [PATCH 35/92] git4idea: Refactored getting git roots to be more generic --- plugins/git4idea/src/git4idea/GitUtil.java | 36 ++++++++++++++++--- .../git4idea/actions/GitRepositoryAction.java | 16 ++++----- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/plugins/git4idea/src/git4idea/GitUtil.java b/plugins/git4idea/src/git4idea/GitUtil.java index 7c926e66e4e1..a80573a5ef53 100644 --- a/plugins/git4idea/src/git4idea/GitUtil.java +++ b/plugins/git4idea/src/git4idea/GitUtil.java @@ -20,6 +20,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vcs.FilePath; +import com.intellij.openapi.vcs.ProjectLevelVcsManager; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager; import com.intellij.openapi.vcs.versionBrowser.CommittedChangeList; @@ -34,6 +35,7 @@ import git4idea.commands.GitCommand; import git4idea.commands.GitSimpleHandler; import git4idea.commands.StringScanner; import git4idea.config.GitConfigUtil; +import git4idea.i18n.GitBundle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -407,6 +409,28 @@ public class GitUtil { return root; } + /** + * Get git roots for the project. The method shows dialogs in the case when roots cannot be retrieved, so it should be called + * from the event dispatch thread. + * + * @param project the project + * @param vcs the git Vcs + * @return the list of the roots + */ + @NotNull + public static List getGitRoots(Project project, GitVcs vcs) throws VcsException { + final VirtualFile[] contentRoots = ProjectLevelVcsManager.getInstance(project).getRootsUnderVcs(vcs); + if (contentRoots == null || contentRoots.length == 0) { + throw new VcsException(GitBundle.getString("repository.action.missing.roots.unconfigured.message")); + } + final List roots = new ArrayList(gitRootsForPaths(Arrays.asList(contentRoots))); + if (roots.size() == 0) { + throw new VcsException(GitBundle.getString("repository.action.missing.roots.misconfigured")); + } + Collections.sort(roots, VIRTUAL_FILE_COMPARATOR); + return roots; + } + /** * Check if the virtual file under git @@ -486,6 +510,7 @@ public class GitUtil { /** * Covert list of files to relative paths * + * @param root a vcs root * @param filePaths a parameters to convert * @return a list of relative paths * @throws IllegalArgumentException if some path is not under root. @@ -493,7 +518,7 @@ public class GitUtil { public static List toRelativePaths(@NotNull VirtualFile root, @NotNull final Collection filePaths) { ArrayList rc = new ArrayList(filePaths.size()); for (FilePath path : filePaths) { - rc.add(GitUtil.relativePath(root, path)); + rc.add(relativePath(root, path)); } return rc; } @@ -501,18 +526,19 @@ public class GitUtil { /** * Covert list of files to relative paths * - * @param filePaths a parameters to convert + * @param root a vcs root + * @param files a parameters to convert * @return a list of relative paths * @throws IllegalArgumentException if some path is not under root. */ public static List toRelativeFiles(@NotNull VirtualFile root, @NotNull final Collection files) { ArrayList rc = new ArrayList(files.size()); for (VirtualFile file : files) { - rc.add(GitUtil.relativePath(root, file)); + rc.add(relativePath(root, file)); } return rc; } - + /** * Refresh files * @@ -718,7 +744,7 @@ public class GitUtil { while (s.hasMoreData()) { final String line = s.line(); final boolean lineIsAStart = line.startsWith("\u0000\u0001"); - if ((! firstStep) && lineIsAStart) { + if ((!firstStep) && lineIsAStart) { final StringScanner innerScanner = new StringScanner(sb.toString()); sb.setLength(0); consumer.consume(GitChangeUtils.parseChangeList(project, root, innerScanner, skipDiffsForMerge)); diff --git a/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java b/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java index 6690da0abc1a..b48d47c575e3 100644 --- a/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java +++ b/plugins/git4idea/src/git4idea/actions/GitRepositoryAction.java @@ -114,19 +114,15 @@ public abstract class GitRepositoryAction extends DumbAwareAction { */ @Nullable public static List getGitRoots(Project project, GitVcs vcs) { - final VirtualFile[] contentRoots = ProjectLevelVcsManager.getInstance(project).getRootsUnderVcs(vcs); - if (contentRoots == null || contentRoots.length == 0) { - Messages.showErrorDialog(project, GitBundle.getString("repository.action.missing.roots.unconfigured.message"), + List roots; + try { + roots = GitUtil.getGitRoots(project, vcs); + } + catch (VcsException e) { + Messages.showErrorDialog(project, e.getMessage(), GitBundle.getString("repository.action.missing.roots.title")); return null; } - final List roots = new ArrayList(GitUtil.gitRootsForPaths(Arrays.asList(contentRoots))); - if (roots.size() == 0) { - Messages.showErrorDialog(project, GitBundle.getString("repository.action.missing.roots.misconfigured"), - GitBundle.getString("repository.action.missing.roots.title")); - return null; - } - Collections.sort(roots, GitUtil.VIRTUAL_FILE_COMPARATOR); return roots; } From 79fddb16900ced1ffb55b6c59f70633666c7c920 Mon Sep 17 00:00:00 2001 From: Constantine Plotnikov Date: Tue, 27 Jul 2010 21:20:00 +0400 Subject: [PATCH 36/92] git4idea: Added reference tracker --- plugins/git4idea/src/git4idea/GitVcs.java | 28 ++ .../src/git4idea/vfs/GitIgnoreTracker.java | 5 +- .../git4idea/vfs/GitReferenceListener.java | 32 ++ .../src/git4idea/vfs/GitReferenceTracker.java | 280 ++++++++++++++++++ 4 files changed, 344 insertions(+), 1 deletion(-) create mode 100644 plugins/git4idea/src/git4idea/vfs/GitReferenceListener.java create mode 100644 plugins/git4idea/src/git4idea/vfs/GitReferenceTracker.java diff --git a/plugins/git4idea/src/git4idea/GitVcs.java b/plugins/git4idea/src/git4idea/GitVcs.java index c32aac887549..b286f4240811 100644 --- a/plugins/git4idea/src/git4idea/GitVcs.java +++ b/plugins/git4idea/src/git4idea/GitVcs.java @@ -178,6 +178,10 @@ public class GitVcs extends AbstractVcs { * The dispatcher object for git configuration events */ private final EventDispatcher myConfigListeners = EventDispatcher.create(GitConfigListener.class); + /** + * The dispatcher object for git configuration events + */ + private final EventDispatcher myReferenceListeners = EventDispatcher.create(GitReferenceListener.class); /** * Tracker for ignored files */ @@ -198,6 +202,10 @@ public class GitVcs extends AbstractVcs { private final TreeDiffProvider myTreeDiffProvider; private final GitCommitAndPushExecutor myCommitAndPushExecutor; + /** + * The reference tracker + */ + private GitReferenceTracker myReferenceTracker; public static GitVcs getInstance(@NotNull Project project) { return (GitVcs)ProjectLevelVcsManager.getInstance(project).findVcsByName(NAME); @@ -230,6 +238,7 @@ public class GitVcs extends AbstractVcs { myOutgoingChangesProvider = new GitOutgoingChangesProvider(myProject); myTreeDiffProvider = new GitTreeDiffProvider(myProject); myCommitAndPushExecutor = new GitCommitAndPushExecutor(gitCheckinEnvironment); + myReferenceTracker = new GitReferenceTracker(myProject, this, myReferenceListeners.getMulticaster()); myTaskQueue = new BackgroundTaskQueue(myProject, GitBundle.getString("task.queue.title")); } @@ -274,6 +283,23 @@ public class GitVcs extends AbstractVcs { myConfigListeners.removeListener(listener); } + /** + * Add listener for git roots + * + * @param listener the listener to add + */ + public void addGitReferenceListener(GitReferenceListener listener) { + myReferenceListeners.addListener(listener); + } + + /** + * Remove listener for git roots + * + * @param listener the listener to remove + */ + public void removeGitReferenceListener(GitReferenceListener listener) { + myReferenceListeners.removeListener(listener); + } /** * Add listener for git roots @@ -481,6 +507,7 @@ public class GitVcs extends AbstractVcs { if (myGitIgnoreTracker == null) { myGitIgnoreTracker = new GitIgnoreTracker(myProject, this); } + myReferenceTracker.activate(); GitUsersComponent.getInstance(myProject).activate(); GitProjectLogManager.getInstance(myProject).activate(); } @@ -506,6 +533,7 @@ public class GitVcs extends AbstractVcs { myConfigTracker.dispose(); myConfigTracker = null; } + myReferenceTracker.deactivate(); GitUsersComponent.getInstance(myProject).deactivate(); GitProjectLogManager.getInstance(myProject).deactivate(); } diff --git a/plugins/git4idea/src/git4idea/vfs/GitIgnoreTracker.java b/plugins/git4idea/src/git4idea/vfs/GitIgnoreTracker.java index 4bb46b4ed0e4..5d9d0708dad9 100644 --- a/plugins/git4idea/src/git4idea/vfs/GitIgnoreTracker.java +++ b/plugins/git4idea/src/git4idea/vfs/GitIgnoreTracker.java @@ -233,7 +233,10 @@ public class GitIgnoreTracker { private static VirtualFile scanParents(VirtualFile root) { VirtualFile meta = root.findChild(GIT_FOLDER); if (meta != null) { - root.findFileByRelativePath(LOCAL_EXCLUDE); + final VirtualFile localExclude = root.findFileByRelativePath(LOCAL_EXCLUDE); + if (localExclude != null) { + localExclude.getTimeStamp(); + } return root; } else { diff --git a/plugins/git4idea/src/git4idea/vfs/GitReferenceListener.java b/plugins/git4idea/src/git4idea/vfs/GitReferenceListener.java new file mode 100644 index 000000000000..13d011d54294 --- /dev/null +++ b/plugins/git4idea/src/git4idea/vfs/GitReferenceListener.java @@ -0,0 +1,32 @@ +/* + * 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.vfs; + +import com.intellij.openapi.vfs.VirtualFile; + +import java.util.EventListener; + +/** + * The listener for reference changes + */ +public interface GitReferenceListener extends EventListener { + /** + * This method is invoked when some references changed. The events do not describe which ones. + * + * @param root the root for which references might have changed, null if all roots might have been affected + */ + void referencesChanged(VirtualFile root); +} diff --git a/plugins/git4idea/src/git4idea/vfs/GitReferenceTracker.java b/plugins/git4idea/src/git4idea/vfs/GitReferenceTracker.java new file mode 100644 index 000000000000..44d726b04ccf --- /dev/null +++ b/plugins/git4idea/src/git4idea/vfs/GitReferenceTracker.java @@ -0,0 +1,280 @@ +/* + * 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.vfs; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vfs.*; +import com.intellij.util.containers.HashSet; +import git4idea.GitUtil; +import git4idea.GitVcs; + +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; + +/** + * The tracker for the git references + */ +public class GitReferenceTracker { + /** + * The root references that must be tracked + */ + private static final Set myRootReferences = new HashSet(); + + static { + myRootReferences.add("HEAD"); + myRootReferences.add("refs"); + myRootReferences.add("MERGE_HEAD"); + // note, they are here intentionally, since their presence changes the way HEAD is interpreted + myRootReferences.add("rebase_merge"); + myRootReferences.add("rebase_apply"); + } + + /** + * The context project + */ + private final Project myProject; + /** + * The vcs instance that requested the tracking + */ + private final GitVcs myVcs; + /** + * The event multicaster + */ + private final GitReferenceListener myListener; + /** + * The listener for vcs roots + */ + private final GitRootsListener myRootsListener; + /** + * The list of git roots + */ + private final AtomicReference> myGitRoots = + new AtomicReference>(Collections.emptyList()); + /** + * The list of git roots + */ + private final AtomicReference myState = new AtomicReference(State.DEACTIVATED); + /** + * File system listener + */ + private final MyVfsListener myVfsListener; + + /** + * The reference tracker + * + * @param project the project + * @param vcs the vcs that created tracker + * @param listener the listener to use for notifications (multicaster is expected) + */ + public GitReferenceTracker(Project project, GitVcs vcs, GitReferenceListener listener) { + myProject = project; + myVcs = vcs; + myListener = listener; + myVfsListener = new MyVfsListener(); + myRootsListener = new GitRootsListener() { + @Override + public void gitRootsChanged() { + checkRoots(); + } + }; + } + + /** + * Start listening for events + */ + public void activate() { + if (!myState.compareAndSet(State.DEACTIVATED, State.ACTIVATED)) { + return; + } + if (myProject.isDefault()) { + return; + } + checkRoots(); + LocalFileSystem.getInstance().addVirtualFileListener(myVfsListener); + myVcs.addGitRootsListener(myRootsListener); + } + + /** + * Deactivate service + */ + public void deactivate() { + if (!myState.compareAndSet(State.ACTIVATED, State.DEACTIVATED)) { + return; + } + removeListeners(); + } + + /** + * Remove listeners from services + */ + private void removeListeners() { + myVcs.removeGitRootsListener(myRootsListener); + LocalFileSystem.getInstance().removeVirtualFileListener(myVfsListener); + } + + /** + * Dispose the tracker removing all listeners + */ + public void dispose() { + if (myState.getAndSet(State.DISPOSED) != State.ACTIVATED) { + return; + } + removeListeners(); + } + + /** + * Visit roots so file listener will be modified if special status changed + */ + private void checkRoots() { + try { + List roots = GitUtil.getGitRoots(myProject, myVcs); + myGitRoots.set(roots); + for (VirtualFile root : roots) { + final VirtualFile git = root.findChild(".git"); + if (git != null) { + for (String name : myRootReferences) { + final VirtualFile child = git.findChild(name); + if (child != null) { + child.getTimeStamp(); + } + } + final VirtualFile infoRefs = root.findFileByRelativePath("info/refs"); + if (infoRefs != null) { + infoRefs.getTimeStamp(); + } + visitRecursively(git.findChild("refs")); + } + } + notifyChanges(null); + } + catch (VcsException e) { + myGitRoots.set(Collections.emptyList()); + } + } + + private void notifyChanges(VirtualFile root) { + myListener.referencesChanged(root); + } + + /** + * Visit recursively + * + * @param child the child directory to visit + */ + private static void visitRecursively(VirtualFile child) { + if (child == null) { + return; + } + child.getTimeStamp(); + LinkedList toVisit = new LinkedList(); + toVisit.add(child); + while (!toVisit.isEmpty()) { + VirtualFile file = toVisit.removeLast(); + if (file.isValid() && file.isDirectory()) { + toVisit.addAll(Arrays.asList(file.getChildren())); + } + } + } + + /** + * The listener for vfs events + */ + class MyVfsListener extends VirtualFileAdapter { + + @Override + public void contentsChanged(VirtualFileEvent event) { + checkFile(event.getParent(), event.getFileName()); + } + + @Override + public void fileCreated(VirtualFileEvent event) { + checkFile(event.getParent(), event.getFileName()); + } + + @Override + public void fileDeleted(VirtualFileEvent event) { + checkFile(event.getParent(), event.getFileName()); + } + + @Override + public void fileMoved(VirtualFileMoveEvent event) { + checkFile(event.getNewParent(), event.getFileName()); + checkFile(event.getOldParent(), event.getFileName()); + } + + @Override + public void fileCopied(VirtualFileCopyEvent event) { + checkFile(event.getParent(), event.getFileName()); + } + + @Override + public void propertyChanged(VirtualFilePropertyEvent event) { + if (event.getPropertyName() == VirtualFile.PROP_NAME) { + checkFile(event.getParent(), (String)event.getOldValue()); + checkFile(event.getParent(), (String)event.getNewValue()); + } + } + + /** + * Check if the file change might affect reference mapping + * + * @param parent the file parent to check + * @param name the name check + */ + private void checkFile(VirtualFile parent, String name) { + VirtualFile git = parent; + while (git != null && !git.getName().equals(".git")) { + git = git.getParent(); + } + if (git == null) { + return; + } + if (parent == git && myRootReferences.contains(name)) { + notifyChanges(git.getParent()); + } + else if (parent.getParent() == git && name.equals("refs") && parent.getName().equals("info")) { + notifyChanges(git.getParent()); + } + else { + while (parent != null && parent.getParent() != git) { + parent = parent.getParent(); + } + if (parent != null && parent.isDirectory() && parent.getName().equals("refs")) { + notifyChanges(git.getParent()); + } + } + } + } + + /** + * The component state + */ + private enum State { + /** + * Initial state + */ + DEACTIVATED, + /** + * Started state + */ + ACTIVATED, + /** + * Disposed + */ + DISPOSED + } +} From beef84ae5fd9acf7f58163ad9c2ed02a7ba3ce21 Mon Sep 17 00:00:00 2001 From: Constantine Plotnikov Date: Fri, 30 Jul 2010 18:09:27 +0400 Subject: [PATCH 37/92] git4idea: the entire refrence checked on the branch test --- .../src/git4idea/validators/GitBranchNameValidator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/git4idea/src/git4idea/validators/GitBranchNameValidator.java b/plugins/git4idea/src/git4idea/validators/GitBranchNameValidator.java index 0f4c5a90dede..6dde70886023 100644 --- a/plugins/git4idea/src/git4idea/validators/GitBranchNameValidator.java +++ b/plugins/git4idea/src/git4idea/validators/GitBranchNameValidator.java @@ -33,7 +33,7 @@ public class GitBranchNameValidator implements InputValidator { // based on the git-check-ref-format command description final String goodChar = "[!-~&&[^\\^~:\\[\\]\\?\\*\\./<>\\|'`]]"; final String component = "(?:" + goodChar + "+\\.?)+"; - REF_FORMAT_PATTERN = Pattern.compile(component + "+(?:/*" + component + ")*"); + REF_FORMAT_PATTERN = Pattern.compile("^" + component + "+(?:/*" + component + ")*$"); } /** From 5c08b021c208b72ea94d8e0b08517fc0d6d7606d Mon Sep 17 00:00:00 2001 From: Constantine Plotnikov Date: Sat, 31 Jul 2010 04:08:38 +0400 Subject: [PATCH 38/92] git4idea: IDEA-55724: initial support for checking out multiple branches added --- plugins/git4idea/src/META-INF/plugin.xml | 3 + plugins/git4idea/src/git4idea/GitUtil.java | 15 + plugins/git4idea/src/git4idea/GitVcs.java | 3 + .../branches/GitBranchConfiguration.java | 170 ++++ .../GitBranchConfigurationChangedDialog.form | 51 + .../GitBranchConfigurationChangedDialog.java | 319 +++++++ .../branches/GitBranchConfigurations.java | 898 ++++++++++++++++++ .../GitBranchConfigurationsListener.java | 31 + .../checkout/branches/GitBranchesWidget.java | 553 +++++++++++ .../checkout/branches/GitCheckoutProcess.java | 756 +++++++++++++++ .../GitManageConfigurationsDialog.form | 121 +++ .../GitManageConfigurationsDialog.java | 350 +++++++ .../branches/GitSwitchBranchesDialog.form | 80 ++ .../branches/GitSwitchBranchesDialog.java | 879 +++++++++++++++++ .../src/git4idea/commands/GitCommand.java | 7 +- .../git4idea/src/git4idea/ui/GitUIUtil.java | 12 + 16 files changed, 4247 insertions(+), 1 deletion(-) create mode 100644 plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfiguration.java create mode 100644 plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurationChangedDialog.form create mode 100644 plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurationChangedDialog.java create mode 100644 plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurations.java create mode 100644 plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurationsListener.java create mode 100644 plugins/git4idea/src/git4idea/checkout/branches/GitBranchesWidget.java create mode 100644 plugins/git4idea/src/git4idea/checkout/branches/GitCheckoutProcess.java create mode 100644 plugins/git4idea/src/git4idea/checkout/branches/GitManageConfigurationsDialog.form create mode 100644 plugins/git4idea/src/git4idea/checkout/branches/GitManageConfigurationsDialog.java create mode 100644 plugins/git4idea/src/git4idea/checkout/branches/GitSwitchBranchesDialog.form create mode 100644 plugins/git4idea/src/git4idea/checkout/branches/GitSwitchBranchesDialog.java diff --git a/plugins/git4idea/src/META-INF/plugin.xml b/plugins/git4idea/src/META-INF/plugin.xml index c2c4a73b4463..400291ee60bc 100644 --- a/plugins/git4idea/src/META-INF/plugin.xml +++ b/plugins/git4idea/src/META-INF/plugin.xml @@ -104,6 +104,9 @@ + diff --git a/plugins/git4idea/src/git4idea/GitUtil.java b/plugins/git4idea/src/git4idea/GitUtil.java index a80573a5ef53..1be7586ac1c9 100644 --- a/plugins/git4idea/src/git4idea/GitUtil.java +++ b/plugins/git4idea/src/git4idea/GitUtil.java @@ -491,6 +491,21 @@ public class GitUtil { return relativePath(VfsUtil.virtualToIoFile(root), VfsUtil.virtualToIoFile(file)); } + /** + * Get relative path + * + * @param root a root file + * @param file a virtual file + * @return a relative path + * @throws IllegalArgumentException if path is not under root. + */ + public static String relativeOrFullPath(final VirtualFile root, VirtualFile file) { + if (root == null) { + file.getPath(); + } + return relativePath(VfsUtil.virtualToIoFile(root), VfsUtil.virtualToIoFile(file)); + } + /** * Get relative path * diff --git a/plugins/git4idea/src/git4idea/GitVcs.java b/plugins/git4idea/src/git4idea/GitVcs.java index b286f4240811..e4fffdff6da5 100644 --- a/plugins/git4idea/src/git4idea/GitVcs.java +++ b/plugins/git4idea/src/git4idea/GitVcs.java @@ -51,6 +51,7 @@ import git4idea.changes.GitCommittedChangeListProvider; import git4idea.changes.GitOutgoingChangesProvider; import git4idea.checkin.GitCheckinEnvironment; import git4idea.checkin.GitCommitAndPushExecutor; +import git4idea.checkout.branches.GitBranchConfigurations; import git4idea.commands.GitCommand; import git4idea.commands.GitSimpleHandler; import git4idea.config.GitVcsConfigurable; @@ -510,6 +511,7 @@ public class GitVcs extends AbstractVcs { myReferenceTracker.activate(); GitUsersComponent.getInstance(myProject).activate(); GitProjectLogManager.getInstance(myProject).activate(); + GitBranchConfigurations.getInstance(myProject).activate(); } /** @@ -517,6 +519,7 @@ public class GitVcs extends AbstractVcs { */ @Override protected void deactivate() { + GitBranchConfigurations.getInstance(myProject).deactivate(); if (myRootTracker != null) { myRootTracker.dispose(); myRootTracker = null; diff --git a/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfiguration.java b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfiguration.java new file mode 100644 index 000000000000..b6ac0cda1cec --- /dev/null +++ b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfiguration.java @@ -0,0 +1,170 @@ +/* + * 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.checkout.branches; + +import com.intellij.util.xmlb.XmlSerializerUtil; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +/** + * The branch configuration wrapper + */ +public class GitBranchConfiguration { + /** + * The configuration + */ + final GitBranchConfigurations myConfig; + /** + * The name of configuration + */ + private String myName; + /** + * The auto-detected flag + */ + private boolean myAutoDetected; + /** + * The root to reference mapping + */ + private HashMap myReferences = new HashMap(); + /** + * The + */ + @Nullable private GitBranchConfigurations.BranchChanges myChanges; + + /** + * The configuration with the specified name + * + * @param config + * @param name + */ + GitBranchConfiguration(GitBranchConfigurations config, String name) { + myConfig = config; + myName = name; + } + + /** + * @return the name of configuration + */ + public String getName() { + synchronized (myConfig.getStateLock()) { + return myName; + } + } + + /** + * @return the name of configuration + */ + public void setName(String name) { + synchronized (myConfig.getStateLock()) { + assert name != null; + if (name.equals(myName)) { + return; + } + assert myConfig.configurationRenamed(this, myName, name) : "Configuration should have existed"; + myName = name; + } + } + + + /** + * @return the copy of list of branches for configuration + */ + public Map getReferences() { + synchronized (myConfig.getStateLock()) { + return new HashMap(myReferences); + } + } + + public void setBranch(String root, String reference) { + synchronized (myConfig.getStateLock()) { + myReferences.put(root, reference); + } + } + + public String getReference(String root) { + synchronized (myConfig.getStateLock()) { + return myReferences.get(root); + } + } + + public void clearReferences() { + synchronized (myConfig.getStateLock()) { + myReferences.clear(); + } + } + + + /** + * Set changes to the configuration. Note that changes are assumed not to change + * + * @param changes + */ + void setChanges(@Nullable GitBranchConfigurations.BranchChanges changes) { + synchronized (myConfig.getStateLock()) { + myChanges = changes; + } + } + + /** + * @return get copy of stored changes descriptor + */ + @Nullable + GitBranchConfigurations.BranchChanges getChanges() { + synchronized (myConfig.getStateLock()) { + if (myChanges == null) { + return null; + } + GitBranchConfigurations.BranchChanges rc = new GitBranchConfigurations.BranchChanges(); + XmlSerializerUtil.copyBean(myChanges, rc); + return rc; + } + } + + + /** + * Set mapping for roots + * + * @param references the new mapping + */ + public void setReferences(Map references) { + synchronized (myConfig.getStateLock()) { + myReferences.clear(); + myReferences.putAll(references); + } + } + + /** + * @return true if the configuration was auto-detected + */ + public boolean isAutoDetected() { + synchronized (myConfig.getStateLock()) { + return myAutoDetected; + } + } + + /** + * Indicate if configuration was auto-detected + * + * @param autoDetected new value + */ + public void setAutoDetected(boolean autoDetected) { + synchronized (myConfig.getStateLock()) { + myAutoDetected = autoDetected; + } + } +} diff --git a/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurationChangedDialog.form b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurationChangedDialog.form new file mode 100644 index 000000000000..3ea33f0a08d7 --- /dev/null +++ b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurationChangedDialog.form @@ -0,0 +1,51 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurationChangedDialog.java b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurationChangedDialog.java new file mode 100644 index 000000000000..717ae4931284 --- /dev/null +++ b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurationChangedDialog.java @@ -0,0 +1,319 @@ +/* + * 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.checkout.branches; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.DocumentAdapter; +import com.intellij.ui.table.JBTable; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.table.AbstractTableModel; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + * The dialog shown to inform of changes in the current configuration + */ +public class GitBranchConfigurationChangedDialog extends DialogWrapper { + /** + * The new configuration exit code + */ + private static final int NEW_CONFIGURATION = NEXT_USER_EXIT_CODE; + /** + * The name text field + */ + private JTextField myNameTextField; + /** + * The table that describes configuration changes + */ + private JBTable myTable; + /** + * The root panel + */ + private JPanel myRootPanel; + /** + * The base directory for the project + */ + private final File myBaseFile; + /** + * The branch descriptors to show in the table + */ + private final List myBranches; + /** + * The new configuration action + */ + private DialogWrapperExitAction myNewAction; + + + /** + * The constructor from project + * + * @param project the project to use to display window + * @param config + * @param branches + * @param names + */ + protected GitBranchConfigurationChangedDialog(Project project, + final GitBranchConfiguration config, + List branches, final Set names) { + super(project, true); + setTitle("Git Branch Configuration Changed"); + myBranches = branches; + VirtualFile baseDir = project.getBaseDir(); + myBaseFile = baseDir == null ? null : new File(baseDir.getPath()); + myTable.setModel(new DescriptorTableModel()); + myNameTextField.setText(config.getName()); + myNewAction = new DialogWrapperExitAction("New Configuration", NEW_CONFIGURATION); + myNameTextField.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + String text = myNameTextField.getText().trim(); + if (text.length() == 0) { + setError("Empty configuration name is not allowed."); + } + else if (text.equals(config.getName())) { + setError(null); + myNewAction.setEnabled(false); + } + else if (names.contains(text)) { + setError("There is another configuration with the same name"); + } + else { + setError(null); + } + } + + private void setError(String s) { + setErrorText(s); + setOKActionEnabled(s == null); + myNewAction.setEnabled(s == null); + } + }); + + setOKButtonText("Update"); + init(); + } + + + /** + * {@inheritDoc} + */ + @Override + protected JComponent createCenterPanel() { + return myRootPanel; + } + + /** + * Show the dialog in AWT thread and wait for its completion + * + * @param settings the settings to use + * @param config the configuration to check + * @param roots the roots collection + * @return null if project is cancelled, or configuration that user has selected for storing the current state (created or updated) + */ + @Nullable + static GitBranchConfiguration showDialog(final GitBranchConfigurations settings, + final GitBranchConfiguration config, + final List roots) + throws VcsException { + final Set names = settings.getConfigurationNames(); + final Ref name = new Ref(); + final Ref code = new Ref(); + final List list = prepareBranchDescriptors(settings, config, roots); + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + GitBranchConfigurationChangedDialog d = new GitBranchConfigurationChangedDialog(settings.getProject(), config, list, names); + d.show(); + code.set(d.getExitCode()); + name.set(d.myNameTextField.getText()); + } + }); + if (code.get() == CANCEL_EXIT_CODE) { + return null; + } + GitBranchConfiguration updateConfig; + synchronized (settings.getStateLock()) { + if (code.get() == OK_EXIT_CODE) { + updateConfig = config; + updateConfig.setName(name.get()); + } + else if (code.get() == NEW_CONFIGURATION) { + updateConfig = settings.createConfiguration(name.get()); + } + else { + throw new RuntimeException("Unexpected exit code: " + code.get()); + } + updateConfig.clearReferences(); + for (BranchDescriptor d : list) { + if (d.root != null) { + updateConfig.setBranch(d.root.getPath(), d.actualRef); + } + } + } + return updateConfig; + } + + + /** + * {@inheritDoc} + */ + @Override + protected Action[] createActions() { + return new Action[]{getOKAction(), myNewAction, getCancelAction()}; + } + + /** + * {@inheritDoc} + */ + @Override + protected String getDimensionServiceKey() { + return getClass().getName(); + } + + static List prepareBranchDescriptors(GitBranchConfigurations settings, + GitBranchConfiguration config, + List roots) + throws VcsException { + List rc = new ArrayList(); + Map map = config.getReferences(); + for (VirtualFile root : roots) { + BranchDescriptor d = new BranchDescriptor(); + d.root = root; + d.actualRef = settings.describeRoot(root); + d.storedRef = map.remove(root.getPath()); + rc.add(d); + } + for (Map.Entry m : map.entrySet()) { + BranchDescriptor d = new BranchDescriptor(); + d.storedRoot = m.getKey(); + d.storedRef = m.getValue(); + rc.add(d); + } + return rc; + } + + /** + * The table model that describes roots + */ + private class DescriptorTableModel extends AbstractTableModel { + /** + * The relative path for the root + */ + private static final int ROOT_COLUMN = 0; + /** + * The actual reference + */ + private static final int ACTUAL_REF_COLUMN = 1; + /** + * The reference stored in the configuration + */ + private static final int STORED_REF_COLUMN = 2; + + + /** + * {@inheritDoc} + */ + @Override + public int getRowCount() { + return myBranches.size(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getColumnCount() { + return STORED_REF_COLUMN + 1; + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + BranchDescriptor d = myBranches.get(rowIndex); + switch (columnIndex) { + case ROOT_COLUMN: + return d.getRoot(myBaseFile); + case ACTUAL_REF_COLUMN: + return d.actualRef == null ? "" : d.actualRef; + case STORED_REF_COLUMN: + return d.storedRef == null ? "" : d.storedRef; + default: + throw new IllegalStateException("Unexpected column"); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String getColumnName(int column) { + switch (column) { + case ROOT_COLUMN: + return "Vcs Root"; + case ACTUAL_REF_COLUMN: + return "Actual"; + case STORED_REF_COLUMN: + return "Configured"; + default: + throw new IllegalStateException("Unexpected column"); + } + } + } + + + /** + * The descriptor for the branch + */ + private static class BranchDescriptor { + /** + * The vcs root + */ + VirtualFile root; + /** + * The branch information stored in the configuration + */ + String storedRoot; + /** + * The stored reference + */ + String storedRef; + /** + * The actual reference + */ + String actualRef; + + public String getRoot(final File baseFile) { + String path = root == null ? storedRoot : root.getPath(); + String relative = baseFile == null ? path : FileUtil.getRelativePath(baseFile, new File(path)); + return relative == null ? path : relative; + } + } +} diff --git a/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurations.java b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurations.java new file mode 100644 index 000000000000..35cdcb832a12 --- /dev/null +++ b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurations.java @@ -0,0 +1,898 @@ +/* + * 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.checkout.branches; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ex.ProjectManagerEx; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.vcs.FileStatus; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager; +import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.util.EventDispatcher; +import git4idea.GitBranch; +import git4idea.GitUtil; +import git4idea.GitVcs; +import git4idea.commands.GitCommand; +import git4idea.commands.GitSimpleHandler; +import git4idea.merge.GitMergeUtil; +import git4idea.rebase.GitRebaseUtils; +import git4idea.ui.GitUIUtil; +import git4idea.vfs.GitReferenceListener; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +/** + * Git branch configurations settings and project level state + */ +@State( + name = "Git.Branch.Configurations", + storages = {@Storage( + id = "ws", + file = "$WORKSPACE_FILE$")}) +public class GitBranchConfigurations implements PersistentStateComponent, Disposable { + /** + * The logger + */ + private static final Logger LOG = Logger.getInstance(GitBranchConfigurations.class.getName()); + /** + * The comparator for branch configuration by name + */ + private static final Comparator CONFIGURATION_COMPARATOR = new Comparator() { + @Override + public int compare(BranchConfiguration o1, BranchConfiguration o2) { + return o1.NAME.compareTo(o2.NAME); + } + }; + /** + * The comparator for branch information by root + */ + private static final Comparator BRANCH_INFO_COMPARATOR = new Comparator() { + @Override + public int compare(BranchInfo o1, BranchInfo o2) { + return o1.ROOT.compareTo(o2.ROOT); + } + }; + /** + * The project + */ + private final Project myProject; + /** + * The git vcs + */ + private final GitVcs myVcs; + /** + * The shelve manager instance + */ + private final ShelveChangesManager myShelveManager; + /** + * The dirty scope manager + */ + private final VcsDirtyScopeManager myDirtyScopeManager; + /** + * Change manager + */ + private final ChangeListManagerEx myChangeManager; + /** + * Project manager + */ + private final ProjectManagerEx myProjectManager; + /** + * The state lock + */ + private final Object myStateLock = new Object(); + /** + * The set of configurations + */ + private final HashMap myConfigurations = new HashMap(); + /** + * Create event dispatcher for configuration events + */ + private final EventDispatcher myListeners = + EventDispatcher.create(GitBranchConfigurationsListener.class); + /** + * The current configuration + */ + private GitBranchConfiguration myCurrentConfiguration; + /** + * The reference listener + */ + private final GitReferenceListener myReferenceListener; + /** + * The current status (cached, invalidated when references change) + */ + private SpecialStatus myCurrentStatus; + /** + * The collection of git roots + */ + private List myGitRoots = Collections.emptyList(); + /** + * If true, checkout background process is in progress + */ + private boolean myCheckoutIsInProgress = false; + /** + * The widget uninstall action (on deactivate) + */ + private Runnable myWidgetUninstall; + /** + * Listener for changes + */ + private final ChangeListAdapter myChangesListener; + + /** + * The constructor used to dependency injection + * + * @param project the project + * @param shelveManager the shelve manager + * @param dirtyScopeManager the dirty scope manager + * @param changeManager the change manager + * @param projectManager the project manager + */ + public GitBranchConfigurations(Project project, ShelveChangesManager shelveManager, + VcsDirtyScopeManager dirtyScopeManager, + ChangeListManagerEx changeManager, + ProjectManagerEx projectManager) { + myProject = project; + myVcs = GitVcs.getInstance(project); + myShelveManager = shelveManager; + myDirtyScopeManager = dirtyScopeManager; + myChangeManager = changeManager; + myProjectManager = projectManager; + myReferenceListener = new GitReferenceListener() { + @Override + public void referencesChanged(VirtualFile root) { + GitBranchConfigurations.this.referencesChanged(); + } + }; + Disposer.register(myProject, this); + myChangesListener = new ChangeListAdapter() { + @Override + public void changesRemoved(Collection changes, ChangeList fromList) { + GitBranchConfigurations.this.referencesChanged(); + } + + @Override + public void changesAdded(Collection changes, ChangeList toList) { + GitBranchConfigurations.this.referencesChanged(); + } + }; + } + + /** + * Add listener + * + * @param l the listener + */ + public void addConfigurationListener(GitBranchConfigurationsListener l) { + myListeners.addListener(l); + } + + /** + * Remove listener + * + * @param l the listener + */ + public void removeConfigurationListener(GitBranchConfigurationsListener l) { + myListeners.removeListener(l); + } + + /** + * Handle reference change, also notified when roots changed. + */ + public void referencesChanged() { + synchronized (myStateLock) { + updateRootCollection(); + updateSpecialStatus(); + fireReferencesChanged(); + } + } + + /** + * Fire that references changed + */ + private void fireReferencesChanged() { + myListeners.getMulticaster().referencesChanged(); + } + + + /** + * Update collections of roots + */ + private void updateRootCollection() { + try { + myGitRoots = GitUtil.getGitRoots(myProject, myVcs); + } + catch (VcsException e) { + LOG.warn("Empty list of roots is detected", e); + myGitRoots = Collections.emptyList(); + } + } + + /** + * Update special status + */ + private void updateSpecialStatus() { + SpecialStatus p = myCurrentStatus; + myCurrentStatus = calculateSpecialStatus(); + if (p != myCurrentStatus) { + myListeners.getMulticaster().specialStatusChanged(); + } + } + + /** + * Activate component + */ + public void activate() { + myVcs.addGitReferenceListener(myReferenceListener); + myChangeManager.addChangeListListener(myChangesListener); + synchronized (myStateLock) { + updateRootCollection(); + if (myCurrentConfiguration == null) { + if (calculateSpecialStatus() == SpecialStatus.NORMAL) { + try { + detectLocals(); + } + catch (VcsException e) { + LOG.error("Exception during detecting local configurations", e); + } + } + } + referencesChanged(); + } + if (!ApplicationManager.getApplication().isHeadlessEnvironment()) { + myWidgetUninstall = GitBranchesWidget.install(myProject, this); + } + } + + /** + * Deactivate component + */ + public void deactivate() { + myVcs.removeGitReferenceListener(myReferenceListener); + myChangeManager.removeChangeListListener(myChangesListener); + if (myWidgetUninstall != null) { + myWidgetUninstall.run(); + myWidgetUninstall = null; + } + } + + /** + * Get component instance + * + * @param project a context project + * @return the git settings + */ + public static GitBranchConfigurations getInstance(Project project) { + return ServiceManager.getService(project, GitBranchConfigurations.class); + } + + + @Override + public void dispose() { + deactivate(); + } + + + /** + * {@inheritDoc} + */ + @SuppressWarnings({"NonPrivateFieldAccessedInSynchronizedContext"}) + @Override + public State getState() { + synchronized (myStateLock) { + State rc = new State(); + rc.CURRENT = myCurrentConfiguration == null ? null : myCurrentConfiguration.getName(); + ArrayList cs = new ArrayList(myConfigurations.size()); + for (GitBranchConfiguration ci : myConfigurations.values()) { + BranchConfiguration c = new BranchConfiguration(); + c.NAME = ci.getName(); + Map map = ci.getReferences(); + ArrayList bs = new ArrayList(map.size()); + for (Map.Entry m : map.entrySet()) { + BranchInfo b = new BranchInfo(); + b.ROOT = m.getKey(); + b.REFERENCE = m.getValue(); + bs.add(b); + } + c.BRANCHES = bs.toArray(new BranchInfo[bs.size()]); + Arrays.sort(c.BRANCHES, BRANCH_INFO_COMPARATOR); + c.CHANGES = ci.getChanges(); + c.IS_AUTO_DETECTED = ci.isAutoDetected(); + cs.add(c); + } + rc.CONFIGURATIONS = cs.toArray(new BranchConfiguration[cs.size()]); + Arrays.sort(rc.CONFIGURATIONS, CONFIGURATION_COMPARATOR); + return rc; + } + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings({"NonPrivateFieldAccessedInSynchronizedContext"}) + @Override + public void loadState(State state) { + synchronized (myStateLock) { + myConfigurations.clear(); + for (BranchConfiguration bc : state.CONFIGURATIONS) { + GitBranchConfiguration n = new GitBranchConfiguration(this, bc.NAME); + myConfigurations.put(n.getName(), n); + for (BranchInfo bi : bc.BRANCHES) { + n.setBranch(bi.ROOT, bi.REFERENCE); + } + myConfigurations.put(bc.NAME, n); + n.setAutoDetected(bc.IS_AUTO_DETECTED); + } + if (myCurrentConfiguration == null) { + myCurrentConfiguration = myConfigurations.get(state.CURRENT); + } + else { + myCurrentConfiguration = myConfigurations.get(myCurrentConfiguration.getName()); + if (myCurrentConfiguration == null) { + myCurrentConfiguration = myConfigurations.get(state.CURRENT); + } + } + fireCurrentConfigurationChanged(); + fireConfigurationsChanged(); + } + } + + /** + * @return the candidate remote configurations + */ + List getRemotesCandidates() { + try { + final List roots; + synchronized (myStateLock) { + roots = myGitRoots; + } + return detectConfigurations(false, roots); + } + catch (VcsException e) { + return Collections.emptyList(); + } + } + + /** + * @return the overall special status + */ + public SpecialStatus getSpecialStatus() { + synchronized (myStateLock) { + return myCurrentStatus; + } + } + + /** + * @return the calculated overall special status + */ + private SpecialStatus calculateSpecialStatus() { + synchronized (myStateLock) { + if (myCheckoutIsInProgress) { + return SpecialStatus.CHECKOUT_IN_PROGRESS; + } + for (VirtualFile root : myGitRoots) { + if (GitRebaseUtils.isRebaseInTheProgress(root)) { + return SpecialStatus.REBASING; + } + if (GitMergeUtil.isMergeInTheProgress(root)) { + return SpecialStatus.MERGING; + } + if (root.findChild(".gitmodules") != null) { + return SpecialStatus.SUBMODULES; + } + } + for (LocalChangeList changeList : myChangeManager.getChangeListsCopy()) { + for (Change change : changeList.getChanges()) { + if (change.getFileStatus() == FileStatus.MERGED_WITH_CONFLICTS) { + return SpecialStatus.MERGING; + } + } + } + return myGitRoots.size() == 0 ? SpecialStatus.NON_GIT : SpecialStatus.NORMAL; + } + } + + /** + * Detect local branch configurations + * + * @throws VcsException if there is a problem with detecting + */ + private void detectLocals() throws VcsException { + synchronized (myStateLock) { + HashMap currents = new HashMap(); + for (VirtualFile root : myGitRoots) { + GitBranch current = GitBranch.current(myProject, root); + currents.put(root, current == null ? "" : current.getName()); + } + if (myConfigurations.isEmpty()) { + List locals = detectConfigurations(true, myGitRoots); + if (locals.isEmpty()) { + // no commits + locals.add("master"); + } + for (String localName : locals) { + GitBranchConfiguration c = createConfiguration(localName); + c.setAutoDetected(true); + boolean currentsMatched = true; + for (VirtualFile root : myGitRoots) { + c.setBranch(root.getPath(), localName); + currentsMatched &= currents.get(root).equals(localName); + } + if (currentsMatched) { + myCurrentConfiguration = c; + } + } + if (myCurrentConfiguration == null) { + // the configuration does not matches any standard, there could be no configurations with spaces at this point + // since it is not allowed branch name. + String name = "Unknown 1"; + GitBranchConfiguration c = createConfiguration(name); + for (VirtualFile root : myGitRoots) { + c.setBranch(root.getPath(), describeRoot(root)); + } + } + } + fireCurrentConfigurationChanged(); + fireConfigurationsChanged(); + } + } + + /** + * The configurations changed + */ + private void fireConfigurationsChanged() { + myListeners.getMulticaster().configurationsChanged(); + } + + /** + * The current configuration changed + */ + private void fireCurrentConfigurationChanged() { + myListeners.getMulticaster().currentConfigurationChanged(); + } + + /** + * Describe vcs root + * + * @param root the root to describe + * @return the current reference + * @throws VcsException if there is a problem with describing the root + */ + String describeRoot(VirtualFile root) throws VcsException { + GitBranch current = GitBranch.current(myProject, root); + if (current == null) { + // It is on the tag or specific commit. In future, support for submodules should be added. + return detectTag(root); + } + else { + return current.getName(); + } + } + + /** + * Get tag name for the head + * + * @param root the root to describe + * @return the commit expression that describes root state + */ + private String detectTag(VirtualFile root) { + try { + GitSimpleHandler h = new GitSimpleHandler(myProject, root, GitCommand.DESCRIBE); + h.addParameters("--tags", "--exact", "HEAD"); + h.setNoSSH(true); + return h.run().trim(); + } + catch (VcsException e) { + if (LOG.isDebugEnabled()) { + LOG.debug("describe HEAD failed for root: " + root.getPath()); + } + GitSimpleHandler h = new GitSimpleHandler(myProject, root, GitCommand.SHOW); + h.setNoSSH(true); + h.addParameters("--pretty=format:%H", "HEAD"); + try { + return h.run().trim(); + } + catch (VcsException e1) { + throw new RuntimeException("Unexpected exception at this time, the failure should have been detected at current(): ", e1); + } + } + } + + /** + * Detect possible configurations + * + * @param roots + * @return a sorted list of branches + * @throws VcsException if there is a problem with running git + */ + private List detectConfigurations(boolean local, final List roots) throws VcsException { + HashSet all = new HashSet(); + HashSet forRoot = new HashSet(); + boolean isFirst = true; + for (VirtualFile root : roots) { + forRoot.clear(); + GitBranch.listAsStrings(myProject, root, !local, local, forRoot, null); + if (isFirst) { + isFirst = false; + all.addAll(forRoot); + } + else { + all.retainAll(forRoot); + } + } + ArrayList rc = new ArrayList(all); + Collections.sort(rc); + return rc; + } + + + /** + * @return the used vcs + */ + GitVcs getVcs() { + return myVcs; + } + + /** + * @return the context project + */ + Project getProject() { + return myProject; + } + + /** + * @return the current configuration + * @throws VcsException if there is a problem with configurations + */ + GitBranchConfiguration getCurrentConfiguration() throws VcsException { + synchronized (myStateLock) { + if (myCurrentConfiguration == null) { + throw new VcsException("The current configuration is not yet detected"); + } + return myCurrentConfiguration; + } + } + + /** + * @return the configuration names + */ + Set getConfigurationNames() { + synchronized (myStateLock) { + return new HashSet(myConfigurations.keySet()); + } + } + + /** + * Create new branch configuration + * + * @param name the configuration name + * @return the created configuration + */ + @NotNull + GitBranchConfiguration createConfiguration(String name) { + synchronized (myStateLock) { + if (myConfigurations.containsKey(name)) { + throw new IllegalStateException("The name " + name + " is already used"); + } + GitBranchConfiguration c = new GitBranchConfiguration(this, name); + myConfigurations.put(name, c); + fireConfigurationsChanged(); + return c; + } + } + + + /** + * Find configuration by name + * + * @param name the name to use + * @return the configuration by name + * @throws VcsException if there is an error in the state + */ + @Nullable + GitBranchConfiguration getConfiguration(String name) throws VcsException { + synchronized (myStateLock) { + return myConfigurations.get(name); + } + } + + /** + * @return the state lock for branch configurations + */ + Object getStateLock() { + return myStateLock; + } + + /** + * Check if configuration with specified name already exists + * + * @param name the name to check + * @return true if the configuration exists + */ + boolean hasConfigurationName(String name) { + synchronized (myStateLock) { + return myConfigurations.containsKey(name); + } + } + + /** + * Set current configuration + * + * @param newConfiguration the new current configuration + */ + void setCurrentConfiguration(GitBranchConfiguration newConfiguration) { + synchronized (myStateLock) { + assert myConfigurations.get(newConfiguration.getName()) == newConfiguration; + myCurrentConfiguration = newConfiguration; + fireCurrentConfigurationChanged(); + } + } + + /** + * @return the shelve manager from project + */ + ShelveChangesManager getShelveManager() { + return myShelveManager; + } + + /** + * Remove configuration + * + * @param toRemove the removed configuration + */ + public void removeConfiguration(@NotNull GitBranchConfiguration toRemove) { + synchronized (myStateLock) { + if (toRemove == myCurrentConfiguration) { + throw new IllegalArgumentException("Unable to remove the current configuration"); + } + myConfigurations.remove(toRemove.getName()); + fireConfigurationsChanged(); + } + } + + /** + * Start checkout process for selected configuration in the background + * + * @param configuration the selected configuration or null if new configuration is needed. + * @param remote the remote pseudo configuration name + * @param quick the quick checkout + */ + public void startCheckout(final GitBranchConfiguration configuration, final String remote, final boolean quick) { + if (remote != null && configuration != null) { + throw new IllegalArgumentException("Either remote or configuration to checkout must be null"); + } + synchronized (myStateLock) { + final SpecialStatus status = calculateSpecialStatus(); + if (status != SpecialStatus.NORMAL) { + throw new IllegalStateException("Checkout cannot be started due to special status (it must have been checked in UI): " + status); + } + myCheckoutIsInProgress = true; + updateSpecialStatus(); + } + final String name = configuration != null ? configuration.getName() : remote == null ? "new configuration" : remote; + final String title = "Checking out " + name; + ProgressManager.getInstance().run(new Task.Backgroundable(myProject, title, false) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + try { + final GitCheckoutProcess process = + new GitCheckoutProcess(GitBranchConfigurations.this, myProject, myShelveManager, myDirtyScopeManager, myChangeManager, + myProjectManager, indicator, configuration, remote, quick); + process.run(); + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + final List exceptions = process.getExceptions(); + String op = (process.isModify() ? "Modification" : "Checkout") + " of " + name; + if (!exceptions.isEmpty()) { + GitUIUtil.showTabErrors(myProject, title, exceptions); + ToolWindowManager.getInstance(myProject).notifyByBalloon( + ChangesViewContentManager.TOOLWINDOW_ID, MessageType.ERROR, op + " failed."); + } + else if (process.isCancelled()) { + ToolWindowManager.getInstance(myProject).notifyByBalloon( + ChangesViewContentManager.TOOLWINDOW_ID, MessageType.WARNING, op + " was cancelled by user."); + + } + else { + ToolWindowManager.getInstance(myProject).notifyByBalloon( + ChangesViewContentManager.TOOLWINDOW_ID, MessageType.INFO, op + " complete."); + } + } + }); + } + catch (Throwable t) { + LOG.error("Unexpected exception from checkout: ", t); + } + finally { + synchronized (myStateLock) { + myCheckoutIsInProgress = false; + updateSpecialStatus(); + } + } + } + }); + } + + /** + * Internal notification about renamed configuration + * + * @param toRename configuration to rename + * @param oldName the old name + * @param newName the new name @return true if configuration actually renamed. + */ + boolean configurationRenamed(GitBranchConfiguration toRename, String oldName, String newName) { + synchronized (myStateLock) { + final GitBranchConfiguration c = myConfigurations.get(oldName); + if (c == toRename) { + myConfigurations.remove(oldName); + myConfigurations.put(newName, c); + } + return c == toRename; + } + } + + /** + * The configuration state + */ + public static class State { + /** + * The current configuration + */ + public String CURRENT; + /** + * The branch configuration + */ + public BranchConfiguration[] CONFIGURATIONS = new BranchConfiguration[0]; + } + + /** + * The branch configuration + */ + public static class BranchConfiguration { + /** + * If true, the configuration was auto-detected + */ + public boolean IS_AUTO_DETECTED; + /** + * The configuration name + */ + public String NAME; + /** + * The branch information + */ + public BranchInfo[] BRANCHES = new BranchInfo[0]; + /** + * The branch changes + */ + public BranchChanges CHANGES; + } + + /** + * Branch mapping information + */ + public static class BranchInfo { + /** + * The vcs root for which information is stored + */ + public String ROOT; + /** + * The local branch or specific commit + */ + public String REFERENCE; + } + + /** + * The changes associated with the branch state + */ + public static class BranchChanges { + /** + * The path to shelve that keeps changes + */ + public String SHELVE_PATH; + /** + * Change list information + */ + public ChangeListInfo[] CHANGE_LISTS = new ChangeListInfo[0]; + /** + * Information about distribution of changes among change lists + */ + public ChangeInfo[] CHANGES = new ChangeInfo[0]; + + } + + /** + * The change list information + */ + public static class ChangeListInfo { + /** + * If true, the change list was a default change list + */ + public boolean IS_DEFAULT = false; + /** + * The change list name + */ + public String NAME; + /** + * The change list comment + */ + public String COMMENT; + } + + /** + * Change information. The change is identified by before path and after path (for deleted) + */ + public static class ChangeInfo { + /** + * The before path + */ + public String BEFORE_PATH; + /** + * The after path + */ + public String AFTER_PATH; + /** + * The name of change list to which change belong + */ + public String CHANGE_LIST_NAME; + } + + /** + * The special status for the roots + */ + public enum SpecialStatus { + /** + * Normal work tree, checkout is possible + */ + NORMAL, + /** + * Rebasing + */ + REBASING, + /** + * Merging + */ + MERGING, + /** + * Non git project + */ + NON_GIT, + /** + * The submodules are detected in the project + */ + SUBMODULES, + /** + * The background checkout process is in progress + */ + CHECKOUT_IN_PROGRESS + } +} diff --git a/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurationsListener.java b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurationsListener.java new file mode 100644 index 000000000000..4014c2f049ad --- /dev/null +++ b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchConfigurationsListener.java @@ -0,0 +1,31 @@ +/* + * 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.checkout.branches; + +import java.util.EventListener; + +/** + * The listener for changes in the branch configurations. The events listed here are needed for the current UI. + */ +public interface GitBranchConfigurationsListener extends EventListener { + void configurationsChanged(); + + void specialStatusChanged(); + + void currentConfigurationChanged(); + + void referencesChanged(); +} diff --git a/plugins/git4idea/src/git4idea/checkout/branches/GitBranchesWidget.java b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchesWidget.java new file mode 100644 index 000000000000..ca47ea25fc41 --- /dev/null +++ b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchesWidget.java @@ -0,0 +1,553 @@ +/* + * 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.checkout.branches; + +import com.intellij.ide.DataManager; +import com.intellij.openapi.actionSystem.*; +import com.intellij.openapi.actionSystem.impl.SimpleDataContext; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.ListPopup; +import com.intellij.openapi.util.Disposer; +import com.intellij.openapi.util.IconLoader; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.CustomStatusBarWidget; +import com.intellij.openapi.wm.StatusBar; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.openapi.wm.WindowManager; +import com.intellij.ui.awt.RelativePoint; +import com.intellij.util.ui.UIUtil; +import git4idea.GitUtil; +import git4idea.GitVcs; +import git4idea.commands.GitCommand; +import git4idea.commands.GitHandlerUtil; +import git4idea.commands.GitLineHandler; +import git4idea.ui.GitUIUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Collection; + +/** + * The git branches widget + */ +public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget { + /** + * The logger + */ + private static final Logger LOG = Logger.getInstance(GitBranchesWidget.class.getName()); + + /** + * The ID of the widget + */ + public static final String ID = "git4idea.BranchConfigurations"; + /** + * The listener + */ + final GitBranchConfigurationsListener myConfigurationsListener; + /** + * The project + */ + final Project myProject; + /** + * The configurations instance + */ + private final GitBranchConfigurations myConfigurations; + /** + * The selectable configurations. Null if invalidated or non-initialized + */ + private AnAction[] mySelectableConfigurations; + /** + * The candidate remote configurations. Null if invalidated or non-initialized + */ + private AnAction[] myRemoveConfigurations; + /** + * The status bar + */ + private StatusBar myStatusBar; + /** + * If true, the popup is enabled + */ + private boolean myPopupEnabled = false; + /** + * The action group for pop up + */ + private DefaultActionGroup myPopupActionGroup; + /** + * The current popup + */ + private ListPopup myPopup; + /** + * The default foreground color + */ + private final Color myDefaultForeground; + + + /** + * The constructor + * + * @param project the project instance + */ + public GitBranchesWidget(Project project, GitBranchConfigurations configurations) { + myProject = project; + setBorder(WidgetBorder.INSTANCE); + //setBorder(BorderFactory.createEtchedBorder()); + myConfigurations = configurations; + myDefaultForeground = getForeground(); + myConfigurationsListener = new MyGitBranchConfigurationsListener(); + myConfigurations.addConfigurationListener(myConfigurationsListener); + setIcon(IconLoader.findIcon("/icons/branch.png", getClass())); + Disposer.register(myConfigurations, this); + addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + showPopup(); + } + }); + updateLabel(); + } + + /** + * Create and install widget + * + * @param project the context project + * @param configurations the configurations to use + * @return the action that uninstalls widget + */ + static Runnable install(final Project project, final GitBranchConfigurations configurations) { + final Ref widget = new Ref(); + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + StatusBar statusBar = WindowManager.getInstance().getStatusBar(project); + if (statusBar != null) { + final GitBranchesWidget w = new GitBranchesWidget(project, configurations); + statusBar.addWidget(w, "after InsertOverwrite", project); + widget.set(w); + } + } + }); + return new Runnable() { + @Override + public void run() { + if (widget.get() != null) { + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + Disposer.dispose(widget.get()); + } + }); + } + } + }; + + } + + /** + * {@inheritDoc} + */ + @Override + public JComponent getComponent() { + return this; + } + + /** + * {@inheritDoc} + */ + @NotNull + @Override + public String ID() { + return ID; + } + + /** + * {@inheritDoc} + */ + @Override + public WidgetPresentation getPresentation(@NotNull Type type) { + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public void install(@NotNull StatusBar statusBar) { + myStatusBar = statusBar; + } + + /** + * {@inheritDoc} + */ + @Override + public void dispose() { + myConfigurations.removeConfigurationListener(myConfigurationsListener); + myStatusBar.removeWidget(ID()); + } + + /** + * @return get or create remotes group + */ + private AnAction[] getRemotes() { + assert myPopupEnabled : "pop should be enabled"; + if (myRemoveConfigurations == null) { + ArrayList rc = new ArrayList(); + for (final String c : myConfigurations.getRemotesCandidates()) { + rc.add(new AnAction(c) { + @Override + public void actionPerformed(AnActionEvent e) { + myConfigurations.startCheckout(null, c, false); + } + }); + } + rc.add(new RefreshRemotesAction()); + myRemoveConfigurations = rc.toArray(new AnAction[rc.size()]); + } + return myRemoveConfigurations; + } + + void showPopup() { + if (!myPopupEnabled) { + return; + } + final DataContext parent = DataManager.getInstance().getDataContext((Component)myStatusBar); + final DataContext dataContext = SimpleDataContext.getSimpleContext(PlatformDataKeys.PROJECT.getName(), myProject, parent); + myPopup = JBPopupFactory.getInstance() + .createActionGroupPopup("Checkout", getPopupActionGroup(), dataContext, JBPopupFactory.ActionSelectionAid.SPEEDSEARCH, true, + new Runnable() { + @Override + public void run() { + myPopup = null; + + } + }, 20); + final Dimension dimension = myPopup.getContent().getPreferredSize(); + final Point at = new Point(0, -dimension.height); + myPopup.show(new RelativePoint(getComponent(), at)); + } + + /** + * @return get or create selectable configuration group + */ + private AnAction[] getSelectable() { + assert myPopupEnabled : "pop should be enabled"; + if (mySelectableConfigurations == null) { + ArrayList rc = new ArrayList(); + + GitBranchConfiguration current; + try { + current = myConfigurations.getCurrentConfiguration(); + } + catch (VcsException e) { + LOG.error("Unexpected error at this point", e); + mySelectableConfigurations = new AnAction[0]; + return mySelectableConfigurations; + } + String name = current == null ? "" : current.getName(); + for (final String c : myConfigurations.getConfigurationNames()) { + if (name.equals(c)) { + // skip current config + continue; + } + rc.add(new AnAction(c) { + @Override + public void actionPerformed(AnActionEvent e) { + try { + final GitBranchConfiguration toCheckout = myConfigurations.getConfiguration(c); + if (toCheckout == null) { + throw new VcsException("The configuration " + c + " cannot be found."); + } + myConfigurations.startCheckout(toCheckout, null, true); + } + catch (VcsException e1) { + GitUIUtil.showOperationError(myProject, e1, "Unable to load: " + c); + } + } + }); + } + mySelectableConfigurations = rc.toArray(new AnAction[rc.size()]); + } + return mySelectableConfigurations; + } + + /** + * @return the action group for popup + */ + ActionGroup getPopupActionGroup() { + if (myPopupActionGroup == null) { + myPopupActionGroup = new DefaultActionGroup(null, false); + myPopupActionGroup.addAction(new AnAction("Manage Configurations ...") { + @Override + public void actionPerformed(AnActionEvent e) { + GitManageConfigurationsDialog.showDialog(myProject, myConfigurations); + } + }); + myPopupActionGroup.addAction(new AnAction("Modify Current Configuration ...") { + @Override + public void actionPerformed(AnActionEvent e) { + try { + final GitBranchConfiguration current = myConfigurations.getCurrentConfiguration(); + myConfigurations.startCheckout(current, null, false); + } + catch (VcsException e1) { + LOG.error("The current configuration must exists at this point", e1); + } + } + }); + myPopupActionGroup.addAction(new AnAction("New Configuration ...") { + @Override + public void actionPerformed(AnActionEvent e) { + myConfigurations.startCheckout(null, null, false); + } + }); + myPopupActionGroup.add(new MyRemotesActionGroup()); + myPopupActionGroup.addSeparator("Branch Configurations"); + myPopupActionGroup.add(new MySelectableActionGroup()); + } + return myPopupActionGroup; + } + + /** + * Update label on the widget + */ + private void updateLabel() { + cancelPopup(); + final GitBranchConfigurations.SpecialStatus status = myConfigurations.getSpecialStatus(); + String t; + myPopupEnabled = false; + switch (status) { + case CHECKOUT_IN_PROGRESS: + t = "Checkout in progress..."; + break; + case MERGING: + t = "Merging..."; + break; + case REBASING: + t = "Rebasing..."; + break; + case NON_GIT: + t = "Non-Git project"; + break; + case SUBMODULES: + t = "Submodules unsupported"; + break; + case NORMAL: + GitBranchConfiguration current; + try { + current = myConfigurations.getCurrentConfiguration(); + } + catch (VcsException e) { + current = null; + } + if (current == null) { + t = "Detection in progress"; + } + else { + myPopupEnabled = true; + t = current.getName(); + } + break; + default: + t = "Unknown status: " + status; + } + setForeground(myPopupEnabled ? myDefaultForeground : Color.RED); + setText(t); + } + + /** + * Cancel popup if it is shown + */ + private void cancelPopup() { + if (myPopup != null) { + myPopup.cancel(); + } + } + + /** + * Remotes action group + */ + class MySelectableActionGroup extends ActionGroup { + /** + * The constructor + */ + public MySelectableActionGroup() { + super(null, false); + } + + /** + * {@inheritDoc} + */ + @NotNull + @Override + public AnAction[] getChildren(@Nullable AnActionEvent e) { + return getSelectable(); + } + } + + /** + * Remotes action group + */ + class MyRemotesActionGroup extends ActionGroup { + /** + * The constructor + */ + public MyRemotesActionGroup() { + super("Remotes", true); + } + + /** + * {@inheritDoc} + */ + @NotNull + @Override + public AnAction[] getChildren(@Nullable AnActionEvent e) { + return getRemotes(); + } + } + + /** + * The configuration listener + */ + class MyGitBranchConfigurationsListener implements GitBranchConfigurationsListener { + /** + * {@inheritDoc} + */ + @Override + public void configurationsChanged() { + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + mySelectableConfigurations = null; + } + }); + } + + + /** + * {@inheritDoc} + */ + @Override + public void specialStatusChanged() { + refreshLabel(); + } + + private void refreshLabel() { + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + updateLabel(); + } + }); + } + + /** + * {@inheritDoc} + */ + @Override + public void currentConfigurationChanged() { + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + mySelectableConfigurations = null; + updateLabel(); + } + }); + } + + /** + * {@inheritDoc} + */ + @Override + public void referencesChanged() { + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + myRemoveConfigurations = null; + updateLabel(); + } + }); + } + } + + /** + * Refresh git remotes + */ + class RefreshRemotesAction extends AnAction { + /** + * The constructor + */ + public RefreshRemotesAction() { + super("Refresh Remotes...", "Fetch all references for git vcs roots", IconLoader.findIcon("/vcs/refresh.png")); + } + + /** + * {@inheritDoc} + */ + @Override + public void actionPerformed(AnActionEvent e) { + + final String title = "Refreshing remotes"; + ProgressManager.getInstance().run(new Task.Backgroundable(myProject, title, false) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + final ArrayList exceptions = new ArrayList(); + try { + for (VirtualFile root : GitUtil.getGitRoots(myProject, GitVcs.getInstance(myProject))) { + GitLineHandler h = new GitLineHandler(myProject, root, GitCommand.FETCH); + h.addParameters("--all", "-v"); + final Collection e = GitHandlerUtil + .doSynchronouslyWithExceptions(h, indicator, "Fetching all for " + GitUtil.relativePath(myProject.getBaseDir(), root)); + exceptions.addAll(e); + } + } + catch (VcsException e1) { + exceptions.add(e1); + } + UIUtil.invokeLaterIfNeeded(new Runnable() { + @Override + public void run() { + if (!exceptions.isEmpty()) { + GitUIUtil.showTabErrors(myProject, title, exceptions); + ToolWindowManager.getInstance(myProject).notifyByBalloon( + ChangesViewContentManager.TOOLWINDOW_ID, MessageType.ERROR, "Refreshing remotes failed."); + } + else { + ToolWindowManager.getInstance(myProject).notifyByBalloon( + ChangesViewContentManager.TOOLWINDOW_ID, MessageType.INFO, "Refreshing remotes complete."); + } + myRemoveConfigurations = null; + cancelPopup(); + } + }); + } + }); + } + } + +} diff --git a/plugins/git4idea/src/git4idea/checkout/branches/GitCheckoutProcess.java b/plugins/git4idea/src/git4idea/checkout/branches/GitCheckoutProcess.java new file mode 100644 index 000000000000..ee3f381920c9 --- /dev/null +++ b/plugins/git4idea/src/git4idea/checkout/branches/GitCheckoutProcess.java @@ -0,0 +1,756 @@ +/* + * 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.checkout.branches; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ex.ProjectManagerEx; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.*; +import com.intellij.openapi.vcs.changes.shelf.ShelveChangesManager; +import com.intellij.openapi.vcs.changes.shelf.ShelvedChangeList; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ui.UIUtil; +import com.intellij.vcsUtil.VcsUtil; +import git4idea.GitRevisionNumber; +import git4idea.GitUtil; +import git4idea.checkout.branches.GitBranchConfigurations.BranchChanges; +import git4idea.checkout.branches.GitBranchConfigurations.ChangeInfo; +import git4idea.checkout.branches.GitBranchConfigurations.ChangeListInfo; +import git4idea.commands.*; +import git4idea.update.GitStashUtils; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.util.*; +import java.util.concurrent.Semaphore; + +/** + * The checkout branch process. It used to organize the entire checkout process + */ +public class GitCheckoutProcess { + /** + * The logger + */ + private static final Logger LOG = Logger.getInstance(GitCheckoutProcess.class.getName()); + /** + * The configuration process + */ + final GitBranchConfigurations myConfig; + /** + * The vcs roots + */ + private List myRoots; + /** + * The vcs exceptions + */ + private List myExceptions; + /** + * The project + */ + private final Project myProject; + /** + * The shelve manager + */ + private final ShelveChangesManager myShelveManager; + /** + * The dirty scope manager + */ + private final VcsDirtyScopeManager myDirtyScopeManager; + /** + * The changes manager + */ + private final ChangeListManagerEx myChangeManager; + /** + * The project manager + */ + private final ProjectManagerEx myProjectManager; + /** + * The progress indicator + */ + private final ProgressIndicator myProgress; + /** + * The name of remote pseudo-configuration + */ + @Nullable private final String myRemoteConfiguration; + /** + * If true, the quick process is being used, and user is not offered to select changes + */ + private boolean myQuick; + /** + * The checkout process is run in the modify mode. Branch name or roots are changed. + */ + private final boolean myIsModify; + /** + * The new configuration + */ + private GitBranchConfiguration myNewConfiguration; + /** + * The new branch mapping + */ + private Map myNewBranchMapping = Collections.emptyMap(); + /** + * The described roots + */ + private final Map myDescribedRoots = new HashMap(); + /** + * If true, the checkout process was cancelled + */ + private boolean myCancelled; + + + /** + * The constructor + * + * @param config the configuration object + * @param project the context project + * @param shelveManager the shelve manager + * @param dirtyScopeManager the dirty scope manager + * @param changeManager the change manager + * @param projectManager the project manager + * @param progress the progress indicator for the process + * @param newConfiguration the new configuration, the current if modify + * @param quick if true, the no changes are assumed to be selected, so dialog need not be shown + */ + public GitCheckoutProcess(GitBranchConfigurations config, + Project project, + ShelveChangesManager shelveManager, + VcsDirtyScopeManager dirtyScopeManager, + ChangeListManagerEx changeManager, + ProjectManagerEx projectManager, + ProgressIndicator progress, + @Nullable GitBranchConfiguration newConfiguration, + @Nullable String remoteConfiguration, + boolean quick) { + myExceptions = Collections.synchronizedList(new ArrayList()); + myConfig = config; + myProject = project; + myShelveManager = shelveManager; + myDirtyScopeManager = dirtyScopeManager; + myChangeManager = changeManager; + myProjectManager = projectManager; + myProgress = progress; + myNewConfiguration = newConfiguration; + myRemoteConfiguration = remoteConfiguration; + boolean f = false; + try { + f = myNewConfiguration == config.getCurrentConfiguration(); + } + catch (VcsException e) { + myExceptions.add(e); + } + myIsModify = f; + myQuick = quick & !myIsModify; + } + + + /** + * Start the checkout process. The all activity is done in this thread. When needed, the activity is scheduled in awt or other threads. + */ + public void run() { + if (!myExceptions.isEmpty()) { + return; + } + myProgress.setText("Staring checkout..."); + try { + myRoots = GitUtil.getGitRoots(myConfig.getProject(), myConfig.getVcs()); + saveAll(); + waitForChanges(); + myProjectManager.blockReloadingProjectOnExternalChanges(); + try { + GitBranchConfiguration oldConfiguration = checkCurrentConfiguration(); + if (oldConfiguration == null) { + myCancelled = true; + return; + } + for (VirtualFile root : myRoots) { + myDescribedRoots.put(root, myConfig.describeRoot(root)); + } + ensureNoChangesInCurrent(oldConfiguration); + if (!myExceptions.isEmpty()) { + return; + } + List changes = collectChanges(); + Collection selected; + myProgress.setText("Verifying the current configuration..."); + if (myQuick && checkRoots()) { + // show switch dialog with new configuration that allows selecting changes (if not quick switch) + selected = Collections.emptyList(); + } + else { + myProgress.setText("Selecting changes to transfer..."); + selected = selectChangesToTransfer(changes); + if (selected == null) { + // the process was cancelled, or no changes that require checkout were made to the current configuration + myCancelled = true; + return; + } + } + // preparation phase finished. do actual checkout. + assert myNewConfiguration != null; + List checkoutRoots = rootsToCheckout(); + if (checkoutRoots.size() > 0) { + // TODO disable saving + myProgress.setText("Shelving changes..."); + Pair changesPair = shelveChanges(myProgress, oldConfiguration.getName(), selected); + try { + // save changes in old root, it also may be aliased with new root + oldConfiguration.setChanges(changesPair.first); + try { + HashSet startedRoots = new HashSet(); + boolean failed = !checkoutAndRefreshRoots(checkoutRoots, startedRoots); + myProgress.setText2(""); + if (!failed) { + myConfig.setCurrentConfiguration(myNewConfiguration); + } + else { + myNewConfiguration = oldConfiguration; + rollbackRootCheckout(startedRoots); + } + } + finally { + final BranchChanges branchChanges = myNewConfiguration.getChanges(); + myNewConfiguration.setChanges(null); + restoreChanges(myProgress, branchChanges); + } + } + finally { + // restore transient shelve + restoreChanges(myProgress, changesPair.second); + } + // TODO enable saving + } + else { + myConfig.setCurrentConfiguration(myNewConfiguration); + } + } + finally { + myProjectManager.unblockReloadingProjectOnExternalChanges(); + } + // launch project update? + } + catch (VcsException e) { + myExceptions.add(e); + } + finally { + saveAll(); // saves configuration changes + } + } + + /** + * Ensure that current configuration contains no changes. This is a bug condition. + * + * @param oldConfiguration the old configuration + */ + private void ensureNoChangesInCurrent(GitBranchConfiguration oldConfiguration) { + final BranchChanges oldChanges = oldConfiguration.getChanges(); + if (oldChanges != null) { + String name = oldChanges.SHELVE_PATH; + for (ShelvedChangeList changeList : myShelveManager.getShelvedChangeLists()) { + if (changeList.PATH.equals(oldChanges.SHELVE_PATH)) { + name = changeList.DESCRIPTION; + break; + } + } + final VcsException ex = new VcsException("The current configuration contains shelve: " + name); + LOG.error(ex); + myExceptions.add(ex); + oldConfiguration.setChanges(null); + } + } + + /** + * Rollback the root checkout + * + * @param startedRoots that roots that has been started and need to be rolled back + */ + private void rollbackRootCheckout(HashSet startedRoots) { + myProgress.setText("Rolling back..."); + for (VirtualFile root : startedRoots) { + myProgress.setText2(root.getPath()); + startedRoots.add(root); + GitLineHandler h = new GitLineHandler(myProject, root, GitCommand.CHECKOUT); + h.setNoSSH(true); + h.addParameters("-f", myDescribedRoots.get(root)); + Collection exceptions = GitHandlerUtil.doSynchronouslyWithExceptions(h, myProgress, h.printableCommandLine()); + myExceptions.addAll(exceptions); + } + // filter out implicitly included roots + ArrayList newRoots = new ArrayList(); + loop: + for (VirtualFile root : startedRoots) { + for (VirtualFile other : startedRoots) { + if (other != root && VfsUtil.isAncestor(other, root, true)) { + continue loop; + } + } + newRoots.add(root); + } + for (VirtualFile root : newRoots) { + root.refresh(false, true); + } + myProgress.setText2(""); + } + + /** + * Checkout needed vcs roots and do vcs refresh on diff files + * + * @param checkoutRoots the roots to checkout + * @param startedRoots the roots for which checkout actually started (Modified by this method) + * @return true if checkout is successful + * @throws VcsException + */ + private boolean checkoutAndRefreshRoots(List checkoutRoots, HashSet startedRoots) throws VcsException { + boolean failed = false; + myProgress.setText("Checking out..."); + HashSet filesToRefresh = new HashSet(); + for (VirtualFile root : checkoutRoots) { + myProgress.setText2(root.getPath()); + startedRoots.add(root); + GitRevisionNumber prev = GitRevisionNumber.resolve(myProject, root, "HEAD"); + GitLineHandler h = new GitLineHandler(myProject, root, GitCommand.CHECKOUT); + h.addParameters("-f"); + String branchedRef = myNewBranchMapping.get(root); + String ref = myNewConfiguration.getReference(root.getPath()); + if (branchedRef != null) { + h.addParameters("-l", "-t", "-b", ref, branchedRef); + } + else { + h.addParameters(ref); + } + Collection exceptions = GitHandlerUtil.doSynchronouslyWithExceptions(h, myProgress, h.printableCommandLine()); + if (!exceptions.isEmpty()) { + myExceptions.addAll(exceptions); + failed = true; + break; + } + GitSimpleHandler d = new GitSimpleHandler(myProject, root, GitCommand.DIFF); + d.addParameters("--name-only", prev.asString() + "..HEAD"); + d.setNoSSH(true); + d.setSilent(true); + d.endOptions(); + try { + File base = new File(root.getPath()); + for (StringScanner s = new StringScanner(d.run()); s.hasMoreData();) { + String l = s.line(); + if (l.length() > 0) { + filesToRefresh.add(new File(base, GitUtil.unescapePath(l))); + } + } + } + catch (VcsException e) { + LOG.error("Unexpected diff failure", e); + myExceptions.add(e); + failed = true; + break; + } + } + if (!failed) { + LocalFileSystem.getInstance().refreshIoFiles(filesToRefresh); + } + return !failed; + } + + /** + * @return set of roots to checkout + */ + private List rootsToCheckout() { + ArrayList rc = new ArrayList(); + for (VirtualFile root : myRoots) { + String current = myDescribedRoots.get(root); + String newRef = myNewConfiguration.getReference(root.getPath()); + if (!current.equals(newRef)) { + rc.add(root); + } + } + return rc; + } + + /** + * @param changes all changes + * @return this method allows renaming and updating, the branch configuration and to select changes to transfer to new configuration. + * null if process is cancelled, or only name changed in the current configuration and no checkout is required. + */ + @Nullable + private Collection selectChangesToTransfer(final List changes) { + final Ref t = new Ref(); + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + try { + t.set(GitSwitchBranchesDialog + .showDialog(myProject, myNewConfiguration, changes, myRoots, myRemoteConfiguration, myConfig, myIsModify)); + } + catch (VcsException e) { + myExceptions.add(e); + } + catch (Throwable e) { + LOG.error("Unexpected error", e); + myExceptions.add(new VcsException("Selecting changes failed", e)); + } + } + }); + if (t.get() == null) { + return null; + } + else { + myNewBranchMapping = t.get().referencesToUse; + myNewConfiguration = t.get().target; + return t.get().changes; + } + } + + /** + * @return true if roots in the new configuration are configured correctly + */ + private boolean checkRoots() { + if (myNewConfiguration == null) { + return false; + } + LocalFileSystem lfs = LocalFileSystem.getInstance(); + HashSet roots = new HashSet(myRoots); + Map branches = myNewConfiguration.getReferences(); + for (Map.Entry m : branches.entrySet()) { + VirtualFile root = lfs.findFileByPath(m.getKey()); + if (root == null || root.findChild(".git") == null || !roots.contains(root)) { + return false; + } + roots.remove(root); + try { + GitRevisionNumber.resolve(myProject, root, m.getValue()); + } + catch (VcsException e) { + return false; + } + } + return roots.isEmpty(); + } + + /** + * @return collect changes from project + */ + private List collectChanges() { + List changeLists = myChangeManager.getChangeLists(); + ArrayList changes = new ArrayList(); + for (LocalChangeList l : changeLists) { + changes.addAll(l.getChanges()); + } + return changes; + } + + + /** + * @return the current configuration, or null if process was cancelled + * @throws VcsException if there is a problem with checking configuration + */ + @Nullable + private GitBranchConfiguration checkCurrentConfiguration() throws VcsException { + GitBranchConfiguration c = myConfig.getCurrentConfiguration(); + if (!myIsModify && !rootsMatchConfiguration(c)) { + // when the current configuration is modified, there is no need to show duplicate dialogs + c = GitBranchConfigurationChangedDialog.showDialog(myConfig, c, myRoots); + } + return c; + } + + /** + * Check of the root mapping has been changed for the current vcs root + * + * @param c the configuration to check + * @return true if nothing shoudlbe checked out + * @throws VcsException + */ + private boolean rootsMatchConfiguration(GitBranchConfiguration c) throws VcsException { + LocalFileSystem lfs = LocalFileSystem.getInstance(); + Map branches = c.getReferences(); + if (branches.size() != myRoots.size()) { + return false; + } + HashSet realSet = new HashSet(); + for (VirtualFile root : myRoots) { + realSet.add(root.getPath()); + } + HashSet storedSet = new HashSet(); + for (Map.Entry m : branches.entrySet()) { + String rootPath = m.getKey(); + storedSet.add(rootPath); + VirtualFile root = lfs.findFileByPath(rootPath); + String ref = myConfig.describeRoot(root); + if (!ref.equals(m.getValue())) { + return false; + } + } + return storedSet.equals(realSet); + } + + private void waitForChanges() throws VcsException { + final Semaphore s = new Semaphore(0); + waitForChangesRefresh("Preparing for the checkout: ", new Runnable() { + @Override + public void run() { + s.release(); + } + }); + try { + s.acquire(); + } + catch (InterruptedException e) { + throw new VcsException("Waiting for changes was interrupted: ", e); + } + } + + + /** + * Save all changes before start of update process + */ + private static void saveAll() { + UIUtil.invokeAndWaitIfNeeded(new Runnable() { + @Override + public void run() { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + FileDocumentManager.getInstance().saveAllDocuments(); + } + }); + } + }); + } + + /** + * Restore changes. + * + * @param progress the progress indicator + * @param changes the changes to restore, null means no changes to restore + * @return true if changes has been restored successfully + */ + private boolean restoreChanges(ProgressIndicator progress, + final BranchChanges changes) { + if (changes == null) { + return true; + } + ShelvedChangeList shelve = null; + for (ShelvedChangeList changeList : myShelveManager.getShelvedChangeLists()) { + if (changeList.PATH.equals(changes.SHELVE_PATH)) { + shelve = changeList; + } + } + if (shelve == null) { + //noinspection ThrowableInstanceNeverThrown + myExceptions.add(new VcsException("Failed to find shelve with path" + changes.SHELVE_PATH)); + return false; + } + progress.setText("Refreshing files before restoring shelve: " + shelve.DESCRIPTION); + GitStashUtils.doSystemUnshelve(myProject, shelve, myShelveManager, myChangeManager, myExceptions); + // dirty files and parse changes + final HashMap, String> parsedChanges = new HashMap, String>(); + for (ChangeInfo changeInfo : changes.CHANGES) { + String before = changeInfo.BEFORE_PATH; + String after = changeInfo.AFTER_PATH; + parsedChanges.put(Pair.create(before, after), changeInfo.CHANGE_LIST_NAME); + if (after != null) { + myDirtyScopeManager.fileDirty(VcsUtil.getFilePath(after)); + } + if (before != null) { + myDirtyScopeManager.fileDirty(VcsUtil.getFilePath(before)); + } + } + final ShelvedChangeList finalShelve = shelve; + try { + waitForChanges(); + HashMap lists = new HashMap(); + for (LocalChangeList localChangeList : myChangeManager.getChangeLists()) { + lists.put(localChangeList.getName(), localChangeList); + } + LocalChangeList defaultList = myChangeManager.getDefaultChangeList(); + for (ChangeListInfo changeListInfo : changes.CHANGE_LISTS) { + LocalChangeList changeList = lists.get(changeListInfo.NAME); + if (changeList != null) { + myChangeManager.setReadOnly(changeList.getName(), false); + } + else { + changeList = myChangeManager.addChangeList(changeListInfo.NAME, changeListInfo.COMMENT); + lists.put(changeListInfo.NAME, changeList); + } + if (changeListInfo.IS_DEFAULT) { + myChangeManager.setDefaultChangeList(changeList); + } + } + for (Change change : defaultList.getChanges()) { + ContentRevision beforeRevision = change.getBeforeRevision(); + String before = beforeRevision == null ? null : beforeRevision.getFile().getPath(); + ContentRevision afterRevision = change.getAfterRevision(); + String after = afterRevision == null ? null : afterRevision.getFile().getPath(); + Pair key = Pair.create(before, after); + String listName = parsedChanges.get(key); + assert listName != null : "List name should be found: " + key; + if (!listName.equals(defaultList.getName())) { + LocalChangeList changeList = lists.get(listName); + assert changeList != null : "Change List should be found: " + listName; + myChangeManager.moveChangesTo(changeList, new Change[]{change}); + } + } + return true; + } + catch (Throwable t) { + //noinspection ThrowableInstanceNeverThrown + myExceptions.add( + new VcsException("Failed to process restore shelved change list: " + finalShelve.DESCRIPTION + ". Please restore it manually.", t)); + return false; + } + } + + /** + * Wait until changes are refreshed + * + * @param title the title of the operation + * @param runnable the process that awaits changes + */ + void waitForChangesRefresh(final String title, final Runnable runnable) { + UIUtil.invokeLaterIfNeeded(new Runnable() { + public void run() { + myChangeManager.invokeAfterUpdate(runnable, InvokeAfterUpdateMode.BACKGROUND_NOT_CANCELLABLE, title, + ModalityState.NON_MODAL); + } + }); + } + + /** + * Shelve selected and transient changes. If creation of one of shelves fails, the other shelve is rolled back + * + * @param configurationName the current configuration name + * @param selectedChanges the selected changes + * @return null if operation fails or pair if operation completed successfully. The first is changes to be stored in old configuration, the second is trainsient changes. + */ + @Nullable + private Pair shelveChanges(ProgressIndicator progress, + String configurationName, + Collection selectedChanges) { + assert myExceptions.isEmpty() : "The method should not be called if there is already problems detected"; + List changeLists = myChangeManager.getChangeListsCopy(); + HashMap changes = new HashMap(); + for (LocalChangeList l : changeLists) { + for (Change change : l.getChanges()) { + changes.put(change, l); + } + } + HashSet selected = new HashSet(selectedChanges); + HashSet other = new HashSet(changes.keySet()); + other.removeAll(selected); + + Date now = new Date(); + BranchChanges storedChanges = shelveChanges(progress, changes, other, + "Shelved changes for configuration " + configurationName + " (created on " + now + ")"); + if (!myExceptions.isEmpty()) { + return null; + } + BranchChanges transientChanges = shelveChanges(progress, changes, selected, + "Transferred changes from configuration " + + configurationName + + " (created on " + + now + + ")"); + if (!myExceptions.isEmpty()) { + restoreChanges(progress, storedChanges); + return null; + } + return Pair.create(storedChanges, transientChanges); + } + + /** + * Shelve changes remembering change configuration + * + * @param progress the progress + * @param changes the all changes to process + * @param toShelve the shelved change subset + * @param description the description of the shelve + * @return branch change set or null if shelve is not created (there will be exceptions in list in case of errors) + */ + @Nullable + private BranchChanges shelveChanges(ProgressIndicator progress, + HashMap changes, + HashSet toShelve, String description + ) { + if (toShelve.isEmpty()) { + return null; + } + HashSet lists = new HashSet(); + ArrayList ci = new ArrayList(toShelve.size()); + for (Change c : toShelve) { + LocalChangeList l = changes.get(c); + lists.add(l); + ChangeInfo i = new ChangeInfo(); + ContentRevision after = c.getAfterRevision(); + if (after != null) { + i.AFTER_PATH = after.getFile().getPath(); + } + ContentRevision before = c.getBeforeRevision(); + if (before != null) { + i.BEFORE_PATH = before.getFile().getPath(); + } + i.CHANGE_LIST_NAME = l.getName(); + ci.add(i); + } + ArrayList li = new ArrayList(lists.size()); + for (LocalChangeList l : lists) { + ChangeListInfo i = new ChangeListInfo(); + i.IS_DEFAULT = l.isDefault(); + i.NAME = l.getName(); + i.COMMENT = l.getComment(); + li.add(i); + } + if (progress != null) { + progress.setText("Creating shelve: " + description); + } + ShelvedChangeList shelved = GitStashUtils.shelveChanges(myProject, myShelveManager, toShelve, description, myExceptions); + if (shelved == null) { + return null; + } + BranchChanges b = new BranchChanges(); + b.SHELVE_PATH = shelved.PATH; + b.CHANGE_LISTS = li.toArray(new ChangeListInfo[li.size()]); + b.CHANGES = ci.toArray(new ChangeInfo[ci.size()]); + return b; + } + + /** + * @return the list of problems + */ + public List getExceptions() { + return myExceptions; + } + + /** + * @return true if the process was cancelled + */ + public boolean isCancelled() { + return myCancelled; + } + + /** + * @return true if the process was modification process + */ + public boolean isModify() { + return myIsModify; + } +} diff --git a/plugins/git4idea/src/git4idea/checkout/branches/GitManageConfigurationsDialog.form b/plugins/git4idea/src/git4idea/checkout/branches/GitManageConfigurationsDialog.form new file mode 100644 index 000000000000..16bcbe702c4b --- /dev/null +++ b/plugins/git4idea/src/git4idea/checkout/branches/GitManageConfigurationsDialog.form @@ -0,0 +1,121 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/plugins/git4idea/src/git4idea/checkout/branches/GitManageConfigurationsDialog.java b/plugins/git4idea/src/git4idea/checkout/branches/GitManageConfigurationsDialog.java new file mode 100644 index 000000000000..4103b5a826de --- /dev/null +++ b/plugins/git4idea/src/git4idea/checkout/branches/GitManageConfigurationsDialog.java @@ -0,0 +1,350 @@ +/* + * 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.checkout.branches; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.shelf.ShelvedChangeList; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.components.JBList; +import com.intellij.ui.table.JBTable; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.AbstractTableModel; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/** + * The manage configurations dialog. Currently only delete and limited view works. + *

+ * TODO auto-detection of configurations + */ +public class GitManageConfigurationsDialog extends DialogWrapper { + /** + * The new configuration exit code + */ + private static final int NEW_CONFIGURATION_EXIT_CODE = NEXT_USER_EXIT_CODE; + /** + * The delete configuration button + */ + private JButton myDeleteButton; + /** + * The configuration name list + */ + private JBList myNamesList; + /** + * The table with branches + */ + private JBTable myBranchesTable; + /** + * The root panel + */ + private JPanel myRootPanel; + /** + * The configuration name label + */ + private JLabel myNameLabel; + /** + * The name label + */ + private JLabel myShelveNameLabel; + /** + * The project to use + */ + private final Project myProject; + /** + * The git configurations to use + */ + private final GitBranchConfigurations myConfigurations; + /** + * Map from shelve path to shelve name + */ + private final HashMap myShelveNames = new HashMap(); + /** + * The table model + */ + private final MyBranchMappingModel myBranchMappingModel; + /** + * The list model with names + */ + private final DefaultListModel myNamesModel; + + /** + * The constructor + * + * @param project the project + * @param configurations the configuration service + */ + protected GitManageConfigurationsDialog(Project project, GitBranchConfigurations configurations) { + super(project, true); + setTitle("Manage Branch Configurations"); + setOKButtonText("Checkout"); + myProject = project; + myConfigurations = configurations; + myBranchMappingModel = new MyBranchMappingModel(); + myBranchesTable.setModel(myBranchMappingModel); + for (ShelvedChangeList shelvedChangeList : configurations.getShelveManager().getShelvedChangeLists()) { + myShelveNames.put(shelvedChangeList.PATH, shelvedChangeList.DESCRIPTION); + } + myNamesList.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + updateOnSelection(); + } + }); + myNamesModel = new DefaultListModel(); + myNamesList.setModel(myNamesModel); + for (String n : myConfigurations.getConfigurationNames()) { + myNamesModel.addElement(n); + } + myDeleteButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + GitBranchConfiguration current = getCurrentConfiguration(); + GitBranchConfiguration selected = getSelectedConfiguration(); + assert selected != null : "The configuration must be selected (the button should be disabled)"; + assert current != selected : "The current must not be the same as selected (the button should be disabled)"; + int i = myNamesList.getSelectedIndex(); + assert i != -1 && myNamesModel.elementAt(i).equals(selected.getName()); + myConfigurations.removeConfiguration(selected); + myNamesModel.removeElementAt(i); + if (i >= myNamesModel.size()) { + i = myNamesModel.size() - 1; + } + myNamesList.setSelectedIndex(i); + } + }); + init(); + if (myNamesModel.size() > 0) { + myNamesList.setSelectedIndex(0); + } + updateOnSelection(); + } + + /** + * Update dialog on selection + */ + private void updateOnSelection() { + GitBranchConfiguration current = getCurrentConfiguration(); + GitBranchConfiguration selected = getSelectedConfiguration(); + myBranchMappingModel.set(selected); + if (selected == null) { + myNameLabel.setText(""); + myShelveNameLabel.setText(""); + } + else { + myNameLabel.setText(selected.getName()); + GitBranchConfigurations.BranchChanges ch = selected.getChanges(); + if (ch == null) { + myShelveNameLabel.setText("No associated shelve"); + myShelveNameLabel.setToolTipText("This configuration has no associated shelve"); + } + else { + String d = myShelveNames.get(ch.SHELVE_PATH); + myShelveNameLabel.setText(d); + myShelveNameLabel.setToolTipText(""); + } + } + boolean isNonCurrent = selected != null || selected != current; + myDeleteButton.setEnabled(isNonCurrent); + setOKActionEnabled(isNonCurrent && myConfigurations.getSpecialStatus() == GitBranchConfigurations.SpecialStatus.NORMAL); + } + + /** + * @return the selected configuration + */ + @Nullable + private GitBranchConfiguration getSelectedConfiguration() { + String value = (String)myNamesList.getSelectedValue(); + GitBranchConfiguration selected; + try { + selected = value == null ? null : myConfigurations.getConfiguration(value); + } + catch (VcsException e1) { + selected = null; + } + return selected; + } + + /** + * @return the current configuration + */ + @Nullable + private GitBranchConfiguration getCurrentConfiguration() { + GitBranchConfiguration current; + try { + current = myConfigurations.getCurrentConfiguration(); + } + catch (VcsException e1) { + current = null; + } + return current; + } + + /** + * {@inheritDoc} + */ + @Override + protected JComponent createCenterPanel() { + return myRootPanel; + } + + /** + * {@inheritDoc} + */ + @Override + protected String getDimensionServiceKey() { + return getClass().getName(); + } + + /** + * {@inheritDoc} + */ + @Override + protected Action[] createActions() { + return new Action[]{getOKAction(), new DialogWrapperExitAction("New Configuration", NEW_CONFIGURATION_EXIT_CODE), getCancelAction()}; + } + + /** + * Show dialog and if ok (checkout) pressed, checkout the respective model. + * + * @param project the context project + * @param configurations the configuration service + */ + public static void showDialog(Project project, GitBranchConfigurations configurations) { + GitManageConfigurationsDialog d = new GitManageConfigurationsDialog(project, configurations); + d.show(); + if (d.getExitCode() == OK_EXIT_CODE) { + configurations.startCheckout(d.getSelectedConfiguration(), null, false); + } + else if (d.getExitCode() == NEW_CONFIGURATION_EXIT_CODE) { + configurations.startCheckout(null, null, false); + } + } + + /** + * The branch mapping model + */ + private class MyBranchMappingModel extends AbstractTableModel { + /** + * The root column + */ + private static final int ROOT = 0; + /** + * The reference column (commit, tag, or branch) + */ + private static final int REFERENCE = 1; + /** + * Total count of columns + */ + private static final int COLUMNS = REFERENCE + 1; + /** + * The mapping in branches first is root the second is reference + */ + final ArrayList> myMapping = new ArrayList>(); + /** + * Base project file + */ + private final File myBaseFile; + + /** + * The constructor + */ + MyBranchMappingModel() { + VirtualFile base = myProject.getBaseDir(); + myBaseFile = base == null ? null : new File(base.getPath()); + } + + /** + * Update table model + * + * @param c the configuration to use to update table model (null means nothing to update) + */ + void set(GitBranchConfiguration c) { + myMapping.clear(); + if (c != null) { + for (Map.Entry e : c.getReferences().entrySet()) { + String root = e.getKey(); + String relative = myBaseFile == null ? null : FileUtil.getRelativePath(myBaseFile, new File(root)); + myMapping.add(Pair.create(relative == null ? root : relative, e.getValue())); + } + } + fireTableDataChanged(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getRowCount() { + return myMapping.size(); + } + + /** + * {@inheritDoc} + */ + @Override + public int getColumnCount() { + return COLUMNS; + } + + /** + * {@inheritDoc} + */ + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + Pair d = myMapping.get(rowIndex); + switch (columnIndex) { + case ROOT: + return d.first; + case REFERENCE: + return d.second; + default: + throw new IllegalStateException("The invalid column number: " + columnIndex); + } + } + + /** + * {@inheritDoc} + */ + @Override + public String getColumnName(int column) { + switch (column) { + case ROOT: + return "Vcs Root"; + case REFERENCE: + return "Reference"; + default: + throw new IllegalStateException("The invalid column number: " + column); + } + } + } +} diff --git a/plugins/git4idea/src/git4idea/checkout/branches/GitSwitchBranchesDialog.form b/plugins/git4idea/src/git4idea/checkout/branches/GitSwitchBranchesDialog.form new file mode 100644 index 000000000000..c4e5ddc02666 --- /dev/null +++ b/plugins/git4idea/src/git4idea/checkout/branches/GitSwitchBranchesDialog.form @@ -0,0 +1,80 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/git4idea/src/git4idea/checkout/branches/GitSwitchBranchesDialog.java b/plugins/git4idea/src/git4idea/checkout/branches/GitSwitchBranchesDialog.java new file mode 100644 index 000000000000..b7e2965e8596 --- /dev/null +++ b/plugins/git4idea/src/git4idea/checkout/branches/GitSwitchBranchesDialog.java @@ -0,0 +1,879 @@ +/* + * 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.checkout.branches; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vcs.VcsException; +import com.intellij.openapi.vcs.changes.Change; +import com.intellij.openapi.vcs.changes.RemoteRevisionsCache; +import com.intellij.openapi.vcs.changes.ui.ChangeNodeDecorator; +import com.intellij.openapi.vcs.changes.ui.ChangesBrowserNode; +import com.intellij.openapi.vcs.changes.ui.ChangesTreeList; +import com.intellij.openapi.vcs.changes.ui.TreeModelBuilder; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.ui.ColoredTableCellRenderer; +import com.intellij.ui.DocumentAdapter; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.table.JBTable; +import com.intellij.util.ui.AbstractTableCellEditor; +import git4idea.GitBranch; +import git4idea.GitRevisionNumber; +import git4idea.validators.GitBranchNameValidator; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.plaf.basic.BasicComboBoxRenderer; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; +import javax.swing.tree.DefaultTreeModel; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.*; +import java.util.List; + +/** + * The switch branches dialog + */ +public class GitSwitchBranchesDialog extends DialogWrapper { + /** + * The branch configuration name text field + */ + private JTextField myNameTextField; + /** + * The changes panel + */ + private JPanel myChangesPanel; + /** + * The root panel + */ + private JPanel myRoot; + /** + * The branches table + */ + private JBTable myBranchesTable; + /** + * Changes to transfer to new configurations label + */ + private JLabel myChangesLabel; + /** + * The changes tree + */ + private final ChangesTreeList myChangesTree; + /** + * The project to use + */ + private final Project myProject; + /** + * The target branch configuration + */ + private final GitBranchConfiguration myTarget; + /** + * The configuration settings object + */ + private final GitBranchConfigurations myConfig; + /** + * If true, the dialog was invoked to modify the current configuration + */ + private final boolean myModify; + /** + * The list of branches to use + */ + private final List myBranches; + /** + * The existing configuration names (used to simplify validation) + */ + private Set myExistingConfigNames; + /** + * Base project directory + */ + private File myBaseFile; + + /** + * The constructor + * + * @param project the project + * @param target the target configuration + * @param allChanges the all changes + * @param roots the collection of roots + * @param remoteBranch the remote branch + * @param config the configuration + * @param isModify the modify flag + * @throws VcsException if there is a problem with detecting the current state + */ + protected GitSwitchBranchesDialog(Project project, + final GitBranchConfiguration target, + final List allChanges, + List roots, + String remoteBranch, final GitBranchConfigurations config, boolean isModify) throws VcsException { + super(project, true); + setTitle(isModify ? "Modify Branch Configuration" : "Checkout Branch Configuration"); + assert (remoteBranch == null) || (target == null) : "There should be no target for remote branch"; + myTarget = target; + myConfig = config; + myModify = isModify; + myProject = project; + VirtualFile baseDir = project.getBaseDir(); + myBaseFile = baseDir == null ? null : new File(baseDir.getPath()); + myExistingConfigNames = myConfig.getConfigurationNames(); + myChangesTree = new ChangesTreeList(myProject, + Collections.emptyList(), + !myModify, + true, + null, + RemoteRevisionsCache.getInstance(project).getChangesNodeDecorator()) { + protected DefaultTreeModel buildTreeModel(final List changes, ChangeNodeDecorator changeNodeDecorator) { + TreeModelBuilder builder = new TreeModelBuilder(myProject, false); + return builder.buildModel(changes, changeNodeDecorator); + } + + protected List getSelectedObjects(final ChangesBrowserNode node) { + return node.getAllChangesUnder(); + } + + @Nullable + protected Change getLeadSelectedObject(final ChangesBrowserNode node) { + final Object o = node.getUserObject(); + if (o instanceof Change) { + return (Change)o; + } + return null; + } + }; + if (remoteBranch != null) { + myBranches = prepareBranchesForRemote(remoteBranch, roots); + } + else { + myBranches = prepareBranchDescriptors(target, roots); + } + if (target == null) { + myNameTextField.setText(generateNewConfigurationName()); + } + else { + myNameTextField.setText(target.getName()); + } + myChangesTree.setChangesToDisplay(allChanges); + myChangesTree.setIncludedChanges(Collections.emptyList()); + myChangesPanel.add(myChangesTree, BorderLayout.CENTER); + myChangesLabel.setLabelFor(myChangesTree); + if (myModify) { + myChangesLabel.setText("Changes in the current configuration"); + } + RootTableModel tableModel = new RootTableModel(); + myBranchesTable.setModel(tableModel); + myBranchesTable.setDefaultRenderer(Pair.class, new PairTableRenderer()); + final TableColumnModel columns = myBranchesTable.getColumnModel(); + final PairTableRenderer renderer = new PairTableRenderer(); + for (Enumeration cs = columns.getColumns(); cs.hasMoreElements();) { + cs.nextElement().setCellRenderer(renderer); + } + TableColumn revisionColumn = columns.getColumn(RootTableModel.REVISION_COLUMN); + revisionColumn.setCellEditor(new ReferenceEditor()); + TableColumn branchColumn = columns.getColumn(RootTableModel.NEW_BRANCH_COLUMN); + branchColumn.setCellEditor(new BranchNameEditor()); + myNameTextField.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + verify(); + } + }); + tableModel.addTableModelListener(new TableModelListener() { + @Override + public void tableChanged(TableModelEvent e) { + verify(); + } + }); + init(); + } + + /** + * The show configuration dialog + * + * @param project the project to use + * @param target the target configuration + * @param allChanges the collection of changes + * @param roots the vcs roots + * @param remoteBranch the remote branch + * @param config the configuration + * @param isModify the modify mode flag + * @return the pair of selected changes and + * @throws VcsException if there is a problem with accessing git + */ + @Nullable + public static Result showDialog(Project project, + @Nullable GitBranchConfiguration target, + final List allChanges, + List roots, + @Nullable String remoteBranch, + final GitBranchConfigurations config, + boolean isModify) throws VcsException { + GitSwitchBranchesDialog d = new GitSwitchBranchesDialog(project, target, allChanges, roots, remoteBranch, config, isModify); + d.show(); + if (d.isOK()) { + return d.createResult(); + } + else { + return null; + } + } + + /** + * @return create dialog result object basing on the dialog state + */ + private Result createResult() { + Result rc = new Result(); + String name = myNameTextField.getText().trim(); + if (myTarget == null) { + rc.target = myConfig.createConfiguration(name); + } + else { + rc.target = myTarget; + rc.target.setName(name); + } + rc.changes = new ArrayList(myChangesTree.getIncludedChanges()); + for (BranchDescriptor d : myBranches) { + if (d.root != null) { + if (!StringUtil.isEmpty(d.newBranchName)) { + rc.referencesToUse.put(d.root, d.referenceToCheckout.trim()); + rc.target.setBranch(d.root.getPath(), d.newBranchName.trim()); + rc.checkoutNeeded.add(d.root); + } + else { + rc.target.setBranch(d.root.getPath(), d.referenceToCheckout.trim()); + if (!d.referenceToCheckout.equals(d.currentReference)) { + rc.checkoutNeeded.add(d.root); + } + } + } + } + return rc; + } + + + /** + * Verify dialog state + */ + private void verify() { + String text = myNameTextField.getText().trim(); + if (text.length() == 0) { + setError("Empty configuration name is not allowed."); + return; + } + else if (myTarget != null && text.equals(myTarget.getName())) { + } + else if (myExistingConfigNames.contains(text)) { + setError("There is another configuration with the same name"); + return; + } + for (BranchDescriptor d : myBranches) { + switch (d.status) { + case BRANCH_NAME_EXISTS: + setError("Duplicate branch name for root " + d.getRoot()); + return; + case INVALID_BRANCH_NAME: + setError("Invalid branch name for root " + d.getRoot()); + return; + case BAD_REVISION: + setError("Invalid revision for root " + d.getRoot()); + return; + case MISSING_REVISION: + setError("The revision must be specified for root " + d.getRoot()); + return; + case CHECKOUT_NEEDED: + case NO_ACTION: + case REMOVED_ROOT: + break; + default: + throw new RuntimeException("Unexpected status: " + d.status); + } + } + setError(null); + } + + private void setError(String s) { + setErrorText(s); + setOKActionEnabled(s == null); + } + + /** + * Generate new configuration name basing on descriptor + * + * @return the generated configuration name + */ + private String generateNewConfigurationName() { + String name = null; + for (BranchDescriptor d : myBranches) { + if (d.newBranchName != null) { + name = d.newBranchName; + break; + } + if (d.existingBranches.contains(d.currentReference)) { + name = d.currentReference; + } + } + if (name == null) { + name = "Unnamed"; + } + if (myExistingConfigNames.contains(name)) { + for (int i = 2; i < Integer.MAX_VALUE; i++) { + String t = name + " " + i; + if (!myExistingConfigNames.contains(t)) { + name = t; + break; + } + } + } + return name; + } + + /** + * Prepare branches for the case of remote checkout + * + * @param remoteBranch the remote branch to checkout + * @param roots the collection of vcs roots + * @return the list of descriptors for the remote + * @throws VcsException if git failed + */ + private List prepareBranchesForRemote(String remoteBranch, List roots) + throws VcsException { + assert roots.size() > 0; + List rc = new ArrayList(); + HashSet allBranches = new HashSet(); + for (VirtualFile root : roots) { + BranchDescriptor d = new BranchDescriptor(); + d.root = root; + d.currentReference = myConfig.describeRoot(root); + d.referenceToCheckout = "remotes/" + remoteBranch; + GitBranch.listAsStrings(myProject, root, false, true, d.existingBranches, null); + GitBranch.listAsStrings(myProject, root, true, true, d.referencesToSelect, null); + allBranches.addAll(d.existingBranches); + rc.add(d); + } + int p = remoteBranch.indexOf('/'); + assert p > 0 && p < remoteBranch.length() - 1 : "Unexpected format for remote branch: " + remoteBranch; + String candidate = remoteBranch.substring(p + 1); + String actual = null; + if (!allBranches.contains(candidate)) { + actual = candidate; + } + else { + for (int i = 2; i < Integer.MAX_VALUE; i++) { + String t = candidate + i; + if (!allBranches.contains(t)) { + actual = t; + break; + } + } + assert actual != null : "Unexpected number of branches: " + remoteBranch; + } + for (BranchDescriptor d : rc) { + d.newBranchName = actual; + d.updateStatus(); + } + return rc; + } + + /** + * Prepare branch descriptors for existing configuration + * + * @param target the target + * @param roots the vcs root + * @return the list of branch descriptors + * @throws VcsException in case of git error + */ + private List prepareBranchDescriptors( + GitBranchConfiguration target, + List roots) + throws VcsException { + Map map = target == null ? Collections.emptyMap() : target.getReferences(); + List rc = new ArrayList(); + for (VirtualFile root : roots) { + BranchDescriptor d = new BranchDescriptor(); + d.root = root; + d.storedReference = map.remove(root.getPath()); + if (d.storedReference != null) { + d.storedRoot = d.root.getPath(); + } + d.currentReference = myConfig.describeRoot(root); + if (d.storedReference != null && !myModify) { + d.referenceToCheckout = d.storedReference; + } + else { + d.referenceToCheckout = d.currentReference; + } + GitBranch.listAsStrings(myProject, root, false, true, d.existingBranches, null); + GitBranch.listAsStrings(myProject, root, true, true, d.referencesToSelect, null); + d.updateStatus(); + rc.add(d); + } + for (Map.Entry m : map.entrySet()) { + String root = m.getKey(); + String ref = m.getValue(); + BranchDescriptor d = new BranchDescriptor(); + d.storedReference = ref; + d.storedRoot = root; + d.referenceToCheckout = ref; + d.updateStatus(); + rc.add(d); + } + return rc; + } + + + @Override + protected JComponent createCenterPanel() { + return myRoot; + } + + /** + * The table model that displays mapping for the vcs roots + */ + class RootTableModel extends AbstractTableModel { + /** + * The vcs root + */ + static final int ROOT_COLUMN = 0; + /** + * The revision + */ + static final int REVISION_COLUMN = 1; + /** + * The name of branch to checkout + */ + static final int NEW_BRANCH_COLUMN = 2; + /** + * The status + */ + static final int STATUS_COLUMN = 3; + /** + * The total number of columns + */ + static final int COLUMNS = STATUS_COLUMN + 1; + + @Override + public int getRowCount() { + return myBranches.size(); + } + + @Override + public int getColumnCount() { + return COLUMNS; + } + + @Override + public boolean isCellEditable(int rowIndex, int columnIndex) { + BranchDescriptor d = myBranches.get(rowIndex); + if (d.root == null) { + return false; + } + return columnIndex == REVISION_COLUMN || columnIndex == NEW_BRANCH_COLUMN; + } + + @Override + public void setValueAt(Object aValue, int rowIndex, int columnIndex) { + String t = (String)aValue; + BranchDescriptor d = myBranches.get(rowIndex); + if (d.root == null) { + return; + } + if (columnIndex == REVISION_COLUMN) { + d.referenceToCheckout = t; + String remotesPrefix = "remotes/"; + if (StringUtil.isEmpty(d.newBranchName) && t.startsWith(remotesPrefix) && d.referencesToSelect.contains(t)) { + int p = t.indexOf(t.indexOf('/'), remotesPrefix.length() + 1); + if (p != -1) { + String c = t.substring(p); + if (!d.existingBranches.contains(c)) { + d.newBranchName = c; + } + else { + for (int i = 2; i < Integer.MAX_VALUE; i++) { + String candidate = c + i; + if (!d.existingBranches.contains(c)) { + d.newBranchName = candidate; + break; + } + } + } + } + } + } + else if (columnIndex == NEW_BRANCH_COLUMN) { + d.newBranchName = t; + } + d.updateStatus(); + fireTableRowsUpdated(rowIndex, rowIndex); + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + BranchDescriptor d = myBranches.get(rowIndex); + switch (columnIndex) { + case ROOT_COLUMN: + return Pair.create(d.getRoot(), d.root != null); + case REVISION_COLUMN: + return Pair.create(d.referenceToCheckout, d.isReferenceValid); + case NEW_BRANCH_COLUMN: + return Pair.create(d.newBranchName == null ? "" : d.newBranchName, d.isNewBranchValid); + case STATUS_COLUMN: + switch (d.status) { + case INVALID_BRANCH_NAME: + return Pair.create("Invalid new branch name", false); + case BAD_REVISION: + return Pair.create("Invalid revision", false); + case MISSING_REVISION: + return Pair.create("Missing revision", false); + case CHECKOUT_NEEDED: + return Pair.create("Checkout", true); + case REMOVED_ROOT: + return Pair.create("Removed root", true); + case NO_ACTION: + return Pair.create("", true); + case BRANCH_NAME_EXISTS: + return Pair.create("Branch name exists", false); + default: + throw new IllegalStateException("Unknown status:" + d.status); + } + default: + throw new IllegalStateException("Unknown column: " + columnIndex); + } + } + + @Override + public String getColumnName(int column) { + switch (column) { + case ROOT_COLUMN: + return "Vcs Root"; + case REVISION_COLUMN: + return "Checkout"; + case NEW_BRANCH_COLUMN: + return "As New Branch"; + case STATUS_COLUMN: + return "Status"; + default: + throw new IllegalStateException("Unknown column: " + column); + } + } + } + + /** + * The object representing row entry + */ + class BranchDescriptor { + /** + * The root to checkout, if null means that the old root is missing. + */ + VirtualFile root; + /** + * Stored root path + */ + String storedRoot; + /** + * Stored reference + */ + String storedReference; + /** + * The commit expression to checkout + */ + String referenceToCheckout; + /** + * if true, branch name is valid + */ + boolean isReferenceValid; + /** + * True if commit expression is valid + */ + RootStatus status; + /** + * The name of of branch + */ + String newBranchName; + /** + * if true, branch name is valid + */ + boolean isNewBranchValid; + /** + * The current type + */ + String currentReference; + /** + * The existing branches + */ + HashSet existingBranches = new HashSet(); + /** + * The existing branches + */ + TreeSet referencesToSelect = new TreeSet(); + + /** + * Update status of the entry + */ + void updateStatus() { + if (root == null) { + status = RootStatus.REMOVED_ROOT; + return; + } + status = branchNameStatus(newBranchName); + isNewBranchValid = status == null; + isReferenceValid = true; + if (referenceToCheckout == null) { + status = RootStatus.MISSING_REVISION; + isReferenceValid = false; + return; + } + try { + GitRevisionNumber.resolve(myProject, root, referenceToCheckout); + if (status == null) { + status = StringUtil.isEmpty(newBranchName) && currentReference.equals(storedReference) + ? RootStatus.NO_ACTION + : RootStatus.CHECKOUT_NEEDED; + } + } + catch (VcsException e) { + isReferenceValid = false; + status = RootStatus.BAD_REVISION; + } + } + + /** + * Get branch name status + * + * @param name the name to check + * @return null if branch name is ok, or status describing the problem + */ + @Nullable + private RootStatus branchNameStatus(final String name) { + RootStatus b = null; + if (!StringUtil.isEmpty(name)) { + if (!GitBranchNameValidator.INSTANCE.checkInput(name)) { + b = RootStatus.INVALID_BRANCH_NAME; + } + if (existingBranches.contains(name)) { + b = RootStatus.BRANCH_NAME_EXISTS; + } + } + return b; + } + + public String getRoot() { + String path = root == null ? storedRoot : root.getPath(); + String relative = myBaseFile == null ? path : FileUtil.getRelativePath(myBaseFile, new File(path)); + return relative == null ? path : relative; + } + } + + + /** + * The root status + */ + enum RootStatus { + /** + * The checkout is needed for the vcs root. Non-error status. + */ + CHECKOUT_NEEDED, + /** + * No action needed for vcs root. Non-error status. + */ + NO_ACTION, + /** + * The branch information does not represents a vcs root anymore. Non-error status. + */ + REMOVED_ROOT, + /** + * The bad revision expression + */ + MISSING_REVISION, + /** + * The revision expression is invalid or could not be evaluated + */ + BAD_REVISION, + /** + * The bad name for the branch + */ + INVALID_BRANCH_NAME, + /** + * The bad name for the branch + */ + BRANCH_NAME_EXISTS + } + + + /** + * Pair text renderer + */ + static class PairTableRenderer extends ColoredTableCellRenderer { + @Override + protected void customizeCellRenderer(JTable table, Object value, boolean selected, boolean hasFocus, int row, int column) { + @SuppressWarnings({"unchecked"}) Pair p = (Pair)value; + String t = p.first == null ? "" : p.first; + if (p.second) { + append(t); + } + else { + append(t, SimpleTextAttributes.ERROR_ATTRIBUTES); + } + } + } + + + /** + * The editor for references + */ + class ReferenceEditor extends AbstractTableCellEditor { + /** + * The root panel + */ + private final JPanel myPanel = new JPanel(new GridBagLayout()); + /** + * Combobox for the panel + */ + private final JComboBox myComboBox = new JComboBox(); + + /** + * The constructor + */ + private ReferenceEditor() { + myComboBox.setEditable(true); + myComboBox.setRenderer(new BasicComboBoxRenderer()); + myComboBox.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent e) { + stopCellEditing(); + } + }); + myPanel.add(myComboBox, + new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), + 0, + 0)); + } + + /** + * {@inheritDoc} + */ + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + BranchDescriptor d = myBranches.get(row); + myComboBox.removeAllItems(); + for (String s : d.referencesToSelect) { + myComboBox.addItem(s); + } + myComboBox.setSelectedItem(d.referenceToCheckout); + return myPanel; + } + + /** + * {@inheritDoc} + */ + public Object getCellEditorValue() { + return myComboBox.getSelectedItem(); + } + } + + /** + * The editor for branch names + */ + class BranchNameEditor extends AbstractTableCellEditor { + /** + * The root panel + */ + private final JPanel myPanel = new JPanel(new GridBagLayout()); + /** + * Combobox for the panel + */ + private final JTextField myTextField = new JTextField(); + /** + * The values that considered invalid + */ + private Set myInvalidValues; + /** + * Default foregorund color (likely black one) + */ + private Color myDefaultForeground; + + /** + * The constructor + */ + private BranchNameEditor() { + myDefaultForeground = myTextField.getForeground(); + myTextField.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + String s = myTextField.getText(); + if (s.length() == 0 && + (myInvalidValues == null || !myInvalidValues.contains(s)) && + (s.length() == 0 || GitBranchNameValidator.INSTANCE.checkInput(s))) { + myTextField.setForeground(myDefaultForeground); + } + else { + myTextField.setForeground(Color.RED); + } + } + }); + myPanel.add(myTextField, + new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), + 0, + 0)); + } + + /** + * {@inheritDoc} + */ + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { + BranchDescriptor d = myBranches.get(row); + myInvalidValues = d.existingBranches; + myTextField.setText(d.newBranchName == null ? "" : d.newBranchName); + return myPanel; + } + + /** + * {@inheritDoc} + */ + @Nullable + public Object getCellEditorValue() { + String s = myTextField.getText().trim(); + return s.length() == 0 ? null : s; + } + } + + /** + * The result of the dialog + */ + public static class Result { + /** + * The roots for which checkout is needed + */ + Collection checkoutNeeded = new ArrayList(); + /** + * The set of selected changes to transfer to new configuration + */ + List changes; + /** + * References to use for new branches + */ + HashMap referencesToUse = new HashMap(); + /** + * The target configuration + */ + GitBranchConfiguration target; + } +} diff --git a/plugins/git4idea/src/git4idea/commands/GitCommand.java b/plugins/git4idea/src/git4idea/commands/GitCommand.java index d2792dc801ed..9082aaedaf66 100644 --- a/plugins/git4idea/src/git4idea/commands/GitCommand.java +++ b/plugins/git4idea/src/git4idea/commands/GitCommand.java @@ -137,6 +137,10 @@ public class GitCommand { * Check attributes command */ public static final GitCommand CHECK_ATTR = read("check-attr"); + /** + * The constant for git command + */ + public static final GitCommand DESCRIBE = meta("describe"); /** * Name of environment variable that specifies editor for the git */ @@ -251,7 +255,8 @@ public class GitCommand { /** * Metadata read/write command */ - META, } + META, + } /** * Thread policy for command diff --git a/plugins/git4idea/src/git4idea/ui/GitUIUtil.java b/plugins/git4idea/src/git4idea/ui/GitUIUtil.java index b91240bbd0c9..b12a98295cd5 100644 --- a/plugins/git4idea/src/git4idea/ui/GitUIUtil.java +++ b/plugins/git4idea/src/git4idea/ui/GitUIUtil.java @@ -17,6 +17,7 @@ package git4idea.ui; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.Messages; +import com.intellij.openapi.vcs.AbstractVcsHelper; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vfs.VirtualFile; import git4idea.GitBranch; @@ -218,6 +219,17 @@ public class GitUIUtil { Messages.showErrorDialog(project, message, GitBundle.message("error.occurred.during", operation)); } + /** + * Show errors on the tab + * + * @param project the context project + * @param title the operation title + * @param errors the errors to display + */ + public static void showTabErrors(Project project, String title, List errors) { + AbstractVcsHelper.getInstance(project).showErrors(errors, title); + } + /** * Setup remotes combobox. The default remote for the current branch is selected by default. * This method gets current branch for the project. From 33f9db9de5bb8de7f38a25de5b174ede0e440280 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 09:16:28 +0100 Subject: [PATCH 39/92] less VFS refreshes in tests? --- .../openapi/vfs/impl/local/LocalFileSystemImpl.java | 6 ------ .../src/com/intellij/testFramework/PlatformTestCase.java | 3 +-- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemImpl.java index fc25cc78b99f..d1e90a14938f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemImpl.java @@ -140,12 +140,6 @@ public final class LocalFileSystemImpl extends LocalFileSystemBase implements Ap } } - ApplicationManager.getApplication().runWriteAction(new Runnable() { - public void run() { - refresh(false); - } - }); - myRootsToWatch.clear(); final File file = new File(FileUtil.getTempDirectory()); diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java index 62e8bf217b27..83d4914d0103 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java @@ -177,7 +177,7 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro assertNotNull("Cannot instantiate ProjectManager component", myProjectManager); File projectFile = getIprFile(); - LocalFileSystem.getInstance().refreshIoFiles(myFilesToDelete); + assert LocalFileSystem.getInstance().findFileByIoFile(projectFile) != null; myProject = createProject(projectFile, getClass().getName() + "." + getName()); @@ -331,7 +331,6 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro for (final File fileToDelete : myFilesToDelete) { delete(fileToDelete); } - LocalFileSystem.getInstance().refreshIoFiles(myFilesToDelete); FileUtil.asyncDelete(new File(myTempDirPath)); From c1a9022097f8da7eaad1e0f20e94c264602ae623 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 09:40:49 +0100 Subject: [PATCH 40/92] don't let an AssertionError from test logger cause deadlock in FileContentQueue --- .../com/intellij/openapi/project/FileContentQueue.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/platform/platform-impl/src/com/intellij/openapi/project/FileContentQueue.java b/platform/platform-impl/src/com/intellij/openapi/project/FileContentQueue.java index fd508a67f21b..137176b40627 100644 --- a/platform/platform-impl/src/com/intellij/openapi/project/FileContentQueue.java +++ b/platform/platform-impl/src/com/intellij/openapi/project/FileContentQueue.java @@ -21,7 +21,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.progress.ProcessCanceledException; import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.vfs.InvalidVirtualFileAccessException; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.persistent.PersistentFS; @@ -124,7 +123,12 @@ public class FileContentQueue { if (e instanceof InterruptedException) throw (InterruptedException)e; if (e instanceof IOException || e instanceof InvalidVirtualFileAccessException) LOG.info(e); - else LOG.error(e); + else if (ApplicationManager.getApplication().isUnitTestMode()) { + e.printStackTrace(); + } + else { + LOG.error(e); + } return false; } From c6245b733c7323880540a70f31220ab7345f2051 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 10:12:20 +0100 Subject: [PATCH 41/92] remove wildcards getting in the way --- .../xml/impl/ImplementationClassCache.java | 49 +++++++++++-------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java b/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java index baa27922a727..90729b5be33e 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java @@ -19,7 +19,6 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.util.Disposer; import com.intellij.util.ReflectionCache; import com.intellij.util.containers.ConcurrentFactoryMap; -import com.intellij.util.xml.DomElement; import com.intellij.util.xml.DomReflectionUtil; import com.intellij.util.xml.Implementation; import gnu.trove.THashMap; @@ -33,9 +32,9 @@ import java.util.TreeSet; /** * @author peter */ -class ImplementationClassCache extends ConcurrentFactoryMap, Class> { - private static final Comparator> CLASS_COMPARATOR = new Comparator>() { - public int compare(final Class o1, final Class o2) { +class ImplementationClassCache { + private static final Comparator CLASS_COMPARATOR = new Comparator() { + public int compare(final Class o1, final Class o2) { if (o1.isAssignableFrom(o2)) return 1; if (o2.isAssignableFrom(o1)) return -1; if (o1.equals(o2)) return 0; @@ -44,22 +43,22 @@ class ImplementationClassCache extends ConcurrentFactoryMap, Class> myImplementationClasses = - new THashMap, Class>(); + private final Map myImplementationClasses = new THashMap(); + private final ConcurrentFactoryMap myCache = new ConcurrentFactoryMap() { + @Nullable + protected Class create(final Class concreteInterface) { + final TreeSet set = new TreeSet(CLASS_COMPARATOR); + findImplementationClassDFS(concreteInterface, set); + if (!set.isEmpty()) { + return set.first(); + } + final Implementation implementation = DomReflectionUtil.findAnnotationDFS(concreteInterface, Implementation.class); + return implementation == null ? null : implementation.value(); + } + }; - @Nullable - protected Class create(final Class concreteInterface) { - final TreeSet> set = new TreeSet>(CLASS_COMPARATOR); - findImplementationClassDFS(concreteInterface, set); - if (!set.isEmpty()) { - return set.first(); - } - final Implementation implementation = DomReflectionUtil.findAnnotationDFS(concreteInterface, Implementation.class); - return implementation == null ? null : implementation.value(); - } - - private void findImplementationClassDFS(final Class concreteInterface, SortedSet> results) { - Class aClass = myImplementationClasses.get(concreteInterface); + private void findImplementationClassDFS(final Class concreteInterface, SortedSet results) { + Class aClass = myImplementationClasses.get(concreteInterface); if (aClass != null) { results.add(aClass); } @@ -70,7 +69,7 @@ class ImplementationClassCache extends ConcurrentFactoryMap domElementClass, Class implementationClass, + public final void registerImplementation(final Class domElementClass, Class implementationClass, final Disposable parentDisposable) { myImplementationClasses.put(domElementClass, implementationClass); if (parentDisposable != null) { @@ -80,7 +79,15 @@ class ImplementationClassCache extends ConcurrentFactoryMap Date: Sat, 31 Jul 2010 10:13:49 +0100 Subject: [PATCH 42/92] VolatileNNLazyValue, AtomicNNLazyValue <: NNLazyValue --- .../com/intellij/openapi/util/AtomicNotNullLazyValue.java | 5 +---- .../util/src/com/intellij/openapi/util/NotNullLazyValue.java | 2 +- .../com/intellij/openapi/util/VolatileNotNullLazyValue.java | 5 +---- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/platform/util/src/com/intellij/openapi/util/AtomicNotNullLazyValue.java b/platform/util/src/com/intellij/openapi/util/AtomicNotNullLazyValue.java index 82c969fc08fc..71046e2be9f6 100644 --- a/platform/util/src/com/intellij/openapi/util/AtomicNotNullLazyValue.java +++ b/platform/util/src/com/intellij/openapi/util/AtomicNotNullLazyValue.java @@ -21,13 +21,10 @@ import org.jetbrains.annotations.NotNull; /** * @author peter */ -public abstract class AtomicNotNullLazyValue { +public abstract class AtomicNotNullLazyValue extends NotNullLazyValue { private volatile T myValue; - @NotNull - protected abstract T compute(); - @NotNull public final T getValue() { if (myValue != null) { diff --git a/platform/util/src/com/intellij/openapi/util/NotNullLazyValue.java b/platform/util/src/com/intellij/openapi/util/NotNullLazyValue.java index 8b10babd5afe..1225fed05b89 100644 --- a/platform/util/src/com/intellij/openapi/util/NotNullLazyValue.java +++ b/platform/util/src/com/intellij/openapi/util/NotNullLazyValue.java @@ -29,7 +29,7 @@ public abstract class NotNullLazyValue { protected abstract T compute(); @NotNull - public final T getValue() { + public T getValue() { if (!myComputed) { myValue = compute(); myComputed = true; diff --git a/platform/util/src/com/intellij/openapi/util/VolatileNotNullLazyValue.java b/platform/util/src/com/intellij/openapi/util/VolatileNotNullLazyValue.java index f2171358e902..344592047984 100644 --- a/platform/util/src/com/intellij/openapi/util/VolatileNotNullLazyValue.java +++ b/platform/util/src/com/intellij/openapi/util/VolatileNotNullLazyValue.java @@ -21,13 +21,10 @@ import org.jetbrains.annotations.NotNull; /** * @author peter */ -public abstract class VolatileNotNullLazyValue { +public abstract class VolatileNotNullLazyValue extends NotNullLazyValue { private volatile T myValue; - @NotNull - protected abstract T compute(); - @NotNull public final T getValue() { T value = myValue; From fd7d09158929fff5da8a58771e2ae83412b86a3b Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 10:33:58 +0100 Subject: [PATCH 43/92] dom.implementationClass EP --- .../AbstractExtensionPointBean.java | 14 ++++++ xml/dom-impl/src/META-INF/DomPlugin.xml | 3 ++ .../xml/impl/DomApplicationComponent.java | 2 +- .../xml/impl/DomImplementationClassEP.java | 30 +++++++++++++ .../xml/impl/ImplementationClassCache.java | 43 +++++++++++++------ 5 files changed, 79 insertions(+), 13 deletions(-) create mode 100644 xml/dom-impl/src/com/intellij/util/xml/impl/DomImplementationClassEP.java diff --git a/platform/extensions/src/com/intellij/openapi/extensions/AbstractExtensionPointBean.java b/platform/extensions/src/com/intellij/openapi/extensions/AbstractExtensionPointBean.java index ada38444c11d..7264cdb55622 100644 --- a/platform/extensions/src/com/intellij/openapi/extensions/AbstractExtensionPointBean.java +++ b/platform/extensions/src/com/intellij/openapi/extensions/AbstractExtensionPointBean.java @@ -15,6 +15,8 @@ */ package com.intellij.openapi.extensions; +import com.intellij.openapi.diagnostic.Logger; +import org.jetbrains.annotations.Nullable; import org.picocontainer.PicoContainer; import org.picocontainer.defaults.ConstructorInjectionComponentAdapter; import org.jetbrains.annotations.NotNull; @@ -23,6 +25,7 @@ import org.jetbrains.annotations.NotNull; * @author peter */ public abstract class AbstractExtensionPointBean implements PluginAware { + private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.extensions.AbstractExtensionPointBean"); protected PluginDescriptor myPluginDescriptor; public final void setPluginDescriptor(PluginDescriptor pluginDescriptor) { @@ -34,6 +37,17 @@ public abstract class AbstractExtensionPointBean implements PluginAware { return (Class)Class.forName(className, true, getLoaderForClass()); } + @Nullable + public final Class findClassNoExceptions(final String className) { + try { + return findClass(className); + } + catch (ClassNotFoundException e) { + LOG.error("Problem loading class " + className + " from plugin " + myPluginDescriptor.getPluginId().getIdString(), e); + return null; + } + } + public ClassLoader getLoaderForClass() { return myPluginDescriptor == null ? getClass().getClassLoader() : myPluginDescriptor.getPluginClassLoader(); } diff --git a/xml/dom-impl/src/META-INF/DomPlugin.xml b/xml/dom-impl/src/META-INF/DomPlugin.xml index c49db1906d63..9792a2384b48 100644 --- a/xml/dom-impl/src/META-INF/DomPlugin.xml +++ b/xml/dom-impl/src/META-INF/DomPlugin.xml @@ -3,6 +3,9 @@ + + diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java index b78efa84713a..838b9ff04279 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java @@ -108,7 +108,7 @@ public class DomApplicationComponent { } public final void registerImplementation(Class domElementClass, Class implementationClass, - final Disposable parentDisposable) { + @Nullable final Disposable parentDisposable) { myCachedImplementationClasses.registerImplementation(domElementClass, implementationClass, parentDisposable); } diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomImplementationClassEP.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomImplementationClassEP.java new file mode 100644 index 000000000000..78bef21aac60 --- /dev/null +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomImplementationClassEP.java @@ -0,0 +1,30 @@ +package com.intellij.util.xml.impl; + +import com.intellij.openapi.extensions.AbstractExtensionPointBean; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.util.xmlb.annotations.Attribute; +import org.jetbrains.annotations.Nullable; + +/** + * @author peter + */ +public class DomImplementationClassEP extends AbstractExtensionPointBean { + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.dom.implementationClass"); + + @Attribute("interfaceClass") + public String interfaceName; + + @Attribute("implementationClass") + public String implementationName; + + + @Nullable + public Class getInterfaceClass() { + return findClassNoExceptions(interfaceName); + } + + @Nullable + public Class getImplementationClass() { + return findClassNoExceptions(implementationName); + } +} diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java b/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java index 90729b5be33e..d07607a608f1 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java @@ -19,13 +19,13 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.util.Disposer; import com.intellij.util.ReflectionCache; import com.intellij.util.containers.ConcurrentFactoryMap; +import com.intellij.util.containers.MultiMap; import com.intellij.util.xml.DomReflectionUtil; import com.intellij.util.xml.Implementation; -import gnu.trove.THashMap; import org.jetbrains.annotations.Nullable; +import java.util.Collection; import java.util.Comparator; -import java.util.Map; import java.util.SortedSet; import java.util.TreeSet; @@ -43,7 +43,7 @@ class ImplementationClassCache { }; - private final Map myImplementationClasses = new THashMap(); + private final MultiMap myImplementationClasses = new MultiMap(); private final ConcurrentFactoryMap myCache = new ConcurrentFactoryMap() { @Nullable protected Class create(final Class concreteInterface) { @@ -57,25 +57,44 @@ class ImplementationClassCache { } }; - private void findImplementationClassDFS(final Class concreteInterface, SortedSet results) { - Class aClass = myImplementationClasses.get(concreteInterface); - if (aClass != null) { - results.add(aClass); + ImplementationClassCache() { + for (DomImplementationClassEP ep : DomImplementationClassEP.EP_NAME.getExtensions()) { + myImplementationClasses.putValue(ep.interfaceName, ep); } - else { + } + + private void findImplementationClassDFS(final Class concreteInterface, SortedSet results) { + final Collection values = myImplementationClasses.get(concreteInterface.getName()); + for (DomImplementationClassEP value : values) { + if (value.getInterfaceClass() == concreteInterface) { + results.add(value.getImplementationClass()); + } + } + if (results.isEmpty()) { for (final Class aClass1 : ReflectionCache.getInterfaces(concreteInterface)) { findImplementationClassDFS(aClass1, results); } } } - public final void registerImplementation(final Class domElementClass, Class implementationClass, - final Disposable parentDisposable) { - myImplementationClasses.put(domElementClass, implementationClass); + public final void registerImplementation(final Class domElementClass, final Class implementationClass, + @Nullable final Disposable parentDisposable) { + final DomImplementationClassEP ep = new DomImplementationClassEP() { + @Override + public Class getInterfaceClass() { + return domElementClass; + } + + @Override + public Class getImplementationClass() { + return implementationClass; + } + }; + myImplementationClasses.putValue(domElementClass.getName(), ep); if (parentDisposable != null) { Disposer.register(parentDisposable, new Disposable() { public void dispose() { - myImplementationClasses.remove(domElementClass); + myImplementationClasses.remove(domElementClass.getName()); } }); } From efff66ce6f81870103ce132c92baf67b5d711c04 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 10:59:08 +0100 Subject: [PATCH 44/92] fix tests? --- .../src/com/intellij/testFramework/PlatformTestCase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java index 83d4914d0103..8273aa43cc1c 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java @@ -177,7 +177,7 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro assertNotNull("Cannot instantiate ProjectManager component", myProjectManager); File projectFile = getIprFile(); - assert LocalFileSystem.getInstance().findFileByIoFile(projectFile) != null; + assert LocalFileSystem.getInstance().refreshAndFindFileByIoFile(projectFile) != null; myProject = createProject(projectFile, getClass().getName() + "." + getName()); From 216e117fe4e420a8851adfe5de12a706e0a3ecdf Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 10:59:28 +0100 Subject: [PATCH 45/92] lazy spring dom implementation classes loading --- xml/dom-impl/src/META-INF/DomPlugin.xml | 2 +- .../com/intellij/util/xml/impl/DomImplementationClassEP.java | 2 +- .../src/com/intellij/util/xml/DomFileDescription.java | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/xml/dom-impl/src/META-INF/DomPlugin.xml b/xml/dom-impl/src/META-INF/DomPlugin.xml index 9792a2384b48..d4be4f6708f5 100644 --- a/xml/dom-impl/src/META-INF/DomPlugin.xml +++ b/xml/dom-impl/src/META-INF/DomPlugin.xml @@ -3,7 +3,7 @@ - EP_NAME = ExtensionPointName.create("com.intellij.dom.implementationClass"); + public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.dom.implementation"); @Attribute("interfaceClass") public String interfaceName; diff --git a/xml/dom-openapi/src/com/intellij/util/xml/DomFileDescription.java b/xml/dom-openapi/src/com/intellij/util/xml/DomFileDescription.java index 5a9e82307d05..95ebdcb3c8c1 100644 --- a/xml/dom-openapi/src/com/intellij/util/xml/DomFileDescription.java +++ b/xml/dom-openapi/src/com/intellij/util/xml/DomFileDescription.java @@ -67,6 +67,7 @@ public class DomFileDescription { * @param domElementClass interface class. * @param implementationClass abstract implementation class. * + * @deprecated use dom.implementation extension point instead * @see #initializeFileDescription() */ public final void registerImplementation(Class domElementClass, Class implementationClass) { From 868518e2521fd3b3b9fd3b7418b1ef07e443affd Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Sat, 31 Jul 2010 13:55:31 +0400 Subject: [PATCH 46/92] bug fixed: show object's synthetic "val$..." fields under object's node when the object is not frame's 'this' object --- .../debugger/ui/tree/render/ClassRenderer.java | 11 +++++++---- .../debugger/engine/evaluation/EvaluationContext.java | 2 ++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java index 36b3a41f4b9e..eddc91bf1e26 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/ClassRenderer.java @@ -39,6 +39,7 @@ import com.intellij.util.StringBuilderSpinAllocator; import com.sun.jdi.*; import org.jdom.Element; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -147,7 +148,7 @@ public class ClassRenderer extends NodeRendererImpl{ final List fields = refType.allFields(); if (fields.size() > 0) { for (final Field field : fields) { - if (!shouldDisplay(field)) { + if (!shouldDisplay(evaluationContext, objRef, field)) { continue; } children.add(nodeManager.createNode(nodeDescriptorFactory.getFieldDescriptor(parentDescriptor, objRef, field), evaluationContext)); @@ -164,13 +165,15 @@ public class ClassRenderer extends NodeRendererImpl{ builder.setChildren(children); } - private boolean shouldDisplay(Field field) { + private boolean shouldDisplay(EvaluationContext context, @NotNull ObjectReference objInstance, @NotNull Field field) { final boolean isSynthetic = DebuggerUtils.isSynthetic(field); if (!SHOW_SYNTHETICS && isSynthetic) { return false; } - if (isSynthetic && StringUtil.startsWith(field.name(), FieldDescriptorImpl.OUTER_LOCAL_VAR_FIELD_PREFIX)) { - return false; + if (isSynthetic) { + if (objInstance.equals(context.getThisObject()) && StringUtil.startsWith(field.name(), FieldDescriptorImpl.OUTER_LOCAL_VAR_FIELD_PREFIX)) { + return false; + } } if(!SHOW_STATIC && field.isStatic()) { return false; diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluationContext.java b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluationContext.java index 985798194293..e50ef4380968 100644 --- a/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluationContext.java +++ b/java/debugger/openapi/src/com/intellij/debugger/engine/evaluation/EvaluationContext.java @@ -32,4 +32,6 @@ public interface EvaluationContext extends StackFrameContext{ Project getProject(); ClassLoaderReference getClassLoader() throws EvaluateException; + + Value getThisObject(); } From 1464df4c4383a7745f82be8179dada376c7336ce Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 11:26:12 +0100 Subject: [PATCH 47/92] fix tests? --- .../src/com/intellij/testFramework/PlatformTestCase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java index 8273aa43cc1c..7b7bcf19b2dd 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java @@ -177,7 +177,7 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro assertNotNull("Cannot instantiate ProjectManager component", myProjectManager); File projectFile = getIprFile(); - assert LocalFileSystem.getInstance().refreshAndFindFileByIoFile(projectFile) != null; + LocalFileSystem.getInstance().refreshIoFiles(myFilesToDelete); myProject = createProject(projectFile, getClass().getName() + "." + getName()); From 8aa563ce23c1f479b5237fcfa123a808ce6ed257 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 12:32:29 +0100 Subject: [PATCH 48/92] don't load inspection profile manager on dom initialization --- .../DomElementAnnotationsManagerImpl.java | 52 +++++-------------- .../DomHighlightingHelperImpl.java | 12 ++--- .../xml/impl/DomApplicationComponent.java | 31 +++++++++++ .../util/xml/impl/DomManagerImpl.java | 48 +++++------------ 4 files changed, 64 insertions(+), 79 deletions(-) diff --git a/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomElementAnnotationsManagerImpl.java b/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomElementAnnotationsManagerImpl.java index 38363c0cc833..963afc1d2bec 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomElementAnnotationsManagerImpl.java +++ b/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomElementAnnotationsManagerImpl.java @@ -46,14 +46,12 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.xml.DomElement; import com.intellij.util.xml.DomFileElement; import com.intellij.util.xml.DomUtil; -import gnu.trove.THashMap; +import com.intellij.util.xml.impl.DomApplicationComponent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; public class DomElementAnnotationsManagerImpl extends DomElementAnnotationsManager { public static final Object LOCK = new Object(); @@ -63,8 +61,6 @@ public class DomElementAnnotationsManagerImpl extends DomElementAnnotationsManag private static final Key> CACHED_VALUE_KEY = Key.create("DomProblemHolderCachedValue"); private final EventDispatcher myDispatcher = EventDispatcher.create(DomHighlightingListener.class); - private final Map> myClass2Annotator = new THashMap>(); - private static final DomElementsProblemsHolder EMPTY_PROBLEMS_HOLDER = new DomElementsProblemsHolder() { @NotNull public List getProblems(DomElement domElement) { @@ -105,16 +101,12 @@ public class DomElementAnnotationsManagerImpl extends DomElementAnnotationsManag } }; - private final DomHighlightingHelperImpl myHighlightingHelper = new DomHighlightingHelperImpl(this); private final ModificationTracker myModificationTracker; - private final ProjectRootManager myProjectRootManager; - private final CachedValuesManager myCachedValuesManager; + private final Project myProject; private long myModificationCount; - public DomElementAnnotationsManagerImpl(Project project, final InspectionProfileManager inspectionProfileManager, ProjectRootManager projectRootManager, - final CachedValuesManager cachedValuesManager) { - myCachedValuesManager = cachedValuesManager; - myProjectRootManager = projectRootManager; + public DomElementAnnotationsManagerImpl(Project project) { + myProject = project; myModificationTracker = new ModificationTracker() { public long getModificationCount() { return myModificationCount; @@ -129,6 +121,8 @@ public class DomElementAnnotationsManagerImpl extends DomElementAnnotationsManag dropAnnotationsCache(); } }; + + final InspectionProfileManager inspectionProfileManager = InspectionProfileManager.getInstance(); inspectionProfileManager.addProfileChangeListener(profileChangeAdapter, project); Disposer.register(project, new Disposable() { public void dispose() { @@ -161,9 +155,9 @@ public class DomElementAnnotationsManagerImpl extends DomElementAnnotationsManag if (isHolderOutdated(element.getFile()) || holder == null) { holder = new DomElementsProblemsHolderImpl(element); rootTag.putUserData(DOM_PROBLEM_HOLDER_KEY, holder); - final CachedValue cachedValue = myCachedValuesManager.createCachedValue(new CachedValueProvider() { + final CachedValue cachedValue = CachedValuesManager.getManager(myProject).createCachedValue(new CachedValueProvider() { public Result compute() { - return new Result(Boolean.FALSE, element, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, myModificationTracker, myProjectRootManager); + return new Result(Boolean.FALSE, element, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT, myModificationTracker, ProjectRootManager.getInstance(myProject)); } }, false); cachedValue.getValue(); @@ -211,33 +205,13 @@ public class DomElementAnnotationsManagerImpl extends DomElementAnnotationsManag return getProblemHolder(element); } - public void annotate(final DomElement element, final DomElementAnnotationHolder holder, final Class rootClass) { - final List list = getAnnotators(rootClass); - if (list != null) { - for (DomElementsAnnotator annotator : list) { - annotator.annotate(element, holder); - } + public static void annotate(final DomElement element, final DomElementAnnotationHolder holder, final Class rootClass) { + final DomElementsAnnotator annotator = DomApplicationComponent.getInstance().getAnnotator(rootClass); + if (annotator != null) { + annotator.annotate(element, holder); } } - - public final void registerDomElementsAnnotator(DomElementsAnnotator annotator, Class aClass) { - getOrCreateAnnotators(aClass).add(annotator); - } - - private List getOrCreateAnnotators(final Class aClass) { - List annotators = getAnnotators(aClass); - if (annotators == null) { - myClass2Annotator.put(aClass, annotators = new ArrayList()); - } - return annotators; - } - - @Nullable - private List getAnnotators(final Class aClass) { - return myClass2Annotator.get(aClass); - } - public List createProblemDescriptors(final InspectionManager manager, DomElementProblemDescriptor problemDescriptor) { return ContainerUtil.createMaybeSingletonList(DomElementsHighlightingUtil.createProblemDescriptors(manager, problemDescriptor)); } @@ -256,7 +230,7 @@ public class DomElementAnnotationsManagerImpl extends DomElementAnnotationsManag } public DomHighlightingHelper getHighlightingHelper() { - return myHighlightingHelper; + return DomHighlightingHelperImpl.INSTANCE; } @NotNull diff --git a/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomHighlightingHelperImpl.java b/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomHighlightingHelperImpl.java index 8f94a1e4ff21..91293ccf588c 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomHighlightingHelperImpl.java +++ b/xml/dom-impl/src/com/intellij/util/xml/highlighting/DomHighlightingHelperImpl.java @@ -51,15 +51,15 @@ import java.util.List; * @author peter */ public class DomHighlightingHelperImpl extends DomHighlightingHelper { + public static final DomHighlightingHelperImpl INSTANCE = new DomHighlightingHelperImpl(); private final GenericValueReferenceProvider myProvider = new GenericValueReferenceProvider(); - private final DomElementAnnotationsManagerImpl myAnnotationsManager; - - public DomHighlightingHelperImpl(final DomElementAnnotationsManagerImpl annotationsManager) { - myAnnotationsManager = annotationsManager; - } + private final DomApplicationComponent myDomApplicationComponent = DomApplicationComponent.getInstance(); public void runAnnotators(DomElement element, DomElementAnnotationHolder holder, Class rootClass) { - myAnnotationsManager.annotate(element, holder, rootClass); + final DomElementsAnnotator annotator = myDomApplicationComponent.getAnnotator(rootClass); + if (annotator != null) { + annotator.annotate(element, holder); + } } @NotNull diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java index 838b9ff04279..fe624c3b66ee 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomApplicationComponent.java @@ -25,6 +25,7 @@ import com.intellij.util.xml.DomElement; import com.intellij.util.xml.DomElementVisitor; import com.intellij.util.xml.DomFileDescription; import com.intellij.util.xml.TypeChooserManager; +import com.intellij.util.xml.highlighting.DomElementsAnnotator; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -46,6 +47,14 @@ public class DomApplicationComponent { private final ImplementationClassCache myCachedImplementationClasses = new ImplementationClassCache(); private final TypeChooserManager myTypeChooserManager = new TypeChooserManager(); final ReflectionAssignabilityCache assignabilityCache = new ReflectionAssignabilityCache(); + private final FactoryMap myClass2Annotator = new ConcurrentFactoryMap() { + + @Override + protected DomElementsAnnotator create(Class key) { + final DomFileDescription desc = findFileDescription(key); + return desc == null ? null : desc.createAnnotator(); + } + }; private final ConcurrentFactoryMap myGenericInfos = new ConcurrentFactoryMap() { @NotNull @@ -101,6 +110,28 @@ public class DomApplicationComponent { myTypeChooserManager.copyFrom(description.getTypeChooserManager()); } + @Nullable + private synchronized DomFileDescription findFileDescription(Class rootElementClass) { + for (Set descriptions : myRootTagName2FileDescription.values()) { + for (DomFileDescription description : descriptions) { + if (description.getRootElementClass() == rootElementClass) { + return description; + } + } + } + + for (DomFileDescription description : myAcceptingOtherRootTagNamesDescriptions) { + if (description.getRootElementClass() == rootElementClass) { + return description; + } + } + return null; + } + + public DomElementsAnnotator getAnnotator(Class rootElementClass) { + return myClass2Annotator.get(rootElementClass); + } + @Nullable final Class getImplementation(final Class concreteInterface) { //noinspection unchecked diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/DomManagerImpl.java b/xml/dom-impl/src/com/intellij/util/xml/impl/DomManagerImpl.java index 15b3ef344dc7..d5ce03964b02 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/DomManagerImpl.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/DomManagerImpl.java @@ -5,7 +5,7 @@ package com.intellij.util.xml.impl; import com.intellij.ide.startup.StartupManagerEx; import com.intellij.openapi.Disposable; -import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; @@ -18,6 +18,7 @@ import com.intellij.openapi.util.Factory; import com.intellij.openapi.util.Key; import com.intellij.openapi.vfs.*; import com.intellij.openapi.vfs.newvfs.NewVirtualFile; +import com.intellij.pom.PomManager; import com.intellij.pom.PomModel; import com.intellij.pom.PomModelAspect; import com.intellij.pom.event.PomModelEvent; @@ -39,9 +40,6 @@ import com.intellij.util.xml.events.DomEvent; import com.intellij.util.xml.events.ElementChangedEvent; import com.intellij.util.xml.events.ElementDefinedEvent; import com.intellij.util.xml.events.ElementUndefinedEvent; -import com.intellij.util.xml.highlighting.DomElementAnnotationsManager; -import com.intellij.util.xml.highlighting.DomElementAnnotationsManagerImpl; -import com.intellij.util.xml.highlighting.DomElementsAnnotator; import com.intellij.util.xml.reflect.AbstractDomChildrenDescription; import com.intellij.util.xml.reflect.DomGenericInfo; import net.sf.cglib.proxy.AdvancedProxy; @@ -78,7 +76,6 @@ public final class DomManagerImpl extends DomManager { private final Project myProject; private final DomApplicationComponent myApplicationComponent; - private final DomElementAnnotationsManagerImpl myAnnotationsManager; private final PsiFileFactory myFileFactory; private long myModificationCount; @@ -86,19 +83,12 @@ public final class DomManagerImpl extends DomManager { private final ProjectFileIndex myFileIndex; private final SemService mySemService; - public DomManagerImpl(final PomModel pomModel, - final Project project, final PsiManager psiManager, - final XmlAspect xmlAspect, final DomElementAnnotationsManager annotationsManager, - final VirtualFileManager virtualFileManager, - final StartupManager startupManager, - final ProjectRootManager projectRootManager, - final DomApplicationComponent applicationComponent, - final ConverterManager converterManager, SemService semService) { + public DomManagerImpl(final Project project, final XmlAspect xmlAspect) { myProject = project; - mySemService = semService; - myConverterManager = (ConverterManagerImpl)converterManager; - myApplicationComponent = applicationComponent; - myAnnotationsManager = (DomElementAnnotationsManagerImpl)annotationsManager; + mySemService = SemService.getSemService(project); + myConverterManager = (ConverterManagerImpl)ServiceManager.getService(ConverterManager.class); + myApplicationComponent = DomApplicationComponent.getInstance(); + final PomModel pomModel = PomManager.getModel(project); pomModel.addModelListener(new PomModelListener() { public void modelChanged(PomModelEvent event) { final XmlChangeSet changeSet = (XmlChangeSet)event.getChangeSet(xmlAspect); @@ -116,6 +106,8 @@ public final class DomManagerImpl extends DomManager { myFileFactory = PsiFileFactory.getInstance(project); + final PsiManager psiManager = PsiManager.getInstance(project); + final Runnable setupVfsListeners = new Runnable() { public void run() { final VirtualFileAdapter listener = new VirtualFileAdapter() { @@ -171,20 +163,18 @@ public final class DomManagerImpl extends DomManager { } } }; - virtualFileManager.addVirtualFileListener(listener, project); + VirtualFileManager.getInstance().addVirtualFileListener(listener, project); } }; + + final StartupManager startupManager = StartupManager.getInstance(project); if (!((StartupManagerEx)startupManager).startupActivityPassed()) { startupManager.registerStartupActivity(setupVfsListeners); } else { setupVfsListeners.run(); } - myFileIndex = projectRootManager.getFileIndex(); - - for (final DomFileDescription description : Extensions.getExtensions(DomFileDescription.EP_NAME)) { - _registerFileDescription(description); - } + myFileIndex = ProjectRootManager.getInstance(project).getFileIndex(); } private void processVfsChange(final VirtualFile file) { @@ -478,19 +468,9 @@ public final class DomManagerImpl extends DomManager { } public final void registerFileDescription(final DomFileDescription description) { - _registerFileDescription(description); - - myApplicationComponent.registerFileDescription(description); - } - - private void _registerFileDescription(final DomFileDescription description) { mySemService.clearCache(); - final DomElementsAnnotator annotator = description.createAnnotator(); - if (annotator != null) { - //noinspection unchecked - myAnnotationsManager.registerDomElementsAnnotator(annotator, description.getRootElementClass()); - } + myApplicationComponent.registerFileDescription(description); } @NotNull From b71c70d9bbd744728d4b9697b9e1f968cef5bc70 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Sat, 31 Jul 2010 15:46:21 +0400 Subject: [PATCH 49/92] Limit text we are showing when suggesting text of long and multiline expressions (IDEA-56847) --- .../com/intellij/refactoring/IntroduceTargetChooser.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/refactoring/IntroduceTargetChooser.java b/platform/lang-impl/src/com/intellij/refactoring/IntroduceTargetChooser.java index 523fa462784a..9a7f50b2cb63 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/IntroduceTargetChooser.java +++ b/platform/lang-impl/src/com/intellij/refactoring/IntroduceTargetChooser.java @@ -64,7 +64,11 @@ public class IntroduceTargetChooser { final T expr = (T)value; if (expr.isValid()) { - setText(renderer.fun(expr)); + String text = renderer.fun(expr); + int firstNewLinePos = text.indexOf('\n'); + String trimmedText = text.substring(0, firstNewLinePos != -1 ? firstNewLinePos : Math.min(100, text.length())); + if (trimmedText.length() != text.length()) trimmedText += " ..."; + setText(trimmedText); } return rendererComponent; } From 1ce758e53cf2ae7417f22f8549f7a3c2930f8a5b Mon Sep 17 00:00:00 2001 From: Eugene Zhuravlev Date: Sat, 31 Jul 2010 15:44:03 +0400 Subject: [PATCH 50/92] less calls to findDependencies() --- .../src/com/intellij/compiler/impl/CompileDriver.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java index b3a7fb1ce535..6efb8555b579 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompileDriver.java @@ -950,6 +950,7 @@ public class CompileDriver { } })); + final DumbService dumbService = DumbService.getInstance(myProject); try { VirtualFile[] snapshot = null; final Map, Collection> chunkMap = new HashMap, Collection>(); @@ -963,6 +964,7 @@ public class CompileDriver { final Set allDependent = new HashSet(); try { int round = 0; + boolean compiledSomethingForThisChunk = false; Collection dependentFiles = Collections.emptyList(); final Function>, Pair>> dependencyFilter = new DependentClassesCumulativeFilter(); @@ -973,8 +975,8 @@ public class CompileDriver { if (context.getProgressIndicator().isCanceled()) { throw new ExitException(ExitStatus.CANCELLED); } - context.getProgressIndicator().setText("Preparing sources for " + compiler.getDescription()); - DumbService.getInstance(myProject).waitForSmartMode(); + + dumbService.waitForSmartMode(); if (round == 0) { if (snapshot == null || ContainerUtil.intersects(generatedTypes, compilerManager.getRegisteredInputTypes(compiler))) { @@ -1022,10 +1024,11 @@ public class CompileDriver { } didSomething |= compiledSomething; + compiledSomethingForThisChunk |= didSomething; } final boolean hasUnprocessedTraverseRoots = context.getDependencyCache().hasUnprocessedTraverseRoots(); - if (!isRebuild && (didSomething || hasUnprocessedTraverseRoots)) { + if (!isRebuild && (compiledSomethingForThisChunk || hasUnprocessedTraverseRoots)) { final Set compiledWithSuccess; final Set compiledWithErrors = CacheUtils.getFilesCompiledWithErrors(context); if (compiledWithErrors.isEmpty()) { @@ -1516,7 +1519,7 @@ public class CompileDriver { }); if (ex[0] != null) { throw ex[0]; - } + } if (onlyCheckStatus) { if (toGenerate.isEmpty() && pathsToRemove.isEmpty()) { From dcb406e4a6ef552f3352df595b275a18ef9b2295 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 13:05:21 +0100 Subject: [PATCH 51/92] don't load element type classes prematurely --- .../src/com/intellij/lang/java/JavaParserDefinition.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 c69bef5561b5..ba84d8e935b6 100644 --- a/java/java-impl/src/com/intellij/lang/java/JavaParserDefinition.java +++ b/java/java-impl/src/com/intellij/lang/java/JavaParserDefinition.java @@ -38,7 +38,6 @@ import org.jetbrains.annotations.NotNull; * @author max */ public class JavaParserDefinition implements ParserDefinition { - private final TokenSet myStringLiteralElementSet = TokenSet.create(JavaElementType.LITERAL_EXPRESSION); @NotNull public Lexer createLexer(Project project) { @@ -66,7 +65,7 @@ public class JavaParserDefinition implements ParserDefinition { @NotNull public TokenSet getStringLiteralElements() { - return myStringLiteralElementSet; + return TokenSet.create(JavaElementType.LITERAL_EXPRESSION); } @NotNull From 8c5fc2d7fc24e1a5cd3bebf6fc1d5bbae99b1e34 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 13:15:29 +0100 Subject: [PATCH 52/92] revert refresh removal --- .../openapi/vfs/impl/local/LocalFileSystemImpl.java | 6 ++++++ .../src/com/intellij/testFramework/PlatformTestCase.java | 1 + 2 files changed, 7 insertions(+) diff --git a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemImpl.java b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemImpl.java index d1e90a14938f..fc25cc78b99f 100644 --- a/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/vfs/impl/local/LocalFileSystemImpl.java @@ -140,6 +140,12 @@ public final class LocalFileSystemImpl extends LocalFileSystemBase implements Ap } } + ApplicationManager.getApplication().runWriteAction(new Runnable() { + public void run() { + refresh(false); + } + }); + myRootsToWatch.clear(); final File file = new File(FileUtil.getTempDirectory()); diff --git a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java index 7b7bcf19b2dd..62e8bf217b27 100644 --- a/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java +++ b/platform/testFramework/src/com/intellij/testFramework/PlatformTestCase.java @@ -331,6 +331,7 @@ public abstract class PlatformTestCase extends UsefulTestCase implements DataPro for (final File fileToDelete : myFilesToDelete) { delete(fileToDelete); } + LocalFileSystem.getInstance().refreshIoFiles(myFilesToDelete); FileUtil.asyncDelete(new File(myTempDirPath)); From d09743b13464122b4f16f29aa6fb47ef28f72add Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 14:27:22 +0100 Subject: [PATCH 53/92] don't use sun.* API --- .../intellij/util/ParameterizedTypeImpl.java | 32 +++++++++++++++++++ .../reflect/DomExtensionsRegistrarImpl.java | 4 +-- 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 platform/util/src/com/intellij/util/ParameterizedTypeImpl.java diff --git a/platform/util/src/com/intellij/util/ParameterizedTypeImpl.java b/platform/util/src/com/intellij/util/ParameterizedTypeImpl.java new file mode 100644 index 000000000000..8c35c4744a33 --- /dev/null +++ b/platform/util/src/com/intellij/util/ParameterizedTypeImpl.java @@ -0,0 +1,32 @@ +package com.intellij.util; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +/** + * @author peter + */ +public class ParameterizedTypeImpl implements ParameterizedType { + private final Type myRawType; + private final Type[] myArguments; + + public ParameterizedTypeImpl(Type rawType, Type... arguments) { + myRawType = rawType; + myArguments = arguments; + } + + @Override + public Type[] getActualTypeArguments() { + return myArguments; + } + + @Override + public Type getRawType() { + return myRawType; + } + + @Override + public Type getOwnerType() { + return null; + } +} diff --git a/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtensionsRegistrarImpl.java b/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtensionsRegistrarImpl.java index 970434217a3c..e5e95aedf984 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtensionsRegistrarImpl.java +++ b/xml/dom-impl/src/com/intellij/util/xml/reflect/DomExtensionsRegistrarImpl.java @@ -15,6 +15,7 @@ */ package com.intellij.util.xml.reflect; +import com.intellij.util.ParameterizedTypeImpl; import com.intellij.util.ReflectionUtil; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; @@ -22,7 +23,6 @@ import com.intellij.util.xml.GenericAttributeValue; import com.intellij.util.xml.XmlName; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; -import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl; import java.lang.reflect.Type; import java.util.List; @@ -71,7 +71,7 @@ public class DomExtensionsRegistrarImpl implements DomExtensionsRegistrar { @NotNull public DomExtension registerGenericAttributeValueChildExtension(@NotNull final XmlName name, final Type parameterType) { - return addExtension(myAttributes, name, ParameterizedTypeImpl.make(GenericAttributeValue.class, new Type[]{parameterType}, null)); + return addExtension(myAttributes, name, new ParameterizedTypeImpl(GenericAttributeValue.class, parameterType)); } @NotNull From 18014598e3c01fca199dde8591a6af8b3ae5c2c4 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Sat, 31 Jul 2010 18:22:09 +0400 Subject: [PATCH 54/92] allow stub builder to skip nodes when building stubs --- .../src/com/intellij/psi/StubBuilder.java | 2 ++ .../psi/stubs/DefaultStubBuilder.java | 14 ++++++++++-- .../intellij/psi/impl/source/PsiFileImpl.java | 22 +++++++++++++------ 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/platform/lang-api/src/com/intellij/psi/StubBuilder.java b/platform/lang-api/src/com/intellij/psi/StubBuilder.java index a17c1493d16b..0f3d95ca369f 100644 --- a/platform/lang-api/src/com/intellij/psi/StubBuilder.java +++ b/platform/lang-api/src/com/intellij/psi/StubBuilder.java @@ -20,7 +20,9 @@ package com.intellij.psi; import com.intellij.psi.stubs.StubElement; +import com.intellij.psi.tree.IElementType; public interface StubBuilder { StubElement buildStubTree(PsiFile file); + boolean skipChildProcessingWhenBuildingStubs(IElementType nodeType, IElementType childType); } \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java b/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java index b65ca3f870c2..aca6d8f11ae6 100644 --- a/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java +++ b/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java @@ -38,8 +38,9 @@ public class DefaultStubBuilder implements StubBuilder { return new PsiFileStubImpl(file); } - protected static StubElement buildStubTreeFor(PsiElement elt, StubElement parentStub) { + protected StubElement buildStubTreeFor(PsiElement elt, StubElement parentStub) { StubElement stub = parentStub; + IElementType eltType; if (elt instanceof StubBasedPsiElement) { final IStubElementType type = ((StubBasedPsiElement)elt).getElementType(); @@ -47,6 +48,7 @@ public class DefaultStubBuilder implements StubBuilder { //noinspection unchecked stub = type.createStub(elt, parentStub); } + eltType = type; } else { final ASTNode node = elt.getNode(); @@ -54,13 +56,21 @@ public class DefaultStubBuilder implements StubBuilder { if (type instanceof IStubElementType && ((IStubElementType)type).shouldCreateStub(node)) { LOG.error("Non-StubBasedPsiElement requests stub creation. Stub type: " + type + ", PSI: " + elt); } + eltType = type; } for (PsiElement child = elt.getFirstChild(); child != null; child = child.getNextSibling()) { - buildStubTreeFor(child, stub); + ASTNode childNode = child.getNode(); + if (!skipChildProcessingWhenBuildingStubs(eltType, childNode != null ? childNode.getElementType():null)) { + buildStubTreeFor(child, stub); + } } return stub; } + @Override + public boolean skipChildProcessingWhenBuildingStubs(IElementType nodeType, IElementType childType) { + return false; + } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java index c4a935a73757..dbca10b833aa 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java @@ -261,10 +261,16 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF private void switchFromStubToAST(ASTNode root, final Iterator> stubs) { ((TreeElement)root).acceptTree(new RecursiveTreeElementWalkingVisitor() { + final StubBuilder builder = ((IStubFileElementType)getContentElementType()).getBuilder(); + @Override protected void visitNode(TreeElement tree) { final IElementType type = tree.getElementType(); - + CompositeElement treeParent = tree.getTreeParent(); + if(treeParent != null && + builder.skipChildProcessingWhenBuildingStubs(treeParent.getElementType(), type)) { + return; + } if (type instanceof IStubElementType && ((IStubElementType)type).shouldCreateStub(tree)) { if (!stubs.hasNext()) { LOG.error("Stub list in" + @@ -889,15 +895,15 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF final PsiFileImpl file = this; final Iterator> stubs = stubTree.getPlainList().iterator(); - stubs.next(); // skip file root stub + stubs.next();// skip file root stub final FileElement fileRoot = file.getTreeElement(); assert fileRoot != null; - bindStubs(fileRoot, stubs); + bindStubs(fileRoot, stubs, ((IStubFileElementType)getContentElementType()).getBuilder()); } @Nullable - private StubElement bindStubs(ASTNode tree, Iterator> stubs) { + private StubElement bindStubs(ASTNode tree, Iterator> stubs, StubBuilder builder) { final IElementType type = tree.getElementType(); if (type instanceof IStubElementType && ((IStubElementType) type).shouldCreateStub(tree)) { @@ -912,9 +918,11 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF } for (ASTNode node : tree.getChildren(null)) { - final StubElement res = bindStubs(node, stubs); - if (res != null) { - return res; + if (!builder.skipChildProcessingWhenBuildingStubs(type, node.getElementType())) { + final StubElement res = bindStubs(node, stubs, builder); + if (res != null) { + return res; + } } } From 87516d558ff43b563a80ed9884df1bc62e3573bd Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 15:39:18 +0100 Subject: [PATCH 55/92] fix dom implementation mapping --- .../intellij/util/xml/impl/ImplementationClassCache.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java b/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java index d07607a608f1..3cc048253fcb 100644 --- a/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java +++ b/xml/dom-impl/src/com/intellij/util/xml/impl/ImplementationClassCache.java @@ -68,12 +68,11 @@ class ImplementationClassCache { for (DomImplementationClassEP value : values) { if (value.getInterfaceClass() == concreteInterface) { results.add(value.getImplementationClass()); + return; } } - if (results.isEmpty()) { - for (final Class aClass1 : ReflectionCache.getInterfaces(concreteInterface)) { - findImplementationClassDFS(aClass1, results); - } + for (final Class aClass1 : ReflectionCache.getInterfaces(concreteInterface)) { + findImplementationClassDFS(aClass1, results); } } From f01f8ff1a972fbd30cedf829bbac087ca6606309 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 19:43:58 +0100 Subject: [PATCH 56/92] yet another test fix --- .../intellij/util/ParameterizedTypeImpl.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/platform/util/src/com/intellij/util/ParameterizedTypeImpl.java b/platform/util/src/com/intellij/util/ParameterizedTypeImpl.java index 8c35c4744a33..2a562c042a19 100644 --- a/platform/util/src/com/intellij/util/ParameterizedTypeImpl.java +++ b/platform/util/src/com/intellij/util/ParameterizedTypeImpl.java @@ -2,6 +2,7 @@ package com.intellij.util; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; +import java.util.Arrays; /** * @author peter @@ -29,4 +30,24 @@ public class ParameterizedTypeImpl implements ParameterizedType { public Type getOwnerType() { return null; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ParameterizedTypeImpl)) return false; + + ParameterizedTypeImpl that = (ParameterizedTypeImpl)o; + + if (!Arrays.equals(myArguments, that.myArguments)) return false; + if (!myRawType.equals(that.myRawType)) return false; + + return true; + } + + @Override + public int hashCode() { + int result = myRawType.hashCode(); + result = 31 * result + Arrays.hashCode(myArguments); + return result; + } } From 0a08772fb145a146492522181b93ebfdabb1351b Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 21:05:01 +0100 Subject: [PATCH 57/92] don't initialize all element types when we need just the one of the file (stub index) --- .../src/com/intellij/lang/java/JavaParserDefinition.java | 5 ++++- .../intellij/psi/impl/java/stubs/JavaStubElementTypes.java | 1 - .../psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java | 3 ++- .../src/com/intellij/psi/impl/source/PsiJavaFileImpl.java | 3 ++- .../com/intellij/psi/impl/source/tree/JavaASTFactory.java | 3 ++- .../com/intellij/psi/impl/source/tree/JavaElementType.java | 1 - .../intellij/psi/impl/source/tree/java/JavaFileElement.java | 3 ++- .../intellij/plugins/relaxNG/compact/RncElementTypes.java | 1 - .../plugins/relaxNG/compact/RncParserDefinition.java | 4 +++- 9 files changed, 15 insertions(+), 9 deletions(-) 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 ba84d8e935b6..569917d83849 100644 --- a/java/java-impl/src/com/intellij/lang/java/JavaParserDefinition.java +++ b/java/java-impl/src/com/intellij/lang/java/JavaParserDefinition.java @@ -26,6 +26,7 @@ import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.impl.java.stubs.JavaStubElementType; +import com.intellij.psi.impl.source.JavaFileElementType; import com.intellij.psi.impl.source.PsiJavaFileImpl; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.tree.IElementType; @@ -39,6 +40,8 @@ import org.jetbrains.annotations.NotNull; */ public class JavaParserDefinition implements ParserDefinition { + public static final IFileElementType JAVA_FILE = new JavaFileElementType(); + @NotNull public Lexer createLexer(Project project) { return new JavaLexer(LanguageLevelProjectExtension.getInstance(project).getLanguageLevel()); @@ -50,7 +53,7 @@ public class JavaParserDefinition implements ParserDefinition { } public IFileElementType getFileNodeType() { - return JavaElementType.JAVA_FILE; + return JAVA_FILE; } @NotNull 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 2d2e9a30512f..5cb8c67820d1 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,7 +22,6 @@ package com.intellij.psi.impl.java.stubs; import com.intellij.psi.impl.source.JavaFileElementType; public class JavaStubElementTypes { - public static final JavaFileElementType FILE = new JavaFileElementType(); 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"); diff --git a/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java b/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java index e70d5f9046be..1c003433c94c 100644 --- a/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java @@ -19,6 +19,7 @@ */ package com.intellij.psi.impl.java.stubs.impl; +import com.intellij.lang.java.JavaParserDefinition; import com.intellij.psi.PsiJavaFile; import com.intellij.psi.PsiClass; import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; @@ -60,7 +61,7 @@ public class PsiJavaFileStubImpl extends PsiFileStubImpl implements } public IStubFileElementType getType() { - return JavaStubElementTypes.FILE; + return JavaParserDefinition.JAVA_FILE; } @SuppressWarnings({"HardCodedStringLiteral"}) diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaFileImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaFileImpl.java index ae7616a343c6..b28900ae348f 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaFileImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaFileImpl.java @@ -15,6 +15,7 @@ */ package com.intellij.psi.impl.source; +import com.intellij.lang.java.JavaParserDefinition; import com.intellij.lexer.JavaLexer; import com.intellij.lexer.Lexer; import com.intellij.openapi.fileTypes.FileType; @@ -33,7 +34,7 @@ import org.jetbrains.annotations.NotNull; public class PsiJavaFileImpl extends PsiJavaFileBaseImpl { public PsiJavaFileImpl(FileViewProvider file) { - super(Constants.JAVA_FILE, Constants.JAVA_FILE, file); + super(JavaParserDefinition.JAVA_FILE, JavaParserDefinition.JAVA_FILE, file); } public String toString(){ diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java index dcd61fa4e1b9..50b30fb170d9 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java @@ -20,6 +20,7 @@ package com.intellij.psi.impl.source.tree; import com.intellij.lang.ASTFactory; +import com.intellij.lang.java.JavaParserDefinition; import com.intellij.psi.PlainTextTokenTypes; import com.intellij.psi.impl.source.*; import com.intellij.psi.impl.source.javadoc.*; @@ -34,7 +35,7 @@ public class JavaASTFactory extends ASTFactory implements Constants { @Override public LazyParseableElement createLazy(ILazyParseableElementType type, CharSequence text) { - if (type == JAVA_FILE) { + if (type == JavaParserDefinition.JAVA_FILE) { return new JavaFileElement(text); } else if (type == PlainTextTokenTypes.PLAIN_TEXT_FILE) { 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 206959232254..e750cdb0dd88 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 @@ -38,7 +38,6 @@ import com.intellij.psi.util.PsiUtil; import com.intellij.util.CharTable; public interface JavaElementType { - IFileElementType JAVA_FILE = JavaStubElementTypes.FILE; IElementType CLASS = JavaStubElementTypes.CLASS; IElementType ANONYMOUS_CLASS = JavaStubElementTypes.ANONYMOUS_CLASS; diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/JavaFileElement.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/JavaFileElement.java index 63477dd3faef..0879c166d09a 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/JavaFileElement.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/JavaFileElement.java @@ -16,6 +16,7 @@ package com.intellij.psi.impl.source.tree.java; import com.intellij.lang.ASTNode; +import com.intellij.lang.java.JavaParserDefinition; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiJavaFile; import com.intellij.psi.impl.source.Constants; @@ -33,7 +34,7 @@ public class JavaFileElement extends FileElement implements Constants { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.JavaFileElement"); public JavaFileElement(CharSequence text) { - super(JAVA_FILE, text); + super(JavaParserDefinition.JAVA_FILE, text); } public TreeElement addInternal(TreeElement first, ASTNode last, ASTNode anchor, Boolean before) { diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/compact/RncElementTypes.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/compact/RncElementTypes.java index 01b3d1e5befd..7454d6926d84 100644 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/compact/RncElementTypes.java +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/compact/RncElementTypes.java @@ -27,7 +27,6 @@ import org.intellij.plugins.relaxNG.compact.psi.impl.*; * Date: 01.08.2007 */ public class RncElementTypes { - public static final IFileElementType FILE_ELEMENT_TYPE = new IFileElementType(RngCompactLanguage.INSTANCE); public static final IElementType DOCUMENT = new RncElementTypeEx("DOCUMENT", RncDocument.class); diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/compact/RncParserDefinition.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/compact/RncParserDefinition.java index 62518d73b6fa..ac48d70d1a94 100644 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/compact/RncParserDefinition.java +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/compact/RncParserDefinition.java @@ -42,6 +42,8 @@ import org.jetbrains.annotations.NotNull; * Date: 10.08.2007 */ public class RncParserDefinition implements ParserDefinition { + public static final IFileElementType FILE_ELEMENT_TYPE = new IFileElementType(RngCompactLanguage.INSTANCE); + @NotNull public Lexer createLexer(Project project) { return new CompactSyntaxLexerAdapter(); @@ -52,7 +54,7 @@ public class RncParserDefinition implements ParserDefinition { } public IFileElementType getFileNodeType() { - return RncElementTypes.FILE_ELEMENT_TYPE; + return FILE_ELEMENT_TYPE; } @NotNull From c02317adf94cf5fc6abc949657efd2167c671c43 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 21:14:13 +0100 Subject: [PATCH 58/92] compilation fix --- .../intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java b/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java index 1c003433c94c..fa75fca5ea54 100644 --- a/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java @@ -61,7 +61,7 @@ public class PsiJavaFileStubImpl extends PsiFileStubImpl implements } public IStubFileElementType getType() { - return JavaParserDefinition.JAVA_FILE; + return (IStubFileElementType)JavaParserDefinition.JAVA_FILE; } @SuppressWarnings({"HardCodedStringLiteral"}) From 13aee369862c548f39981ef5155ae164945e1cc0 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 21:16:00 +0100 Subject: [PATCH 59/92] no casts --- .../src/com/intellij/lang/java/JavaParserDefinition.java | 3 ++- .../intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) 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 569917d83849..9e877182a462 100644 --- a/java/java-impl/src/com/intellij/lang/java/JavaParserDefinition.java +++ b/java/java-impl/src/com/intellij/lang/java/JavaParserDefinition.java @@ -31,6 +31,7 @@ import com.intellij.psi.impl.source.PsiJavaFileImpl; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IFileElementType; +import com.intellij.psi.tree.IStubFileElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; @@ -40,7 +41,7 @@ import org.jetbrains.annotations.NotNull; */ public class JavaParserDefinition implements ParserDefinition { - public static final IFileElementType JAVA_FILE = new JavaFileElementType(); + public static final IStubFileElementType JAVA_FILE = new JavaFileElementType(); @NotNull public Lexer createLexer(Project project) { diff --git a/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java b/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java index fa75fca5ea54..1c003433c94c 100644 --- a/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java @@ -61,7 +61,7 @@ public class PsiJavaFileStubImpl extends PsiFileStubImpl implements } public IStubFileElementType getType() { - return (IStubFileElementType)JavaParserDefinition.JAVA_FILE; + return JavaParserDefinition.JAVA_FILE; } @SuppressWarnings({"HardCodedStringLiteral"}) From 682e3e91e4b0430bb9595205a5d978e80532b661 Mon Sep 17 00:00:00 2001 From: peter Date: Sat, 31 Jul 2010 22:08:49 +0100 Subject: [PATCH 60/92] ReferenceProvidersRegistry is a service, don't register references in project components (js, relaxNG, web services) --- plugins/relaxng/src/META-INF/plugin.xml | 1 + .../plugins/relaxNG/ProjectLoader.java | 35 +---------------- .../relaxNG/RelaxNGReferenceContributor.java | 39 +++++++++++++++++++ 3 files changed, 41 insertions(+), 34 deletions(-) create mode 100644 plugins/relaxng/src/org/intellij/plugins/relaxNG/RelaxNGReferenceContributor.java diff --git a/plugins/relaxng/src/META-INF/plugin.xml b/plugins/relaxng/src/META-INF/plugin.xml index 4f0e9b76ea5d..90d431df57fb 100644 --- a/plugins/relaxng/src/META-INF/plugin.xml +++ b/plugins/relaxng/src/META-INF/plugin.xml @@ -64,6 +64,7 @@ + diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/ProjectLoader.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/ProjectLoader.java index 2e356ce53c8a..03d39ca86fc8 100644 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/ProjectLoader.java +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/ProjectLoader.java @@ -20,22 +20,16 @@ import com.intellij.lang.ASTNode; import com.intellij.openapi.components.ProjectComponent; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; -import com.intellij.patterns.StringPattern; -import com.intellij.patterns.XmlNamedElementPattern; import com.intellij.psi.AbstractElementManipulator; import com.intellij.psi.PsiManager; -import com.intellij.psi.filters.position.PatternFilter; -import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; import com.intellij.util.IncorrectOperationException; import com.intellij.util.xml.DomFileDescription; import com.intellij.util.xml.DomManager; import com.intellij.util.xml.highlighting.DomElementsAnnotator; -import com.intellij.xml.util.XmlUtil; import org.intellij.plugins.relaxNG.compact.RncTokenTypes; import org.intellij.plugins.relaxNG.compact.psi.RncFileReference; import org.intellij.plugins.relaxNG.compact.psi.util.RenameUtil; import org.intellij.plugins.relaxNG.model.annotation.ModelAnnotator; -import org.intellij.plugins.relaxNG.references.PrefixReferenceProvider; import org.intellij.plugins.relaxNG.xml.dom.*; import org.intellij.plugins.relaxNG.xml.dom.impl.RngDefineImpl; import org.intellij.plugins.relaxNG.xml.dom.impl.RngGrammarImpl; @@ -44,9 +38,6 @@ import org.intellij.plugins.relaxNG.xml.dom.impl.RngRefImpl; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import static com.intellij.patterns.StandardPatterns.string; -import static com.intellij.patterns.XmlPatterns.*; - /** * Created by IntelliJ IDEA. * User: sweinreuter @@ -55,13 +46,6 @@ import static com.intellij.patterns.XmlPatterns.*; public class ProjectLoader implements ProjectComponent { public static final String RNG_NAMESPACE = "http://relaxng.org/ns/structure/1.0"; - private static final XmlNamedElementPattern RNG_TAG_PATTERN = xmlTag().withNamespace(string().equalTo(RNG_NAMESPACE)); - - private static final XmlNamedElementPattern.XmlAttributePattern NAME_ATTR_PATTERN = xmlAttribute("name"); - private static final StringPattern ELEMENT_OR_ATTRIBUTE_PATTERN = string().oneOf("element", "attribute"); - private static final XmlNamedElementPattern.XmlAttributePattern NAME_PATTERN = NAME_ATTR_PATTERN.withParent( - RNG_TAG_PATTERN.withLocalName(ELEMENT_OR_ATTRIBUTE_PATTERN)); - private final Project myProject; public ProjectLoader(Project project) { @@ -74,25 +58,8 @@ public class ProjectLoader implements ProjectComponent { return "RELAX-NG"; } - public void initComponent() { - registerFileDescriptions(); - - registerReferences(); - } - - private void registerReferences() { - final ReferenceProvidersRegistry registry = ReferenceProvidersRegistry.getInstance(myProject); - XmlUtil.registerXmlAttributeValueReferenceProvider(registry, new String[]{ - "name" - }, new PatternFilter(xmlAttributeValue().withParent(NAME_PATTERN)), true, new PrefixReferenceProvider()); - -// final XmlAttributeValuePattern id = xmlAttributeValue().withParent(xmlAttribute()).with(IdRefProvider.HAS_ID_REF_TYPE); -// final XmlAttributeValuePattern idref = xmlAttributeValue().withParent(xmlAttribute()).with(IdRefProvider.HAS_ID_TYPE); -// registry.registerXmlAttributeValueReferenceProvider(null, new PatternFilter(or(id, idref)), false, new IdRefProvider()); - } - @SuppressWarnings({ "deprecation" }) - private void registerFileDescriptions() { + public void initComponent() { final DomManager mgr = DomManager.getDomManager(myProject); mgr.registerFileDescription(new MyFileDescription(RngGrammar.class, "grammar")); mgr.registerFileDescription(new MyFileDescription(RngElement.class, "element")); diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/RelaxNGReferenceContributor.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/RelaxNGReferenceContributor.java new file mode 100644 index 000000000000..08c8a1e63b91 --- /dev/null +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/RelaxNGReferenceContributor.java @@ -0,0 +1,39 @@ +package org.intellij.plugins.relaxNG; + +import com.intellij.patterns.StringPattern; +import com.intellij.patterns.XmlNamedElementPattern; +import com.intellij.psi.PsiReferenceContributor; +import com.intellij.psi.PsiReferenceRegistrar; +import com.intellij.psi.filters.position.PatternFilter; +import com.intellij.xml.util.XmlUtil; +import org.intellij.plugins.relaxNG.references.PrefixReferenceProvider; + +import static com.intellij.patterns.StandardPatterns.string; +import static com.intellij.patterns.XmlPatterns.xmlAttribute; +import static com.intellij.patterns.XmlPatterns.xmlAttributeValue; +import static com.intellij.patterns.XmlPatterns.xmlTag; + +/** + * @author peter + */ +public class RelaxNGReferenceContributor extends PsiReferenceContributor { + private static final XmlNamedElementPattern RNG_TAG_PATTERN = xmlTag().withNamespace(string().equalTo(ProjectLoader.RNG_NAMESPACE)); + + private static final XmlNamedElementPattern.XmlAttributePattern NAME_ATTR_PATTERN = xmlAttribute("name"); + private static final StringPattern ELEMENT_OR_ATTRIBUTE_PATTERN = string().oneOf("element", "attribute"); + + private static final XmlNamedElementPattern.XmlAttributePattern NAME_PATTERN = NAME_ATTR_PATTERN.withParent( + RNG_TAG_PATTERN.withLocalName(ELEMENT_OR_ATTRIBUTE_PATTERN)); + + @Override + public void registerReferenceProviders(PsiReferenceRegistrar registrar) { + XmlUtil.registerXmlAttributeValueReferenceProvider(registrar, new String[]{ + "name" + }, new PatternFilter(xmlAttributeValue().withParent(NAME_PATTERN)), true, new PrefixReferenceProvider()); + +// final XmlAttributeValuePattern id = xmlAttributeValue().withParent(xmlAttribute()).with(IdRefProvider.HAS_ID_REF_TYPE); +// final XmlAttributeValuePattern idref = xmlAttributeValue().withParent(xmlAttribute()).with(IdRefProvider.HAS_ID_TYPE); +// registry.registerXmlAttributeValueReferenceProvider(null, new PatternFilter(or(id, idref)), false, new IdRefProvider()); + + } +} From ef9d9c308f9969e7e03208219785105237e449b6 Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 1 Aug 2010 00:20:07 +0100 Subject: [PATCH 61/92] sorry, *ElementTypes classes register their (stub) element types as a side effect, which is then used by stub serializer --- .../src/com/intellij/lang/java/JavaParserDefinition.java | 7 ++----- .../intellij/psi/impl/java/stubs/JavaStubElementTypes.java | 2 ++ .../psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java | 3 +-- .../src/com/intellij/psi/impl/source/PsiJavaFileImpl.java | 4 ++-- .../com/intellij/psi/impl/source/tree/JavaASTFactory.java | 4 ++-- .../com/intellij/psi/impl/source/tree/JavaElementType.java | 7 ++----- .../psi/impl/source/tree/java/JavaFileElement.java | 4 ++-- 7 files changed, 13 insertions(+), 18 deletions(-) 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 9e877182a462..b6ce684ffcab 100644 --- a/java/java-impl/src/com/intellij/lang/java/JavaParserDefinition.java +++ b/java/java-impl/src/com/intellij/lang/java/JavaParserDefinition.java @@ -26,12 +26,11 @@ import com.intellij.openapi.roots.LanguageLevelProjectExtension; import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.*; import com.intellij.psi.impl.java.stubs.JavaStubElementType; -import com.intellij.psi.impl.source.JavaFileElementType; +import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; import com.intellij.psi.impl.source.PsiJavaFileImpl; import com.intellij.psi.impl.source.tree.JavaElementType; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.IFileElementType; -import com.intellij.psi.tree.IStubFileElementType; import com.intellij.psi.tree.TokenSet; import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; @@ -41,8 +40,6 @@ import org.jetbrains.annotations.NotNull; */ public class JavaParserDefinition implements ParserDefinition { - public static final IStubFileElementType JAVA_FILE = new JavaFileElementType(); - @NotNull public Lexer createLexer(Project project) { return new JavaLexer(LanguageLevelProjectExtension.getInstance(project).getLanguageLevel()); @@ -54,7 +51,7 @@ public class JavaParserDefinition implements ParserDefinition { } public IFileElementType getFileNodeType() { - return JAVA_FILE; + return JavaStubElementTypes.JAVA_FILE; } @NotNull 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 5cb8c67820d1..1356956b46fd 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 @@ -20,6 +20,7 @@ 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"); @@ -46,4 +47,5 @@ public class JavaStubElementTypes { 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(); } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java b/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java index 1c003433c94c..5b603539f2a9 100644 --- a/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiJavaFileStubImpl.java @@ -19,7 +19,6 @@ */ package com.intellij.psi.impl.java.stubs.impl; -import com.intellij.lang.java.JavaParserDefinition; import com.intellij.psi.PsiJavaFile; import com.intellij.psi.PsiClass; import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; @@ -61,7 +60,7 @@ public class PsiJavaFileStubImpl extends PsiFileStubImpl implements } public IStubFileElementType getType() { - return JavaParserDefinition.JAVA_FILE; + return JavaStubElementTypes.JAVA_FILE; } @SuppressWarnings({"HardCodedStringLiteral"}) diff --git a/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaFileImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaFileImpl.java index b28900ae348f..7dd47f61f9b0 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaFileImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/PsiJavaFileImpl.java @@ -15,7 +15,6 @@ */ package com.intellij.psi.impl.source; -import com.intellij.lang.java.JavaParserDefinition; import com.intellij.lexer.JavaLexer; import com.intellij.lexer.Lexer; import com.intellij.openapi.fileTypes.FileType; @@ -27,6 +26,7 @@ import com.intellij.psi.FileViewProvider; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiElementFactory; import com.intellij.psi.PsiPackageStatement; +import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.IncorrectOperationException; import com.intellij.testFramework.LightVirtualFile; @@ -34,7 +34,7 @@ import org.jetbrains.annotations.NotNull; public class PsiJavaFileImpl extends PsiJavaFileBaseImpl { public PsiJavaFileImpl(FileViewProvider file) { - super(JavaParserDefinition.JAVA_FILE, JavaParserDefinition.JAVA_FILE, file); + super(JavaStubElementTypes.JAVA_FILE, JavaStubElementTypes.JAVA_FILE, file); } public String toString(){ diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java index 50b30fb170d9..916dbe16cef6 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/JavaASTFactory.java @@ -20,8 +20,8 @@ package com.intellij.psi.impl.source.tree; import com.intellij.lang.ASTFactory; -import com.intellij.lang.java.JavaParserDefinition; import com.intellij.psi.PlainTextTokenTypes; +import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; import com.intellij.psi.impl.source.*; import com.intellij.psi.impl.source.javadoc.*; import com.intellij.psi.impl.source.tree.java.*; @@ -35,7 +35,7 @@ public class JavaASTFactory extends ASTFactory implements Constants { @Override public LazyParseableElement createLazy(ILazyParseableElementType type, CharSequence text) { - if (type == JavaParserDefinition.JAVA_FILE) { + if (type == JavaStubElementTypes.JAVA_FILE) { return new JavaFileElement(text); } else if (type == PlainTextTokenTypes.PLAIN_TEXT_FILE) { 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 e750cdb0dd88..0a33cb21bb80 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 @@ -24,15 +24,12 @@ import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.JavaTokenType; import com.intellij.psi.PsiManager; import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; +import com.intellij.psi.impl.source.JavaFileElementType; import com.intellij.psi.impl.source.parsing.JavaParsingContext; import com.intellij.psi.impl.source.parsing.Parsing; import com.intellij.psi.impl.source.tree.java.PsiCodeBlockImpl; import com.intellij.psi.text.BlockSupport; -import com.intellij.psi.tree.IElementType; -import com.intellij.psi.tree.IErrorCounterReparseableElementType; -import com.intellij.psi.tree.IFileElementType; -import com.intellij.psi.tree.ILazyParseableElementType; -import com.intellij.psi.tree.TokenSet; +import com.intellij.psi.tree.*; import com.intellij.psi.tree.java.IJavaElementType; import com.intellij.psi.util.PsiUtil; import com.intellij.util.CharTable; diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/JavaFileElement.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/JavaFileElement.java index 0879c166d09a..cd9472e8b1f4 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/JavaFileElement.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/JavaFileElement.java @@ -16,9 +16,9 @@ package com.intellij.psi.impl.source.tree.java; import com.intellij.lang.ASTNode; -import com.intellij.lang.java.JavaParserDefinition; import com.intellij.openapi.diagnostic.Logger; import com.intellij.psi.PsiJavaFile; +import com.intellij.psi.impl.java.stubs.JavaStubElementTypes; import com.intellij.psi.impl.source.Constants; import com.intellij.psi.impl.source.SourceTreeToPsiMap; import com.intellij.psi.impl.source.tree.ChildRole; @@ -34,7 +34,7 @@ public class JavaFileElement extends FileElement implements Constants { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.source.tree.java.JavaFileElement"); public JavaFileElement(CharSequence text) { - super(JavaParserDefinition.JAVA_FILE, text); + super(JavaStubElementTypes.JAVA_FILE, text); } public TreeElement addInternal(TreeElement first, ASTNode last, ASTNode anchor, Boolean before) { From e7bf0269c87e49d0f13dcaa278d04313c3e4b3ac Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Sun, 1 Aug 2010 14:53:12 +0400 Subject: [PATCH 62/92] building indices over ast nodes for javascript, avoid SOE --- .../psi/stubs/DefaultStubBuilder.java | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java b/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java index aca6d8f11ae6..460d52de5406 100644 --- a/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java +++ b/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java @@ -31,14 +31,14 @@ public class DefaultStubBuilder implements StubBuilder { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.stubs.DefaultStubBuilder"); public StubElement buildStubTree(final PsiFile file) { - return buildStubTreeFor(file, createStubForFile(file)); + return buildStubTreeFor(file, createStubForFile(file), this); } protected StubElement createStubForFile(final PsiFile file) { return new PsiFileStubImpl(file); } - protected StubElement buildStubTreeFor(PsiElement elt, StubElement parentStub) { + protected static StubElement buildStubTreeFor(PsiElement elt, StubElement parentStub, StubBuilder filter) { StubElement stub = parentStub; IElementType eltType; if (elt instanceof StubBasedPsiElement) { @@ -61,8 +61,34 @@ public class DefaultStubBuilder implements StubBuilder { for (PsiElement child = elt.getFirstChild(); child != null; child = child.getNextSibling()) { ASTNode childNode = child.getNode(); - if (!skipChildProcessingWhenBuildingStubs(eltType, childNode != null ? childNode.getElementType():null)) { - buildStubTreeFor(child, stub); + if (!filter.skipChildProcessingWhenBuildingStubs(eltType, childNode != null ? childNode.getElementType():null)) { + buildStubTreeFor(child, stub, filter); + } + } + + return stub; + } + + protected static StubElement buildStubTreeFor(ASTNode node, StubElement parentStub, StubBuilder filter) { + StubElement stub = parentStub; + IElementType nodeType = node.getElementType(); + + if (nodeType instanceof IStubElementType) { + final IStubElementType type = (IStubElementType)nodeType; + + if (type.shouldCreateStub(node)) { + //noinspection unchecked + PsiElement element = node.getPsi(); + if (!(element instanceof StubBasedPsiElement)) { + LOG.error("Non-StubBasedPsiElement requests stub creation. Stub type: " + type + ", PSI: " + element); + } + stub = type.createStub(element, parentStub); + } + } + + for (ASTNode childNode = node.getFirstChildNode(); childNode != null; childNode = childNode.getTreeNext()) { + if (!filter.skipChildProcessingWhenBuildingStubs(nodeType, childNode.getElementType())) { + buildStubTreeFor(childNode, stub, filter); } } From bf03698d628dbaf2ec57dc693576bf46d624596d Mon Sep 17 00:00:00 2001 From: "Kirill.Safonov" Date: Sun, 1 Aug 2010 16:32:28 +0400 Subject: [PATCH 63/92] 'Export test results' action (IDEA-11707) - UI and workflow done; exported contents to be improved --- .../src/messages/ActionsBundle.properties | 2 + .../src/messages/ExecutionBundle.properties | 14 + .../src/META-INF/PlatformExtensions.xml | 4 + .../src/idea/PlatformActions.xml | 2 + .../testframework/TestConsoleProperties.java | 4 + .../execution/testframework/ToolbarPanel.java | 10 +- .../export/ExportTestResultsAction.java | 295 ++++++++++++++++++ .../ExportTestResultsConfiguration.java | 122 ++++++++ .../export/ExportTestResultsDialog.java | 88 ++++++ .../export/ExportTestResultsForm.form | 126 ++++++++ .../export/ExportTestResultsForm.java | 197 ++++++++++++ .../export/TestResultsXmlFormatter.java | 200 ++++++++++++ platform/testRunner/testRunner.iml | 1 + 13 files changed, 1064 insertions(+), 1 deletion(-) create mode 100644 platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsAction.java create mode 100644 platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsConfiguration.java create mode 100644 platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsDialog.java create mode 100644 platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsForm.form create mode 100644 platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsForm.java create mode 100644 platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java diff --git a/platform/platform-resources-en/src/messages/ActionsBundle.properties b/platform/platform-resources-en/src/messages/ActionsBundle.properties index 67b7f3c15e8c..61a74a964483 100644 --- a/platform/platform-resources-en/src/messages/ActionsBundle.properties +++ b/platform/platform-resources-en/src/messages/ActionsBundle.properties @@ -1171,5 +1171,7 @@ group.ToolsBasicGroup.description=Tools Basic Group group.ToolbarNewElement.text=Toolbar New Element Group action.NewElementToolbarAction.text=Create New File action.ShowRegistry.text=Registry +action.ExportTestResults.text=Export Test Results... +action.ExportTestResults.description=Export test results to the file action.TogglePowerSave.text=Power Save Mode action.TogglePowerSave.description=Power save mode disables background code analysis and other background operations diff --git a/platform/platform-resources-en/src/messages/ExecutionBundle.properties b/platform/platform-resources-en/src/messages/ExecutionBundle.properties index 7a9c91ff9229..5a07ed9170ba 100644 --- a/platform/platform-resources-en/src/messages/ExecutionBundle.properties +++ b/platform/platform-resources-en/src/messages/ExecutionBundle.properties @@ -286,3 +286,17 @@ env.vars.checkbox.title=&Include parent environment variables before.launch.compile.step=Make execute.before.run.debug.dialog.title=Execute {0} Before Run/Debug + +export.test.results.filename=Test Results - {0} +export.test.results.succeeded=Test results exported successfully to {0} +export.test.results.failed=Test results export failed: {0} +export.test.results.custom.template.chooser.title=Choose Custom Template +export.test.results.output.folder.chooser.title=Choose Output Folder +export.test.results.custom.template.path.empty=User-defined tempate path is empty +export.test.results.custom.template.not.found=User-defined tempate file ''{0}'' is not found +export.test.results.task.name=Exporting test results +export.test.results.open.editor=O&pen exported file in editor +export.test.results.open.browser=O&pen exported file in browser +export.test.results.dialog.title=Export Test Results +export.test.results.output.path.empty=Output path is empty +export.test.results.output.filename.empty=Output file name is empty diff --git a/platform/platform-resources/src/META-INF/PlatformExtensions.xml b/platform/platform-resources/src/META-INF/PlatformExtensions.xml index 5a558a867988..59c859ebc6bd 100644 --- a/platform/platform-resources/src/META-INF/PlatformExtensions.xml +++ b/platform/platform-resources/src/META-INF/PlatformExtensions.xml @@ -152,6 +152,10 @@ + + + diff --git a/platform/platform-resources/src/idea/PlatformActions.xml b/platform/platform-resources/src/idea/PlatformActions.xml index aa0719c09fb5..5f89db231ef4 100644 --- a/platform/platform-resources/src/idea/PlatformActions.xml +++ b/platform/platform-resources/src/idea/PlatformActions.xml @@ -444,5 +444,7 @@ + + diff --git a/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java b/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java index 81420f866b65..ad68caf50dee 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/TestConsoleProperties.java @@ -98,6 +98,10 @@ public abstract class TestConsoleProperties extends StoringPropertyContainer imp } } + public Executor getExecutor() { + return myExecutor; + } + public boolean isDebug() { return myExecutor.getId() == DefaultDebugExecutor.EXECUTOR_ID; } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/ToolbarPanel.java b/platform/testRunner/src/com/intellij/execution/testframework/ToolbarPanel.java index 818b4f2ca9e8..c8672fca7366 100644 --- a/platform/testRunner/src/com/intellij/execution/testframework/ToolbarPanel.java +++ b/platform/testRunner/src/com/intellij/execution/testframework/ToolbarPanel.java @@ -27,6 +27,7 @@ import com.intellij.execution.testframework.actions.ScrollToTestSourceAction; import com.intellij.execution.testframework.actions.ShowStatisticsAction; import com.intellij.execution.testframework.actions.TestFrameworkActions; import com.intellij.execution.testframework.actions.TestTreeExpander; +import com.intellij.execution.testframework.export.ExportTestResultsAction; import com.intellij.ide.CommonActionsManager; import com.intellij.ide.OccurenceNavigator; import com.intellij.openapi.Disposable; @@ -46,6 +47,7 @@ public class ToolbarPanel extends JPanel implements OccurenceNavigator, Disposab protected final TestTreeExpander myTreeExpander = new TestTreeExpander(); protected final FailedTestsNavigator myOccurenceNavigator; protected final ScrollToTestSourceAction myScrollToSource; + private final ExportTestResultsAction myExportAction; private final ArrayList myActions = new ArrayList(); @@ -101,6 +103,10 @@ public class ToolbarPanel extends JPanel implements OccurenceNavigator, Disposab myActions.add(toggleModelAction); actionGroup.add(toggleModelAction); } + + myExportAction = ExportTestResultsAction.create(properties.getExecutor().getToolWindowId(), properties.getConfiguration()); + actionGroup.addAction(myExportAction); + appendAdditionalActions(actionGroup, properties, runnerSettings, configurationSettings, parent); add(ActionManager.getInstance(). @@ -118,6 +124,7 @@ public class ToolbarPanel extends JPanel implements OccurenceNavigator, Disposab myScrollToSource.setModel(model); myTreeExpander.setModel(model); myOccurenceNavigator.setModel(model); + myExportAction.setModel(model); for (ToggleModelAction action : myActions) { action.setModel(model); } @@ -148,7 +155,8 @@ public class ToolbarPanel extends JPanel implements OccurenceNavigator, Disposab } public void dispose() { - myScrollToSource.setModel(null); + myScrollToSource.setModel(null); + myExportAction.setModel(null); } } diff --git a/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsAction.java b/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsAction.java new file mode 100644 index 000000000000..7e46e475477d --- /dev/null +++ b/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsAction.java @@ -0,0 +1,295 @@ +/* + * 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.execution.testframework.export; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.execution.configurations.RuntimeConfiguration; +import com.intellij.execution.testframework.AbstractTestProxy; +import com.intellij.execution.testframework.TestFrameworkRunningModel; +import com.intellij.ide.BrowserUtil; +import com.intellij.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.PlatformDataKeys; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.ex.ApplicationManagerEx; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.progress.PerformInBackgroundOption; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.DumbAwareAction; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.MessageType; +import com.intellij.openapi.util.Computable; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.ToolWindowManager; +import com.intellij.util.io.URLUtil; +import org.apache.tools.ant.taskdefs.optional.junit.XMLConstants; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.xml.sax.SAXException; + +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Source; +import javax.xml.transform.TransformerException; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TransformerHandler; +import javax.xml.transform.stream.StreamResult; +import javax.xml.transform.stream.StreamSource; +import java.io.File; +import java.io.IOException; +import java.io.StringWriter; +import java.net.URL; + +public class ExportTestResultsAction extends DumbAwareAction { + private static final String ID = "ExportTestResults"; + + private static final Logger LOG = Logger.getInstance(ExportTestResultsAction.class.getName()); + + private TestFrameworkRunningModel myModel; + private String myToolWindowId; + private RuntimeConfiguration myRunConfiguration; + + public static ExportTestResultsAction create(String toolWindowId, RuntimeConfiguration runtimeConfiguration) { + ExportTestResultsAction action = new ExportTestResultsAction(); + action.copyFrom(ActionManager.getInstance().getAction(ID)); + action.myToolWindowId = toolWindowId; + action.myRunConfiguration = runtimeConfiguration; + return action; + } + + public void setModel(TestFrameworkRunningModel model) { + myModel = model; + } + + @Override + public void update(AnActionEvent e) { + e.getPresentation().setEnabled(isEnabled(e.getDataContext())); + } + + private boolean isEnabled(DataContext dataContext) { + if (!ApplicationManagerEx.getApplicationEx().isInternal()) { + return false; + } + + if (myModel == null) { + return false; + } + + if (PlatformDataKeys.PROJECT.getData(dataContext) == null) { + return false; + } + + AbstractTestProxy root = myModel.getRoot(); + return !root.isInProgress() && !root.getChildren().isEmpty(); + } + + @Override + public void actionPerformed(AnActionEvent e) { + final Project project = PlatformDataKeys.PROJECT.getData(e.getDataContext()); + final ExportTestResultsConfiguration config = ExportTestResultsConfiguration.getInstance(project); + LOG.assertTrue(project != null); + + String name = ExecutionBundle.message("export.test.results.filename", myRunConfiguration.getName()); + String filename = name + "." + config.getExportFormat().getDefaultExtension(); + ExportTestResultsDialog d = new ExportTestResultsDialog(project, config, filename); + d.show(); + if (!d.isOK()) { + return; + } + filename = d.getFileName(); + + final String filename_ = filename; + ProgressManager.getInstance().run( + new Task.Backgroundable(project, ExecutionBundle.message("export.test.results.task.name"), false, new PerformInBackgroundOption() { + @Override + public boolean shouldStartInBackground() { + return true; + } + + @Override + public void processSentToBackground() { + } + }) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + indicator.setIndeterminate(true); + + File outputFolder; + if (StringUtil.isNotEmpty(config.getOutputFolder())) { + if (FileUtil.isAbsolute(config.getOutputFolder())) { + outputFolder = new File(config.getOutputFolder()); + } + else { + outputFolder = new File(new File(project.getLocation()), config.getOutputFolder()); + } + } + else { + outputFolder = new File(project.getLocation()); + + } + final File outputFile = new File(outputFolder, filename_); + final String outputText; + try { + outputText = getOutputText(project); + if (outputText == null) { + return; + } + } + catch (IOException ex) { + LOG.warn(ex); + showBalloon(project, MessageType.ERROR, ExecutionBundle.message("export.test.results.failed", ex.getMessage()), null); + return; + } + catch (TransformerException ex) { + LOG.warn(ex); + showBalloon(project, MessageType.ERROR, ExecutionBundle.message("export.test.results.failed", ex.getMessage()), null); + return; + } + catch (SAXException ex) { + LOG.warn(ex); + showBalloon(project, MessageType.ERROR, ExecutionBundle.message("export.test.results.failed", ex.getMessage()), null); + return; + } + + final Ref result = new Ref(); + final Ref error = new Ref(); + ApplicationManager.getApplication().invokeAndWait(new Runnable() { + @Override + public void run() { + result.set(ApplicationManager.getApplication().runWriteAction(new Computable() { + @Override + public VirtualFile compute() { + outputFile.getParentFile().mkdirs(); + final VirtualFile parent = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(outputFile.getParentFile()); + if (parent == null || !parent.isValid()) { + error.set(ExecutionBundle.message("failed.to.create.output.file: ''" + outputFile.getPath() + "''")); + return null; + } + + try { + VirtualFile result = parent.createChildData(this, outputFile.getName()); + VfsUtil.saveText(result, outputText); + return result; + } + catch (IOException e) { + LOG.warn(e); + error.set(e.getMessage()); + return null; + } + } + })); + } + }, ModalityState.defaultModalityState()); + + if (!result.isNull()) { + if (config.isOpenResults()) { + openEditorOrBrowser(result.get(), project, config.getExportFormat() == ExportTestResultsConfiguration.ExportFormat.Xml); + } + else { + HyperlinkListener listener = new HyperlinkListener() { + @Override + public void hyperlinkUpdate(HyperlinkEvent e) { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + openEditorOrBrowser(result.get(), project, config.getExportFormat() == ExportTestResultsConfiguration.ExportFormat.Xml); + } + } + }; + showBalloon(project, MessageType.INFO, ExecutionBundle.message("export.test.results.succeeded", outputFile.getName()), + listener); + } + } + else { + showBalloon(project, MessageType.ERROR, ExecutionBundle.message("export.test.results.failed", error.get()), null); + } + } + }); + } + + private static void openEditorOrBrowser(final VirtualFile result, final Project project, final boolean editor) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + @Override + public void run() { + if (editor) { + FileEditorManager.getInstance(project).openFile(result, true); + } + else { + BrowserUtil.launchBrowser(result.getUrl()); + } + } + }); + } + + @Nullable + private String getOutputText(Project project) throws IOException, TransformerException, SAXException { + ExportTestResultsConfiguration config = ExportTestResultsConfiguration.getInstance(project); + ExportTestResultsConfiguration.ExportFormat exportFormat = config.getExportFormat(); + + SAXTransformerFactory transformerFactory = (SAXTransformerFactory)SAXTransformerFactory.newInstance(); + TransformerHandler handler; + if (exportFormat == ExportTestResultsConfiguration.ExportFormat.Xml) { + handler = transformerFactory.newTransformerHandler(); + handler.getTransformer().setOutputProperty(OutputKeys.INDENT, "yes"); + handler.getTransformer().setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); + } + else { + Source xslSource; + if (config.getExportFormat() == ExportTestResultsConfiguration.ExportFormat.BundledTemplate) { + URL bundledXsltUrl = XMLConstants.class.getResource("/org/apache/tools/ant/taskdefs/optional/junit/xsl/junit-noframes.xsl"); + xslSource = new StreamSource(URLUtil.openStream(bundledXsltUrl)); + } + else { + File xslFile = new File(config.getUserTemplatePath()); + if (!xslFile.isFile()) { + showBalloon(myRunConfiguration.getProject(), MessageType.ERROR, + ExecutionBundle.message("export.test.results.custom.template.not.found", xslFile.getPath()), null); + return null; + } + xslSource = new StreamSource(xslFile); + } + handler = transformerFactory.newTransformerHandler(xslSource); + handler.getTransformer().setParameter("TITLE", ExecutionBundle.message("export.test.results.filename", myRunConfiguration.getName(), + myRunConfiguration.getType().getDisplayName())); + } + + StringWriter w = new StringWriter(); + handler.setResult(new StreamResult(w)); + TestResultsXmlFormatter.execute(myModel.getRoot(), myRunConfiguration, handler); + return w.toString(); + } + + private void showBalloon(final Project project, final MessageType type, final String text, @Nullable final HyperlinkListener listener) { + ApplicationManager.getApplication().invokeLater(new Runnable() { + public void run() { + if (project.isDisposed()) return; + if (ToolWindowManager.getInstance(project).getToolWindow(myToolWindowId) != null) { + ToolWindowManager.getInstance(project).notifyByBalloon(myToolWindowId, type, text, null, listener); + } + } + }); + } + +} diff --git a/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsConfiguration.java b/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsConfiguration.java new file mode 100644 index 000000000000..f82cb0c86e1b --- /dev/null +++ b/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsConfiguration.java @@ -0,0 +1,122 @@ +/* + * 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.execution.testframework.export; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.openapi.project.Project; +import com.intellij.util.xmlb.annotations.Attribute; + +@State( + name = "ExportTestResults", + storages = {@Storage( + id = "other", + file = "$WORKSPACE_FILE$")}) +public class ExportTestResultsConfiguration implements PersistentStateComponent { + + public enum ExportFormat { + Xml("xml"), BundledTemplate("html"), UserTemplate("html"); + + private final String myExtension; + + ExportFormat(String extension) { + myExtension = extension; + } + + public String getDefaultExtension() { + return myExtension; + } + } + + public static class State { + + @Attribute("outputFolder") + public String outputFolder; + + @Attribute("openResultsInEditor") + public boolean openResultsInEditor; + + @Attribute("userTempatePath") + public String userTemplatePath; + + private ExportFormat myExportFormat = ExportFormat.BundledTemplate; + + @Attribute("exportFormat") + public String getExportFormat() { + return myExportFormat.name(); + } + + public void setExportFormat(String exportFormat) { + try { + myExportFormat = ExportFormat.valueOf(exportFormat); + } + catch (IllegalArgumentException e) { + myExportFormat = ExportFormat.BundledTemplate; + } + } + } + + private State myState = new State(); + + public static ExportTestResultsConfiguration getInstance(Project project) { + return ServiceManager.getService(project, ExportTestResultsConfiguration.class); + } + + @Override + public State getState() { + return myState; + } + + @Override + public void loadState(State state) { + myState = state; + } + + public String getOutputFolder() { + return myState.outputFolder; + } + + public void setOutputFolder(String outputFolder) { + myState.outputFolder = outputFolder; + } + + public boolean isOpenResults() { + return myState.openResultsInEditor; + } + + public void setOpenResults(boolean openResultsInEditor) { + myState.openResultsInEditor = openResultsInEditor; + } + + public ExportFormat getExportFormat() { + return myState.myExportFormat; + } + + public void setExportFormat(ExportFormat exportFormat) { + myState.myExportFormat = exportFormat; + } + + public String getUserTemplatePath() { + return myState.userTemplatePath; + } + + public void setUserTemplatePath(String userTemplatePath) { + myState.userTemplatePath = userTemplatePath; + } + +} diff --git a/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsDialog.java b/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsDialog.java new file mode 100644 index 000000000000..f33762756d67 --- /dev/null +++ b/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsDialog.java @@ -0,0 +1,88 @@ +/* + * 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.execution.testframework.export; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +public class ExportTestResultsDialog extends DialogWrapper { + private final Project myProject; + private final ExportTestResultsForm myForm; + + public ExportTestResultsDialog(Project project, ExportTestResultsConfiguration config, String defaultFileName) { + super(project); + myProject = project; + String defaultFolder = StringUtil.isNotEmpty(config.getOutputFolder()) ? + FileUtil.toSystemDependentName(config.getOutputFolder()) : FileUtil.toSystemDependentName(project.getLocation()); + myForm = new ExportTestResultsForm(config, defaultFileName, defaultFolder); + myForm.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + revalidate(); + + } + }); + + setSize(600, 400); + setTitle(ExecutionBundle.message("export.test.results.dialog.title")); + init(); + + revalidate(); + } + + @Override + protected void doOKAction() { + myForm.apply(ExportTestResultsConfiguration.getInstance(myProject)); + super.doOKAction(); + } + + @Override + public JComponent getPreferredFocusedComponent() { + return myForm.getPreferredFocusedComponent(); + } + + private void revalidate() { + String message = myForm.validate(); + myForm.showMessage(message); + setOKActionEnabled(message == null); + } + + @Override + protected JComponent createCenterPanel() { + return myForm.getContentPane(); + } + + @Override + protected String getDimensionServiceKey() { + return "export.test.results"; + } + + @Override + protected String getHelpId() { + return "reference.settings.ide.settings.export.test.results"; + } + + public String getFileName() { + return myForm.getFileName(); + } +} diff --git a/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsForm.form b/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsForm.form new file mode 100644 index 000000000000..4a363d5f5407 --- /dev/null +++ b/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsForm.form @@ -0,0 +1,126 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsForm.java b/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsForm.java new file mode 100644 index 000000000000..536e18025a95 --- /dev/null +++ b/platform/testRunner/src/com/intellij/execution/testframework/export/ExportTestResultsForm.java @@ -0,0 +1,197 @@ +/* + * 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.execution.testframework.export; + +import com.intellij.execution.ExecutionBundle; +import com.intellij.openapi.fileChooser.FileChooserDescriptor; +import com.intellij.openapi.ui.TextComponentAccessor; +import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.wm.IdeFocusManager; +import com.intellij.ui.DocumentAdapter; +import com.intellij.ui.UserActivityListener; +import com.intellij.ui.UserActivityWatcher; +import com.intellij.util.EventDispatcher; +import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; + +public class ExportTestResultsForm { + private JRadioButton myXmlRb; + private JRadioButton myBundledTemplateRb; + private TextFieldWithBrowseButton myCustomTemplateField; + private TextFieldWithBrowseButton myFolderField; + private JPanel myContentPane; + private JLabel myOutputFolderLabel; + private JRadioButton myCustomTemplateRb; + private JTextField myFileNameField; + private JLabel myMessageLabel; + private JCheckBox myOpenExportedFileCb; + + private final EventDispatcher myEventDispatcher = EventDispatcher.create(ChangeListener.class); + + public ExportTestResultsForm(ExportTestResultsConfiguration config, String defaultFileName, String defaultFolder) { + ActionListener listener = new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + updateOnFormatChange(); + } + }; + myXmlRb.addActionListener(listener); + myBundledTemplateRb.addActionListener(listener); + myCustomTemplateRb.addActionListener(listener); + + myOutputFolderLabel.setLabelFor(myFolderField.getChildComponent()); + + myFileNameField.setText(defaultFileName); + + myCustomTemplateField + .addBrowseFolderListener(ExecutionBundle.message("export.test.results.custom.template.chooser.title"), null, null, + new FileChooserDescriptor(true, false, false, false, false, false) { + public boolean isFileSelectable(VirtualFile file) { + return "xsl".equalsIgnoreCase(file.getExtension()) || "xslt".equalsIgnoreCase(file.getExtension()); + } + }, TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT, false); + + myFolderField + .addBrowseFolderListener(ExecutionBundle.message("export.test.results.output.folder.chooser.title"), null, null, + new FileChooserDescriptor(false, true, false, false, false, false), + TextComponentAccessor.TEXT_FIELD_WHOLE_TEXT, false); + + myFileNameField.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + updateOpenInLabel(); + } + }); + + UserActivityWatcher watcher = new UserActivityWatcher(); + watcher.register(myContentPane); + watcher.addUserActivityListener(new UserActivityListener() { + @Override + public void stateChanged() { + myEventDispatcher.getMulticaster().stateChanged(new ChangeEvent(this)); + } + }); + + myMessageLabel.setIcon(UIUtil.getBalloonWarningIcon()); + JRadioButton b; + if (config.getExportFormat() == ExportTestResultsConfiguration.ExportFormat.Xml) { + b = myXmlRb; + } + else if (config.getExportFormat() == ExportTestResultsConfiguration.ExportFormat.BundledTemplate) { + b = myBundledTemplateRb; + } + else { + b = myCustomTemplateRb; + } + b.setSelected(true); + IdeFocusManager.findInstanceByComponent(myContentPane).requestFocus(b, true); + myFolderField.setText(defaultFolder); + myCustomTemplateField.setText(FileUtil.toSystemDependentName(StringUtil.notNullize(config.getUserTemplatePath()))); + myOpenExportedFileCb.setSelected(config.isOpenResults()); + updateOnFormatChange(); + updateOpenInLabel(); + } + + private void updateOpenInLabel() { + myOpenExportedFileCb.setText(ExecutionBundle.message( + shouldOpenInBrowser(myFileNameField.getText()) ? "export.test.results.open.browser" : "export.test.results.open.editor")); + } + + public static boolean shouldOpenInBrowser(String filename) { + return StringUtil.isNotEmpty(filename) && (filename.endsWith(".html") || filename.endsWith(".htm")); + } + + private void updateOnFormatChange() { + if (getExportFormat() == ExportTestResultsConfiguration.ExportFormat.UserTemplate) { + myCustomTemplateField.setEnabled(true); + IdeFocusManager.findInstanceByComponent(myContentPane).requestFocus(myCustomTemplateField.getChildComponent(), true); + } + else { + myCustomTemplateField.setEnabled(false); + } + String filename = myFileNameField.getText(); + if (filename != null && filename.indexOf('.') != -1) { + myFileNameField.setText(filename.substring(0, filename.lastIndexOf('.') + 1) + getExportFormat().getDefaultExtension()); + } + } + + public void apply(ExportTestResultsConfiguration config) { + config.setExportFormat(getExportFormat()); + config.setUserTemplatePath(FileUtil.toSystemIndependentName(myCustomTemplateField.getText())); + config.setOutputFolder(FileUtil.toSystemIndependentName(myFolderField.getText())); + config.setOpenResults(myOpenExportedFileCb.isSelected()); + } + + private ExportTestResultsConfiguration.ExportFormat getExportFormat() { + if (myXmlRb.isSelected()) return ExportTestResultsConfiguration.ExportFormat.Xml; + if (myBundledTemplateRb.isSelected()) return ExportTestResultsConfiguration.ExportFormat.BundledTemplate; + return ExportTestResultsConfiguration.ExportFormat.UserTemplate; + } + + public JComponent getContentPane() { + return myContentPane; + } + + public void addChangeListener(ChangeListener changeListener) { + myEventDispatcher.addListener(changeListener); + } + + @Nullable + public String validate() { + if (getExportFormat() == ExportTestResultsConfiguration.ExportFormat.UserTemplate) { + if (StringUtil.isEmpty(myCustomTemplateField.getText())) { + return ExecutionBundle.message("export.test.results.custom.template.path.empty"); + } + File file = new File(myCustomTemplateField.getText()); + if (!file.isFile()) { + return ExecutionBundle.message("export.test.results.custom.template.not.found", file.getAbsolutePath()); + } + } + + if (StringUtil.isEmpty(myFileNameField.getText())) { + return ExecutionBundle.message("export.test.results.output.filename.empty"); + } + if (StringUtil.isEmpty(myFolderField.getText())) { + return ExecutionBundle.message("export.test.results.output.path.empty"); + } + + return null; + } + + public void showMessage(@Nullable String message) { + myMessageLabel.setText(message); + myMessageLabel.setVisible(message != null); + } + + public JComponent getPreferredFocusedComponent() { + return myFileNameField; + } + + public String getFileName() { + return myFileNameField.getText(); + } +} diff --git a/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java b/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java new file mode 100644 index 000000000000..889584fa9745 --- /dev/null +++ b/platform/testRunner/src/com/intellij/execution/testframework/export/TestResultsXmlFormatter.java @@ -0,0 +1,200 @@ +/* + * 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.execution.testframework.export; + +import com.intellij.execution.configurations.RuntimeConfiguration; +import com.intellij.execution.filters.HyperlinkInfo; +import com.intellij.execution.testframework.AbstractTestProxy; +import com.intellij.execution.testframework.Printable; +import com.intellij.execution.testframework.Printer; +import com.intellij.execution.ui.ConsoleViewContentType; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.util.Ref; +import com.intellij.util.PairProcessor; +import org.apache.tools.ant.taskdefs.optional.junit.XMLConstants; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; + +import java.util.LinkedHashMap; +import java.util.Map; + +// this class generates resulting XML compatible to that of XMLJUnitResultFormatter + +public class TestResultsXmlFormatter implements XMLConstants { + private static final Logger LOG = Logger.getInstance(TestResultsXmlFormatter.class.getName()); + + private final RuntimeConfiguration myRuntimeConfiguration; + private final ContentHandler myResultHandler; + private final AbstractTestProxy myTestRoot; + + public static void execute(AbstractTestProxy root, RuntimeConfiguration runtimeConfiguration, ContentHandler resultHandler) + throws SAXException { + new TestResultsXmlFormatter(root, runtimeConfiguration, resultHandler).execute(); + } + + private TestResultsXmlFormatter(AbstractTestProxy root, RuntimeConfiguration runtimeConfiguration, ContentHandler resultHandler) { + myRuntimeConfiguration = runtimeConfiguration; + myTestRoot = root; + myResultHandler = resultHandler; + } + + private void execute() throws SAXException { + // we'll try to represent tree of any depth as the classic "Suite-Test" tree of a depth one + + // first, figure out the maximum depth + final Ref maxDepth = new Ref(0); + AbstractTestProxy testRoot = myTestRoot; + visit(testRoot, 0, false, new PairProcessor() { + @Override + public boolean process(AbstractTestProxy node, Integer depth) { + maxDepth.set(Math.max(maxDepth.get(), depth)); + return true; + } + }); + + if (maxDepth.get() > 1 && testRoot.getChildren().size() == 1) { + testRoot = testRoot.getChildren().get(0); + maxDepth.set(maxDepth.get() - 1); + } + + myResultHandler.startDocument(); + startElement(TESTSUITES, new LinkedHashMap()); + + final Ref error = Ref.create(null); + visit(testRoot, 0, true, new PairProcessor() { + @Override + public boolean process(AbstractTestProxy node, Integer depth) { + if (depth == maxDepth.get() - 1) { + try { + processSuite(node); + } + catch (SAXException e) { + error.set(e); + return false; + } + } + return true; + } + }); + + if (!error.isNull()) { + throw error.get(); + } + + endElement(TESTSUITES); + myResultHandler.endDocument(); + } + + private void processSuite(AbstractTestProxy suite) throws SAXException { + int succeeded = 0; + int failed = 0; + int interrupted = 0; + + for (AbstractTestProxy test : suite.getChildren()) { + if (test.isPassed()) { + succeeded++; + } + else if (test.isDefect()) { + failed++; + } + else if (test.isInterrupted()) { + interrupted++; + } + else { + LOG.error("Unexpected test status: " + test); + } + } + + StringBuilder suiteName = new StringBuilder(); + for (AbstractTestProxy node = suite; node != null; node = node.getParent()) { + if (suiteName.length() > 0) { + suiteName.insert(0, " - "); + } + suiteName.insert(0, node.getName()); + } + + LinkedHashMap suiteAttrs = new LinkedHashMap(); + suiteAttrs.put(ATTR_NAME, suiteName.toString()); + suiteAttrs.put(ATTR_TESTS, String.valueOf(succeeded + failed + interrupted)); + suiteAttrs.put(ATTR_FAILURES, String.valueOf(failed)); + startElement(TESTSUITE, suiteAttrs); + + for (AbstractTestProxy test : suite.getChildren()) { + LinkedHashMap testAttrs = new LinkedHashMap(); + testAttrs.put(ATTR_NAME, test.getName()); + startElement(TESTCASE, testAttrs); + if (test.isDefect()) { + startElement(FAILURE, new LinkedHashMap()); + final StringBuilder output = new StringBuilder(); + test.printOn(new Printer() { + @Override + public void print(String text, ConsoleViewContentType contentType) { + output.append(text); + } + + @Override + public void onNewAvailable(Printable printable) { + } + + @Override + public void printHyperlink(String text, HyperlinkInfo info) { + } + + @Override + public void mark() { + } + }); + writeText(output.toString()); + endElement(FAILURE); + } + endElement(TESTCASE); + } + endElement(TESTSUITE); + } + + private static boolean visit(AbstractTestProxy node, int depth, boolean visitSelf, PairProcessor processor) { + if (visitSelf) { + if (!processor.process(node, depth)) { + return false; + } + } + for (AbstractTestProxy child : node.getChildren()) { + if (!visit(child, depth + 1, true, processor)) { + return false; + } + } + return true; + } + + private void startElement(String name, LinkedHashMap attributes) throws SAXException { + AttributesImpl attrs = new AttributesImpl(); + for (Map.Entry entry : attributes.entrySet()) { + attrs.addAttribute("", entry.getKey(), entry.getKey(), "CDATA", entry.getValue()); + } + myResultHandler.startElement("", name, name, attrs); + } + + private void endElement(String name) throws SAXException { + myResultHandler.endElement("", name, name); + } + + private void writeText(String text) throws SAXException { + final char[] chars = text.toCharArray(); + myResultHandler.characters(chars, 0, chars.length); + } + +} diff --git a/platform/testRunner/testRunner.iml b/platform/testRunner/testRunner.iml index 5c2b923541d0..01430add824a 100644 --- a/platform/testRunner/testRunner.iml +++ b/platform/testRunner/testRunner.iml @@ -11,6 +11,7 @@ + From 4467616674a38b96e01db1f88f74b5170cda0562 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Sun, 1 Aug 2010 17:11:08 +0400 Subject: [PATCH 64/92] Cyclic autocomplete for words should work with empty prefix (WI-2497) --- .../completion/actions/HippieWordCompletionHandler.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/actions/HippieWordCompletionHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/actions/HippieWordCompletionHandler.java index 5207f8d26456..789394cddb78 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/actions/HippieWordCompletionHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/actions/HippieWordCompletionHandler.java @@ -62,7 +62,7 @@ public class HippieWordCompletionHandler implements CodeInsightActionHandler { String oldPrefix = completionState.oldPrefix; CompletionVariant lastProposedVariant = completionState.lastProposedVariant; - if (lastProposedVariant == null || oldPrefix == null || !new CamelHumpMatcher(oldPrefix).prefixMatches(currentPrefix) || oldPrefix.length() == 0 || + if (lastProposedVariant == null || oldPrefix == null || !new CamelHumpMatcher(oldPrefix).prefixMatches(currentPrefix) || //oldPrefix.length() == 0 || !currentPrefix.equals(lastProposedVariant.variant)) { //we are starting over oldPrefix = currentPrefix; From 9cfb7ac4f87c5bc7ae2925ed159da49354aa4062 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Sun, 1 Aug 2010 17:23:48 +0400 Subject: [PATCH 65/92] New Java parser (enum parsing fixed) --- .../src/com/intellij/lang/java/parser/DeclarationParser.java | 1 + .../lang/java/parser/partial/DeclarationParserTest.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index 9e153b7ff725..c9e55e75cded 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -107,6 +107,7 @@ public class DeclarationParser { if (!expect(builder, JavaTokenType.COMMA)) { if (builder.getTokenType() != null && builder.getTokenType() != JavaTokenType.SEMICOLON) { error(builder, JavaErrorMessages.message("expected.comma.or.semicolon")); + return; } } } diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java index 2617874e8d32..e72df739108f 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java @@ -61,7 +61,7 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testEnumBody3() { doParserTest("{ RED, GREEN, BLUE, }", false, true); } public void testEnumBody4() { doParserTest("{ RED(0), GREEN(1), BLUE(2); }", false, true); } public void testEnumBody5() { doParserTest("{ @ANNOTATION A(10) }", false, true); } - //public void testEnumBody6() { doParserTest("{ RED, GREEN, BLUE\n OurEnum() {} }", false, true); } + public void testEnumBody6() { doParserTest("{ RED, GREEN, BLUE\n OurEnum() {} }", false, true); } public void testEnumWithInitializedConstants() { doParserTest("{ A(10) { },\n B { void method() {} } }", false, true); } public void testEnumWithoutConstants() { doParserTest("{ private A }", false, true); } From f4a6176736ed0276304ced3802c063e2a3ca95ad Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Sun, 1 Aug 2010 17:25:45 +0400 Subject: [PATCH 66/92] New Java parser (cleanup) --- .../src/com/intellij/lang/java/parser/FileParser.java | 6 +++--- .../src/com/intellij/lang/java/parser/JavaParser.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/java/java-impl/src/com/intellij/lang/java/parser/FileParser.java b/java/java-impl/src/com/intellij/lang/java/parser/FileParser.java index c29ffef181ad..8bdacf5384b3 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/FileParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/FileParser.java @@ -52,10 +52,10 @@ public class FileParser { continue; } - final PsiBuilder.Marker decl = DeclarationParser.parse(builder, DeclarationParser.Context.FILE); - if (decl != null) { + final PsiBuilder.Marker declaration = DeclarationParser.parse(builder, DeclarationParser.Context.FILE); + if (declaration != null) { if (invalidElements != null) { - invalidElements.errorBefore(JavaErrorMessages.message("expected.class.or.interface"), decl); + invalidElements.errorBefore(JavaErrorMessages.message("expected.class.or.interface"), declaration); invalidElements = null; } continue; diff --git a/java/java-impl/src/com/intellij/lang/java/parser/JavaParser.java b/java/java-impl/src/com/intellij/lang/java/parser/JavaParser.java index 9d1e8a49cea7..dbe1ab0f5121 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/JavaParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/JavaParser.java @@ -34,12 +34,12 @@ public class JavaParser implements PsiParser { @NotNull public ASTNode parse(final IElementType rootType, final PsiBuilder builder) { - final PsiBuilder.Marker root = builder.mark(); setLanguageLevel(builder, myLanguageLevel); + final PsiBuilder.Marker root = builder.mark(); FileParser.parse(builder); - root.done(rootType); + return builder.getTreeBuilt(); } } From 9d5f3cb532247e26d9d1d102c981082cd5634419 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Sun, 1 Aug 2010 18:02:14 +0400 Subject: [PATCH 67/92] trying to decrease method appettite for locals to avoid SOE --- .../psi/stubs/DefaultStubBuilder.java | 50 +++++++++---------- 1 file changed, 23 insertions(+), 27 deletions(-) diff --git a/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java b/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java index 460d52de5406..0b2987b50243 100644 --- a/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java +++ b/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java @@ -21,55 +21,51 @@ package com.intellij.psi.stubs; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.psi.StubBasedPsiElement; -import com.intellij.psi.StubBuilder; +import com.intellij.psi.*; import com.intellij.psi.tree.IElementType; public class DefaultStubBuilder implements StubBuilder { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.stubs.DefaultStubBuilder"); public StubElement buildStubTree(final PsiFile file) { - return buildStubTreeFor(file, createStubForFile(file), this); + return buildStubTreeFor(file, createStubForFile(file)); } protected StubElement createStubForFile(final PsiFile file) { return new PsiFileStubImpl(file); } - protected static StubElement buildStubTreeFor(PsiElement elt, StubElement parentStub, StubBuilder filter) { - StubElement stub = parentStub; + // Beware many recursive invokations of this method, the code below reuse locals and parameters to avoid SOE + protected StubElement buildStubTreeFor(PsiElement elt, StubElement parentStub) { + ASTNode node; IElementType eltType; if (elt instanceof StubBasedPsiElement) { - final IStubElementType type = ((StubBasedPsiElement)elt).getElementType(); + eltType = ((StubBasedPsiElement)elt).getElementType(); - if (type.shouldCreateStub(elt.getNode())) { + if (((IStubElementType)eltType).shouldCreateStub(elt.getNode())) { //noinspection unchecked - stub = type.createStub(elt, parentStub); + parentStub = ((IStubElementType)eltType).createStub(elt, parentStub); } - eltType = type; } else { - final ASTNode node = elt.getNode(); - final IElementType type = node == null? null : node.getElementType(); - if (type instanceof IStubElementType && ((IStubElementType)type).shouldCreateStub(node)) { - LOG.error("Non-StubBasedPsiElement requests stub creation. Stub type: " + type + ", PSI: " + elt); - } - eltType = type; - } - - for (PsiElement child = elt.getFirstChild(); child != null; child = child.getNextSibling()) { - ASTNode childNode = child.getNode(); - if (!filter.skipChildProcessingWhenBuildingStubs(eltType, childNode != null ? childNode.getElementType():null)) { - buildStubTreeFor(child, stub, filter); + node = elt.getNode(); + eltType = node == null? null : node.getElementType(); + if (eltType instanceof IStubElementType && ((IStubElementType)eltType).shouldCreateStub(node)) { + LOG.error("Non-StubBasedPsiElement requests stub creation. Stub type: " + eltType + ", PSI: " + elt); } } - return stub; + for (elt = elt.getFirstChild(); elt != null; elt = elt.getNextSibling()) { + node = elt.getNode(); + if (!skipChildProcessingWhenBuildingStubs(eltType, node != null ? node.getElementType():null)) { + buildStubTreeFor(elt, parentStub); + } + } + + return parentStub; } - protected static StubElement buildStubTreeFor(ASTNode node, StubElement parentStub, StubBuilder filter) { + protected StubElement buildStubTreeFor(ASTNode node, StubElement parentStub) { StubElement stub = parentStub; IElementType nodeType = node.getElementType(); @@ -87,8 +83,8 @@ public class DefaultStubBuilder implements StubBuilder { } for (ASTNode childNode = node.getFirstChildNode(); childNode != null; childNode = childNode.getTreeNext()) { - if (!filter.skipChildProcessingWhenBuildingStubs(nodeType, childNode.getElementType())) { - buildStubTreeFor(childNode, stub, filter); + if (!skipChildProcessingWhenBuildingStubs(nodeType, childNode.getElementType())) { + buildStubTreeFor(childNode, stub); } } From 81723772830deb10ddd3ca328346ce69339e6a75 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Sun, 1 Aug 2010 21:38:50 +0400 Subject: [PATCH 68/92] Loop invariant optimization --- .../intellij/psi/impl/source/parsing/DeclarationParsing.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/java-impl/src/com/intellij/psi/impl/source/parsing/DeclarationParsing.java b/java/java-impl/src/com/intellij/psi/impl/source/parsing/DeclarationParsing.java index 4f8b677b1fc2..1fcd0a3532c1 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/parsing/DeclarationParsing.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/parsing/DeclarationParsing.java @@ -905,13 +905,14 @@ public class DeclarationParsing extends Parsing { if (tokenType != JavaTokenType.SEMICOLON && tokenType != JavaTokenType.LBRACE){ CompositeElement invalidElementsGroup = Factory.createErrorElement(JavaErrorMessages.message("expected.lbrace.or.semicolon")); method.rawAddChildren(invalidElementsGroup); + + final CharSequence buf = lexer.getBufferSequence(); Loop: while(true){ tokenType = lexer.getTokenType(); // Heuristic. Going to next line obviously means method signature is over, starting new method. // Necessary for correct CompleteStatementTest operation. - final CharSequence buf = lexer.getBufferSequence(); int start = lexer.getTokenStart(); for (int i = start - 1; i >= 0; i--) { if (buf.charAt(i) == '\n') break Loop; From 46cdd6d8978871030c16889e52486c42a8c84ffc Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Sun, 1 Aug 2010 21:39:32 +0400 Subject: [PATCH 69/92] PSI builder's javadoc updated --- platform/lang-api/src/com/intellij/lang/PsiBuilder.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/lang-api/src/com/intellij/lang/PsiBuilder.java b/platform/lang-api/src/com/intellij/lang/PsiBuilder.java index c9c10570ad5d..41dfb275a916 100644 --- a/platform/lang-api/src/com/intellij/lang/PsiBuilder.java +++ b/platform/lang-api/src/com/intellij/lang/PsiBuilder.java @@ -164,6 +164,7 @@ public interface PsiBuilder extends UserDataHolder { /** * Adds an error marker with the specified message text at the current position in the tree. + *
Note: from series of subsequent errors messages only first will be part of resulting tree. * * @param messageText the text of the error message displayed to the user. */ From d9f8050ad355b7d89bf3b7cb9065e3462e742a25 Mon Sep 17 00:00:00 2001 From: Roman Shevchenko Date: Sun, 1 Aug 2010 22:04:25 +0400 Subject: [PATCH 70/92] New Java parser (completion hack) --- .../lang/java/parser/DeclarationParser.java | 17 +++++++++++-- .../declarations/CompletionHack2.txt | 25 +++++++++++++++++++ .../parser/partial/DeclarationParserTest.java | 1 + 3 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 java/java-tests/testData/psi/parser-partial/declarations/CompletionHack2.txt diff --git a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java index c9e55e75cded..34f5ae8bda41 100644 --- a/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java +++ b/java/java-impl/src/com/intellij/lang/java/parser/DeclarationParser.java @@ -40,6 +40,8 @@ public class DeclarationParser { private static final TokenSet AFTER_END_DECLARATION_SET = TokenSet.create(JavaElementType.FIELD, JavaElementType.METHOD); private static final TokenSet BEFORE_LBRACE_ELEMENTS_SET = TokenSet.create( JavaTokenType.IDENTIFIER, JavaTokenType.COMMA, JavaTokenType.EXTENDS_KEYWORD, JavaTokenType.IMPLEMENTS_KEYWORD); + private static final TokenSet APPEND_TO_METHOD_SET = TokenSet.create( + JavaTokenType.IDENTIFIER, JavaTokenType.COMMA, JavaTokenType.THROWS_KEYWORD); private DeclarationParser() { } @@ -411,8 +413,19 @@ public class DeclarationParser { StatementParser.parseCodeBlock(builder); } else { - error(builder, JavaErrorMessages.message("expected.lbrace.or.semicolon")); - // todo: special treatment - like in fields (DeclarationParserTest.testMultiLineUnclosed()) + final PsiBuilder.Marker error = builder.mark(); + // heuristic: going to next line obviously means method signature is over, starting new method (actually, another one completion hack) + final CharSequence text = builder.getOriginalText(); + Loop: + while (true) { + for (int i = builder.getCurrentOffset() - 1; i >= 0; i--) { + final char ch = text.charAt(i); + if (ch == '\n') break Loop; + else if (ch != ' ' && ch != '\t') break; + } + if (!expect(builder, APPEND_TO_METHOD_SET)) break; + } + error.error(JavaErrorMessages.message("expected.lbrace.or.semicolon")); } declaration.done(anno ? JavaElementType.ANNOTATION_METHOD : JavaElementType.METHOD); diff --git a/java/java-tests/testData/psi/parser-partial/declarations/CompletionHack2.txt b/java/java-tests/testData/psi/parser-partial/declarations/CompletionHack2.txt new file mode 100644 index 000000000000..9c43b6b5b3df --- /dev/null +++ b/java/java-tests/testData/psi/parser-partial/declarations/CompletionHack2.txt @@ -0,0 +1,25 @@ +PsiJavaFile:CompletionHack2.java + PsiJavaToken:LBRACE('{') + PsiWhiteSpace(' ') + PsiAnnotationMethod:foo + PsiModifierList: + + PsiTypeParameterList + + PsiTypeElement:String + PsiJavaCodeReferenceElement:String + PsiIdentifier:String('String') + PsiReferenceParameterList + + PsiWhiteSpace(' ') + PsiIdentifier:foo('foo') + PsiParameterList:() + PsiJavaToken:LPARENTH('(') + PsiJavaToken:RPARENTH(')') + PsiReferenceList + + PsiWhiteSpace(' ') + PsiErrorElement:'{' or ';' expected + PsiIdentifier:def('def') + PsiWhiteSpace(' ') + PsiJavaToken:RBRACE('}') \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java index e72df739108f..8a51afbf9b6d 100644 --- a/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java +++ b/java/java-tests/testSrc/com/intellij/lang/java/parser/partial/DeclarationParserTest.java @@ -96,6 +96,7 @@ public class DeclarationParserTest extends JavaParsingTestCase { public void testErrors() { doParserTest("{ public static protected int f1 = 0; }", false, false); } public void testCompletionHack0() { doParserTest("{ \n String s = \"\"; }", false, false); } public void testCompletionHack1() { doParserTest("{ x(Collection x); }", false, false); } public void testParameterAnnotation() { doParserTest("{ void foo (@Annotation(value=77) int param) {} }", false, false); } public void testParameterizedMethod() { doParserTest("{ @Nullable T bar() {} }", false, false); } From 3b62cfab4547ecfd0ea628e18408faed47c497be Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 1 Aug 2010 17:31:39 +0100 Subject: [PATCH 71/92] JavaConcatenationInjectorManager -> service --- .../JavaConcatenationInjectorManager.java | 59 ++++--------------- plugins/IntelliLang/src/META-INF/plugin.xml | 10 +++- .../plugins/intelliLang/Configuration.java | 6 +- resources/src/META-INF/IdeaPlugin.xml | 4 ++ resources/src/componentSets/Core.xml | 3 - 5 files changed, 30 insertions(+), 52 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaConcatenationInjectorManager.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaConcatenationInjectorManager.java index 7cc23ec62cf4..2d2128e9a14e 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaConcatenationInjectorManager.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/injected/JavaConcatenationInjectorManager.java @@ -16,10 +16,9 @@ package com.intellij.psi.impl.source.tree.injected; import com.intellij.lang.injection.ConcatenationAwareInjector; -import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.lang.injection.MultiHostInjector; import com.intellij.lang.injection.MultiHostRegistrar; -import com.intellij.openapi.components.ProjectComponent; +import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.extensions.*; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Key; @@ -30,27 +29,22 @@ import com.intellij.psi.impl.PsiManagerEx; import com.intellij.psi.impl.PsiParameterizedCachedValue; import com.intellij.psi.util.*; import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; /** * @author cdr */ -public class JavaConcatenationInjectorManager implements ProjectComponent, ModificationTracker { +public class JavaConcatenationInjectorManager implements ModificationTracker { public static final ExtensionPointName CONCATENATION_INJECTOR_EP_NAME = ExtensionPointName.create("com.intellij.concatenationAwareInjector"); - private final AtomicReference myRegisteredConcatenationAdapter = new AtomicReference(); - private final InjectedLanguageManager myInjectedLanguageManager; private volatile long myModificationCounter; private static final ConcatenationPsiCachedValueProvider CONCATENATION_PSI_CACHED_VALUE_PROVIDER = new ConcatenationPsiCachedValueProvider(); - public JavaConcatenationInjectorManager(Project project, InjectedLanguageManager injectedLanguageManager, PsiManagerEx psiManagerEx) { - myInjectedLanguageManager = injectedLanguageManager; + public JavaConcatenationInjectorManager(Project project, PsiManagerEx psiManagerEx) { final ExtensionPoint concatPoint = Extensions.getArea(project).getExtensionPoint(CONCATENATION_INJECTOR_EP_NAME); concatPoint.addExtensionPointListener(new ExtensionPointListener() { public void extensionAdded(ConcatenationAwareInjector injector, @Nullable PluginDescriptor pluginDescriptor) { @@ -68,30 +62,8 @@ public class JavaConcatenationInjectorManager implements ProjectComponent, Modif }); } - public void projectOpened() { - - } - - public void projectClosed() { - - } - - @NonNls - @NotNull - public String getComponentName() { - return "JavaConcatenationInjectorManager"; - } - - public void initComponent() { - - } - - public void disposeComponent() { - - } - public static JavaConcatenationInjectorManager getInstance(final Project project) { - return project.getComponent(JavaConcatenationInjectorManager.class); + return ServiceManager.getService(project, JavaConcatenationInjectorManager.class); } public long getModificationCount() { @@ -151,10 +123,16 @@ public class JavaConcatenationInjectorManager implements ProjectComponent, Modif } private static final Key> INJECTED_PSI_IN_CONCATENATION = Key.create("INJECTED_PSI_IN_CONCATENATION"); - private final Concatenation2InjectorAdapter myConcatenation2InjectorAdapter = new Concatenation2InjectorAdapter(); - private class Concatenation2InjectorAdapter implements MultiHostInjector { + + public static class Concatenation2InjectorAdapter implements MultiHostInjector { + private final JavaConcatenationInjectorManager myManager; + + public Concatenation2InjectorAdapter(Project project) { + myManager = getInstance(project); + } + public void getLanguagesToInject(@NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) { - if (myConcatenationInjectors.isEmpty()) return; + if (myManager.myConcatenationInjectors.isEmpty()) return; ParameterizedCachedValue cachedValue = context.getUserData(INJECTED_PSI_IN_CONCATENATION); MultiHostRegistrarImpl result; @@ -203,16 +181,5 @@ public class JavaConcatenationInjectorManager implements ProjectComponent, Modif private void concatenationInjectorsChanged() { myModificationCounter++; - if (myConcatenationInjectors.isEmpty()) { - MultiHostInjector prev = myRegisteredConcatenationAdapter.getAndSet(null); - if (prev != null) { - myInjectedLanguageManager.unregisterMultiHostInjector(prev); - } - } - else { - if (myRegisteredConcatenationAdapter.compareAndSet(null, myConcatenation2InjectorAdapter)) { - myInjectedLanguageManager.registerMultiHostInjector(myConcatenation2InjectorAdapter); - } - } } } diff --git a/plugins/IntelliLang/src/META-INF/plugin.xml b/plugins/IntelliLang/src/META-INF/plugin.xml index 086bf2d42211..c6cfab4527e6 100644 --- a/plugins/IntelliLang/src/META-INF/plugin.xml +++ b/plugins/IntelliLang/src/META-INF/plugin.xml @@ -38,8 +38,6 @@ - @@ -48,4 +46,12 @@ + + + + + org.intellij.plugins.intelliLang.Configuration + + diff --git a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/Configuration.java b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/Configuration.java index ef099cf3fbf1..033435004128 100644 --- a/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/Configuration.java +++ b/plugins/IntelliLang/src/org/intellij/plugins/intelliLang/Configuration.java @@ -19,6 +19,7 @@ import com.intellij.ide.plugins.IdeaPluginDescriptor; import com.intellij.ide.plugins.IdeaPluginDescriptorImpl; import com.intellij.ide.plugins.PluginManager; import com.intellij.ide.plugins.cl.PluginClassLoader; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.command.UndoConfirmationPolicy; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.command.undo.DocumentReference; @@ -64,6 +65,9 @@ import java.util.concurrent.CopyOnWriteArrayList; * Configuration that holds configured xml tag, attribute and method parameter * injection settings as well as the annotations to use for injection, pattern * validation and for substituting non-compile time constant expression. + * + * Making it a service may result in FileContentUtil.reparseFiles at a random loading moment which may cause + * mysterious PSI validity losses */ @State( name = Configuration.COMPONENT_NAME, @@ -282,7 +286,7 @@ public final class Configuration implements PersistentStateComponent, M } public static Configuration getInstance() { - return ServiceManager.getService(Configuration.class); + return ApplicationManager.getApplication().getComponent(Configuration.class); } public String getLanguageAnnotationClass() { diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index e0f068715d39..7e04947e4239 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -364,6 +364,10 @@ + + + diff --git a/resources/src/componentSets/Core.xml b/resources/src/componentSets/Core.xml index 57482e8c7f26..346675cc3fe6 100644 --- a/resources/src/componentSets/Core.xml +++ b/resources/src/componentSets/Core.xml @@ -15,9 +15,6 @@ - - com.intellij.psi.impl.source.tree.injected.JavaConcatenationInjectorManager - com.intellij.facet.impl.autodetecting.FacetAutodetectingManager com.intellij.facet.impl.autodetecting.FacetAutodetectingManagerImpl From efbae925ec13589e371e3a2e998c243fa68a199a Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 1 Aug 2010 18:00:56 +0100 Subject: [PATCH 72/92] move relaxng dom registration to plugin.xml --- plugins/relaxng/src/META-INF/plugin.xml | 16 ++- .../plugins/relaxNG/ApplicationLoader.java | 5 +- .../plugins/relaxNG/ProjectLoader.java | 119 ------------------ .../relaxNG/RelaxNGReferenceContributor.java | 2 +- .../relaxNG/RncFileReferenceManipulator.java | 35 ++++++ .../relaxNG/RngDocumentationProvider.java | 2 +- .../relaxNG/RngDomFileDescription.java | 54 ++++++++ .../relaxNG/convert/ConvertSchemaAction.java | 4 +- .../inspections/UnusedDefineInspection.java | 4 +- .../model/descriptors/RngNsDescriptor.java | 4 +- .../model/resolve/RelaxIncludeProvider.java | 4 +- .../model/resolve/RelaxSymbolIndex.java | 6 +- .../validation/RngSchemaValidator.java | 4 +- .../xml/dom/impl/CreatePatternFix.java | 4 +- .../xml/dom/impl/RngHrefConverter.java | 4 +- .../plugins/relaxNG/AbstractParsingTest.java | 1 - 16 files changed, 123 insertions(+), 145 deletions(-) delete mode 100644 plugins/relaxng/src/org/intellij/plugins/relaxNG/ProjectLoader.java create mode 100644 plugins/relaxng/src/org/intellij/plugins/relaxNG/RncFileReferenceManipulator.java create mode 100644 plugins/relaxng/src/org/intellij/plugins/relaxNG/RngDomFileDescription.java diff --git a/plugins/relaxng/src/META-INF/plugin.xml b/plugins/relaxng/src/META-INF/plugin.xml index 90d431df57fb..7932449418d3 100644 --- a/plugins/relaxng/src/META-INF/plugin.xml +++ b/plugins/relaxng/src/META-INF/plugin.xml @@ -55,7 +55,7 @@ - + @@ -65,12 +65,20 @@ + + + + + + + + + + + - - org.intellij.plugins.relaxNG.ProjectLoader - org.intellij.plugins.relaxNG.config.NoNamespaceConfig org.intellij.plugins.relaxNG.config.NoNamespaceConfigImpl diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/ApplicationLoader.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/ApplicationLoader.java index 7f8e3230a070..f9d826e09858 100644 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/ApplicationLoader.java +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/ApplicationLoader.java @@ -52,6 +52,7 @@ import org.jetbrains.annotations.NotNull; public class ApplicationLoader implements ApplicationComponent, InspectionToolProvider { private static final String RNG_EXT = "rng"; private static final String VALIDATE_XML = "ValidateXml"; + public static final String RNG_NAMESPACE = "http://relaxng.org/ns/structure/1.0"; @NonNls @NotNull @@ -79,7 +80,7 @@ public class ApplicationLoader implements ApplicationComponent, InspectionToolPr final MetaDataRegistrar registrar = MetaDataRegistrar.getInstance(); registrar.registerMetaData( new AndFilter( - new NamespaceFilter(ProjectLoader.RNG_NAMESPACE), + new NamespaceFilter(RNG_NAMESPACE), new ClassFilter(XmlDocument.class) ), RngNsDescriptor.class); @@ -116,7 +117,7 @@ public class ApplicationLoader implements ApplicationComponent, InspectionToolPr public class ResourceProvider implements StandardResourceProvider { public void registerResources(ResourceRegistrar registrar) { - registrar.addStdResource(ProjectLoader.RNG_NAMESPACE, "resources/relaxng.rng", getClass()); + registrar.addStdResource(RNG_NAMESPACE, "resources/relaxng.rng", getClass()); registrar.addIgnoredResource("http://relaxng.org/ns/compatibility/annotations/1.0"); } } diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/ProjectLoader.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/ProjectLoader.java deleted file mode 100644 index 03d39ca86fc8..000000000000 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/ProjectLoader.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2007 Sascha Weinreuter - * - * 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.intellij.plugins.relaxNG; - -import com.intellij.lang.ASTNode; -import com.intellij.openapi.components.ProjectComponent; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.AbstractElementManipulator; -import com.intellij.psi.PsiManager; -import com.intellij.util.IncorrectOperationException; -import com.intellij.util.xml.DomFileDescription; -import com.intellij.util.xml.DomManager; -import com.intellij.util.xml.highlighting.DomElementsAnnotator; -import org.intellij.plugins.relaxNG.compact.RncTokenTypes; -import org.intellij.plugins.relaxNG.compact.psi.RncFileReference; -import org.intellij.plugins.relaxNG.compact.psi.util.RenameUtil; -import org.intellij.plugins.relaxNG.model.annotation.ModelAnnotator; -import org.intellij.plugins.relaxNG.xml.dom.*; -import org.intellij.plugins.relaxNG.xml.dom.impl.RngDefineImpl; -import org.intellij.plugins.relaxNG.xml.dom.impl.RngGrammarImpl; -import org.intellij.plugins.relaxNG.xml.dom.impl.RngIncludeImpl; -import org.intellij.plugins.relaxNG.xml.dom.impl.RngRefImpl; -import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; - -/** - * Created by IntelliJ IDEA. - * User: sweinreuter - * Date: 16.07.2007 - */ -public class ProjectLoader implements ProjectComponent { - public static final String RNG_NAMESPACE = "http://relaxng.org/ns/structure/1.0"; - - private final Project myProject; - - public ProjectLoader(Project project) { - myProject = project; - } - - @NonNls - @NotNull - public String getComponentName() { - return "RELAX-NG"; - } - - @SuppressWarnings({ "deprecation" }) - public void initComponent() { - final DomManager mgr = DomManager.getDomManager(myProject); - mgr.registerFileDescription(new MyFileDescription(RngGrammar.class, "grammar")); - mgr.registerFileDescription(new MyFileDescription(RngElement.class, "element")); - mgr.registerFileDescription(new MyFileDescription(RngChoice.class, "choice")); - mgr.registerFileDescription(new MyFileDescription(RngGroup.class, "group")); - mgr.registerFileDescription(new MyFileDescription(RngInterleave.class, "interleave")); - } - - public void disposeComponent() { - } - - public void projectOpened() { - } - - public void projectClosed() { - } - - private static class MyFileDescription extends DomFileDescription { - public MyFileDescription(Class elementClass, String rootTagName) { - super(elementClass, rootTagName); - registerNamespacePolicy("RELAX-NG", RNG_NAMESPACE); - registerImplementation(RngDefine.class, RngDefineImpl.class); - registerImplementation(RngGrammar.class, RngGrammarImpl.class); - registerImplementation(RngInclude.class, RngIncludeImpl.class); - registerImplementation(RngRef.class, RngRefImpl.class); - } - - public boolean isAutomaticHighlightingEnabled() { - return true; - } - - public DomElementsAnnotator createAnnotator() { - return new ModelAnnotator(); - } - } - - public static class RncFileReferenceManipulator extends AbstractElementManipulator { - public RncFileReference handleContentChange(RncFileReference element, TextRange range, String newContent) throws IncorrectOperationException { - final ASTNode node = element.getNode(); - assert node != null; - - final ASTNode literal = node.findChildByType(RncTokenTypes.LITERAL); - if (literal != null) { - assert range.equals(element.getReferenceRange()); - final PsiManager manager = PsiManager.getInstance(element.getProject()); - final ASTNode newChild = RenameUtil.createLiteralNode(manager, newContent); - literal.getTreeParent().replaceChild(literal, newChild); - } - return element; - } - - @Override - public TextRange getRangeInElement(RncFileReference element) { - return element.getReferenceRange(); - } - } -} diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/RelaxNGReferenceContributor.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/RelaxNGReferenceContributor.java index 08c8a1e63b91..08b281c95183 100644 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/RelaxNGReferenceContributor.java +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/RelaxNGReferenceContributor.java @@ -17,7 +17,7 @@ import static com.intellij.patterns.XmlPatterns.xmlTag; * @author peter */ public class RelaxNGReferenceContributor extends PsiReferenceContributor { - private static final XmlNamedElementPattern RNG_TAG_PATTERN = xmlTag().withNamespace(string().equalTo(ProjectLoader.RNG_NAMESPACE)); + private static final XmlNamedElementPattern RNG_TAG_PATTERN = xmlTag().withNamespace(string().equalTo(ApplicationLoader.RNG_NAMESPACE)); private static final XmlNamedElementPattern.XmlAttributePattern NAME_ATTR_PATTERN = xmlAttribute("name"); private static final StringPattern ELEMENT_OR_ATTRIBUTE_PATTERN = string().oneOf("element", "attribute"); diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/RncFileReferenceManipulator.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/RncFileReferenceManipulator.java new file mode 100644 index 000000000000..e9bd6a354e1e --- /dev/null +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/RncFileReferenceManipulator.java @@ -0,0 +1,35 @@ +package org.intellij.plugins.relaxNG; + +import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.AbstractElementManipulator; +import com.intellij.psi.PsiManager; +import com.intellij.util.IncorrectOperationException; +import org.intellij.plugins.relaxNG.compact.RncTokenTypes; +import org.intellij.plugins.relaxNG.compact.psi.RncFileReference; +import org.intellij.plugins.relaxNG.compact.psi.util.RenameUtil; + +/** +* @author peter +*/ +public class RncFileReferenceManipulator extends AbstractElementManipulator { + public RncFileReference handleContentChange(RncFileReference element, TextRange range, String newContent) throws + IncorrectOperationException { + final ASTNode node = element.getNode(); + assert node != null; + + final ASTNode literal = node.findChildByType(RncTokenTypes.LITERAL); + if (literal != null) { + assert range.equals(element.getReferenceRange()); + final PsiManager manager = PsiManager.getInstance(element.getProject()); + final ASTNode newChild = RenameUtil.createLiteralNode(manager, newContent); + literal.getTreeParent().replaceChild(literal, newChild); + } + return element; + } + + @Override + public TextRange getRangeInElement(RncFileReference element) { + return element.getReferenceRange(); + } +} diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/RngDocumentationProvider.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/RngDocumentationProvider.java index 303d717e3a57..4ffd00be4f23 100644 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/RngDocumentationProvider.java +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/RngDocumentationProvider.java @@ -102,7 +102,7 @@ public class RngDocumentationProvider implements DocumentationProvider { } private static StringBuilder getDocumentationFromTag(XmlTag tag, String localName, String kind) { - if (tag.getNamespace().equals(ProjectLoader.RNG_NAMESPACE)) { + if (tag.getNamespace().equals(ApplicationLoader.RNG_NAMESPACE)) { final StringBuilder sb = new StringBuilder(); sb.append(kind).append(": ").append(localName).append("
"); final XmlTag[] docTags = tag.findSubTags("documentation", COMPATIBILITY_ANNOTATIONS_1_0); diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/RngDomFileDescription.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/RngDomFileDescription.java new file mode 100644 index 000000000000..7e1e2a2313e3 --- /dev/null +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/RngDomFileDescription.java @@ -0,0 +1,54 @@ +package org.intellij.plugins.relaxNG; + +import com.intellij.util.xml.DomFileDescription; +import com.intellij.util.xml.highlighting.DomElementsAnnotator; +import org.intellij.plugins.relaxNG.model.annotation.ModelAnnotator; +import org.intellij.plugins.relaxNG.xml.dom.*; + +/** +* @author peter +*/ +public class RngDomFileDescription extends DomFileDescription { + public RngDomFileDescription(Class elementClass, String rootTagName) { + super(elementClass, rootTagName); + registerNamespacePolicy("RELAX-NG", ApplicationLoader.RNG_NAMESPACE); + } + + public boolean isAutomaticHighlightingEnabled() { + return true; + } + + public DomElementsAnnotator createAnnotator() { + return new ModelAnnotator(); + } + + public static class RngGrammarDescription extends RngDomFileDescription { + public RngGrammarDescription() { + super(RngGrammar.class, "grammar"); + } + } + + public static class RngElementDescription extends RngDomFileDescription { + public RngElementDescription() { + super(RngElement.class, "element"); + } + } + + public static class RngChoiceDescription extends RngDomFileDescription { + public RngChoiceDescription() { + super(RngChoice.class, "choice"); + } + } + + public static class RngGroupDescription extends RngDomFileDescription { + public RngGroupDescription() { + super(RngGroup.class, "group"); + } + } + + public static class RngInterleaveDescription extends RngDomFileDescription { + public RngInterleaveDescription() { + super(RngInterleave.class, "interleave"); + } + } +} diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/convert/ConvertSchemaAction.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/convert/ConvertSchemaAction.java index cd0ec5aba48f..7c9eb3eae4ff 100644 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/convert/ConvertSchemaAction.java +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/convert/ConvertSchemaAction.java @@ -29,7 +29,7 @@ import com.intellij.psi.PsiManager; import com.intellij.psi.xml.XmlDocument; import com.intellij.psi.xml.XmlFile; import com.intellij.psi.xml.XmlTag; -import org.intellij.plugins.relaxNG.ProjectLoader; +import org.intellij.plugins.relaxNG.ApplicationLoader; import org.intellij.plugins.relaxNG.compact.RncFileType; import org.intellij.plugins.relaxNG.validation.ValidateAction; @@ -71,7 +71,7 @@ public class ConvertSchemaAction extends AnAction { final XmlTag rootTag = document.getRootTag(); assert rootTag != null; final String uri = rootTag.getNamespace(); - if (ProjectLoader.RNG_NAMESPACE.equals(uri) && files.length == 1) { + if (ApplicationLoader.RNG_NAMESPACE.equals(uri) && files.length == 1) { return SchemaType.RNG; } } diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/inspections/UnusedDefineInspection.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/inspections/UnusedDefineInspection.java index 1561231e607f..b6d4eeade213 100644 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/inspections/UnusedDefineInspection.java +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/inspections/UnusedDefineInspection.java @@ -40,7 +40,7 @@ import com.intellij.util.IncorrectOperationException; import com.intellij.util.Query; import com.intellij.util.xml.DomElement; import com.intellij.util.xml.DomManager; -import org.intellij.plugins.relaxNG.ProjectLoader; +import org.intellij.plugins.relaxNG.ApplicationLoader; import org.intellij.plugins.relaxNG.compact.psi.RncDefine; import org.intellij.plugins.relaxNG.compact.psi.RncElementVisitor; import org.intellij.plugins.relaxNG.compact.psi.RncGrammar; @@ -135,7 +135,7 @@ public class UnusedDefineInspection extends BaseInspection { if (!tag.getLocalName().equals("define")) { return; } - if (!tag.getNamespace().equals(ProjectLoader.RNG_NAMESPACE)) { + if (!tag.getNamespace().equals(ApplicationLoader.RNG_NAMESPACE)) { return; } if (tag.getAttribute("combine") != null) { diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/model/descriptors/RngNsDescriptor.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/model/descriptors/RngNsDescriptor.java index b2c86cf6d550..7e9aaac14dcd 100644 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/model/descriptors/RngNsDescriptor.java +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/model/descriptors/RngNsDescriptor.java @@ -39,7 +39,7 @@ import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.xml.XmlElementDescriptor; import com.intellij.xml.XmlNSDescriptor; -import org.intellij.plugins.relaxNG.ProjectLoader; +import org.intellij.plugins.relaxNG.ApplicationLoader; import org.intellij.plugins.relaxNG.model.resolve.RelaxIncludeIndex; import org.intellij.plugins.relaxNG.validation.RngParser; import org.intellij.plugins.relaxNG.validation.XmlInstanceValidator; @@ -243,7 +243,7 @@ public class RngNsDescriptor implements XmlNSDescriptor, Validator { return; } // RNG XML itself is validated by parsing it with Jing, so we don't want to schema-validate it - if (!ProjectLoader.RNG_NAMESPACE.equals(rootTag.getNamespace())) { + if (!ApplicationLoader.RNG_NAMESPACE.equals(rootTag.getNamespace())) { XmlInstanceValidator.doValidation(doc, host, getDescriptorFile()); } } diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxIncludeProvider.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxIncludeProvider.java index 3fd4d874a02e..9f2867d3907e 100644 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxIncludeProvider.java +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxIncludeProvider.java @@ -7,7 +7,7 @@ import com.intellij.psi.impl.include.FileIncludeInfo; import com.intellij.psi.impl.include.FileIncludeProvider; import com.intellij.util.indexing.FileContent; import com.intellij.util.xml.NanoXmlUtil; -import org.intellij.plugins.relaxNG.ProjectLoader; +import org.intellij.plugins.relaxNG.ApplicationLoader; import org.intellij.plugins.relaxNG.compact.RncFileType; import org.intellij.plugins.relaxNG.compact.psi.RncElement; import org.intellij.plugins.relaxNG.compact.psi.RncElementVisitor; @@ -72,7 +72,7 @@ public class RelaxIncludeProvider extends FileIncludeProvider { @Override public void startElement(String name, String nsPrefix, String nsURI, String systemID, int lineNr) throws Exception { - boolean isRngTag = ProjectLoader.RNG_NAMESPACE.equals(nsURI); + boolean isRngTag = ApplicationLoader.RNG_NAMESPACE.equals(nsURI); if (!isRNG) { // analyzing start tag if (!isRngTag) { throw new NanoXmlUtil.ParserStoppedException(); diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxSymbolIndex.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxSymbolIndex.java index 43d4baa8a535..2f198f753b72 100644 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxSymbolIndex.java +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/model/resolve/RelaxSymbolIndex.java @@ -23,7 +23,7 @@ import com.intellij.util.io.KeyDescriptor; import com.intellij.util.text.CharArrayUtil; import com.intellij.util.text.CharSequenceReader; import com.intellij.util.xml.NanoXmlUtil; -import org.intellij.plugins.relaxNG.ProjectLoader; +import org.intellij.plugins.relaxNG.ApplicationLoader; import org.intellij.plugins.relaxNG.compact.RncFileType; import org.intellij.plugins.relaxNG.model.CommonElement; import org.intellij.plugins.relaxNG.model.Define; @@ -68,7 +68,7 @@ public class RelaxSymbolIndex extends ScalarIndexExtension { final HashMap map = new HashMap(); if (inputData.getFileType() == XmlFileType.INSTANCE) { CharSequence inputDataContentAsText = inputData.getContentAsText(); - if (CharArrayUtil.indexOf(inputDataContentAsText, ProjectLoader.RNG_NAMESPACE, 0) == -1) return Collections.EMPTY_MAP; + if (CharArrayUtil.indexOf(inputDataContentAsText, ApplicationLoader.RNG_NAMESPACE, 0) == -1) return Collections.EMPTY_MAP; NanoXmlUtil.parse(new CharSequenceReader(inputDataContentAsText), new NanoXmlUtil.IXMLBuilderAdapter() { NanoXmlUtil.IXMLBuilderAdapter attributeHandler; int depth; @@ -83,7 +83,7 @@ public class RelaxSymbolIndex extends ScalarIndexExtension { @Override public void startElement(String name, String nsPrefix, String nsURI, String systemID, int lineNr) throws Exception { attributeHandler = null; - if (depth == 1 && ProjectLoader.RNG_NAMESPACE.equals(nsURI)) { + if (depth == 1 && ApplicationLoader.RNG_NAMESPACE.equals(nsURI)) { if ("define".equals(name)) { attributeHandler = new NanoXmlUtil.IXMLBuilderAdapter() { @Override diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/validation/RngSchemaValidator.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/validation/RngSchemaValidator.java index f13a4bbb3647..3deebb759938 100644 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/validation/RngSchemaValidator.java +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/validation/RngSchemaValidator.java @@ -31,7 +31,7 @@ import com.intellij.psi.*; import com.intellij.psi.search.PsiElementProcessor; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.*; -import org.intellij.plugins.relaxNG.ProjectLoader; +import org.intellij.plugins.relaxNG.ApplicationLoader; import org.intellij.plugins.relaxNG.compact.RncFileType; import org.intellij.plugins.relaxNG.compact.psi.RncFile; import org.intellij.plugins.relaxNG.model.resolve.RelaxIncludeIndex; @@ -66,7 +66,7 @@ public class RngSchemaValidator implements ExternalAnnotator { if (rootTag == null) { return; } - if (!ProjectLoader.RNG_NAMESPACE.equals(rootTag.getNamespace())) { + if (!ApplicationLoader.RNG_NAMESPACE.equals(rootTag.getNamespace())) { return; } } else { diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/xml/dom/impl/CreatePatternFix.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/xml/dom/impl/CreatePatternFix.java index 8fe650678b8f..661e34100528 100644 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/xml/dom/impl/CreatePatternFix.java +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/xml/dom/impl/CreatePatternFix.java @@ -30,7 +30,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlTag; import com.intellij.psi.xml.XmlText; import com.intellij.util.IncorrectOperationException; -import org.intellij.plugins.relaxNG.ProjectLoader; +import org.intellij.plugins.relaxNG.ApplicationLoader; import org.intellij.plugins.relaxNG.xml.dom.RngGrammar; import org.jetbrains.annotations.NotNull; @@ -98,7 +98,7 @@ class CreatePatternFix implements IntentionAction, LocalQuickFix { private void doFix() throws IncorrectOperationException { final XmlTag tag = PsiTreeUtil.getParentOfType(myReference.getElement(), XmlTag.class); assert tag != null; - final XmlTag defineTag = tag.createChildTag("define", ProjectLoader.RNG_NAMESPACE, "\n \n", false); + final XmlTag defineTag = tag.createChildTag("define", ApplicationLoader.RNG_NAMESPACE, "\n \n", false); defineTag.setAttribute("name", myReference.getCanonicalText()); final RngGrammar grammar = ((DefinitionReference)myReference).getScope(); diff --git a/plugins/relaxng/src/org/intellij/plugins/relaxNG/xml/dom/impl/RngHrefConverter.java b/plugins/relaxng/src/org/intellij/plugins/relaxNG/xml/dom/impl/RngHrefConverter.java index 9753566519ab..d6413006cbb5 100644 --- a/plugins/relaxng/src/org/intellij/plugins/relaxNG/xml/dom/impl/RngHrefConverter.java +++ b/plugins/relaxng/src/org/intellij/plugins/relaxNG/xml/dom/impl/RngHrefConverter.java @@ -21,7 +21,7 @@ import com.intellij.psi.PsiReference; import com.intellij.psi.impl.source.resolve.reference.impl.providers.FileReferenceSet; import com.intellij.psi.xml.XmlFile; import com.intellij.util.xml.*; -import org.intellij.plugins.relaxNG.ProjectLoader; +import org.intellij.plugins.relaxNG.ApplicationLoader; import org.intellij.plugins.relaxNG.references.FileReferenceUtil; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; @@ -59,7 +59,7 @@ public class RngHrefConverter extends Converter implements CustomRefere } final FileReferenceSet set = FileReferenceSet.createSet(element, false, false, false); if (set != null) { - return FileReferenceUtil.restrict(set, FileReferenceUtil.byNamespace(ProjectLoader.RNG_NAMESPACE), true); + return FileReferenceUtil.restrict(set, FileReferenceUtil.byNamespace(ApplicationLoader.RNG_NAMESPACE), true); } else { return PsiReference.EMPTY_ARRAY; } diff --git a/plugins/relaxng/test/org/intellij/plugins/relaxNG/AbstractParsingTest.java b/plugins/relaxng/test/org/intellij/plugins/relaxNG/AbstractParsingTest.java index 9bc0f75ce406..956eb4af68bb 100644 --- a/plugins/relaxng/test/org/intellij/plugins/relaxNG/AbstractParsingTest.java +++ b/plugins/relaxng/test/org/intellij/plugins/relaxNG/AbstractParsingTest.java @@ -42,7 +42,6 @@ public abstract class AbstractParsingTest extends ParsingTestCase { new WriteAction() { protected void run(Result result) throws Throwable { new ApplicationLoader().initComponent(); - new ProjectLoader(getProject()).initComponent(); ExternalResourceManagerEx.getInstanceEx().addIgnoredResource("urn:test:undefined"); } }.execute(); From e12ed6e09b24a1971c41d9ee9e7c8bc112335e8f Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 1 Aug 2010 18:09:09 +0100 Subject: [PATCH 73/92] minor --- .../intellij/openapi/extensions/impl/ExtensionsAreaImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionsAreaImpl.java b/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionsAreaImpl.java index 8acb2052ab20..9575048d6c30 100644 --- a/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionsAreaImpl.java +++ b/platform/extensions/src/com/intellij/openapi/extensions/impl/ExtensionsAreaImpl.java @@ -128,12 +128,13 @@ public class ExtensionsAreaImpl implements ExtensionsArea { String implClass = extensionElement.getAttributeValue("implementation"); ExtensionComponentAdapter adapter; + final PicoContainer container = getPluginContainer(pluginId.getIdString()); if (implClass != null) { - adapter = new ExtensionComponentAdapter(implClass, extensionElement, getPluginContainer(pluginId.getIdString()), pluginDescriptor, shouldDeserializeInstance(extensionElement)); + adapter = new ExtensionComponentAdapter(implClass, extensionElement, container, pluginDescriptor, shouldDeserializeInstance(extensionElement)); } else { final ExtensionPoint extensionPoint = getExtensionPoint(epName); - adapter = new ExtensionComponentAdapter(extensionPoint.getBeanClassName(), extensionElement, getPluginContainer(pluginId.getIdString()), pluginDescriptor, true); + adapter = new ExtensionComponentAdapter(extensionPoint.getBeanClassName(), extensionElement, container, pluginDescriptor, true); } myExtensionElement2extension.put(extensionElement, adapter); internalGetPluginContainer().registerComponent(adapter); From b4118c8ba118b3efa219ae0594a8098114f487f0 Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 1 Aug 2010 19:15:03 +0100 Subject: [PATCH 74/92] optimized standard schemas loading: don't ask Class.getResource if we can live without that --- .../javaee/ExternalResourceManagerImpl.java | 7 +- .../javaee/ResourceRegistrarImpl.java | 70 +++++++++++++++++-- 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java b/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java index 988e98126dc5..d2ed0abeab97 100644 --- a/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java +++ b/xml/impl/src/com/intellij/javaee/ExternalResourceManagerImpl.java @@ -30,6 +30,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.xml.XmlFile; import com.intellij.util.ArrayUtil; +import com.intellij.util.containers.CollectionFactory; import com.intellij.util.containers.HashMap; import com.intellij.xml.XmlSchemaProvider; import com.intellij.xml.util.XmlUtil; @@ -102,13 +103,13 @@ public class ExternalResourceManagerImpl extends ExternalResourceManagerEx imple } @Nullable - static Map getMap(@NotNull final Map> resources, + static Map getMap(@NotNull final Map> resources, @Nullable final String version, final boolean create) { - Map map = resources.get(version); + Map map = resources.get(version); if (map == null) { if (create) { - map = new HashMap(); + map = CollectionFactory.hashMap(); resources.put(version, map); } else if (version == null || !version.equals(DEFAULT_VERSION)) { diff --git a/xml/impl/src/com/intellij/javaee/ResourceRegistrarImpl.java b/xml/impl/src/com/intellij/javaee/ResourceRegistrarImpl.java index 74d3acbbafa3..2302599c5734 100644 --- a/xml/impl/src/com/intellij/javaee/ResourceRegistrarImpl.java +++ b/xml/impl/src/com/intellij/javaee/ResourceRegistrarImpl.java @@ -18,6 +18,9 @@ package com.intellij.javaee; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.CollectionFactory; import com.intellij.util.containers.HashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; @@ -34,6 +37,7 @@ public class ResourceRegistrarImpl implements ResourceRegistrar { private final Map> myResources = new HashMap>(); private final List myIgnored = new ArrayList(); + private final Map> myCache = CollectionFactory.hashMap(); public void addStdResource(@NonNls String resource, @NonNls String fileName) { addStdResource(resource, null, fileName, getClass()); @@ -44,11 +48,12 @@ public class ResourceRegistrarImpl implements ResourceRegistrar { } public void addStdResource(@NonNls String resource, @NonNls String version, @NonNls String fileName, Class klass) { - final String file = getFile(fileName, klass); + final VirtualFile file = getFile(fileName, klass); if (file != null) { + saveCache(fileName, klass, file); final Map map = ExternalResourceManagerImpl.getMap(myResources, version, true); assert map != null; - map.put(resource, file); + map.put(resource, file.getPath()); } else { String message = "Cannot find standard resource. filename:" + fileName + " klass=" + klass; @@ -82,14 +87,71 @@ public class ResourceRegistrarImpl implements ResourceRegistrar { } @Nullable - private static String getFile(String name, Class klass) { + private VirtualFile getFile(String name, Class klass) { + VirtualFile file = findUsingCache(name, klass); + if (file != null) { + return file; + } + final URL resource = klass.getResource(name); if (resource == null) return null; String path = FileUtil.unquote(resource.toString()); // this is done by FileUtil for windows path = path.replace('\\','/'); - return path; + + return VfsUtil.findRelativeFile(path, null); + } + + @Nullable + private VirtualFile findUsingCache(String name, Class klass) { + if (!name.startsWith("/")) return null; + + final Map cache = myCache.get(klass.getClassLoader()); + if (cache == null) return null; + + int end = name.length(); + while (true) { + final int i = name.lastIndexOf('/', end); + if (i < 0) break; + + final VirtualFile existing = cache.get(name.substring(0, i)); + if (existing != null) { + final VirtualFile guess = existing.findFileByRelativePath(name.substring(i + 1)); + if (guess != null) { + return guess; + } + } + + end = i - 1; + } + + return null; + } + + private void saveCache(String name, Class klass, VirtualFile file) { + if (!name.startsWith("/")) return; + + final ClassLoader loader = klass.getClassLoader(); + Map cache = myCache.get(loader); + if (cache == null) { + myCache.put(loader, cache = CollectionFactory.hashMap()); + } + + String parent = name; + VirtualFile current = file; + while (true) { + if (current.equals(cache.get(parent))) break; + + cache.put(parent, current); + final int i = parent.lastIndexOf('/'); + if (i <= 0) break; + if (!parent.substring(i + 1).equals(current.getName())) { + break; + } + parent = parent.substring(0, i); + current = current.getParent(); + } } public Map> getResources() { From 1270c4967afc84a524cd8de96772fb9e7f75fc70 Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 1 Aug 2010 19:19:22 +0100 Subject: [PATCH 75/92] load reference contributors only once in an application --- .../source/resolve/reference/ReferenceProvidersRegistry.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistry.java b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistry.java index 8ae866c773b8..d41c92a1525f 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistry.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/ReferenceProvidersRegistry.java @@ -50,6 +50,7 @@ import java.util.concurrent.ConcurrentMap; * To change this template use Options | File Templates. */ public class ReferenceProvidersRegistry extends PsiReferenceRegistrar { + private static final PsiReferenceContributor[] ourExtensions = Extensions.getExtensions(PsiReferenceContributor.EP_NAME); private final ConcurrentMap myBindingsMap = new ConcurrentHashMap(); private final ConcurrentMap myNamedBindingsMap = new ConcurrentHashMap(); private final FactoryMap> myKnownSupers = new ConcurrentFactoryMap>() { @@ -91,7 +92,7 @@ public class ReferenceProvidersRegistry extends PsiReferenceRegistrar { public ReferenceProvidersRegistry(Project project) { myProject = project; - for (final PsiReferenceContributor contributor : Extensions.getExtensions(PsiReferenceContributor.EP_NAME)) { + for (final PsiReferenceContributor contributor : ourExtensions) { contributor.registerReferenceProviders(this); } } From f1ea5832ddc19e9b518889840c66926c471e0a1d Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 1 Aug 2010 21:12:16 +0100 Subject: [PATCH 76/92] return url, not path --- xml/impl/src/com/intellij/javaee/ResourceRegistrarImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xml/impl/src/com/intellij/javaee/ResourceRegistrarImpl.java b/xml/impl/src/com/intellij/javaee/ResourceRegistrarImpl.java index 2302599c5734..e54d1370cf91 100644 --- a/xml/impl/src/com/intellij/javaee/ResourceRegistrarImpl.java +++ b/xml/impl/src/com/intellij/javaee/ResourceRegistrarImpl.java @@ -53,7 +53,7 @@ public class ResourceRegistrarImpl implements ResourceRegistrar { saveCache(fileName, klass, file); final Map map = ExternalResourceManagerImpl.getMap(myResources, version, true); assert map != null; - map.put(resource, file.getPath()); + map.put(resource, file.getUrl()); } else { String message = "Cannot find standard resource. filename:" + fileName + " klass=" + klass; From c8554436f54b8eb7945efc18058faac537349506 Mon Sep 17 00:00:00 2001 From: "Maxim.Mossienko" Date: Mon, 2 Aug 2010 00:20:28 +0400 Subject: [PATCH 77/92] avoid SOE in tests --- .../psi/stubs/DefaultStubBuilder.java | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java b/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java index 0b2987b50243..9beea67d8bb9 100644 --- a/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java +++ b/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java @@ -21,7 +21,10 @@ package com.intellij.psi.stubs; import com.intellij.lang.ASTNode; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.psi.*; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.StubBasedPsiElement; +import com.intellij.psi.StubBuilder; import com.intellij.psi.tree.IElementType; public class DefaultStubBuilder implements StubBuilder { @@ -35,34 +38,29 @@ public class DefaultStubBuilder implements StubBuilder { return new PsiFileStubImpl(file); } - // Beware many recursive invokations of this method, the code below reuse locals and parameters to avoid SOE - protected StubElement buildStubTreeFor(PsiElement elt, StubElement parentStub) { - ASTNode node; - IElementType eltType; + protected static StubElement buildStubTreeFor(PsiElement elt, StubElement parentStub) { + StubElement stub = parentStub; if (elt instanceof StubBasedPsiElement) { - eltType = ((StubBasedPsiElement)elt).getElementType(); + final IStubElementType type = ((StubBasedPsiElement)elt).getElementType(); - if (((IStubElementType)eltType).shouldCreateStub(elt.getNode())) { + if (type.shouldCreateStub(elt.getNode())) { //noinspection unchecked - parentStub = ((IStubElementType)eltType).createStub(elt, parentStub); + stub = type.createStub(elt, parentStub); } } else { - node = elt.getNode(); - eltType = node == null? null : node.getElementType(); - if (eltType instanceof IStubElementType && ((IStubElementType)eltType).shouldCreateStub(node)) { - LOG.error("Non-StubBasedPsiElement requests stub creation. Stub type: " + eltType + ", PSI: " + elt); + final ASTNode node = elt.getNode(); + final IElementType type = node == null? null : node.getElementType(); + if (type instanceof IStubElementType && ((IStubElementType)type).shouldCreateStub(node)) { + LOG.error("Non-StubBasedPsiElement requests stub creation. Stub type: " + type + ", PSI: " + elt); } } - for (elt = elt.getFirstChild(); elt != null; elt = elt.getNextSibling()) { - node = elt.getNode(); - if (!skipChildProcessingWhenBuildingStubs(eltType, node != null ? node.getElementType():null)) { - buildStubTreeFor(elt, parentStub); - } + for (PsiElement child = elt.getFirstChild(); child != null; child = child.getNextSibling()) { + buildStubTreeFor(child, stub); // do not filter via skipChildProcessingWhenBuildingStubs here due to SOE in tests } - return parentStub; + return stub; } protected StubElement buildStubTreeFor(ASTNode node, StubElement parentStub) { From 3202bc302b6037bd73d50e0223596a01d64d0306 Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 1 Aug 2010 22:09:27 +0100 Subject: [PATCH 78/92] diagnostics --- .../intention/impl/ConvertToBasicLatinAction.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertToBasicLatinAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertToBasicLatinAction.java index 98a5bcf697aa..5c173d0c96b8 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertToBasicLatinAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertToBasicLatinAction.java @@ -49,7 +49,10 @@ public class ConvertToBasicLatinAction extends PsiElementBaseIntentionAction { @Override public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) { + System.out.println("ConvertToBasicLatinAction.isAvailable"); + System.out.println("element = " + element); final Pair pair = findHandler(element); + System.out.println("pair = " + pair); if (pair == null) return false; final String text = pair.first.getText(); @@ -74,16 +77,22 @@ public class ConvertToBasicLatinAction extends PsiElementBaseIntentionAction { @Override public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { + System.out.println("ConvertToBasicLatinAction.invoke"); PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); + System.out.println("element = " + element); if (element == null) return; final Pair pair = findHandler(element); + System.out.println("pair = " + pair); if (pair == null) return; element = pair.first; final Handler handler = pair.second; + System.out.println("handler = " + handler); final String newText = handler.processText(element); + System.out.println("newText = " + newText); final PsiElement newElement = handler.createReplacement(element, newText); + System.out.println("newElement = " + newElement); element.replace(newElement); } From 0d22ed9147bb9a69bf8ca41af2d1957dd73cac2d Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 1 Aug 2010 22:46:57 +0100 Subject: [PATCH 79/92] slashes to blame? --- .../com/intellij/javaee/ResourceRegistrarImpl.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/xml/impl/src/com/intellij/javaee/ResourceRegistrarImpl.java b/xml/impl/src/com/intellij/javaee/ResourceRegistrarImpl.java index e54d1370cf91..b306e1d0f27f 100644 --- a/xml/impl/src/com/intellij/javaee/ResourceRegistrarImpl.java +++ b/xml/impl/src/com/intellij/javaee/ResourceRegistrarImpl.java @@ -18,7 +18,6 @@ package com.intellij.javaee; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.VfsUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.containers.CollectionFactory; import com.intellij.util.containers.HashMap; @@ -48,12 +47,12 @@ public class ResourceRegistrarImpl implements ResourceRegistrar { } public void addStdResource(@NonNls String resource, @NonNls String version, @NonNls String fileName, Class klass) { - final VirtualFile file = getFile(fileName, klass); + final String file = getFile(fileName, klass); if (file != null) { - saveCache(fileName, klass, file); + //saveCache(fileName, klass, file); final Map map = ExternalResourceManagerImpl.getMap(myResources, version, true); assert map != null; - map.put(resource, file.getUrl()); + map.put(resource, file); } else { String message = "Cannot find standard resource. filename:" + fileName + " klass=" + klass; @@ -87,11 +86,13 @@ public class ResourceRegistrarImpl implements ResourceRegistrar { } @Nullable - private VirtualFile getFile(String name, Class klass) { + private String getFile(String name, Class klass) { + /* VirtualFile file = findUsingCache(name, klass); if (file != null) { return file; } + */ final URL resource = klass.getResource(name); if (resource == null) return null; @@ -100,7 +101,7 @@ public class ResourceRegistrarImpl implements ResourceRegistrar { // this is done by FileUtil for windows path = path.replace('\\','/'); - return VfsUtil.findRelativeFile(path, null); + return path;//VfsUtil.findRelativeFile(path, null); } @Nullable From be14a81dc0e8887a34cf149854bba6293eef9cb3 Mon Sep 17 00:00:00 2001 From: peter Date: Sun, 1 Aug 2010 22:47:40 +0100 Subject: [PATCH 80/92] remove diagnostics --- .../intention/impl/ConvertToBasicLatinAction.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertToBasicLatinAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertToBasicLatinAction.java index 5c173d0c96b8..98a5bcf697aa 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertToBasicLatinAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/ConvertToBasicLatinAction.java @@ -49,10 +49,7 @@ public class ConvertToBasicLatinAction extends PsiElementBaseIntentionAction { @Override public boolean isAvailable(@NotNull final Project project, final Editor editor, @NotNull final PsiElement element) { - System.out.println("ConvertToBasicLatinAction.isAvailable"); - System.out.println("element = " + element); final Pair pair = findHandler(element); - System.out.println("pair = " + pair); if (pair == null) return false; final String text = pair.first.getText(); @@ -77,22 +74,16 @@ public class ConvertToBasicLatinAction extends PsiElementBaseIntentionAction { @Override public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { - System.out.println("ConvertToBasicLatinAction.invoke"); PsiElement element = file.findElementAt(editor.getCaretModel().getOffset()); - System.out.println("element = " + element); if (element == null) return; final Pair pair = findHandler(element); - System.out.println("pair = " + pair); if (pair == null) return; element = pair.first; final Handler handler = pair.second; - System.out.println("handler = " + handler); final String newText = handler.processText(element); - System.out.println("newText = " + newText); final PsiElement newElement = handler.createReplacement(element, newText); - System.out.println("newElement = " + newElement); element.replace(newElement); } From 6326b9aa33dd1eacfecfa9980987216226ba88c7 Mon Sep 17 00:00:00 2001 From: anna Date: Wed, 28 Jul 2010 13:05:09 +0400 Subject: [PATCH 81/92] skip local inspections for big files --- .../intellij/codeInspection/ex/GlobalInspectionContextImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java index e8573862ecd3..934410bb9ca5 100644 --- a/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInspection/ex/GlobalInspectionContextImpl.java @@ -515,6 +515,7 @@ public class GlobalInspectionContextImpl implements GlobalInspectionContext { final VirtualFile virtualFile = file.getVirtualFile(); if (virtualFile != null) { incrementJobDoneAmount(LOCAL_ANALYSIS, ProjectUtil.calcRelativeToProjectPath(virtualFile, myProject)); + if (SingleRootFileViewProvider.isTooLarge(virtualFile)) return; } final FileViewProvider viewProvider = psiManager.findViewProvider(virtualFile); From 59d92c6f4241fc133cb802987a59599c626892d9 Mon Sep 17 00:00:00 2001 From: anna Date: Wed, 28 Jul 2010 15:56:29 +0400 Subject: [PATCH 82/92] move classes and packages in one move --- .../MoveClassesHandler.java | 74 ------ .../MoveClassesOrPackagesHandlerBase.java | 249 +++++++++++++++++- .../MovePackagesHandler.java | 220 ---------------- .../moveInner/MoveInnerToUpperHandler.java | 18 +- .../MoveInnerToUpperOrMembersHandler.java | 10 +- .../MoveInstanceMethodHandlerDelegate.java | 2 +- .../move/moveMembers/MoveMembersHandler.java | 2 +- .../impl/MoveDropTargetListener.java | 10 +- .../refactoring/move/MoveHandler.java | 4 +- .../refactoring/move/MoveHandlerDelegate.java | 4 +- .../MoveFilesOrDirectoriesHandler.java | 2 +- .../move/MoveGroovyScriptHandler.java | 2 +- resources/src/META-INF/IdeaPlugin.xml | 3 +- 13 files changed, 272 insertions(+), 328 deletions(-) delete mode 100644 java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesHandler.java delete mode 100644 java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MovePackagesHandler.java diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesHandler.java b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesHandler.java deleted file mode 100644 index f8520402ae25..000000000000 --- a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesHandler.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2000-2009 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.refactoring.move.moveClassesOrPackages; - -import com.intellij.codeInsight.daemon.impl.CollectHighlightsUtil; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.LangDataKeys; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.psi.*; -import com.intellij.psi.impl.source.jsp.jspJava.JspClass; -import org.jetbrains.annotations.Nullable; - -public class MoveClassesHandler extends MoveClassesOrPackagesHandlerBase { - public boolean canMove(final PsiElement[] elements, @Nullable final PsiElement targetContainer) { - for(PsiElement element: elements) { - PsiFile parentFile; - if (element instanceof PsiJavaFile) { - final PsiClass[] classes = ((PsiJavaFile)element).getClasses(); - if (classes.length == 0) return false; - for (PsiClass aClass : classes) { - if (aClass instanceof JspClass) return false; - } - parentFile = (PsiFile)element; - } else { - if (element instanceof JspClass) return false; - if (!(element instanceof PsiClass)) return false; - if (!(element.getParent() instanceof PsiFile)) return false; - parentFile = (PsiFile)element.getParent(); - } - if (CollectHighlightsUtil.isOutsideSourceRootJavaFile(parentFile)) return false; - } - return super.canMove(elements, targetContainer); - } - - public boolean isValidTarget(final PsiElement psiElement) { - return psiElement instanceof PsiClass || - MovePackagesHandler.isPackageOrDirectory(psiElement); - } - - public boolean tryToMove(final PsiElement element, final Project project, final DataContext dataContext, final PsiReference reference, - final Editor editor) { - if (CollectHighlightsUtil.isOutsideSourceRootJavaFile(element.getContainingFile())) return false; - if (isReferenceInAnonymousClass(reference)) return false; - - if (element instanceof PsiClass && !(element instanceof PsiAnonymousClass) && element.getParent() instanceof PsiFile) { - MoveClassesOrPackagesImpl.doMove(project, new PsiElement[]{element}, - LangDataKeys.TARGET_PSI_ELEMENT.getData(dataContext), null); - return true; - } - return false; - } - - public static boolean isReferenceInAnonymousClass(@Nullable final PsiReference reference) { - if (reference instanceof PsiJavaCodeReferenceElement && - ((PsiJavaCodeReferenceElement)reference).getParent() instanceof PsiAnonymousClass) { - return true; - } - return false; - } -} diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesHandlerBase.java b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesHandlerBase.java index fce85569fe92..59505843c02c 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesHandlerBase.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesHandlerBase.java @@ -15,28 +15,127 @@ */ package com.intellij.refactoring.move.moveClassesOrPackages; +import com.intellij.codeInsight.daemon.impl.CollectHighlightsUtil; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.actionSystem.LangDataKeys; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.roots.ProjectRootManager; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.*; +import com.intellij.psi.impl.source.jsp.jspJava.JspClass; +import com.intellij.refactoring.JavaRefactoringSettings; +import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.move.MoveHandlerDelegate; import com.intellij.refactoring.move.MoveCallback; import com.intellij.refactoring.util.CommonRefactoringUtil; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPackage; -import com.intellij.psi.PsiDirectory; -import com.intellij.psi.JavaDirectoryService; import com.intellij.openapi.project.Project; +import com.intellij.refactoring.util.RadioUpDownListener; +import com.intellij.refactoring.util.RefactoringUtil; +import com.intellij.util.containers.HashSet; +import org.jetbrains.annotations.Nullable; +import javax.swing.*; +import java.awt.*; import java.util.Arrays; -public abstract class MoveClassesOrPackagesHandlerBase extends MoveHandlerDelegate { +public class MoveClassesOrPackagesHandlerBase extends MoveHandlerDelegate { + private static final Logger LOG = Logger.getInstance("#" + MoveClassesOrPackagesHandlerBase.class.getName()); protected static boolean isPackageOrDirectory(final PsiElement element) { if (element instanceof PsiPackage) return true; return element instanceof PsiDirectory && JavaDirectoryService.getInstance().getPackage((PsiDirectory)element) != null; } + public static boolean isReferenceInAnonymousClass(@Nullable final PsiReference reference) { + if (reference instanceof PsiJavaCodeReferenceElement && + ((PsiJavaCodeReferenceElement)reference).getParent() instanceof PsiAnonymousClass) { + return true; + } + return false; + } + + @Override + public boolean canMove(PsiElement[] elements, @Nullable PsiElement targetContainer) { + for (PsiElement element : elements) { + if (!isPackageOrDirectory(element)) { + PsiFile parentFile; + if (element instanceof PsiJavaFile) { + final PsiClass[] classes = ((PsiJavaFile)element).getClasses(); + if (classes.length == 0) return false; + for (PsiClass aClass : classes) { + if (aClass instanceof JspClass) return false; + } + parentFile = (PsiFile)element; + } + else { + if (element instanceof JspClass) return false; + if (!(element instanceof PsiClass)) return false; + if (!(element.getParent() instanceof PsiFile)) return false; + parentFile = (PsiFile)element.getParent(); + } + if (CollectHighlightsUtil.isOutsideSourceRootJavaFile(parentFile)) return false; + } + } + return super.canMove(elements, targetContainer); + } + + @Override + public boolean isValidTarget(PsiElement psiElement, PsiElement[] sources) { + if (isPackageOrDirectory(psiElement)) return true; + boolean hasPackageOrDir = false; + for (PsiElement source : sources) { + hasPackageOrDir |= isPackageOrDirectory(source); + } + return !hasPackageOrDir && psiElement instanceof PsiClass; + } + public PsiElement[] adjustForMove(final Project project, final PsiElement[] sourceElements, final PsiElement targetElement) { return MoveClassesOrPackagesImpl.adjustForMove(project,sourceElements, targetElement); } public void doMove(final Project project, final PsiElement[] elements, final PsiElement targetContainer, final MoveCallback callback) { + if (canMoveOrRearrangePackages(elements) ) { + final PsiDirectory[] directories = new PsiDirectory[elements.length]; + System.arraycopy(elements, 0, directories, 0, directories.length); + SelectMoveOrRearrangePackageDialog dialog = new SelectMoveOrRearrangePackageDialog(project, directories, targetContainer == null); + dialog.show(); + if (!dialog.isOK()) return; + + if (dialog.isPackageRearrageSelected()) { + MoveClassesOrPackagesImpl.doRearrangePackage(project, directories); + return; + } + + if (dialog.isMoveDirectory()) { + if (targetContainer instanceof PsiDirectory) { + final JavaRefactoringSettings refactoringSettings = JavaRefactoringSettings.getInstance(); + final MoveDirectoryWithClassesProcessor processor = + new MoveDirectoryWithClassesProcessor(project, directories, (PsiDirectory)targetContainer, + refactoringSettings.RENAME_SEARCH_IN_COMMENTS_FOR_PACKAGE, + refactoringSettings.RENAME_SEARCH_IN_COMMENTS_FOR_PACKAGE, true, callback); + processor.run(); + } + else { + final MoveClassesOrPackagesToNewDirectoryDialog dlg = + new MoveClassesOrPackagesToNewDirectoryDialog(directories[0], new PsiElement[2], false, callback) { + @Override + protected void performRefactoring(Project project, + final PsiDirectory targetDirectory, + PsiPackage aPackage, + boolean searchInComments, + boolean searchForTextOccurences) { + final MoveDirectoryWithClassesProcessor processor = + new MoveDirectoryWithClassesProcessor(project, directories, targetDirectory, searchInComments, searchForTextOccurences, + true, callback); + processor.run(); + } + }; + dlg.show(); + } + return; + } + } if (tryDirectoryMove ( project, elements, targetContainer, callback)) { return; } @@ -55,4 +154,144 @@ public abstract class MoveClassesOrPackagesHandlerBase extends MoveHandlerDelega } return false; } + + private static boolean canMoveOrRearrangePackages(PsiElement[] elements) { + if (elements.length == 0) return false; + final Project project = elements[0].getProject(); + if (ProjectRootManager.getInstance(project).getContentSourceRoots().length == 1) { + return false; + } + for (PsiElement element : elements) { + if (!(element instanceof PsiDirectory)) return false; + final PsiDirectory directory = ((PsiDirectory)element); + if (RefactoringUtil.isSourceRoot(directory)) { + return false; + } + final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(directory); + if (aPackage == null) return false; + if ("".equals(aPackage.getQualifiedName())) return false; + final VirtualFile sourceRootForFile = ProjectRootManager.getInstance(element.getProject()).getFileIndex() + .getSourceRootForFile(directory.getVirtualFile()); + if (sourceRootForFile == null) return false; + } + return true; + } + + private static class SelectMoveOrRearrangePackageDialog extends DialogWrapper { + private JRadioButton myRbMovePackage; + private JRadioButton myRbRearrangePackage; + private JRadioButton myRbMoveDirectory; + + private final PsiDirectory[] myDirectories; + private final boolean myRearrangePackagesEnabled; + + public SelectMoveOrRearrangePackageDialog(Project project, PsiDirectory[] directories) { + this(project, directories, true); + } + + public SelectMoveOrRearrangePackageDialog(Project project, PsiDirectory[] directories, boolean rearrangePackagesEnabled) { + super(project, true); + myDirectories = directories; + myRearrangePackagesEnabled = rearrangePackagesEnabled; + setTitle(RefactoringBundle.message("select.refactoring.title")); + init(); + } + + protected JComponent createNorthPanel() { + return new JLabel(RefactoringBundle.message("what.would.you.like.to.do")); + } + + public JComponent getPreferredFocusedComponent() { + return myRbMovePackage; + } + + protected String getDimensionServiceKey() { + return "#com.intellij.refactoring.move.MoveHandler.SelectRefactoringDialog"; + } + + + protected JComponent createCenterPanel() { + JPanel panel = new JPanel(new BorderLayout()); + + + final HashSet packages = new HashSet(); + for (PsiDirectory directory : myDirectories) { + packages.add(JavaDirectoryService.getInstance().getPackage(directory).getQualifiedName()); + } + final String moveDescription; + LOG.assertTrue(myDirectories.length > 0); + LOG.assertTrue(packages.size() > 0); + if (packages.size() > 1) { + moveDescription = RefactoringBundle.message("move.packages.to.another.package", packages.size()); + } + else { + final String qName = packages.iterator().next(); + moveDescription = RefactoringBundle.message("move.package.to.another.package", qName); + } + + myRbMovePackage = new JRadioButton(); + myRbMovePackage.setText(moveDescription); + myRbMovePackage.setSelected(true); + + final String rearrangeDescription; + if (myDirectories.length > 1) { + rearrangeDescription = RefactoringBundle.message("move.directories.to.another.source.root", myDirectories.length); + } + else { + rearrangeDescription = RefactoringBundle.message("move.directory.to.another.source.root", myDirectories[0].getVirtualFile().getPresentableUrl()); + } + myRbRearrangePackage = new JRadioButton(); + myRbRearrangePackage.setText(rearrangeDescription); + myRbRearrangePackage.setVisible(myRearrangePackagesEnabled); + + final String moveDirectoryDescription; + if (myDirectories.length > 1) { + moveDirectoryDescription = "Move everything from " + myDirectories.length + " directories to another directory"; + } + else { + moveDirectoryDescription = "Move everything from " + myDirectories[0].getVirtualFile().getPresentableUrl() + " to another directory"; + } + myRbMoveDirectory = new JRadioButton(); + myRbMoveDirectory.setMnemonic('d'); + myRbMoveDirectory.setText(moveDirectoryDescription); + + ButtonGroup gr = new ButtonGroup(); + gr.add(myRbMovePackage); + gr.add(myRbRearrangePackage); + gr.add(myRbMoveDirectory); + + new RadioUpDownListener(myRbMovePackage, myRbRearrangePackage, myRbMoveDirectory); + + Box box = Box.createVerticalBox(); + box.add(Box.createVerticalStrut(5)); + box.add(myRbMovePackage); + box.add(myRbRearrangePackage); + box.add(myRbMoveDirectory); + panel.add(box, BorderLayout.CENTER); + return panel; + } + + public boolean isPackageRearrageSelected() { + return myRbRearrangePackage.isSelected(); + } + + public boolean isMoveDirectory() { + return myRbMoveDirectory.isSelected(); + } + } + + + public boolean tryToMove(final PsiElement element, final Project project, final DataContext dataContext, final PsiReference reference, + final Editor editor) { + if (isPackageOrDirectory(element)) return false; + if (CollectHighlightsUtil.isOutsideSourceRootJavaFile(element.getContainingFile())) return false; + if (isReferenceInAnonymousClass(reference)) return false; + + if (element instanceof PsiClass && !(element instanceof PsiAnonymousClass) && element.getParent() instanceof PsiFile) { + MoveClassesOrPackagesImpl.doMove(project, new PsiElement[]{element}, + LangDataKeys.TARGET_PSI_ELEMENT.getData(dataContext), null); + return true; + } + return false; + } } diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MovePackagesHandler.java b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MovePackagesHandler.java deleted file mode 100644 index 388ba520fda2..000000000000 --- a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MovePackagesHandler.java +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright 2000-2009 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.refactoring.move.moveClassesOrPackages; - -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.roots.ProjectRootManager; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.JavaDirectoryService; -import com.intellij.psi.PsiDirectory; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiPackage; -import com.intellij.refactoring.JavaRefactoringSettings; -import com.intellij.refactoring.RefactoringBundle; -import com.intellij.refactoring.move.MoveCallback; -import com.intellij.refactoring.util.RadioUpDownListener; -import com.intellij.refactoring.util.RefactoringUtil; -import com.intellij.util.containers.HashSet; - -import javax.swing.*; -import java.awt.*; - -public class MovePackagesHandler extends MoveClassesOrPackagesHandlerBase { - private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.move.moveClassesOrPackages.MovePackagesHandler"); - - public boolean canMove(final PsiElement[] elements, final PsiElement targetContainer) { - for(PsiElement element: elements) { - if (!isPackageOrDirectory(element)) return false; - } - return super.canMove(elements, targetContainer); - } - - public boolean isValidTarget(final PsiElement psiElement) { - return isPackageOrDirectory(psiElement); - } - - public void doMove(final Project project, final PsiElement[] elements, final PsiElement targetContainer, final MoveCallback callback) { - if (canMoveOrRearrangePackages(elements) ) { - final PsiDirectory[] directories = new PsiDirectory[elements.length]; - System.arraycopy(elements, 0, directories, 0, directories.length); - SelectMoveOrRearrangePackageDialog dialog = new SelectMoveOrRearrangePackageDialog(project, directories, targetContainer == null); - dialog.show(); - if (!dialog.isOK()) return; - - if (dialog.isPackageRearrageSelected()) { - MoveClassesOrPackagesImpl.doRearrangePackage(project, directories); - return; - } - - if (dialog.isMoveDirectory()) { - if (targetContainer instanceof PsiDirectory) { - final JavaRefactoringSettings refactoringSettings = JavaRefactoringSettings.getInstance(); - final MoveDirectoryWithClassesProcessor processor = - new MoveDirectoryWithClassesProcessor(project, directories, (PsiDirectory)targetContainer, - refactoringSettings.RENAME_SEARCH_IN_COMMENTS_FOR_PACKAGE, - refactoringSettings.RENAME_SEARCH_IN_COMMENTS_FOR_PACKAGE, true, callback); - processor.run(); - } - else { - final MoveClassesOrPackagesToNewDirectoryDialog dlg = - new MoveClassesOrPackagesToNewDirectoryDialog(directories[0], new PsiElement[2], false, callback) { - @Override - protected void performRefactoring(Project project, - final PsiDirectory targetDirectory, - PsiPackage aPackage, - boolean searchInComments, - boolean searchForTextOccurences) { - final MoveDirectoryWithClassesProcessor processor = - new MoveDirectoryWithClassesProcessor(project, directories, targetDirectory, searchInComments, searchForTextOccurences, - true, callback); - processor.run(); - } - }; - dlg.show(); - } - return; - } - } - super.doMove(project, elements, targetContainer, callback); - } - - private static boolean canMoveOrRearrangePackages(PsiElement[] elements) { - if (elements.length == 0) return false; - final Project project = elements[0].getProject(); - if (ProjectRootManager.getInstance(project).getContentSourceRoots().length == 1) { - return false; - } - for (PsiElement element : elements) { - if (!(element instanceof PsiDirectory)) return false; - final PsiDirectory directory = ((PsiDirectory)element); - if (RefactoringUtil.isSourceRoot(directory)) { - return false; - } - final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(directory); - if (aPackage == null) return false; - if ("".equals(aPackage.getQualifiedName())) return false; - final VirtualFile sourceRootForFile = ProjectRootManager.getInstance(element.getProject()).getFileIndex() - .getSourceRootForFile(directory.getVirtualFile()); - if (sourceRootForFile == null) return false; - } - return true; - } - - private static class SelectMoveOrRearrangePackageDialog extends DialogWrapper { - private JRadioButton myRbMovePackage; - private JRadioButton myRbRearrangePackage; - private JRadioButton myRbMoveDirectory; - - private final PsiDirectory[] myDirectories; - private final boolean myRearrangePackagesEnabled; - - public SelectMoveOrRearrangePackageDialog(Project project, PsiDirectory[] directories) { - this(project, directories, true); - } - - public SelectMoveOrRearrangePackageDialog(Project project, PsiDirectory[] directories, boolean rearrangePackagesEnabled) { - super(project, true); - myDirectories = directories; - myRearrangePackagesEnabled = rearrangePackagesEnabled; - setTitle(RefactoringBundle.message("select.refactoring.title")); - init(); - } - - protected JComponent createNorthPanel() { - return new JLabel(RefactoringBundle.message("what.would.you.like.to.do")); - } - - public JComponent getPreferredFocusedComponent() { - return myRbMovePackage; - } - - protected String getDimensionServiceKey() { - return "#com.intellij.refactoring.move.MoveHandler.SelectRefactoringDialog"; - } - - - protected JComponent createCenterPanel() { - JPanel panel = new JPanel(new BorderLayout()); - - - final HashSet packages = new HashSet(); - for (PsiDirectory directory : myDirectories) { - packages.add(JavaDirectoryService.getInstance().getPackage(directory).getQualifiedName()); - } - final String moveDescription; - LOG.assertTrue(myDirectories.length > 0); - LOG.assertTrue(packages.size() > 0); - if (packages.size() > 1) { - moveDescription = RefactoringBundle.message("move.packages.to.another.package", packages.size()); - } - else { - final String qName = packages.iterator().next(); - moveDescription = RefactoringBundle.message("move.package.to.another.package", qName); - } - - myRbMovePackage = new JRadioButton(); - myRbMovePackage.setText(moveDescription); - myRbMovePackage.setSelected(true); - - final String rearrangeDescription; - if (myDirectories.length > 1) { - rearrangeDescription = RefactoringBundle.message("move.directories.to.another.source.root", myDirectories.length); - } - else { - rearrangeDescription = RefactoringBundle.message("move.directory.to.another.source.root", myDirectories[0].getVirtualFile().getPresentableUrl()); - } - myRbRearrangePackage = new JRadioButton(); - myRbRearrangePackage.setText(rearrangeDescription); - myRbRearrangePackage.setVisible(myRearrangePackagesEnabled); - - final String moveDirectoryDescription; - if (myDirectories.length > 1) { - moveDirectoryDescription = "Move everything from " + myDirectories.length + " directories to another directory"; - } - else { - moveDirectoryDescription = "Move everything from " + myDirectories[0].getVirtualFile().getPresentableUrl() + " to another directory"; - } - myRbMoveDirectory = new JRadioButton(); - myRbMoveDirectory.setMnemonic('d'); - myRbMoveDirectory.setText(moveDirectoryDescription); - - ButtonGroup gr = new ButtonGroup(); - gr.add(myRbMovePackage); - gr.add(myRbRearrangePackage); - gr.add(myRbMoveDirectory); - - new RadioUpDownListener(myRbMovePackage, myRbRearrangePackage, myRbMoveDirectory); - - Box box = Box.createVerticalBox(); - box.add(Box.createVerticalStrut(5)); - box.add(myRbMovePackage); - box.add(myRbRearrangePackage); - box.add(myRbMoveDirectory); - panel.add(box, BorderLayout.CENTER); - return panel; - } - - public boolean isPackageRearrageSelected() { - return myRbRearrangePackage.isSelected(); - } - - public boolean isMoveDirectory() { - return myRbMoveDirectory.isSelected(); - } - } -} diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveInner/MoveInnerToUpperHandler.java b/java/java-impl/src/com/intellij/refactoring/move/moveInner/MoveInnerToUpperHandler.java index 01a35e0b7a7a..60ecb96b7087 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveInner/MoveInnerToUpperHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveInner/MoveInnerToUpperHandler.java @@ -15,20 +15,20 @@ */ package com.intellij.refactoring.move.moveInner; +import com.intellij.featureStatistics.FeatureUsageTracker; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiModifier; import com.intellij.psi.PsiReference; import com.intellij.psi.impl.source.jsp.jspJava.JspClass; -import com.intellij.refactoring.move.MoveHandlerDelegate; -import com.intellij.refactoring.move.MoveCallback; -import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesHandler; -import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.RefactoringBundle; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.editor.Editor; -import com.intellij.featureStatistics.FeatureUsageTracker; +import com.intellij.refactoring.move.MoveCallback; +import com.intellij.refactoring.move.MoveHandlerDelegate; +import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesHandlerBase; +import com.intellij.refactoring.util.CommonRefactoringUtil; import org.jetbrains.annotations.Nullable; public class MoveInnerToUpperHandler extends MoveHandlerDelegate { @@ -51,7 +51,7 @@ public class MoveInnerToUpperHandler extends MoveHandlerDelegate { public boolean tryToMove(final PsiElement element, final Project project, final DataContext dataContext, final PsiReference reference, final Editor editor) { - if (isNonStaticInnerClass(element) && !MoveClassesHandler.isReferenceInAnonymousClass(reference)) { + if (isNonStaticInnerClass(element) && !MoveClassesOrPackagesHandlerBase.isReferenceInAnonymousClass(reference)) { PsiClass aClass = (PsiClass) element; FeatureUsageTracker.getInstance().triggerFeatureUsed("refactoring.move.moveInner"); final PsiClass containingClass = aClass.getContainingClass(); diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveInner/MoveInnerToUpperOrMembersHandler.java b/java/java-impl/src/com/intellij/refactoring/move/moveInner/MoveInnerToUpperOrMembersHandler.java index 17ced69240b0..1eac058c11cd 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveInner/MoveInnerToUpperOrMembersHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveInner/MoveInnerToUpperOrMembersHandler.java @@ -15,10 +15,11 @@ */ package com.intellij.refactoring.move.moveInner; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.featureStatistics.FeatureUsageTracker; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiModifier; @@ -26,10 +27,9 @@ import com.intellij.psi.PsiReference; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.move.MoveCallback; import com.intellij.refactoring.move.MoveHandlerDelegate; -import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesHandler; +import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesHandlerBase; import com.intellij.refactoring.move.moveMembers.MoveMembersHandler; import com.intellij.refactoring.util.RadioUpDownListener; -import com.intellij.featureStatistics.FeatureUsageTracker; import org.jetbrains.annotations.Nullable; import javax.swing.*; @@ -62,7 +62,7 @@ public class MoveInnerToUpperOrMembersHandler extends MoveHandlerDelegate { public boolean tryToMove(final PsiElement element, final Project project, final DataContext dataContext, final PsiReference reference, final Editor editor) { - if (isStaticInnerClass(element) && !MoveClassesHandler.isReferenceInAnonymousClass(reference)) { + if (isStaticInnerClass(element) && !MoveClassesOrPackagesHandlerBase.isReferenceInAnonymousClass(reference)) { FeatureUsageTracker.getInstance().triggerFeatureUsed("refactoring.move.moveInner"); PsiClass aClass = (PsiClass) element; SelectInnerOrMembersRefactoringDialog dialog = new SelectInnerOrMembersRefactoringDialog(aClass, project); diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveInstanceMethod/MoveInstanceMethodHandlerDelegate.java b/java/java-impl/src/com/intellij/refactoring/move/moveInstanceMethod/MoveInstanceMethodHandlerDelegate.java index bdfb83429f51..cc59c77209ee 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveInstanceMethod/MoveInstanceMethodHandlerDelegate.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveInstanceMethod/MoveInstanceMethodHandlerDelegate.java @@ -35,7 +35,7 @@ public class MoveInstanceMethodHandlerDelegate extends MoveHandlerDelegate { return super.canMove(elements, targetContainer); } - public boolean isValidTarget(final PsiElement psiElement) { + public boolean isValidTarget(final PsiElement psiElement, PsiElement[] sources) { return psiElement instanceof PsiClass && !(psiElement instanceof PsiAnonymousClass); } diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveMembers/MoveMembersHandler.java b/java/java-impl/src/com/intellij/refactoring/move/moveMembers/MoveMembersHandler.java index 70751c3988d2..138055ef8dbb 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveMembers/MoveMembersHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveMembers/MoveMembersHandler.java @@ -32,7 +32,7 @@ public class MoveMembersHandler extends MoveHandlerDelegate { return super.canMove(elements, targetContainer); } - public boolean isValidTarget(final PsiElement psiElement) { + public boolean isValidTarget(final PsiElement psiElement, PsiElement[] sources) { return psiElement instanceof PsiClass && !(psiElement instanceof PsiAnonymousClass); } diff --git a/platform/lang-impl/src/com/intellij/ide/projectView/impl/MoveDropTargetListener.java b/platform/lang-impl/src/com/intellij/ide/projectView/impl/MoveDropTargetListener.java index dda8641229a5..5d7cdacbe814 100644 --- a/platform/lang-impl/src/com/intellij/ide/projectView/impl/MoveDropTargetListener.java +++ b/platform/lang-impl/src/com/intellij/ide/projectView/impl/MoveDropTargetListener.java @@ -188,7 +188,7 @@ class MoveDropTargetListener implements DropTargetListener { if (dropHandler.isValidTarget(sourceNodes, currentNode)) { return currentNode; } - if (!dropHandler.shouldDelegateToParent(currentNode)) return null; + if (!dropHandler.shouldDelegateToParent(sourceNodes, currentNode)) return null; currentNode = currentNode.getParent(); if (currentNode == null) return null; } @@ -214,7 +214,7 @@ class MoveDropTargetListener implements DropTargetListener { boolean isValidTarget(@NotNull TreeNode[] sourceNodes, @NotNull TreeNode targetNode); - boolean shouldDelegateToParent(@NotNull TreeNode targetNode); + boolean shouldDelegateToParent(TreeNode[] sourceNodes, @NotNull TreeNode targetNode); boolean isDropRedundant(@NotNull TreeNode sourceNode, @NotNull TreeNode targetNode); @@ -335,9 +335,9 @@ class MoveDropTargetListener implements DropTargetListener { return sourceNode.getParent() == targetNode; } - public boolean shouldDelegateToParent(@NotNull final TreeNode targetNode) { + public boolean shouldDelegateToParent(TreeNode[] sourceNodes, @NotNull final TreeNode targetNode) { final PsiElement psiElement = getPsiElement(targetNode); - return !MoveHandler.isValidTarget(psiElement); + return !MoveHandler.isValidTarget(psiElement, getPsiElements(sourceNodes)); } public void doDropFiles(List fileList, TreeNode targetNode) { @@ -377,7 +377,7 @@ class MoveDropTargetListener implements DropTargetListener { return false; } - public boolean shouldDelegateToParent(@NotNull final TreeNode targetNode) { + public boolean shouldDelegateToParent(TreeNode[] sourceNodes, @NotNull final TreeNode targetNode) { final PsiElement psiElement = getPsiElement(targetNode); return psiElement == null || (!(psiElement instanceof PsiDirectoryContainer) && !(psiElement instanceof PsiDirectory)); } diff --git a/platform/lang-impl/src/com/intellij/refactoring/move/MoveHandler.java b/platform/lang-impl/src/com/intellij/refactoring/move/MoveHandler.java index d78608c0d5ba..17c82469ca5d 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/move/MoveHandler.java +++ b/platform/lang-impl/src/com/intellij/refactoring/move/MoveHandler.java @@ -128,10 +128,10 @@ public class MoveHandler implements RefactoringActionHandler { return false; } - public static boolean isValidTarget(final PsiElement psiElement) { + public static boolean isValidTarget(final PsiElement psiElement, PsiElement[] elements) { if (psiElement != null) { for(MoveHandlerDelegate delegate: Extensions.getExtensions(MoveHandlerDelegate.EP_NAME)) { - if (delegate.isValidTarget(psiElement)) return true; + if (delegate.isValidTarget(psiElement, elements)) return true; } } diff --git a/platform/lang-impl/src/com/intellij/refactoring/move/MoveHandlerDelegate.java b/platform/lang-impl/src/com/intellij/refactoring/move/MoveHandlerDelegate.java index 09f60a7acf2b..4ff2fbfe7d20 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/move/MoveHandlerDelegate.java +++ b/platform/lang-impl/src/com/intellij/refactoring/move/MoveHandlerDelegate.java @@ -31,10 +31,10 @@ public abstract class MoveHandlerDelegate { public static final ExtensionPointName EP_NAME = ExtensionPointName.create("com.intellij.refactoring.moveHandler"); public boolean canMove(PsiElement[] elements, @Nullable final PsiElement targetContainer) { - return targetContainer == null || isValidTarget(targetContainer); + return targetContainer == null || isValidTarget(targetContainer, elements); } - public boolean isValidTarget(final PsiElement psiElement) { + public boolean isValidTarget(final PsiElement psiElement, PsiElement[] sources) { return false; } diff --git a/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesHandler.java b/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesHandler.java index b15a75bdd130..64bcf2a661d8 100644 --- a/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesHandler.java +++ b/platform/lang-impl/src/com/intellij/refactoring/move/moveFilesOrDirectories/MoveFilesOrDirectoriesHandler.java @@ -55,7 +55,7 @@ public class MoveFilesOrDirectoriesHandler extends MoveHandlerDelegate { return super.canMove(elements, targetContainer); } - public boolean isValidTarget(final PsiElement psiElement) { + public boolean isValidTarget(final PsiElement psiElement, PsiElement[] sources) { return psiElement instanceof PsiDirectory || psiElement instanceof PsiDirectoryContainer; } diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/move/MoveGroovyScriptHandler.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/move/MoveGroovyScriptHandler.java index 021866bfd065..0ae540a5596e 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/move/MoveGroovyScriptHandler.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/refactoring/move/MoveGroovyScriptHandler.java @@ -72,7 +72,7 @@ public class MoveGroovyScriptHandler extends MoveClassesOrPackagesHandlerBase { } @Override - public boolean isValidTarget(PsiElement psiElement) { + public boolean isValidTarget(PsiElement psiElement, PsiElement[] sources) { return isPackageOrDirectory(psiElement); } diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 7e04947e4239..6bef89755ffa 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -970,8 +970,7 @@ - - + From a09ddeb79236bc3b59ed856b701fa2ab613bcdd5 Mon Sep 17 00:00:00 2001 From: anna Date: Wed, 28 Jul 2010 17:10:17 +0400 Subject: [PATCH 83/92] move: leave classes in one file if they were together --- .../MoveJavaClassHandler.java | 43 ++++++++++--------- .../after/src1/target/pack1/S1.java | 6 ++- .../before/src1/pack1/S1.java | 4 ++ 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveJavaClassHandler.java b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveJavaClassHandler.java index 60c548626a68..585544210885 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveJavaClassHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveJavaClassHandler.java @@ -37,29 +37,30 @@ public class MoveJavaClassHandler implements MoveClassHandler { final PsiPackage newPackage = JavaDirectoryService.getInstance().getPackage(moveDestination); PsiClass newClass = null; - if (file instanceof PsiJavaFile && ((PsiJavaFile)file).getClasses().length > 1) { - correctSelfReferences(aClass, newPackage); - final PsiClass created = JavaDirectoryService.getInstance().createClass(moveDestination, aClass.getName()); - if (aClass.getDocComment() == null) { - final PsiDocComment createdDocComment = created.getDocComment(); - if (createdDocComment != null) { - aClass.addAfter(createdDocComment, null); + if (file instanceof PsiJavaFile) { + if (!moveDestination.equals(file.getContainingDirectory()) && + moveDestination.findFile(file.getName()) != null) { + // moving second of two classes which were in the same file to a different directory (IDEADEV-3089) + correctSelfReferences(aClass, newPackage); + final PsiFile newFile = moveDestination.findFile(file.getName()); + LOG.assertTrue(newFile != null); + newClass = (PsiClass)newFile.add(aClass); + correctOldClassReferences(newClass, aClass); + aClass.delete(); + } + else if (((PsiJavaFile)file).getClasses().length > 1) { + correctSelfReferences(aClass, newPackage); + final PsiClass created = JavaDirectoryService.getInstance().createClass(moveDestination, aClass.getName()); + if (aClass.getDocComment() == null) { + final PsiDocComment createdDocComment = created.getDocComment(); + if (createdDocComment != null) { + aClass.addAfter(createdDocComment, null); + } } + newClass = (PsiClass)created.replace(aClass); + correctOldClassReferences(newClass, aClass); + aClass.delete(); } - newClass = (PsiClass)created.replace(aClass); - correctOldClassReferences(newClass, aClass); - aClass.delete(); - } - else if (file instanceof PsiJavaFile && - !moveDestination.equals(file.getContainingDirectory()) && - moveDestination.findFile(file.getName()) != null) { - // moving second of two classes which were in the same file to a different directory (IDEADEV-3089) - correctSelfReferences(aClass, newPackage); - final PsiFile newFile = moveDestination.findFile(file.getName()); - LOG.assertTrue(newFile != null); - newClass = (PsiClass)newFile.add(aClass); - correctOldClassReferences(newClass, aClass); - aClass.delete(); } return newClass; } diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/multipleClassesInOneFile/after/src1/target/pack1/S1.java b/java/java-tests/testData/refactoring/movePackageAsDir/multipleClassesInOneFile/after/src1/target/pack1/S1.java index 00ad41e4dace..a7cde21bdb69 100644 --- a/java/java-tests/testData/refactoring/movePackageAsDir/multipleClassesInOneFile/after/src1/target/pack1/S1.java +++ b/java/java-tests/testData/refactoring/movePackageAsDir/multipleClassesInOneFile/after/src1/target/pack1/S1.java @@ -4,4 +4,8 @@ public class S1{ S11 s11; } -class S11{} \ No newline at end of file +class S11{} + +class S12{} + +class S13{} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/movePackageAsDir/multipleClassesInOneFile/before/src1/pack1/S1.java b/java/java-tests/testData/refactoring/movePackageAsDir/multipleClassesInOneFile/before/src1/pack1/S1.java index c6942e2ca40a..78e22ab07f1c 100644 --- a/java/java-tests/testData/refactoring/movePackageAsDir/multipleClassesInOneFile/before/src1/pack1/S1.java +++ b/java/java-tests/testData/refactoring/movePackageAsDir/multipleClassesInOneFile/before/src1/pack1/S1.java @@ -5,3 +5,7 @@ public class S1{ } class S11{} + +class S12{} + +class S13{} From 1d2ffa77bd6f6f548e12613e8bc23dc742e4b084 Mon Sep 17 00:00:00 2001 From: anna Date: Wed, 28 Jul 2010 19:41:49 +0400 Subject: [PATCH 84/92] form move: move as files --- .../MoveClassesOrPackagesHandlerBase.java | 2 +- .../projectView/FormMoveProvider.java | 92 ++++++------------- 2 files changed, 27 insertions(+), 67 deletions(-) diff --git a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesHandlerBase.java b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesHandlerBase.java index 59505843c02c..15dded63dda6 100644 --- a/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesHandlerBase.java +++ b/java/java-impl/src/com/intellij/refactoring/move/moveClassesOrPackages/MoveClassesOrPackagesHandlerBase.java @@ -42,7 +42,7 @@ import java.util.Arrays; public class MoveClassesOrPackagesHandlerBase extends MoveHandlerDelegate { private static final Logger LOG = Logger.getInstance("#" + MoveClassesOrPackagesHandlerBase.class.getName()); - protected static boolean isPackageOrDirectory(final PsiElement element) { + public static boolean isPackageOrDirectory(final PsiElement element) { if (element instanceof PsiPackage) return true; return element instanceof PsiDirectory && JavaDirectoryService.getInstance().getPackage((PsiDirectory)element) != null; } diff --git a/plugins/ui-designer/src/com/intellij/uiDesigner/projectView/FormMoveProvider.java b/plugins/ui-designer/src/com/intellij/uiDesigner/projectView/FormMoveProvider.java index 743f334efcf7..4ace55652680 100644 --- a/plugins/ui-designer/src/com/intellij/uiDesigner/projectView/FormMoveProvider.java +++ b/plugins/ui-designer/src/com/intellij/uiDesigner/projectView/FormMoveProvider.java @@ -22,21 +22,17 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.intellij.refactoring.MoveDestination; +import com.intellij.psi.PsiPackage; import com.intellij.refactoring.RefactoringActionHandler; import com.intellij.refactoring.actions.MoveAction; -import com.intellij.refactoring.move.MoveClassesOrPackagesCallback; -import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesImpl; -import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor; -import com.intellij.uiDesigner.UIDesignerBundle; -import com.intellij.usageView.UsageInfo; -import com.intellij.usageView.UsageViewUtil; -import com.intellij.util.IncorrectOperationException; +import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesHandlerBase; +import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; + +import java.util.HashSet; +import java.util.Set; /** * @author yole @@ -58,72 +54,36 @@ public class FormMoveProvider implements MoveAction.MoveProvider, RefactoringAct } public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { + final Set filesOrDirs = new HashSet(); + for (PsiElement element : elements) { + if (element instanceof PsiPackage) continue; + if (MoveClassesOrPackagesHandlerBase.isPackageOrDirectory(element)) { + filesOrDirs.add(element); + } else { + final PsiFile containingFile = element.getContainingFile(); + if (containingFile != null) { + filesOrDirs.add(containingFile); + } + } + } Form[] forms = Form.DATA_KEY.getData(dataContext); LOG.assertTrue(forms != null); PsiClass[] classesToMove = new PsiClass[forms.length]; PsiFile[] filesToMove = new PsiFile[forms.length]; for(int i=0; i Date: Thu, 29 Jul 2010 15:46:13 +0400 Subject: [PATCH 85/92] message fixed --- .../com/intellij/ide/util/projectWizard/ProjectNameStep.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectNameStep.java b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectNameStep.java index a46dd4b37b69..30f2e572413f 100644 --- a/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectNameStep.java +++ b/java/idea-ui/src/com/intellij/ide/util/projectWizard/ProjectNameStep.java @@ -46,7 +46,8 @@ public class ProjectNameStep extends ModuleWizardStep { myWizardContext = wizardContext; myNamePathComponent = new NamePathComponent(IdeBundle.message("label.project.name"), IdeBundle.message("label.component.file.location", StringUtil.capitalize(myWizardContext.getPresentationName())), 'a', 'l', - IdeBundle.message("title.select.project.file.directory"), IdeBundle.message("description.select.project.file.directory")); + IdeBundle.message("title.select.project.file.directory", myWizardContext.getPresentationName()), + IdeBundle.message("description.select.project.file.directory", myWizardContext.getPresentationName())); myPanel = new JPanel(new GridBagLayout()); myPanel.setBorder(BorderFactory.createEtchedBorder()); From 0f14a58c99351c1aa057dcc02e7f3dc396f136d0 Mon Sep 17 00:00:00 2001 From: Oleg Shpynov Date: Mon, 2 Aug 2010 12:30:10 +0400 Subject: [PATCH 86/92] Make symbols highlighting annotator independent --- .../intellij/lexer/MergingLexerAdapter.java | 130 +++-------------- .../lexer/MergingLexerAdapterBase.java | 137 ++++++++++++++++++ 2 files changed, 159 insertions(+), 108 deletions(-) create mode 100644 platform/lang-api/src/com/intellij/lexer/MergingLexerAdapterBase.java diff --git a/platform/lang-api/src/com/intellij/lexer/MergingLexerAdapter.java b/platform/lang-api/src/com/intellij/lexer/MergingLexerAdapter.java index 83e36d78eb1e..fb2fa23d82d7 100644 --- a/platform/lang-api/src/com/intellij/lexer/MergingLexerAdapter.java +++ b/platform/lang-api/src/com/intellij/lexer/MergingLexerAdapter.java @@ -18,114 +18,28 @@ package com.intellij.lexer; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; -public class MergingLexerAdapter extends DelegateLexer { - private final TokenSet myTokensToMerge; - private IElementType myTokenType; - private int myState; - private int myTokenStart; - - public MergingLexerAdapter(Lexer original, TokenSet tokensToMerge){ - super(original); - myTokensToMerge = tokensToMerge; - } - - public void start(final CharSequence buffer, final int startOffset, final int endOffset, final int initialState) { - super.start(buffer, startOffset, endOffset, initialState); - myTokenType = null; - } - - public int getState(){ - locateToken(); - return myState; - } - - public IElementType getTokenType(){ - locateToken(); - return myTokenType; - } - - public int getTokenStart(){ - locateToken(); - return myTokenStart; - } - - public int getTokenEnd(){ - locateToken(); - return super.getTokenStart(); - } - - public void advance(){ - myTokenType = null; - } - - - private void locateToken(){ - if (myTokenType == null){ - Lexer orig = getDelegate(); - - myTokenType = orig.getTokenType(); - myTokenStart = orig.getTokenStart(); - myState = orig.getState(); - if (myTokenType == null) return; - orig.advance(); - if (myTokensToMerge.contains(myTokenType)){ - while(true){ - IElementType tokenType = orig.getTokenType(); - if (tokenType != myTokenType) break; - orig.advance(); +public class MergingLexerAdapter extends MergingLexerAdapterBase { + public MergingLexerAdapter(final Lexer original, final TokenSet tokensToMerge){ + super(original, new MergeFunction() { + private IElementType currentTokenType; + @Override + public boolean startMerge(final IElementType type) { + if (tokensToMerge.contains(type)){ + currentTokenType = type; + return true; } + return false; } - } + + @Override + public boolean mergeWith(final IElementType type) { + return currentTokenType == type; + } + + @Override + public IElementType getMergedTokenType(final IElementType type) { + return type; + } + }); } - - public Lexer getOriginal() { - return getDelegate(); - } - - public void restore(LexerPosition position) { - MyLexerPosition pos = (MyLexerPosition)position; - - getDelegate().restore(pos.getOriginalPosition()); - myTokenType = pos.getType(); - myTokenStart = pos.getOffset(); - myState = pos.getOldState(); - } - - public LexerPosition getCurrentPosition() { - return new MyLexerPosition(myTokenStart, myTokenType, getDelegate().getCurrentPosition(), myState); - } - - private static class MyLexerPosition implements LexerPosition{ - private final int myOffset; - private final IElementType myTokenType; - private final LexerPosition myOriginalPosition; - private final int myOldState; - - public MyLexerPosition(final int offset, final IElementType tokenType, final LexerPosition originalPosition, int oldState) { - myOffset = offset; - myTokenType = tokenType; - myOriginalPosition = originalPosition; - myOldState = oldState; - } - - public int getOffset() { - return myOffset; - } - - public int getState() { - return myOriginalPosition.getState(); - } - - public IElementType getType() { - return myTokenType; - } - - public LexerPosition getOriginalPosition() { - return myOriginalPosition; - } - - public int getOldState() { - return myOldState; - } - } -} +} \ No newline at end of file diff --git a/platform/lang-api/src/com/intellij/lexer/MergingLexerAdapterBase.java b/platform/lang-api/src/com/intellij/lexer/MergingLexerAdapterBase.java new file mode 100644 index 000000000000..7fbff121ddc6 --- /dev/null +++ b/platform/lang-api/src/com/intellij/lexer/MergingLexerAdapterBase.java @@ -0,0 +1,137 @@ +/* + * 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.lexer; + +import com.intellij.psi.tree.IElementType; + +public class MergingLexerAdapterBase extends DelegateLexer { + private IElementType myTokenType; + private int myState; + private int myTokenStart; + private final MergeFunction myMergeFunction; + + public MergingLexerAdapterBase(final Lexer original, final MergeFunction mergeFunction){ + super(original); + myMergeFunction = mergeFunction; + } + + public void start(final CharSequence buffer, final int startOffset, final int endOffset, final int initialState) { + super.start(buffer, startOffset, endOffset, initialState); + myTokenType = null; + } + + public int getState(){ + locateToken(); + return myState; + } + + public IElementType getTokenType(){ + locateToken(); + return myTokenType; + } + + public int getTokenStart(){ + locateToken(); + return myTokenStart; + } + + public int getTokenEnd(){ + locateToken(); + return super.getTokenStart(); + } + + public void advance(){ + myTokenType = null; + } + + + private void locateToken(){ + if (myTokenType == null){ + Lexer orig = getDelegate(); + + myTokenType = orig.getTokenType(); + myTokenStart = orig.getTokenStart(); + myState = orig.getState(); + if (myTokenType == null) return; + orig.advance(); + if (myMergeFunction.startMerge(myTokenType)){ + myTokenType = myMergeFunction.getMergedTokenType(myTokenType); + while(true){ + IElementType tokenType = orig.getTokenType(); + if (!myMergeFunction.mergeWith(tokenType)) break; + orig.advance(); + } + } + } + } + + public Lexer getOriginal() { + return getDelegate(); + } + + public void restore(LexerPosition position) { + MyLexerPosition pos = (MyLexerPosition)position; + + getDelegate().restore(pos.getOriginalPosition()); + myTokenType = pos.getType(); + myTokenStart = pos.getOffset(); + myState = pos.getOldState(); + } + + public LexerPosition getCurrentPosition() { + return new MyLexerPosition(myTokenStart, myTokenType, getDelegate().getCurrentPosition(), myState); + } + + private static class MyLexerPosition implements LexerPosition{ + private final int myOffset; + private final IElementType myTokenType; + private final LexerPosition myOriginalPosition; + private final int myOldState; + + public MyLexerPosition(final int offset, final IElementType tokenType, final LexerPosition originalPosition, int oldState) { + myOffset = offset; + myTokenType = tokenType; + myOriginalPosition = originalPosition; + myOldState = oldState; + } + + public int getOffset() { + return myOffset; + } + + public int getState() { + return myOriginalPosition.getState(); + } + + public IElementType getType() { + return myTokenType; + } + + public LexerPosition getOriginalPosition() { + return myOriginalPosition; + } + + public int getOldState() { + return myOldState; + } + } + + protected interface MergeFunction { + boolean startMerge(IElementType type); + boolean mergeWith(IElementType type); + IElementType getMergedTokenType(IElementType type); + } +} From 54ddd292358419b65104a19cc2c64144b39da87b Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Fri, 30 Jul 2010 14:51:28 +0400 Subject: [PATCH 87/92] Debugger, XDebugger: ctrl+c to copy value --- .../com/intellij/debugger/actions/DebuggerActions.java | 1 + .../src/com/intellij/debugger/ui/impl/InspectPanel.java | 6 ++++-- .../intellij/debugger/ui/impl/UpdatableDebuggerView.java | 3 ++- .../src/com/intellij/debugger/ui/impl/VariablesPanel.java | 1 + .../src/com/intellij/debugger/ui/impl/WatchPanel.java | 5 +++-- .../intellij/openapi/actionSystem/CommonShortcuts.java | 4 ++++ .../intellij/xdebugger/impl/actions/XDebuggerActions.java | 1 + .../xdebugger/impl/ui/tree/XDebuggerTreePanel.java | 8 ++++---- 8 files changed, 20 insertions(+), 9 deletions(-) diff --git a/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerActions.java b/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerActions.java index 2a2ae16f9c61..6b5eadf8ff34 100644 --- a/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerActions.java +++ b/java/debugger/impl/src/com/intellij/debugger/actions/DebuggerActions.java @@ -38,6 +38,7 @@ public interface DebuggerActions extends XDebuggerActions { @NonNls String NEW_WATCH = "Debugger.NewWatch"; @NonNls String EDIT_WATCH = "Debugger.EditWatch"; @NonNls String MARK_OBJECT = "Debugger.MarkObject"; + @NonNls String COPY_VALUE = "Debugger.CopyValue"; @NonNls String SET_VALUE = "Debugger.SetValue"; @NonNls String EDIT_FRAME_SOURCE = "Debugger.EditFrameSource"; @NonNls String EDIT_NODE_SOURCE = "Debugger.EditNodeSource"; diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/InspectPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/InspectPanel.java index 5ebe1cbe28c4..4fc5a4d92453 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/InspectPanel.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/InspectPanel.java @@ -27,6 +27,7 @@ import com.intellij.debugger.ui.impl.watch.DebuggerTree; import com.intellij.debugger.ui.impl.watch.NodeDescriptorImpl; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionPopupMenu; +import com.intellij.openapi.actionSystem.CommonShortcuts; import com.intellij.openapi.project.Project; import com.intellij.ui.ScrollPaneFactory; import org.jetbrains.annotations.NotNull; @@ -40,8 +41,9 @@ public class InspectPanel extends DebuggerTreePanel { getInspectTree().setInspectDescriptor(inspectDescriptor); add(ScrollPaneFactory.createScrollPane(getInspectTree()), BorderLayout.CENTER); - final Disposable disposable = DebuggerAction.installEditAction(getInspectTree(), DebuggerActions.EDIT_NODE_SOURCE); - registerDisposable(disposable); + registerDisposable(DebuggerAction.installEditAction(getInspectTree(), DebuggerActions.EDIT_NODE_SOURCE)); + + overrideShortcut(getInspectTree(), DebuggerActions.COPY_VALUE, CommonShortcuts.getCopy()); setUpdateEnabled(true); } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/UpdatableDebuggerView.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/UpdatableDebuggerView.java index df266f486f19..a7319092470c 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/UpdatableDebuggerView.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/UpdatableDebuggerView.java @@ -24,6 +24,7 @@ import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.CustomShortcutSet; +import com.intellij.openapi.actionSystem.ShortcutSet; import com.intellij.openapi.project.Project; import com.intellij.util.Alarm; import com.sun.jdi.VMDisconnectedException; @@ -124,7 +125,7 @@ public abstract class UpdatableDebuggerView extends JPanel implements DebuggerVi myDisposables.clear(); } - protected void overrideShortcut(final JComponent forComponent, final String actionId, final CustomShortcutSet shortcutSet) { + protected void overrideShortcut(final JComponent forComponent, final String actionId, final ShortcutSet shortcutSet) { final AnAction action = ActionManager.getInstance().getAction(actionId); action.registerCustomShortcutSet(shortcutSet, forComponent); registerDisposable(new Disposable() { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java index 08f65ac0a10b..7dd4cd58eda6 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/VariablesPanel.java @@ -43,6 +43,7 @@ public class VariablesPanel extends DebuggerTreePanel implements DataProvider{ add(ScrollPaneFactory.createScrollPane(frameTree), BorderLayout.CENTER); registerDisposable(DebuggerAction.installEditAction(frameTree, DebuggerActions.EDIT_NODE_SOURCE)); + overrideShortcut(frameTree, DebuggerActions.COPY_VALUE, CommonShortcuts.getCopy()); overrideShortcut(frameTree, DebuggerActions.SET_VALUE, new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0))); new ValueNodeDnD(myTree, parent); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/impl/WatchPanel.java b/java/debugger/impl/src/com/intellij/debugger/ui/impl/WatchPanel.java index a961cdf15a47..2889e65f37df 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/impl/WatchPanel.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/impl/WatchPanel.java @@ -30,6 +30,7 @@ import com.intellij.debugger.ui.impl.watch.DebuggerTreeNodeImpl; import com.intellij.debugger.ui.impl.watch.WatchItemDescriptor; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionPopupMenu; +import com.intellij.openapi.actionSystem.CommonShortcuts; import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.project.Project; import com.intellij.ui.ScrollPaneFactory; @@ -44,8 +45,8 @@ public abstract class WatchPanel extends DebuggerTreePanel { public WatchPanel(Project project, DebuggerStateManager stateManager) { super(project, stateManager); add(ScrollPaneFactory.createScrollPane(getWatchTree()), BorderLayout.CENTER); - final Disposable disposable = DebuggerAction.installEditAction(getWatchTree(), DebuggerActions.EDIT_NODE_SOURCE); - registerDisposable(disposable); + registerDisposable(DebuggerAction.installEditAction(getWatchTree(), DebuggerActions.EDIT_NODE_SOURCE)); + overrideShortcut(getWatchTree(), DebuggerActions.COPY_VALUE, CommonShortcuts.getCopy()); } protected DebuggerTree createTreeView() { diff --git a/platform/platform-api/src/com/intellij/openapi/actionSystem/CommonShortcuts.java b/platform/platform-api/src/com/intellij/openapi/actionSystem/CommonShortcuts.java index 115fa90883b9..a01c28d4d235 100644 --- a/platform/platform-api/src/com/intellij/openapi/actionSystem/CommonShortcuts.java +++ b/platform/platform-api/src/com/intellij/openapi/actionSystem/CommonShortcuts.java @@ -39,6 +39,10 @@ public class CommonShortcuts { public static final ShortcutSet DOUBLE_CLICK_1 = new CustomShortcutSet(new Shortcut[]{new MouseShortcut(MouseEvent.BUTTON1, 0, 2)}); + public static ShortcutSet getCopy() { + return new CustomShortcutSet(KeymapManager.getInstance().getActiveKeymap().getShortcuts(IdeActions.ACTION_COPY)); + } + public static ShortcutSet getRerun() { return new CustomShortcutSet(KeymapManager.getInstance().getActiveKeymap().getShortcuts(IdeActions.ACTION_RERUN)); } diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/XDebuggerActions.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/XDebuggerActions.java index ce1621ff2213..0a6926167058 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/XDebuggerActions.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/actions/XDebuggerActions.java @@ -51,6 +51,7 @@ public interface XDebuggerActions { @NonNls String XREMOVE_WATCH = "XDebugger.RemoveWatch"; @NonNls String XEDIT_WATCH = "XDebugger.EditWatch"; + @NonNls String COPY_VALUE = "XDebugger.CopyValue"; @NonNls String SET_VALUE = "XDebugger.SetValue"; @NonNls String MUTE_BREAKPOINTS = "XDebugger.MuteBreakpoints"; diff --git a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTreePanel.java b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTreePanel.java index 5b47aaf607fc..e3318e620a1a 100644 --- a/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTreePanel.java +++ b/platform/xdebugger-impl/src/com/intellij/xdebugger/impl/ui/tree/XDebuggerTreePanel.java @@ -59,10 +59,10 @@ public class XDebuggerTreePanel implements DnDSource { popupMenu.getComponent().show(comp, x, y); } }; - AnAction setValueAction = actionManager.getAction(XDebuggerActions.SET_VALUE); - setValueAction.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0)), myTree); - AnAction jumpToSourceAction = actionManager.getAction(XDebuggerActions.JUMP_TO_SOURCE); - jumpToSourceAction.registerCustomShortcutSet(CommonShortcuts.getEditSource(), myTree); + actionManager.getAction(XDebuggerActions.SET_VALUE).registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0)), myTree); + actionManager.getAction(XDebuggerActions.COPY_VALUE).registerCustomShortcutSet(CommonShortcuts.getCopy(), myTree); + actionManager.getAction(XDebuggerActions.JUMP_TO_SOURCE).registerCustomShortcutSet(CommonShortcuts.getEditSource(), myTree); + myTree.addMouseListener(myPopupHandler); } From aa56e73d0f552390bbcb13cc381058af5e22fc98 Mon Sep 17 00:00:00 2001 From: Anton Makeev Date: Mon, 2 Aug 2010 12:54:47 +0400 Subject: [PATCH 88/92] CIDR-debugger: display strings --- .../openapi/util/text/StringUtil.java | 60 ++++++------------- .../src/com/intellij/util/ExceptionUtil.java | 60 +++++++++++++++++++ 2 files changed, 77 insertions(+), 43 deletions(-) diff --git a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java index a62221988df3..fe4299e6e766 100644 --- a/platform/util/src/com/intellij/openapi/util/text/StringUtil.java +++ b/platform/util/src/com/intellij/openapi/util/text/StringUtil.java @@ -19,6 +19,7 @@ import com.intellij.CommonBundle; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.util.TextRange; import com.intellij.util.ArrayUtil; +import com.intellij.util.ExceptionUtil; import com.intellij.util.Function; import com.intellij.util.SmartList; import com.intellij.util.text.CharArrayCharSequence; @@ -34,6 +35,8 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.text.DateFormat; import java.util.*; +import java.util.regex.MatchResult; +import java.util.regex.Matcher; import java.util.regex.Pattern; //TeamCity inherits StringUtil: do not add private constructors!!! @@ -799,56 +802,16 @@ public class StringUtil { @NotNull public static String getThrowableText(@NotNull Throwable aThrowable) { - StringWriter stringWriter = new StringWriter(); - PrintWriter writer = new PrintWriter(stringWriter); - aThrowable.printStackTrace(writer); - return stringWriter.getBuffer().toString(); + return ExceptionUtil.getThrowableText(aThrowable); } @NotNull public static String getThrowableText(@NotNull Throwable aThrowable, @NonNls @NotNull final String stackFrameSkipPattern) { - @NonNls final String prefix = "\tat "; - final String skipPattern = prefix + stackFrameSkipPattern; - final StringWriter stringWriter = new StringWriter(); - final PrintWriter writer = new PrintWriter(stringWriter) { - boolean skipping = false; - public void println(final String x) { - if (x != null) { - if (!skipping && x.startsWith(skipPattern)) skipping = true; - else if (skipping && !x.startsWith(prefix)) skipping = false; - } - if (skipping) return; - super.println(x); - } - }; - aThrowable.printStackTrace(writer); - return stringWriter.getBuffer().toString(); + return ExceptionUtil.getThrowableText(aThrowable, stackFrameSkipPattern); } public static String getMessage(@NotNull Throwable e) { - String result = e.getMessage(); - @NonNls final String exceptionPattern = "Exception: "; - @NonNls final String errorPattern = "Error: "; - - while ((result == null || result.contains(exceptionPattern) || result.contains(errorPattern)) && e.getCause() != null) { - e = e.getCause(); - result = e.getMessage(); - } - - if (result != null) { - result = extractMessage(result, exceptionPattern); - result = extractMessage(result, errorPattern); - } - - return result; - } - - @NotNull - private static String extractMessage(@NotNull String result, @NotNull final String errorPattern) { - if (result.lastIndexOf(errorPattern) >= 0) { - result = result.substring(result.lastIndexOf(errorPattern) + errorPattern.length()); - } - return result; + return ExceptionUtil.getMessage(e); } @NotNull @@ -1188,6 +1151,17 @@ public class StringUtil { return result.toString(); } + public static List findMatches(String s, Pattern pattern) { + List result = new SmartList(); + Matcher m = pattern.matcher(s); + while(m.find()) { + if (m.groupCount() > 0) { + result.add(m.group(1)); + } + } + return result; + } + /** * Find position of the first charachter accepted by given filter * @param s the string to search diff --git a/platform/util/src/com/intellij/util/ExceptionUtil.java b/platform/util/src/com/intellij/util/ExceptionUtil.java index 87723dad03c6..1fe9b2efdf4e 100644 --- a/platform/util/src/com/intellij/util/ExceptionUtil.java +++ b/platform/util/src/com/intellij/util/ExceptionUtil.java @@ -15,6 +15,12 @@ */ package com.intellij.util; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; + +import java.io.PrintWriter; +import java.io.StringWriter; + public class ExceptionUtil { public static Throwable getRootCause(Throwable e) { while (true) { @@ -33,4 +39,58 @@ public class ExceptionUtil { public static boolean causedBy(Throwable e, Class klass) { return findCause(e, klass) != null; } + + @NotNull + public static String getThrowableText(@NotNull Throwable aThrowable) { + StringWriter stringWriter = new StringWriter(); + PrintWriter writer = new PrintWriter(stringWriter); + aThrowable.printStackTrace(writer); + return stringWriter.getBuffer().toString(); + } + + @NotNull + public static String getThrowableText(@NotNull Throwable aThrowable, @NonNls @NotNull final String stackFrameSkipPattern) { + @NonNls final String prefix = "\tat "; + final String skipPattern = prefix + stackFrameSkipPattern; + final StringWriter stringWriter = new StringWriter(); + final PrintWriter writer = new PrintWriter(stringWriter) { + boolean skipping = false; + public void println(final String x) { + if (x != null) { + if (!skipping && x.startsWith(skipPattern)) skipping = true; + else if (skipping && !x.startsWith(prefix)) skipping = false; + } + if (skipping) return; + super.println(x); + } + }; + aThrowable.printStackTrace(writer); + return stringWriter.getBuffer().toString(); + } + + public static String getMessage(@NotNull Throwable e) { + String result = e.getMessage(); + @NonNls final String exceptionPattern = "Exception: "; + @NonNls final String errorPattern = "Error: "; + + while ((result == null || result.contains(exceptionPattern) || result.contains(errorPattern)) && e.getCause() != null) { + e = e.getCause(); + result = e.getMessage(); + } + + if (result != null) { + result = extractMessage(result, exceptionPattern); + result = extractMessage(result, errorPattern); + } + + return result; + } + + @NotNull + private static String extractMessage(@NotNull String result, @NotNull final String errorPattern) { + if (result.lastIndexOf(errorPattern) >= 0) { + result = result.substring(result.lastIndexOf(errorPattern) + errorPattern.length()); + } + return result; + } } From a89603e5c0d19b098f705191b952e12702dcdbc7 Mon Sep 17 00:00:00 2001 From: Alexey Pegov Date: Mon, 2 Aug 2010 13:51:22 +0400 Subject: [PATCH 89/92] fix git branches status bar widget: there is no InsertOverwrite widget on Macs :) --- .../src/git4idea/checkout/branches/GitBranchesWidget.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/git4idea/src/git4idea/checkout/branches/GitBranchesWidget.java b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchesWidget.java index ca47ea25fc41..94657453bc93 100644 --- a/plugins/git4idea/src/git4idea/checkout/branches/GitBranchesWidget.java +++ b/plugins/git4idea/src/git4idea/checkout/branches/GitBranchesWidget.java @@ -29,6 +29,7 @@ import com.intellij.openapi.ui.popup.ListPopup; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.IconLoader; import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.vcs.VcsException; import com.intellij.openapi.vcs.changes.ui.ChangesViewContentManager; import com.intellij.openapi.vfs.VirtualFile; @@ -148,7 +149,7 @@ public class GitBranchesWidget extends JLabel implements CustomStatusBarWidget { StatusBar statusBar = WindowManager.getInstance().getStatusBar(project); if (statusBar != null) { final GitBranchesWidget w = new GitBranchesWidget(project, configurations); - statusBar.addWidget(w, "after InsertOverwrite", project); + statusBar.addWidget(w, "after " + (SystemInfo.isMac ? "Encoding" : "InsertOverwrite"), project); widget.set(w); } } From 0340b76b8183c07bc35b2f269a9bfe2ada132749 Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Wed, 28 Jul 2010 17:12:51 +0400 Subject: [PATCH 90/92] Create JavaEE Web Page action --- .../actions/CreateFileFromTemplateAction.java | 74 +++++++++++++++++++ .../ide/actions/CreateFromTemplateAction.java | 51 ++++--------- .../ide/actions/TemplateKindProvider.java | 56 ++++++++++++++ .../ide/actions/CreateHtmlFileAction.java | 33 ++++----- 4 files changed, 157 insertions(+), 57 deletions(-) create mode 100644 platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateAction.java create mode 100644 platform/lang-impl/src/com/intellij/ide/actions/TemplateKindProvider.java diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateAction.java b/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateAction.java new file mode 100644 index 000000000000..7f161b853d26 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/actions/CreateFileFromTemplateAction.java @@ -0,0 +1,74 @@ +/* + * 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.fileTemplates.FileTemplate; +import com.intellij.ide.fileTemplates.FileTemplateManager; +import com.intellij.ide.fileTemplates.FileTemplateUtil; +import com.intellij.ide.util.PropertiesComponent; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.PsiDirectory; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.IncorrectOperationException; + +import javax.swing.*; + +/** + * @author Dmitry Avdeev + */ +public abstract class CreateFileFromTemplateAction extends CreateFromTemplateAction { + + public CreateFileFromTemplateAction(String text, String description, Icon icon) { + super(text, description, icon); + } + + protected PsiFile createFileFromTemplate(String name, FileTemplate template, PsiDirectory dir) { + + PsiElement element; + try { + element = FileTemplateUtil + .createFromTemplate(template, name, FileTemplateManager.getInstance().getDefaultProperties(), dir); + final PsiFile psiFile = element.getContainingFile(); + + final VirtualFile virtualFile = psiFile.getVirtualFile(); + if (virtualFile != null) { + FileEditorManager.getInstance(dir.getProject()).openFile(virtualFile, true); + String property = getDefaultTemplateProperty(); + if (property != null) { + PropertiesComponent.getInstance(dir.getProject()).setValue(property, template.getName()); + } + return psiFile; + } + } + catch (IncorrectOperationException e) { + throw e; + } + catch (Exception e) { + LOG.error(e); + } + + return null; + } + + + @Override + protected PsiFile createFile(String name, String templateName, PsiDirectory dir) { + final FileTemplate template = FileTemplateManager.getInstance().getInternalTemplate(templateName); + return createFileFromTemplate(name, template, dir); + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/actions/CreateFromTemplateAction.java b/platform/lang-impl/src/com/intellij/ide/actions/CreateFromTemplateAction.java index edca8c915d14..34928e5d968b 100644 --- a/platform/lang-impl/src/com/intellij/ide/actions/CreateFromTemplateAction.java +++ b/platform/lang-impl/src/com/intellij/ide/actions/CreateFromTemplateAction.java @@ -17,19 +17,13 @@ package com.intellij.ide.actions; import com.intellij.CommonBundle; import com.intellij.ide.IdeView; -import com.intellij.ide.fileTemplates.FileTemplate; -import com.intellij.ide.fileTemplates.FileTemplateManager; -import com.intellij.ide.fileTemplates.FileTemplateUtil; +import com.intellij.ide.util.PropertiesComponent; import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Ref; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -39,37 +33,12 @@ import javax.swing.*; * @author Eugene.Kudelevsky */ public abstract class CreateFromTemplateAction extends AnAction { - private static final Logger LOG = Logger.getInstance("#com.intellij.ide.actions.CreateFromTemplateAction"); + protected static final Logger LOG = Logger.getInstance("#com.intellij.ide.actions.CreateFromTemplateAction"); public CreateFromTemplateAction(String text, String description, Icon icon) { super(text, description, icon); } - - protected static PsiFile createFileFromTemplate(String name, String templateName, PsiDirectory dir) { - final FileTemplate template = FileTemplateManager.getInstance().getInternalTemplate(templateName); - - PsiElement element; - try { - element = FileTemplateUtil - .createFromTemplate(template, name, FileTemplateManager.getInstance().getDefaultProperties(), dir); - final PsiFile psiFile = element.getContainingFile(); - - final VirtualFile virtualFile = psiFile.getVirtualFile(); - if (virtualFile != null) { - FileEditorManager.getInstance(dir.getProject()).openFile(virtualFile, true); - return psiFile; - } - } - catch (IncorrectOperationException e) { - throw e; - } - catch (Exception e) { - LOG.error(e); - } - - return null; - } - + public final void actionPerformed(final AnActionEvent e) { final DataContext dataContext = e.getDataContext(); @@ -105,22 +74,30 @@ public abstract class CreateFromTemplateAction extends AnA }); if (createdElement != null) { view.selectElement(createdElement); - postProcesss(createdElement, selectedTemplateName.get()); + postProcess(createdElement, selectedTemplateName.get()); } } - protected void postProcesss(T createdElement, String templateName) { + protected void postProcess(T createdElement, String templateName) { } @Nullable protected abstract T createFile(String name, String templateName, PsiDirectory dir); - protected abstract void checkBeforeCreate(String name, String templateName, PsiDirectory dir); + protected void checkBeforeCreate(String name, String templateName, PsiDirectory dir) { + dir.checkCreateFile(name); + } protected abstract void buildDialog(Project project, PsiDirectory directory, CreateFileFromTemplateDialog.Builder builder); @Nullable protected String getDefaultTemplateName(@NotNull PsiDirectory dir) { + String property = getDefaultTemplateProperty(); + return property == null ? null : PropertiesComponent.getInstance(dir.getProject()).getValue(property); + } + + @Nullable + protected String getDefaultTemplateProperty() { return null; } diff --git a/platform/lang-impl/src/com/intellij/ide/actions/TemplateKindProvider.java b/platform/lang-impl/src/com/intellij/ide/actions/TemplateKindProvider.java new file mode 100644 index 000000000000..5353aa6286db --- /dev/null +++ b/platform/lang-impl/src/com/intellij/ide/actions/TemplateKindProvider.java @@ -0,0 +1,56 @@ +/* + * 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.openapi.actionSystem.ActionManager; +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.extensions.Extensions; +import com.intellij.psi.PsiDirectory; + +import javax.swing.*; + +/** + * @author Dmitry Avdeev + */ +public abstract class TemplateKindProvider { + + private final static ExtensionPointName EP_NAME = + ExtensionPointName.create("com.intellij.javaee.templateKindProvider"); + + public static void addAdditionalKinds(AnAction action, PsiDirectory dir, CreateFileFromTemplateDialog.Builder builder) { + String id = ActionManager.getInstance().getId(action); + for (TemplateKindProvider provider : Extensions.getExtensions(EP_NAME)) { + for (Kind kind : provider.getAdditionalKinds(dir)) { + builder.addKind(kind.name, kind.icon, kind.templateName); + } + } + } + public abstract boolean isAvailable(Class actionClass); + public abstract Kind[] getAdditionalKinds(PsiDirectory dir); + + public static class Kind { + public final String name; + public final String templateName; + public final Icon icon; + + public Kind(String name, String templateName, Icon icon) { + this.name = name; + this.templateName = templateName; + this.icon = icon; + } + } +} diff --git a/xml/impl/src/com/intellij/ide/actions/CreateHtmlFileAction.java b/xml/impl/src/com/intellij/ide/actions/CreateHtmlFileAction.java index 9727f397e814..cd1d5d130e0f 100644 --- a/xml/impl/src/com/intellij/ide/actions/CreateHtmlFileAction.java +++ b/xml/impl/src/com/intellij/ide/actions/CreateHtmlFileAction.java @@ -16,21 +16,16 @@ package com.intellij.ide.actions; import com.intellij.ide.fileTemplates.FileTemplateManager; -import com.intellij.ide.util.PropertiesComponent; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.fileTypes.StdFileTypes; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiDirectory; -import com.intellij.psi.PsiFile; import com.intellij.xml.XmlBundle; import org.jetbrains.annotations.NonNls; -import org.jetbrains.annotations.NotNull; /** * @author Eugene.Kudelevsky */ -public class CreateHtmlFileAction extends CreateFromTemplateAction { - private static final Logger LOG = Logger.getInstance("#com.intellij.ide.actions.CreateHtmlFileAction"); +public class CreateHtmlFileAction extends CreateFileFromTemplateAction { @NonNls private static final String DEFAULT_HTML_TEMPLATE_PROPERTY = "DefaultHtmlFileTemplate"; @@ -39,15 +34,8 @@ public class CreateHtmlFileAction extends CreateFromTemplateAction { } @Override - protected PsiFile createFile(String name, String templateName, PsiDirectory dir) { - PropertiesComponent.getInstance(dir.getProject()).setValue(DEFAULT_HTML_TEMPLATE_PROPERTY, templateName); - - return createFileFromTemplate(name, templateName, dir); - } - - @Override - protected void checkBeforeCreate(String name, String templateName, PsiDirectory dir) { - dir.checkCreateFile(name); + protected String getDefaultTemplateProperty() { + return DEFAULT_HTML_TEMPLATE_PROPERTY; } @Override @@ -59,13 +47,18 @@ public class CreateHtmlFileAction extends CreateFromTemplateAction { .addKind("XHTML file", StdFileTypes.XHTML.getIcon(), FileTemplateManager.INTERNAL_XHTML_TEMPLATE_NAME); } - @Override - protected String getDefaultTemplateName(@NotNull PsiDirectory dir) { - return PropertiesComponent.getInstance(dir.getProject()).getValue(DEFAULT_HTML_TEMPLATE_PROPERTY); - } - @Override protected String getActionName(PsiDirectory directory, String newName, String templateName) { return XmlBundle.message("new.html.file.action"); } + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof CreateHtmlFileAction; + } } From 93eb8cf2b7aa205927ca47f200f562513097575b Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Thu, 29 Jul 2010 18:59:25 +0400 Subject: [PATCH 91/92] el variables from including files: JspFileIndex refactored to use BaseJspFile --- .../source/tree/java/PsiLocalVariableImpl.java | 7 ++++--- .../psi/impl/source/jsp/JspContextManager.java | 17 ++++++++++------- .../src/com/intellij/psi/jsp/JspSpiUtil.java | 4 ++-- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiLocalVariableImpl.java b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiLocalVariableImpl.java index 6c9680020c0a..178e328233b8 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiLocalVariableImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/tree/java/PsiLocalVariableImpl.java @@ -27,6 +27,7 @@ import com.intellij.psi.impl.source.codeStyle.CodeEditUtil; import com.intellij.psi.impl.source.jsp.JspContextManager; import com.intellij.psi.impl.source.jsp.jspJava.JspCodeBlock; import com.intellij.psi.impl.source.tree.*; +import com.intellij.psi.jsp.BaseJspFile; import com.intellij.psi.jsp.JspElementType; import com.intellij.psi.jsp.JspFile; import com.intellij.psi.jsp.JspSpiUtil; @@ -289,10 +290,10 @@ public class PsiLocalVariableImpl extends CompositePsiElement implements PsiLoca } final Set allIncluded = new THashSet(10); - final JspFile rootContext = contextManager.getRootContextFile(jspFile); + final BaseJspFile rootContext = contextManager.getRootContextFile(jspFile); allIncluded.add(rootContext); - JspSpiUtil.visitAllIncludedFilesRecursively(rootContext, new Processor() { - public boolean process(final JspFile file) { + JspSpiUtil.visitAllIncludedFilesRecursively(rootContext, new Processor() { + public boolean process(final BaseJspFile file) { allIncluded.add(file); return true; } diff --git a/java/jsp-spi/src/com/intellij/psi/impl/source/jsp/JspContextManager.java b/java/jsp-spi/src/com/intellij/psi/impl/source/jsp/JspContextManager.java index 763f100865ea..976932ac0ad7 100644 --- a/java/jsp-spi/src/com/intellij/psi/impl/source/jsp/JspContextManager.java +++ b/java/jsp-spi/src/com/intellij/psi/impl/source/jsp/JspContextManager.java @@ -18,6 +18,7 @@ package com.intellij.psi.impl.source.jsp; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; +import com.intellij.psi.jsp.BaseJspFile; import com.intellij.psi.jsp.JspFile; import com.intellij.util.containers.HashSet; import org.jetbrains.annotations.NotNull; @@ -32,20 +33,22 @@ public abstract class JspContextManager { return project.getComponent(JspContextManager.class); } - public abstract JspFile[] getSuitableContextFiles(@NotNull PsiFile file); + public abstract BaseJspFile[] getSuitableContextFiles(@NotNull PsiFile file); - public abstract void setContextFile(@NotNull PsiFile file, @Nullable JspFile contextFile, final boolean userDefined); + public abstract void setContextFile(@NotNull PsiFile file, @Nullable BaseJspFile contextFile, final boolean userDefined); - public abstract @Nullable JspFile getContextFile(@NotNull PsiFile file); + public abstract @Nullable + BaseJspFile getContextFile(@NotNull PsiFile file); public abstract @Nullable JspFile getConfiguredContextFile(@NotNull PsiFile file); - public @NotNull JspFile getRootContextFile(@NotNull JspFile file) { - JspFile rootContext = file; - HashSet recursionPreventer = new HashSet(); + public @NotNull + BaseJspFile getRootContextFile(@NotNull BaseJspFile file) { + BaseJspFile rootContext = file; + HashSet recursionPreventer = new HashSet(); do { recursionPreventer.add(rootContext); - JspFile context = getContextFile(rootContext); + BaseJspFile context = getContextFile(rootContext); if (context == null || recursionPreventer.contains(context)) break; rootContext = context; } diff --git a/java/jsp-spi/src/com/intellij/psi/jsp/JspSpiUtil.java b/java/jsp-spi/src/com/intellij/psi/jsp/JspSpiUtil.java index 542fdd95908e..01ba9e47b174 100644 --- a/java/jsp-spi/src/com/intellij/psi/jsp/JspSpiUtil.java +++ b/java/jsp-spi/src/com/intellij/psi/jsp/JspSpiUtil.java @@ -62,14 +62,14 @@ public abstract class JspSpiUtil { protected abstract int _escapeCharsInJspContext(JspFile file, int offset, String toEscape) throws IncorrectOperationException; - public static void visitAllIncludedFilesRecursively(JspFile jspFile, Processor visitor) { + public static void visitAllIncludedFilesRecursively(BaseJspFile jspFile, Processor visitor) { final JspSpiUtil util = getJspSpiUtil(); if (util != null) { util._visitAllIncludedFilesRecursively(jspFile, visitor); } } - protected abstract void _visitAllIncludedFilesRecursively(JspFile jspFile, Processor visitor); + protected abstract void _visitAllIncludedFilesRecursively(BaseJspFile jspFile, Processor visitor); @Nullable public static PsiElement resolveMethodPropertyReference(@NotNull PsiReference reference, @Nullable PsiClass resolvedClass, boolean readable) { From d1e3929c84f73180c660f02d00bc834ebf75ca4d Mon Sep 17 00:00:00 2001 From: Dmitry Avdeev Date: Fri, 30 Jul 2010 19:04:28 +0400 Subject: [PATCH 92/92] JspFileIndex refactored to use FBI --- .../impl/source/jsp/JspContextManager.java | 2 +- .../psi/impl/include/FileIncludeInfo.java | 10 +- .../psi/impl/include/FileIncludeManager.java | 6 +- .../psi/impl/include/FileIncludeIndex.java | 2 +- .../psi/impl/include/FileIncludeInfoImpl.java | 4 +- .../impl/include/FileIncludeManagerImpl.java | 130 ++++++++++-------- .../psi/impl/include/FileIncludeProvider.java | 9 -- .../impl/providers/FileReferenceSet.java | 20 +-- 8 files changed, 85 insertions(+), 98 deletions(-) diff --git a/java/jsp-spi/src/com/intellij/psi/impl/source/jsp/JspContextManager.java b/java/jsp-spi/src/com/intellij/psi/impl/source/jsp/JspContextManager.java index 976932ac0ad7..21d1701b096d 100644 --- a/java/jsp-spi/src/com/intellij/psi/impl/source/jsp/JspContextManager.java +++ b/java/jsp-spi/src/com/intellij/psi/impl/source/jsp/JspContextManager.java @@ -35,7 +35,7 @@ public abstract class JspContextManager { public abstract BaseJspFile[] getSuitableContextFiles(@NotNull PsiFile file); - public abstract void setContextFile(@NotNull PsiFile file, @Nullable BaseJspFile contextFile, final boolean userDefined); + public abstract void setContextFile(@NotNull PsiFile file, @Nullable BaseJspFile contextFile); public abstract @Nullable BaseJspFile getContextFile(@NotNull PsiFile file); diff --git a/platform/lang-api/src/com/intellij/psi/impl/include/FileIncludeInfo.java b/platform/lang-api/src/com/intellij/psi/impl/include/FileIncludeInfo.java index 82ed4f5c4c1c..f4b925897175 100644 --- a/platform/lang-api/src/com/intellij/psi/impl/include/FileIncludeInfo.java +++ b/platform/lang-api/src/com/intellij/psi/impl/include/FileIncludeInfo.java @@ -35,16 +35,12 @@ public class FileIncludeInfo { this.runtimeOnly = runtimeOnly; } - public FileIncludeInfo(@NotNull String fileName, @NotNull String path, int offset) { - this(fileName, path, offset, false); - } - - public FileIncludeInfo(@NotNull String fileName, @NotNull String path) { - this(fileName, path, -1, false); + public FileIncludeInfo(@NotNull String path, int offset) { + this(getFileName(path), path, offset, false); } public FileIncludeInfo(@NotNull String path) { - this(getFileName(path), path, -1, false); + this(path, -1); } private static String getFileName(String path) { diff --git a/platform/lang-api/src/com/intellij/psi/impl/include/FileIncludeManager.java b/platform/lang-api/src/com/intellij/psi/impl/include/FileIncludeManager.java index 834f6772f29d..7d56a7e75cb2 100644 --- a/platform/lang-api/src/com/intellij/psi/impl/include/FileIncludeManager.java +++ b/platform/lang-api/src/com/intellij/psi/impl/include/FileIncludeManager.java @@ -18,9 +18,11 @@ package com.intellij.psi.impl.include; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiFileSystemItem; +import com.intellij.util.Processor; import org.jetbrains.annotations.Nullable; /** @@ -36,6 +38,8 @@ public abstract class FileIncludeManager { public abstract VirtualFile[] getIncludingFiles(VirtualFile file, boolean compileTimeOnly); + public abstract void processIncludingFiles(PsiFile context, Processor> processor); + @Nullable - public abstract PsiFileSystemItem resolveFileReference(String text, PsiFile context); + public abstract PsiFileSystemItem resolveFileInclude(FileIncludeInfo info, PsiFile context); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/include/FileIncludeIndex.java b/platform/lang-impl/src/com/intellij/psi/impl/include/FileIncludeIndex.java index 997c4578e2f4..e12030ffd4cf 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/include/FileIncludeIndex.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/include/FileIncludeIndex.java @@ -163,7 +163,7 @@ public class FileIncludeIndex extends FileBasedIndexExtension infoList = FileIncludeIndex.getIncludes(file.getVirtualFile(), scope); - ArrayList files = new ArrayList(); - for (FileIncludeInfoImpl info : infoList) { - if (compileTimeOnly && info.runtimeOnly) { - continue; - } - FileIncludeProvider includeProvider = myProviderMap.get(info.providerId); - if (includeProvider != null) { - PsiFileSystemItem virtualFile = includeProvider.resolveInclude(info, file, myProject); - if (virtualFile != null) { - files.add(virtualFile.getVirtualFile()); + protected VirtualFile[] computeFiles(final PsiFile file, final boolean compileTimeOnly) { + final ArrayList files = new ArrayList(); + processIncludes(file, new Processor() { + @Override + public boolean process(FileIncludeInfo info) { + if (compileTimeOnly != info.runtimeOnly) { + PsiFileSystemItem virtualFile = resolveFileInclude(info, file); + if (virtualFile != null) { + files.add(virtualFile.getVirtualFile()); + } } + return true; } + + }); + return files.toArray(new VirtualFile[files.size()]); + } + }; + + public void processIncludes(PsiFile file, Processor processor) { + GlobalSearchScope scope = GlobalSearchScope.allScope(myProject); + List infoList = FileIncludeIndex.getIncludes(file.getVirtualFile(), scope); + for (FileIncludeInfoImpl info : infoList) { + if (!processor.process(info)) { + return; } + } + } + + private final IncludeCacheHolder myIncludingHolder = new IncludeCacheHolder("compile time contexts", "runtime contexts") { + @Override + protected VirtualFile[] computeFiles(PsiFile context, boolean compileTimeOnly) { + final ArrayList files = new ArrayList(); + processIncludingFiles(context, new Processor>() { + @Override + public boolean process(Pair virtualFileFileIncludeInfoPair) { + files.add(virtualFileFileIncludeInfoPair.first); + return true; + } + }); return VfsUtil.toVirtualFileArray(files); } }; - private final IncludeCacheHolder myIncludingHolder = new IncludeCacheHolder("compile time contexts", "runtime contexts") { - @Override - protected VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly) { - MultiMap infoList = FileIncludeIndex.getIncludingFileCandidates(file.getName(), GlobalSearchScope.allScope(myProject)); - ArrayList files = new ArrayList(); - for (VirtualFile candidate : infoList.keySet()) { - PsiFile psiFile = myPsiManager.findFile(candidate); - if (psiFile == null) continue; - for (FileIncludeInfoImpl info : infoList.get(candidate)) { - FileIncludeProvider includeProvider = myProviderMap.get(info.providerId); - if (includeProvider != null) { - if (file.equals(includeProvider.resolveInclude(info, psiFile, myProject))) { - files.add(candidate); - } + public void processIncludingFiles(PsiFile context, Processor> processor) { + context = context.getOriginalFile(); + VirtualFile contextFile = context.getVirtualFile(); + if (contextFile == null) return; + MultiMap infoList = FileIncludeIndex.getIncludingFileCandidates(context.getName(), GlobalSearchScope.allScope(myProject)); + for (VirtualFile candidate : infoList.keySet()) { + PsiFile psiFile = myPsiManager.findFile(candidate); + if (psiFile == null || context.equals(psiFile)) continue; + for (FileIncludeInfo info : infoList.get(candidate)) { + PsiFileSystemItem item = resolveFileInclude(info, psiFile); + if (item != null && contextFile.equals(item.getVirtualFile())) { + if (!processor.process(Pair.create(candidate, info))) { + return; } } } - return VfsUtil.toVirtualFileArray(files); } - }; + } public FileIncludeManagerImpl(Project project, PsiManager psiManager, PsiFileFactory psiFileFactory, CachedValuesManager cachedValuesManager) { @@ -120,19 +142,23 @@ public class FileIncludeManagerImpl extends FileIncludeManager { } } - @Override public VirtualFile[] getIncludingFiles(VirtualFile file, boolean compileTimeOnly) { return myIncludingHolder.getAllFiles(file, compileTimeOnly); } @Override - public PsiFileSystemItem resolveFileReference(String text, PsiFile context) { - - PsiFileImpl psiFile = (PsiFileImpl)myPsiFileFactory.createFileFromText("dummy.txt", text); + public PsiFileSystemItem resolveFileInclude(FileIncludeInfo info, PsiFile context) { + + PsiFileImpl psiFile = (PsiFileImpl)myPsiFileFactory.createFileFromText("dummy.txt", info.path); psiFile.setOriginalFile(context); - return new FileReferenceSet(psiFile).resolve(); + return new FileReferenceSet(psiFile) { + @Override + protected boolean useIncludingFileAsContext() { + return false; + } + }.resolve(); } private abstract class IncludeCacheHolder { @@ -140,14 +166,14 @@ public class FileIncludeManagerImpl extends FileIncludeManager { private final Key> COMPILE_TIME_KEY; private final Key> RUNTIME_KEY; - private final ParameterizedCachedValueProvider COMPILE_TIME_PROVIDER = new IncludedFilesProvider(false) { + private final ParameterizedCachedValueProvider COMPILE_TIME_PROVIDER = new IncludedFilesProvider(true) { @Override protected VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly) { return IncludeCacheHolder.this.computeFiles(file, compileTimeOnly); } }; - private final ParameterizedCachedValueProvider RUNTIME_PROVIDER = new IncludedFilesProvider(true) { + private final ParameterizedCachedValueProvider RUNTIME_PROVIDER = new IncludedFilesProvider(false) { @Override protected VirtualFile[] computeFiles(PsiFile file, boolean compileTimeOnly) { return IncludeCacheHolder.this.computeFiles(file, compileTimeOnly); @@ -160,36 +186,20 @@ public class FileIncludeManagerImpl extends FileIncludeManager { } public VirtualFile[] getAllFiles(VirtualFile file, boolean compileTimeOnly) { - ArrayList result = new ArrayList(); + Set result = new HashSet(); getFilesRecursively(file, compileTimeOnly, result); - switch (result.size()) { - case 0: - return VirtualFile.EMPTY_ARRAY; - case 1: - return result.get(0); - default: - int size = 0; - for (VirtualFile[] files : result) { - size+=files.length; - } - VirtualFile[] files = new VirtualFile[size]; - int pos = 0; - for (VirtualFile[] virtualFiles : result) { - System.arraycopy(virtualFiles, 0, files, pos, virtualFiles.length); - pos += virtualFiles.length; - } - return files; - } + return result.toArray(new VirtualFile[result.size()]); } - private void getFilesRecursively(VirtualFile file, boolean compileTimeOnly, List result) { + private void getFilesRecursively(VirtualFile file, boolean compileTimeOnly, Set result) { + if (result.contains(file)) return; PsiFile psiFile = myPsiManager.findFile(file); if (psiFile == null) return; VirtualFile[] includes = compileTimeOnly ? myCachedValuesManager.getParameterizedCachedValue(psiFile, COMPILE_TIME_KEY, COMPILE_TIME_PROVIDER, false, psiFile) : myCachedValuesManager.getParameterizedCachedValue(psiFile, RUNTIME_KEY, RUNTIME_PROVIDER, false, psiFile); if (includes.length != 0) { - result.add(includes); + result.addAll(Arrays.asList(includes)); for (VirtualFile include : includes) { getFilesRecursively(include, compileTimeOnly, result); } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/include/FileIncludeProvider.java b/platform/lang-impl/src/com/intellij/psi/impl/include/FileIncludeProvider.java index 031865cffb30..2161f95fa29f 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/include/FileIncludeProvider.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/include/FileIncludeProvider.java @@ -17,13 +17,9 @@ package com.intellij.psi.impl.include; import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; -import com.intellij.psi.PsiFileSystemItem; import com.intellij.util.indexing.FileContent; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; /** * @author Dmitry Avdeev @@ -39,9 +35,4 @@ public abstract class FileIncludeProvider { @NotNull public abstract FileIncludeInfo[] getIncludeInfos(FileContent content); - - @Nullable - public PsiFileSystemItem resolveInclude(FileIncludeInfo include, PsiFile context, Project project) { - return FileIncludeManager.getManager(project).resolveFileReference(include.path, context); - } } diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceSet.java b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceSet.java index 828fdc42cc23..6de47a442bf5 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceSet.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceSet.java @@ -22,15 +22,10 @@ import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil; -import com.intellij.psi.util.CachedValue; -import com.intellij.psi.util.CachedValueProvider; -import com.intellij.psi.util.CachedValuesManager; -import com.intellij.psi.util.PsiModificationTracker; import com.intellij.util.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -46,7 +41,6 @@ public class FileReferenceSet { private static final FileType[] EMPTY_FILE_TYPES = {}; private static final char SEPARATOR = '/'; private static final String SEPARATOR_STRING = "/"; - private static final Key>> DEFAULT_CONTEXTS_KEY = new Key>>("default file contexts"); public static final CustomizableReferenceProvider.CustomizationKey>> DEFAULT_PATH_EVALUATOR_OPTION = new CustomizableReferenceProvider.CustomizationKey>>(PsiBundle.message("default.path.evaluator.option")); public static final Function> ABSOLUTE_TOP_LEVEL = new Function>() { @@ -262,17 +256,7 @@ public class FileReferenceSet { return getAbsoluteTopLevelDirLocations(file); } - final CachedValueProvider> myDefaultContextProvider = new CachedValueProvider>() { - public Result> compute() { - final Collection contexts = getContextByFile(file); - return Result.createSingleDependency(contexts, - PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); - } - }; - final CachedValuesManager cachedValuesManager = CachedValuesManager.getManager(myElement.getProject()); - final Collection value = - cachedValuesManager.getCachedValue(file, DEFAULT_CONTEXTS_KEY, myDefaultContextProvider, false); - return value == null ? Collections.emptyList() : value; + return getContextByFile(file); } @Nullable @@ -285,7 +269,7 @@ public class FileReferenceSet { return file.getOriginalFile(); } - @Nullable + @NotNull private Collection getContextByFile(@NotNull PsiFile file) { final PsiElement context = file.getContext(); if (context != null) file = context.getContainingFile();
Shelve Path:" + + StringUtil.escapeXml(ch.SHELVE_PATH) + + "
Shelve Path:" + + StringUtil.escapeXml(d) + + "