IDEA-93662 Groovy: complete type in tuple declarations

This commit is contained in:
Max Medvedev
2012-11-04 19:23:18 +04:00
parent 674f405384
commit d058d4b92f
9 changed files with 97 additions and 45 deletions
@@ -59,6 +59,7 @@ lcurly.expected='{' expected
comma.expected=',' expected
rcurly.expected='}' expected
rparen.expected=')' expected
comma.or.rparen.expected=',' or ')' expected
semi.expected=';' expected
gt.expected='>' expected
else.without.if='else' without 'if'
@@ -358,8 +358,9 @@ public class GroovyCompletionContributor extends CompletionContributor {
private static boolean couldContainReference(PsiElement position) {
return IN_CATCH_TYPE.accepts(position) ||
AFTER_AT.accepts(position) ||
GroovyCompletionUtil.isFirstElementAfterPossibleModifiersInVariableDeclaration(position, true);
AFTER_AT.accepts(position) ||
GroovyCompletionUtil.isFirstElementAfterPossibleModifiersInVariableDeclaration(position, true) ||
GroovyCompletionUtil.isTupleVarNameWithoutTypeDeclared(position);
}
public static boolean isClassNamePossible(PsiElement position) {
@@ -734,6 +735,10 @@ public class GroovyCompletionContributor extends CompletionContributor {
iterator.advance();
}
while (!iterator.atEnd() && WHITE_SPACES_OR_COMMENTS.contains(iterator.getTokenType())) {
iterator.advance();
}
if (!iterator.atEnd() && iterator.getTokenType() == mLPAREN) {
return true;
}
@@ -106,7 +106,7 @@ public class GroovyCompletionData {
for (String keyword : addExtendsImplements(position)) {
result.addElement(keyword(keyword, TailType.HUMBLE_SPACE_BEFORE_WORD));
}
addExtendsForTypeParams(position, result);
registerControlCompletion(position, result);
@@ -216,7 +216,7 @@ public class GroovyCompletionData {
if (ext && impl) {
return new String[]{PsiKeyword.EXTENDS, PsiKeyword.IMPLEMENTS};
}
return new String[]{ext ? PsiKeyword.EXTENDS : PsiKeyword.IMPLEMENTS};
}
@@ -541,6 +541,8 @@ public class GroovyCompletionData {
}
if (isTupleVarNameWithoutTypeDeclared(context)) return true;
if (previous != null && GroovyTokenTypes.mAT.equals(previous.getNode().getElementType())) {
return false;
}
@@ -186,10 +186,11 @@ public class GroovyCompletionUtil {
return ((GrParameter)parent).getTypeElementGroovy() == null;
}
final PsiElement parent1 = parent.getParent();
if (!(parent1 instanceof GrVariableDeclaration)) return false;
final PsiElement pparent = parent.getParent();
if (!(pparent instanceof GrVariableDeclaration)) return false;
if (((GrVariableDeclaration)pparent).isTuple()) return false;
final GrVariableDeclaration variableDeclaration = (GrVariableDeclaration)parent1;
final GrVariableDeclaration variableDeclaration = (GrVariableDeclaration)pparent;
if (variableDeclaration.getTypeElementGroovy() != null) return false;
return variableDeclaration.getVariables()[0] == parent;
@@ -232,7 +233,6 @@ public class GroovyCompletionUtil {
context.getParent().getParent().getParent().getParent() instanceof GrTypeDefinitionBody &&
context.getTextRange().getStartOffset() ==
context.getParent().getParent().getParent().getParent().getTextRange().getStartOffset();
}
@@ -307,8 +307,8 @@ public class GroovyCompletionUtil {
private static boolean getterMatches(PrefixMatcher matcher, PsiMethod element, String importedName) {
return isSimplePropertyGetter(element) &&
(matcher.prefixMatches(getGetterNameNonBoolean(importedName)) ||
element.getReturnType() == PsiType.BOOLEAN && matcher.prefixMatches(getGetterNameBoolean(importedName)));
(matcher.prefixMatches(getGetterNameNonBoolean(importedName)) ||
element.getReturnType() == PsiType.BOOLEAN && matcher.prefixMatches(getGetterNameBoolean(importedName)));
}
public static LookupElement createClassLookupItem(PsiClass psiClass) {
@@ -316,7 +316,9 @@ public class GroovyCompletionUtil {
return AllClassesGetter.createLookupItem(psiClass, new GroovyClassNameInsertHandler());
}
private static List<? extends LookupElement> generateLookupForImportedElement(GroovyResolveResult resolveResult, String importedName, boolean alias) {
private static List<? extends LookupElement> generateLookupForImportedElement(GroovyResolveResult resolveResult,
String importedName,
boolean alias) {
final PsiElement element = resolveResult.getElement();
assert element != null;
final PsiSubstitutor substitutor = resolveResult.getSubstitutor();
@@ -380,7 +382,10 @@ public class GroovyCompletionUtil {
}
private static LookupElementBuilder setTypeText(PsiElement element, LookupElementBuilder builder, PsiSubstitutor substitutor, @Nullable PsiElement position) {
private static LookupElementBuilder setTypeText(PsiElement element,
LookupElementBuilder builder,
PsiSubstitutor substitutor,
@Nullable PsiElement position) {
PsiType type = null;
if (element instanceof GrVariable) {
if (position != null && GroovyRefactoringUtil.isLocalVariable(element)) {
@@ -599,4 +604,14 @@ public class GroovyCompletionUtil {
static boolean isTypelessParameter(PsiElement context) {
return (context.getParent() instanceof GrParameter && ((GrParameter)context.getParent()).getTypeElementGroovy() == null);
}
public static boolean isTupleVarNameWithoutTypeDeclared(PsiElement position) {
PsiElement parent = position.getParent();
PsiElement pparent = parent.getParent();
return parent instanceof GrVariable &&
((GrVariable)parent).getNameIdentifierGroovy() == position &&
((GrVariable)parent).getTypeElementGroovy() == null &&
pparent instanceof GrVariableDeclaration &&
((GrVariableDeclaration)pparent).isTuple();
}
}
@@ -17,60 +17,80 @@ package org.jetbrains.plugins.groovy.lang.parser.parsing.statements;
import com.intellij.lang.PsiBuilder;
import com.intellij.psi.tree.IElementType;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyBundle;
import org.jetbrains.plugins.groovy.lang.parser.parsing.types.TypeSpec;
import org.jetbrains.plugins.groovy.lang.parser.parsing.util.ParserUtils;
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.*;
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mCOMMA;
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mIDENT;
import static org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes.mLPAREN;
import static org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes.*;
/**
* @author ilyas
*/
public class TupleParse {
public static boolean parseTuple(PsiBuilder builder, @Nullable IElementType tupleType, IElementType componentType) {
if (builder.getTokenType() != mLPAREN) return false;
public static boolean parseTupleForAssignment(PsiBuilder builder) {
PsiBuilder.Marker marker = parseTuple(builder, REFERENCE_EXPRESSION, false);
if (marker == null) return false;
marker.done(TUPLE_EXPRESSION);
return true;
}
public static boolean parseTupleForVariableDeclaration(PsiBuilder builder) {
PsiBuilder.Marker marker = parseTuple(builder, VARIABLE, true);
if (marker == null) return false;
marker.drop();
return true;
}
public static PsiBuilder.Marker parseTuple(PsiBuilder builder, IElementType componentType, boolean acceptType) {
if (builder.getTokenType() != mLPAREN) return null;
final PsiBuilder.Marker marker = builder.mark();
builder.advanceLexer();
int count = 0;
do {
//skip unnecessary commas
while (ParserUtils.getToken(builder, mCOMMA)) {
count++;
builder.error(GroovyBundle.message("identifier.expected"));
}
//parse modifiers for definitions
PsiBuilder.Marker typeMarker = builder.mark();
TypeSpec.parse(builder);
if (builder.getTokenType() != mIDENT) {
typeMarker.rollbackTo();
if (acceptType) {
//parse modifiers for definitions
PsiBuilder.Marker typeMarker = builder.mark();
TypeSpec.parse(builder);
if (builder.getTokenType() != mIDENT) {
typeMarker.rollbackTo();
}
else {
typeMarker.drop();
}
}
else {
typeMarker.drop();
}
PsiBuilder.Marker varMarker = builder.mark();
PsiBuilder.Marker componentMarker = builder.mark();
if (!ParserUtils.getToken(builder, mIDENT)) {
builder.error(GroovyBundle.message("identifier.expected"));
varMarker.drop();
componentMarker.drop();
}
else {
varMarker.done(componentType);
componentMarker.done(componentType);
count++;
}
}
while (ParserUtils.getToken(builder, mCOMMA));
if (ParserUtils.getToken(builder, mRPAREN)) {
if (tupleType != null) {
marker.done(tupleType);
}
else {
marker.drop();
}
return true;
return marker;
}
else if (count > 0) { //accept tuple if there was at least one comma or parsed tuple element inside it
builder.error(GroovyBundle.message("comma.or.rparen.expected"));
return marker;
}
else {
marker.rollbackTo();
return false;
return null;
}
}
}
@@ -203,16 +203,11 @@ public class VariableDefinitions implements GroovyElementTypes {
}
private static IElementType parseDeclarator(PsiBuilder builder, boolean isTuple) {
if (!isTuple) {
if (builder.getTokenType() == mIDENT) {
ParserUtils.getToken(builder, mIDENT);
return mIDENT;
}
if (isTuple && builder.getTokenType() == mLPAREN && TupleParse.parseTupleForVariableDeclaration(builder)) {
return TUPLE_DECLARATION;
}
else if (builder.getTokenType() == mLPAREN && isTuple) {
if (TupleParse.parseTuple(builder, null, VARIABLE)) {
return TUPLE_DECLARATION;
}
if (!isTuple && ParserUtils.getToken(builder, mIDENT)) {
return mIDENT;
}
return WRONGWAY;
}
@@ -59,7 +59,7 @@ public class AssignmentExpression implements GroovyElementTypes {
private static boolean parseSide(PsiBuilder builder, GroovyParser parser, boolean tuple, boolean comExprAllowed) {
if (tuple) {
return TupleParse.parseTuple(builder, TUPLE_EXPRESSION, REFERENCE_EXPRESSION);
return TupleParse.parseTupleForAssignment(builder);
}
if (comExprAllowed) {
@@ -95,6 +95,7 @@ public class KeywordCompletionTest extends CompletionTestBase {
void testAssertInClosure() { doTest() }
void testAfterLabel() { doTest() }
void testKeywordsInParentheses() { doTest() }
void testCompletionInTupleVar(){ doTest() }
String basePath = TestUtils.testDataPath + 'groovy/oldCompletion/keyword'
@@ -0,0 +1,13 @@
def (<caret>a, b) = [1, 2]
-----
boolean
byte
char
double
float
i
int
integer
long
short
void