JavaKeywordCompletion: simplify

make short code out of long ElementFilters
remove dead logic
cleanup unused filters
also fixes IDEA-179855 Double "final" suggestion after annotation
This commit is contained in:
peter
2017-10-13 09:49:22 +02:00
parent 100ad715f8
commit 44a2ffa2ff
12 changed files with 42 additions and 315 deletions
@@ -21,17 +21,11 @@ import com.intellij.codeInsight.TailTypes;
import com.intellij.codeInsight.completion.util.ParenthesesInsertHandler;
import com.intellij.codeInsight.daemon.impl.analysis.LambdaHighlightingUtil;
import com.intellij.codeInsight.lookup.*;
import com.intellij.openapi.util.AtomicNotNullLazyValue;
import com.intellij.openapi.util.Conditions;
import com.intellij.openapi.util.NotNullLazyValue;
import com.intellij.patterns.ElementPattern;
import com.intellij.patterns.PsiElementPattern;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.filters.*;
import com.intellij.psi.filters.position.*;
import com.intellij.psi.impl.source.jsp.jspJava.JspClassLevelDeclarationStatement;
import com.intellij.psi.jsp.JspElementType;
import com.intellij.psi.templateLanguages.OuterLanguageElement;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
@@ -41,7 +35,6 @@ import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.JBIterable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@@ -63,59 +56,33 @@ public class JavaKeywordCompletion {
psiElement().withTreeParent(
psiElement(PsiParameterList.class).andNot(psiElement(PsiAnnotationParameterList.class)))));
private static final AndFilter START_OF_CODE_FRAGMENT = new AndFilter(
new ScopeFilter(new AndFilter(
new ClassFilter(JavaCodeFragment.class),
new ClassFilter(PsiExpressionCodeFragment.class, false),
new ClassFilter(PsiJavaCodeReferenceCodeFragment.class, false),
new ClassFilter(PsiTypeCodeFragment.class, false)
)),
new StartElementFilter()
);
private static boolean isStatementCodeFragment(PsiFile file) {
return file instanceof JavaCodeFragment &&
!(file instanceof PsiExpressionCodeFragment ||
file instanceof PsiJavaCodeReferenceCodeFragment ||
file instanceof PsiTypeCodeFragment);
}
static final NotNullLazyValue<ElementFilter> END_OF_BLOCK = new AtomicNotNullLazyValue<ElementFilter>() {
@NotNull
@Override
protected ElementFilter compute() {
return new OrFilter(
new AndFilter(
new LeftNeighbour(
new OrFilter(
new AndFilter (
new TextFilter("{", "}", ";", ":", "else"),
new NotFilter (
new SuperParentFilter(new ClassFilter(PsiAnnotation.class))
)
),
new ElementFilter() {
@Override
public boolean isAcceptable(Object element, @Nullable PsiElement context) {
return ((PsiElement)element).getText().endsWith("*/");
}
@Override
public boolean isClassAcceptable(Class hintClass) {
return true;
}
},
new TokenTypeFilter(JspElementType.HOLDER_TEMPLATE_DATA),
new ClassFilter(OuterLanguageElement.class),
new AndFilter(
new TextFilter(")"),
new NotFilter(
new OrFilter(
new ParentElementFilter(new ClassFilter(PsiExpressionList.class)),
new ParentElementFilter(new ClassFilter(PsiParameterList.class)),
new ParentElementFilter(new ClassFilter(PsiTypeCastExpression.class))
)
)
))),
new NotFilter(new TextFilter("."))
),
START_OF_CODE_FRAGMENT
);
static boolean isEndOfBlock(@NotNull PsiElement element) {
PsiElement prev = prevSignificantLeaf(element);
if (prev == null) {
PsiFile file = element.getContainingFile();
return !(file instanceof PsiCodeFragment) || isStatementCodeFragment(file);
}
};
if (psiElement().inside(psiAnnotation()).accepts(prev)) return false;
if (prev instanceof OuterLanguageElement) return true;
if (psiElement().withText(string().oneOf("{", "}", ";", ":", "else")).accepts(prev)) return true;
if (prev.textMatches(")")) {
PsiElement parent = prev.getParent();
return !(parent instanceof PsiExpressionList ||
parent instanceof PsiParameterList ||
parent instanceof PsiTypeCastExpression);
}
return false;
}
static final ElementPattern<PsiElement> START_SWITCH =
psiElement().afterLeaf(psiElement().withText("{").withParents(PsiCodeBlock.class, PsiSwitchStatement.class));
@@ -135,16 +102,6 @@ public class JavaKeywordCompletion {
PsiKeyword.CHAR, PsiKeyword.BYTE
);
private static final NotNullLazyValue<ElementFilter> CLASS_BODY = new AtomicNotNullLazyValue<ElementFilter>() {
@NotNull
@Override
protected ElementFilter compute() {
return new OrFilter(
new AfterElementFilter(new TextFilter("{")),
new ScopeFilter(new ClassFilter(JspClassLevelDeclarationStatement.class)));
}
};
static final PsiElementPattern<PsiElement,?> START_FOR = psiElement().afterLeaf(psiElement().withText("(").afterLeaf("for"));
private static final ElementPattern<PsiElement> CLASS_REFERENCE =
psiElement().withParent(psiReferenceExpression().referencing(psiClass().andNot(psiElement(PsiTypeParameter.class))));
@@ -165,7 +122,6 @@ public class JavaKeywordCompletion {
private final CompletionParameters myParameters;
private final JavaCompletionSession mySession;
private final PsiElement myPosition;
private final String myPrefix;
private final PrefixMatcher myKeywordMatcher;
private final List<LookupElement> myResults = new ArrayList<>();
private final PsiElement myPrevLeaf;
@@ -173,8 +129,7 @@ public class JavaKeywordCompletion {
JavaKeywordCompletion(CompletionParameters parameters, JavaCompletionSession session) {
myParameters = parameters;
mySession = session;
myPrefix = session.getMatcher().getPrefix();
myKeywordMatcher = new FixingLayoutPlainMatcher(myPrefix);
myKeywordMatcher = new FixingLayoutPlainMatcher(session.getMatcher().getPrefix());
myPosition = parameters.getPosition();
myPrevLeaf = prevSignificantLeaf(myPosition);
@@ -494,7 +449,7 @@ public class JavaKeywordCompletion {
addKeyword(new OverridableSpace(createKeyword(PsiKeyword.PACKAGE), TailType.HUMBLE_SPACE_BEFORE_WORD));
addKeyword(new OverridableSpace(createKeyword(PsiKeyword.IMPORT), TailType.HUMBLE_SPACE_BEFORE_WORD));
}
else if (END_OF_BLOCK.getValue().isAcceptable(myPosition, myPosition) && PsiTreeUtil.getParentOfType(myPosition, PsiMember.class) == null) {
else if (isEndOfBlock(myPosition) && PsiTreeUtil.getParentOfType(myPosition, PsiMember.class) == null) {
addKeyword(new OverridableSpace(createKeyword(PsiKeyword.IMPORT), TailType.HUMBLE_SPACE_BEFORE_WORD));
}
}
@@ -637,7 +592,7 @@ public class JavaKeywordCompletion {
return true;
}
return END_OF_BLOCK.getValue().isAcceptable(position, position);
return isEndOfBlock(position);
}
static boolean isAfterPrimitiveOrArrayType(PsiElement element) {
@@ -708,16 +663,6 @@ public class JavaKeywordCompletion {
static boolean isDeclarationStart(@NotNull PsiElement position) {
if (psiElement().afterLeaf("@", ".").accepts(position)) return false;
if (new FilterPattern(CLASS_BODY.getValue()).accepts(position)) {
if (new FilterPattern(END_OF_BLOCK.getValue()).accepts(position)) return true;
if (psiElement().afterLeaf(or(
psiElement().inside(PsiModifierList.class),
psiElement().withElementType(JavaTokenType.GT).inside(PsiTypeParameterList.class)
)).accepts(position)) {
return true;
}
}
PsiElement parent = position.getParent();
if (parent instanceof PsiJavaCodeReferenceElement && parent.getParent() instanceof PsiTypeElement) {
PsiElement typeHolder = psiApi().parents(parent.getParent()).skipWhile(Conditions.instanceOf(PsiTypeElement.class)).first();
@@ -780,7 +725,7 @@ public class JavaKeywordCompletion {
return false;
}
if (END_OF_BLOCK.getValue().isAcceptable(position, position) &&
if (isEndOfBlock(position) &&
PsiTreeUtil.getParentOfType(position, PsiCodeBlock.class, true, PsiMember.class) != null) {
return !isForLoopMachinery(position);
}
@@ -150,14 +150,7 @@ public class ModifierChooser {
if (parent == null) return false;
PsiElement prev = FilterPositionUtil.searchNonSpaceNonCommentBack(element);
if (parent instanceof PsiJavaFile || parent instanceof PsiClass) {
if (prev == null || JavaKeywordCompletion.END_OF_BLOCK.getValue().isAcceptable(element, prev.getParent())) {
return true;
}
}
return false;
return (parent instanceof PsiJavaFile || parent instanceof PsiClass) &&
JavaKeywordCompletion.isEndOfBlock(element);
}
}
@@ -1,45 +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.psi.filters.position;
import com.intellij.psi.PsiElement;
import com.intellij.psi.filters.ElementFilter;
public class AfterElementFilter extends PositionElementFilter{
public AfterElementFilter(ElementFilter filter){
setFilter(filter);
}
@Override
public boolean isAcceptable(Object element, PsiElement scope){
if (!(element instanceof PsiElement)) return false;
PsiElement currentChild = getOwnerChild(scope, (PsiElement) element);
PsiElement currentElement = scope.getFirstChild();
while(currentElement != null){
if(currentElement == currentChild)
break;
if(getFilter().isAcceptable(currentElement, scope)){
return true;
}
currentElement = currentElement.getNextSibling();
}
return false;
}
public String toString(){
return "after(" + getFilter().toString() + ")";
}
}
@@ -1,33 +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.psi.filters.position;
import com.intellij.openapi.util.InvalidDataException;
import com.intellij.psi.PsiElement;
import com.intellij.psi.filters.FilterUtil;
import org.jdom.Element;
public class StartElementFilter extends PositionElementFilter{
@Override
public boolean isAcceptable(Object element, PsiElement context){
if (!(element instanceof PsiElement)) return false;
return FilterUtil.getPreviousElement((PsiElement) element, false) == null;
}
public String toString(){
return "start";
}
}
@@ -1,53 +0,0 @@
/*
* Copyright 2000-2014 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.psi.filters.position;
import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.javadoc.PsiDocToken;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.xml.XmlToken;
import com.intellij.util.ReflectionUtil;
public class TokenTypeFilter implements ElementFilter{
private final IElementType myType;
public TokenTypeFilter(IElementType type){
myType = type;
}
@Override
public boolean isClassAcceptable(Class hintClass){
return ReflectionUtil.isAssignable(PsiDocToken.class, hintClass) || ReflectionUtil.isAssignable(XmlToken.class, hintClass);
}
@Override
public boolean isAcceptable(Object element, PsiElement context){
if(element instanceof PsiElement) {
final ASTNode node = ((PsiElement)element).getNode();
return node != null && node.getElementType() == myType;
}
else if(element instanceof ASTNode){
return ((ASTNode)element).getElementType() == myType;
}
return false;
}
public String toString(){
return "token-type(" + myType + ")";
}
}
@@ -0,0 +1,3 @@
class A {
void foo(@Anno() fi<caret>)
}
@@ -0,0 +1,3 @@
class A {
void foo(@Anno() final <caret>)
}
@@ -164,6 +164,8 @@ public class KeywordCompletionTest extends LightCompletionTestCase {
checkResultByTestName();
}
public void testFinalAfterAnnotationAttributes() { doTest(); }
public void testTryInExpression() {
configureByTestName();
assertEquals("toString", myItems[0].getLookupString());
@@ -15,7 +15,6 @@
*/
package com.intellij.psi.filters.position;
import com.intellij.psi.PsiElement;
import com.intellij.psi.filters.ElementFilter;
public abstract class PositionElementFilter implements ElementFilter {
@@ -33,11 +32,4 @@ public abstract class PositionElementFilter implements ElementFilter {
public boolean isClassAcceptable(Class hintClass) {
return true;
}
protected static PsiElement getOwnerChild(final PsiElement scope, PsiElement element) {
while (element != null && element.getParent() != scope) {
element = element.getParent();
}
return element;
}
}
@@ -1,42 +0,0 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.psi.filters.position;
import com.intellij.psi.PsiElement;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.filters.FilterPositionUtil;
import org.jetbrains.annotations.Nullable;
public class AfterElement extends PositionElementFilter {
public AfterElement(ElementFilter filter){
setFilter(filter);
}
@Override
public boolean isAcceptable(Object element, @Nullable PsiElement context){
if (!(element instanceof PsiElement)) return false;
final PsiElement previous = FilterPositionUtil.searchNonSpaceNonCommentBack((PsiElement) element, true);
if(previous != null){
return getFilter().isAcceptable(previous, context);
}
return false;
}
public String toString(){
return "after(" +getFilter()+")";
}
}
@@ -20,6 +20,9 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.filters.ElementFilter;
import com.intellij.psi.filters.FilterPositionUtil;
/**
* @deprecated please consider using {@link com.intellij.patterns.ElementPattern} or checks in code instead
*/
public class LeftNeighbour extends PositionElementFilter{
public LeftNeighbour(ElementFilter filter){
@@ -1,41 +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.psi.filters.position;
import com.intellij.psi.PsiElement;
import com.intellij.psi.filters.ElementFilter;
public class SuperParentFilter extends PositionElementFilter{
public SuperParentFilter(ElementFilter filter){
setFilter(filter);
}
@Override
public boolean isAcceptable(Object element, PsiElement scope){
if (!(element instanceof PsiElement)) return false;
while((element = ((PsiElement) element).getParent()) != null){
if(getFilter().isAcceptable(element, scope))
return true;
}
return false;
}
public String toString(){
return "super-parent(" +getFilter()+")";
}
}