Empty import list placement and comment binding

This commit is contained in:
Roman Shevchenko
2010-08-10 13:49:16 +04:00
parent 84487c5937
commit 88b087a894
22 changed files with 275 additions and 103 deletions
@@ -25,15 +25,14 @@ import com.intellij.psi.impl.java.stubs.impl.PsiImportListStubImpl;
import com.intellij.psi.impl.source.PsiImportListImpl;
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 JavaImportListElementType extends JavaStubElementType<PsiImportListStub, PsiImportList> {
public JavaImportListElementType() {
super("IMPORT_LIST");
super("IMPORT_LIST", true);
}
public PsiImportList createPsi(final PsiImportListStub stub) {
@@ -60,4 +59,4 @@ public class JavaImportListElementType extends JavaStubElementType<PsiImportList
public void indexStub(final PsiImportListStub stub, final IndexSink sink) {
}
}
}
@@ -29,8 +29,8 @@ import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.SmartList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class ParseUtil extends ParseUtilBase {
private ParseUtil() { }
@@ -57,7 +57,6 @@ public class ParseUtil extends ParseUtilBase {
}
private static class JavaMissingTokenInserter extends MissingTokenInserter {
public JavaMissingTokenInserter(final CompositeElement root,
final Lexer lexer,
final int startOffset,
@@ -71,6 +70,7 @@ public class ParseUtil extends ParseUtilBase {
@Override
public void invoke() {
super.invoke();
moveEmptyImportList(myRoot);
bindComments(myRoot);
}
@@ -84,6 +84,26 @@ public class ParseUtil extends ParseUtilBase {
GTTokens.advance(next.getElementType(), myLexer);
}
protected boolean isInsertAfterElement(final TreeElement treeNext) {
return treeNext instanceof ModifierListElement;
}
private static void moveEmptyImportList(ASTNode root) {
TreeElement first = (TreeElement)root.getFirstChildNode();
TreeElement next = (TreeElement)TreeUtil.skipElements(first, ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET);
if (next == null) return;
if (next.getElementType() == JavaElementType.PACKAGE_STATEMENT) {
first = next.getTreeNext();
next = (TreeElement)TreeUtil.skipElements(first, ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET);
}
if (next != null && next != first && isEmptyImportList(next)) {
next.rawRemove();
first.rawInsertBeforeMe(next);
}
}
private static void bindComments(ASTNode root) {
if (TreeUtil.isLeafOrCollapsedChameleon(root)) return;
@@ -91,10 +111,7 @@ public class ParseUtil extends ParseUtilBase {
((TreeElement)root).acceptTree(new RecursiveTreeElementWalkingVisitor(false) {
@Override
protected void visitNode(TreeElement child) {
IElementType type = child.getElementType();
if (type == JavaDocElementType.DOC_COMMENT ||
type == JavaTokenType.END_OF_LINE_COMMENT ||
type == JavaTokenType.C_STYLE_COMMENT) {
if (ElementType.JAVA_COMMENT_BIT_SET.contains(child.getElementType())) {
comments.add(child);
}
if (TreeUtil.isLeafOrCollapsedChameleon(child)) return;
@@ -102,19 +119,15 @@ public class ParseUtil extends ParseUtilBase {
super.visitNode(child);
}
});
ListIterator<ASTNode> iterator;
// we'll only bind additional preceding comments in pass 2 when the declaration does not yet have a "doc comment"
boolean docCommentBound = false;
Iterator<ASTNode> iterator = comments.iterator();
iterator = comments.listIterator();
while (iterator.hasNext()) {
ASTNode child = iterator.next();
IElementType type = child.getElementType();
if (type == JavaDocElementType.DOC_COMMENT) {
if (bindDocComment((TreeElement)child)) {
iterator.remove();
docCommentBound = true;
}
if (bindDocComment((TreeElement)child)) iterator.remove();
}
// bind "trailing comments" (like "int a; // comment")
else if (type == JavaTokenType.END_OF_LINE_COMMENT || type == JavaTokenType.C_STYLE_COMMENT) {
@@ -123,13 +136,11 @@ public class ParseUtil extends ParseUtilBase {
}
// pass 2: bind preceding comments (like "// comment \n void f();")
if (!docCommentBound) {
for (ASTNode child : comments) {
if (child.getElementType() == JavaTokenType.END_OF_LINE_COMMENT || child.getElementType() == JavaTokenType.C_STYLE_COMMENT) {
TreeElement next = (TreeElement)TreeUtil.skipElements(child, PRECEDING_COMMENT_OR_SPACE_BIT_SET);
bindPrecedingComment((TreeElement)child, next);
}
}
iterator = comments.listIterator(comments.size());
while (iterator.hasPrevious()) {
ASTNode child = iterator.previous();
TreeElement next = (TreeElement)TreeUtil.skipElements(child.getTreeNext(), ElementType.JAVA_WHITESPACE_BIT_SET);
bindPrecedingComment((TreeElement)child, next);
}
}
@@ -140,10 +151,8 @@ public class ParseUtil extends ParseUtilBase {
TreeElement importList = null;
// bypass meaningless tokens and hold'em in hands
while (element.getElementType() == TokenType.WHITE_SPACE ||
element.getElementType() == JavaTokenType.C_STYLE_COMMENT ||
element.getElementType() == JavaTokenType.END_OF_LINE_COMMENT ||
element.getElementType() == JavaElementType.IMPORT_LIST && element.getTextLength() == 0) {
while (ElementType.JAVA_PLAIN_COMMENT_OR_WHITESPACE_BIT_SET.contains(element.getElementType()) ||
isEmptyImportList(element)) {
if (element.getElementType() == JavaElementType.IMPORT_LIST) importList = element;
if (startSpaces == null) startSpaces = element;
element = element.getTreeNext();
@@ -199,68 +208,40 @@ public class ParseUtil extends ParseUtilBase {
return false;
}
private static final TokenSet BIND_PRECEDING_COMMENT_BIT_SET = TokenSet.create(JavaElementType.FIELD, JavaElementType.METHOD,
JavaElementType.CLASS, JavaElementType.CLASS_INITIALIZER);
private static final TokenSet PRECEDING_COMMENT_OR_SPACE_BIT_SET = ElementType.JAVA_COMMENT_OR_WHITESPACE_BIT_SET;
private static final TokenSet BIND_PRECEDING_COMMENT_BIT_SET = ElementType.FULL_MEMBER_BIT_SET;
private static void bindPrecedingComment(TreeElement comment, ASTNode bindTo) {
if (bindTo == null || bindTo.getFirstChildNode() != null && bindTo.getFirstChildNode().getElementType() == JavaDocElementType.DOC_COMMENT) return;
if (bindTo == null ||
!BIND_PRECEDING_COMMENT_BIT_SET.contains(bindTo.getElementType()) ||
(bindTo.getFirstChildNode() != null && bindTo.getFirstChildNode().getElementType() == JavaDocElementType.DOC_COMMENT) ||
!isBindingComment(comment)) return;
if (bindTo.getElementType() == JavaElementType.IMPORT_LIST && bindTo.getTextLength() == 0) {
bindTo = bindTo.getTreeNext();
}
ASTNode toStart = isBindingComment(comment) ? comment : null;
if (bindTo != null && BIND_PRECEDING_COMMENT_BIT_SET.contains(bindTo.getElementType())) {
for (ASTNode child = comment; child != bindTo; child = child.getTreeNext()) {
if (child.getElementType() == TokenType.WHITE_SPACE) {
int count = StringUtil.getLineBreakCount(child.getText());
if (count > 1) toStart = null;
}
else {
if (child.getTreePrev() != null && child.getTreePrev().getElementType() == TokenType.WHITE_SPACE) {
LeafElement prev = (LeafElement)child.getTreePrev();
char lastC = prev.charAt(prev.getTextLength() - 1);
if (lastC == '\n' || lastC == '\r') toStart = isBindingComment(child) ? child : null;
}
else {
return;
}
}
}
if (toStart == null) return;
TreeElement first = (TreeElement)bindTo.getFirstChildNode();
TreeElement child = (TreeElement)toStart;
while (child != bindTo) {
TreeElement next = child.getTreeNext();
if (child.getElementType() != JavaElementType.IMPORT_LIST) {
child.rawRemove();
first.rawInsertBeforeMe(child);
}
child = next;
}
final TreeElement first = (TreeElement)bindTo.getFirstChildNode();
TreeElement child = comment;
while (child != bindTo) {
final TreeElement next = child.getTreeNext();
child.rawRemove();
first.rawInsertBeforeMe(child);
child = next;
}
}
private static boolean isBindingComment(final ASTNode node) {
ASTNode prev = node.getTreePrev();
if (prev != null) {
if (prev.getElementType() != TokenType.WHITE_SPACE) {
return false;
}
else {
if (!prev.textContains('\n')) return false;
}
}
private static boolean isBindingComment(final ASTNode comment) {
ASTNode prev = comment.getTreePrev();
if (isEmptyImportList(prev)) prev = prev.getTreePrev();
final boolean prevOk = prev == null ||
(prev.getElementType() == TokenType.WHITE_SPACE && prev.textContains('\n'));
return true;
final ASTNode next = comment.getTreeNext();
final boolean nextOk = next != null &&
next.getElementType() == TokenType.WHITE_SPACE &&
StringUtil.getLineBreakCount(next.getText()) < 2;
return prevOk && nextOk;
}
protected boolean isInsertAfterElement(final TreeElement treeNext) {
return treeNext instanceof ModifierListElement;
private static boolean isEmptyImportList(final ASTNode node) {
return node != null && node.getElementType() == JavaElementType.IMPORT_LIST && node.getTextLength() == 0;
}
}
}
@@ -28,8 +28,11 @@ public interface ElementType extends JavaTokenType, JavaDocTokenType,
JspElementType.JSP_TEMPLATE_EXPRESSION);
TokenSet JAVA_WHITESPACE_BIT_SET = TokenSet.create(WHITE_SPACE);
TokenSet JAVA_COMMENT_BIT_SET = TokenSet.create(END_OF_LINE_COMMENT, C_STYLE_COMMENT, DOC_COMMENT);
TokenSet JAVA_PLAIN_COMMENT_BIT_SET = TokenSet.create(END_OF_LINE_COMMENT, C_STYLE_COMMENT);
TokenSet JAVA_COMMENT_BIT_SET = TokenSet.orSet(JAVA_PLAIN_COMMENT_BIT_SET, TokenSet.create(DOC_COMMENT));
TokenSet JAVA_PLAIN_COMMENT_OR_WHITESPACE_BIT_SET = TokenSet.orSet(JAVA_WHITESPACE_BIT_SET, JAVA_PLAIN_COMMENT_BIT_SET);
TokenSet JAVA_COMMENT_OR_WHITESPACE_BIT_SET = TokenSet.orSet(JAVA_WHITESPACE_BIT_SET, JAVA_COMMENT_BIT_SET);
TokenSet KEYWORD_BIT_SET = TokenSet.create(
@@ -1,8 +1,8 @@
PsiJavaFile:ImportListBug.java
PsiDocComment
PsiDocToken:DOC_COMMENT_END('/**/')
PsiImportList
<empty list>
PsiDocComment
PsiDocToken:DOC_COMMENT_END('/**/')
PsiClass:AClass
PsiDocComment
PsiDocToken:DOC_COMMENT_START('/**')
@@ -20,4 +20,4 @@ PsiJavaFile:ImportListBug.java
<empty list>
PsiJavaToken:LBRACE('{')
PsiWhiteSpace('\n')
PsiJavaToken:RBRACE('}')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,5 @@
/** doc */
class C {
// plain
int f;
}
@@ -0,0 +1,36 @@
PsiJavaFile:BindBefore5.java
PsiImportList
<empty list>
PsiClass:C
PsiDocComment
PsiDocToken:DOC_COMMENT_START('/**')
PsiWhiteSpace(' ')
PsiDocToken:DOC_COMMENT_DATA('doc ')
PsiDocToken:DOC_COMMENT_END('*/')
PsiWhiteSpace('\n')
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:C('C')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiWhiteSpace('\n ')
PsiField:f
PsiComment(END_OF_LINE_COMMENT)('// plain')
PsiWhiteSpace('\n ')
PsiModifierList:
<empty list>
PsiTypeElement:int
PsiKeyword:int('int')
PsiWhiteSpace(' ')
PsiIdentifier:f('f')
PsiJavaToken:SEMICOLON(';')
PsiWhiteSpace('\n')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,4 @@
package p;
// comment
class A { }
@@ -0,0 +1,30 @@
PsiJavaFile:BindBeforeClass1.java
PsiPackageStatement:p
PsiKeyword:package('package')
PsiWhiteSpace(' ')
PsiJavaCodeReferenceElement:p
PsiIdentifier:p('p')
PsiReferenceParameterList
<empty list>
PsiJavaToken:SEMICOLON(';')
PsiImportList
<empty list>
PsiWhiteSpace('\n\n')
PsiClass:A
PsiComment(END_OF_LINE_COMMENT)('// comment')
PsiWhiteSpace('\n')
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:A('A')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiWhiteSpace(' ')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,2 @@
// comment
class A { }
@@ -0,0 +1,21 @@
PsiJavaFile:BindBeforeClass2.java
PsiImportList
<empty list>
PsiClass:A
PsiComment(END_OF_LINE_COMMENT)('// comment')
PsiWhiteSpace('\n')
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:A('A')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiWhiteSpace(' ')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,3 @@
// comment
class A { }
@@ -0,0 +1,21 @@
PsiJavaFile:BindBeforeClass3.java
PsiImportList
<empty list>
PsiComment(END_OF_LINE_COMMENT)('// comment')
PsiWhiteSpace('\n\n')
PsiClass:A
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:A('A')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiWhiteSpace(' ')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,2 @@
package p;// comment
class A { }
@@ -0,0 +1,29 @@
PsiJavaFile:BindBeforeClass4.java
PsiPackageStatement:p
PsiKeyword:package('package')
PsiWhiteSpace(' ')
PsiJavaCodeReferenceElement:p
PsiIdentifier:p('p')
PsiReferenceParameterList
<empty list>
PsiJavaToken:SEMICOLON(';')
PsiImportList
<empty list>
PsiComment(END_OF_LINE_COMMENT)('// comment')
PsiWhiteSpace('\n')
PsiClass:A
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:A('A')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiWhiteSpace(' ')
PsiJavaToken:RBRACE('}')
@@ -0,0 +1,3 @@
// comment 1
// comment 2
class A { }
@@ -0,0 +1,23 @@
PsiJavaFile:BindBeforeClass5.java
PsiImportList
<empty list>
PsiClass:A
PsiComment(END_OF_LINE_COMMENT)('// comment 1')
PsiWhiteSpace('\n')
PsiComment(END_OF_LINE_COMMENT)('// comment 2')
PsiWhiteSpace('\n')
PsiModifierList:
<empty list>
PsiKeyword:class('class')
PsiWhiteSpace(' ')
PsiIdentifier:A('A')
PsiTypeParameterList
<empty list>
PsiReferenceList
<empty list>
PsiReferenceList
<empty list>
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiWhiteSpace(' ')
PsiJavaToken:RBRACE('}')
@@ -1,8 +1,8 @@
PsiJavaFile:Errors.java
PsiComment(END_OF_LINE_COMMENT)('// illegal modifier combinations')
PsiWhiteSpace('\n\n')
PsiImportList
<empty list>
PsiComment(END_OF_LINE_COMMENT)('// illegal modifier combinations')
PsiWhiteSpace('\n\n')
PsiClass:a
PsiModifierList:abstract public
PsiKeyword:abstract('abstract')
@@ -1,9 +1,9 @@
PsiJavaFile:Anonymous2.java
PsiComment(END_OF_LINE_COMMENT)('// "Copy 'a' to temp final variable" "true"')
PsiWhiteSpace('\n')
PsiImportList
<empty list>
PsiClass:DoubleTrouble
PsiComment(END_OF_LINE_COMMENT)('// "Copy 'a' to temp final variable" "true"')
PsiWhiteSpace('\n')
PsiModifierList:public
PsiKeyword:public('public')
PsiWhiteSpace(' ')
@@ -13,9 +13,9 @@ PsiJavaFile:BindDocComment1.java
PsiReferenceParameterList
<empty list>
PsiJavaToken:SEMICOLON(';')
PsiWhiteSpace('\n')
PsiImportList
<empty list>
PsiWhiteSpace('\n')
PsiClass:A
PsiModifierList:
<empty list>
@@ -31,4 +31,4 @@ PsiJavaFile:BindDocComment1.java
PsiWhiteSpace(' ')
PsiJavaToken:LBRACE('{')
PsiWhiteSpace(' ')
PsiJavaToken:RBRACE('}')
PsiJavaToken:RBRACE('}')
@@ -4,7 +4,6 @@ package com.intellij.lang.java.parser.declarationParsing;
import com.intellij.lang.java.parser.JavaParsingTestCase;
// todo: fix parser or test data and uncomment test
public class ClassParsingTest extends JavaParsingTestCase {
public ClassParsingTest() {
super("parser-full/declarationParsing/class");
@@ -32,9 +31,11 @@ public class ClassParsingTest extends JavaParsingTestCase {
public void testEnumWithImport() { doTest(true); }
public void testEnumWithoutConstants() { doTest(true); }
public void testEmptyImportList() { doTest(true); }
public void testLongClass() {doTest(false);}
//public void testIncompleteAnnotation() {doTest(true);}
public void testLongClass() { doTest(false); }
public void testIncompleteAnnotation() { doTestDefaultParser(true); } // todo: fix
//public void testExtraOpeningBraceInMethod() {doTest(true);}
public void testExtraClosingBraceInMethod() {doTest(true);}
public void testExtraOpeningBraceInMethod() { doTestDefaultParser(true); } // todo: fix
public void testExtraClosingBraceInMethod() { doTest(true);}
public void testError() { doTestDefaultParser(true);} // todo: fix
}
@@ -14,4 +14,11 @@ public class CommentBindingTest extends JavaParsingTestCase {
public void testBindBefore3() { doTest(true); }
public void testBindBefore3a() { doTest(true); }
public void testBindBefore4() { doTest(true); }
public void testBindBefore5() { doTest(true); }
public void testBindBeforeClass1() { doTest(true); }
public void testBindBeforeClass2() { doTest(true); }
public void testBindBeforeClass3() { doTest(true); }
public void testBindBeforeClass4() { doTest(true); }
public void testBindBeforeClass5() { doTest(true); }
}
@@ -36,11 +36,13 @@ public class MissingTokenInserter {
private final TokenProcessor myProcessor;
private final ParsingContext myContext;
public MissingTokenInserter(final CompositeElement root, final Lexer lexer,
final int startOffset,
final int endOffset,
final int state,
final TokenProcessor processor, final ParsingContext context) {
public MissingTokenInserter(final CompositeElement root,
final Lexer lexer,
final int startOffset,
final int endOffset,
final int state,
final TokenProcessor processor,
final ParsingContext context) {
myRoot = root;
myLexer = lexer;
myStartOffset = startOffset;
@@ -66,8 +68,8 @@ public class MissingTokenInserter {
}
return;
}
{
// Missing in the begining
else {
// Missing in the beginning
final IElementType tokenType = getNextTokenType();
if (tokenType != leaf.getElementType() && myProcessor.isTokenValid(tokenType)) {
final TreeElement firstMissing = myProcessor.process(myLexer, myContext);