Merge branch 'master' of git.labs.intellij.net:idea/community

This commit is contained in:
Bas Leijdekkers
2011-03-30 19:16:32 +02:00
24 changed files with 226 additions and 130 deletions
@@ -526,9 +526,12 @@ public class JavaCompletionData extends JavaAwareCompletionData{
return;
}
final PsiExpressionStatement expressionStatement = PsiTreeUtil.getParentOfType(position, PsiExpressionStatement.class);
if (expressionStatement != null && expressionStatement.getTextRange().getStartOffset() == position.getTextRange().getStartOffset()) {
if (!psiElement().withSuperParent(2, PsiSwitchStatement.class).accepts(expressionStatement)) {
PsiStatement statement = PsiTreeUtil.getParentOfType(position, PsiExpressionStatement.class);
if (statement == null) {
statement = PsiTreeUtil.getParentOfType(position, PsiDeclarationStatement.class);
}
if (statement != null && statement.getTextRange().getStartOffset() == position.getTextRange().getStartOffset()) {
if (!psiElement().withSuperParent(2, PsiSwitchStatement.class).accepts(statement)) {
result.addElement(createKeyword(position, PsiKeyword.FINAL));
}
}
@@ -103,11 +103,12 @@ public class OverrideImplementUtil {
Map<MethodSignature, PsiMethod> finals = new LinkedHashMap<MethodSignature,PsiMethod>();
Map<MethodSignature, PsiMethod> concretes = new LinkedHashMap<MethodSignature,PsiMethod>();
LOG.assertTrue(aClass.isValid());
Collection<HierarchicalMethodSignature> allMethodSigs = aClass.getVisibleSignatures();
PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(aClass.getProject()).getResolveHelper();
for (HierarchicalMethodSignature signature : allMethodSigs) {
PsiMethod method = signature.getMethod();
LOG.assertTrue(method.isValid());
LOG.assertTrue(method.isValid(), aClass);
if (method.hasModifierProperty(PsiModifier.STATIC) || !resolveHelper.isAccessible(method, aClass, aClass)) continue;
PsiClass hisClass = method.getContainingClass();
@@ -15,6 +15,7 @@
*/
package com.intellij.psi.impl;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.HierarchicalMethodSignatureImpl;
@@ -32,6 +33,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
public class PsiSuperMethodImplUtil {
private static final Logger LOG = Logger.getInstance("#com.intellij.psi.impl.PsiSuperMethodImplUtil");
private static final PsiCacheKey<Map<MethodSignature, HierarchicalMethodSignature>, PsiClass> SIGNATURES_KEY = PsiCacheKey
.create("SIGNATURES_KEY", new NotNullFunction<PsiClass, Map<MethodSignature, HierarchicalMethodSignature>>() {
@NotNull
@@ -134,6 +136,7 @@ public class PsiSuperMethodImplUtil {
});
for (PsiMethod method : aClass.getMethods()) {
LOG.assertTrue(method.isValid());
if (!includePrivates && method.hasModifierProperty(PsiModifier.PRIVATE)) continue;
final MethodSignatureBackedByPsiMethod signature = MethodSignatureBackedByPsiMethod.create(method, substitutor, isInRawContext);
HierarchicalMethodSignatureImpl newH = new HierarchicalMethodSignatureImpl(signature);
@@ -145,6 +148,7 @@ public class PsiSuperMethodImplUtil {
}
list.add(method);
LOG.assertTrue(newH.getMethod().isValid());
result.put(signature, newH);
map.put(signature, newH);
}
@@ -188,6 +192,7 @@ public class PsiSuperMethodImplUtil {
HierarchicalMethodSignatureImpl hierarchicalMethodSignature = entry.getValue();
MethodSignature methodSignature = entry.getKey();
if (result.get(methodSignature) == null && PsiUtil.isAccessible(hierarchicalMethodSignature.getMethod(), aClass, aClass)) {
LOG.assertTrue(hierarchicalMethodSignature.getMethod().isValid());
result.put(methodSignature, hierarchicalMethodSignature);
}
}
@@ -213,6 +218,7 @@ public class PsiSuperMethodImplUtil {
}
// just drop an invalid method declaration there - to highlight accordingly
else if (!result.containsKey(signature)) {
LOG.assertTrue(hierarchicalMethodSignature.getMethod().isValid());
result.put(signature, hierarchicalMethodSignature);
}
}
@@ -1 +1 @@
public enum Foo { private Fo<caret> }
public enum Foooooo { private Fooo<caret> }
@@ -1 +1 @@
public enum Foo { private Foo<caret> }
public enum Foooooo { private Foooooo<caret> }
@@ -0,0 +1,9 @@
import java.util.ArrayList;
public class Foooo {
{
for (fin<caret> Object o : new ArrayList())
}
}
@@ -1,6 +0,0 @@
public class Beda {
{
Object clsLoader;
cL<caret>
}
}
@@ -1,8 +0,0 @@
class Foo {
{
Object classLoader;
Object classBeforeLoader;
cL<caret>
}
}
@@ -1,8 +0,0 @@
class Foo {
{
Object classLoader;
Object classLoader2;
cL<caret>
}
}
@@ -5,7 +5,6 @@
package com.intellij.codeInsight.completion;
import com.intellij.JavaTestUtil;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.lookup.LookupElement;
import com.intellij.codeInsight.lookup.impl.LookupImpl;
import com.intellij.psi.PsiClass;
@@ -68,32 +67,6 @@ public class NormalCompletionOrderingTest extends CompletionSortingTestCase {
checkPreferredItems(0, "getService", "getService", "class");
}
public void testUppercaseMatters() throws Throwable {
final int old = CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE;
try {
CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE = CodeInsightSettings.FIRST_LETTER;
checkPreferredItems(0, "classLoader", "class", "classBeforeLoader", "clone");
}
finally {
CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE = old;
}
}
public void testUppercaseMatters2() throws Throwable {
final int old = CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE;
try {
CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE = CodeInsightSettings.ALL;
checkPreferredItems(0, "classLoader", "classLoader2");
}
finally {
CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE = old;
}
}
public void testHonorUppercaseLetters() throws Throwable {
checkPreferredItems(0, "clsLoader", "clone", "class");
}
public void testClassStaticMembersInVoidContext() throws Throwable {
checkPreferredItems(0, "booleanMethod", "voidMethod", "AN_OBJECT", "BOOLEAN", "class");
}
@@ -232,4 +205,4 @@ public class NormalCompletionOrderingTest extends CompletionSortingTestCase {
checkPreferredItems(0, "Bar9", "Bar1", "Bar2", "Bar3", "Bar4");
}
}
}
@@ -709,8 +709,12 @@ public class NormalCompletionTest extends LightFixtureCompletionTestCase {
public void testFinalInForLoop() throws Throwable {
configure()
checkResultByFile(getTestName(false) + ".java")
assertOrderedEquals myFixture.lookupElementStrings, 'final'
assertStringItems 'final'
}
public void testFinalInForLoop2() throws Throwable {
configure()
assertStringItems 'final', 'finalize'
}
public void testPrimitiveTypesInForLoop() throws Throwable { doPrimitiveTypeTest() }
@@ -142,11 +142,4 @@ public class VariablesCompletionTest extends CompletionTestCase {
assertStringItems(LineTokenizer.tokenize(FileDocumentManager.getInstance().getDocument(result).getCharsSequence(), false));
}
private void assertStringItems(String... strings) {
assertNotNull(myItems);
for (int i = 0; i < myItems.length; i++) {
assertEquals(strings[i], myItems[i].toString());
}
}
}
@@ -7,9 +7,12 @@ import com.intellij.codeInsight.lookup.impl.LookupImpl;
import com.intellij.psi.statistics.StatisticsManager;
import com.intellij.psi.statistics.impl.StatisticsManagerImpl;
import com.intellij.testFramework.PlatformTestCase;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.List;
/**
* @author mike
@@ -95,4 +98,15 @@ public abstract class CompletionTestCase extends DaemonAnalyzerTestCase {
protected LookupImpl getActiveLookup() {
return (LookupImpl)LookupManager.getActiveLookup(myEditor);
}
protected void assertStringItems(String... strings) {
assertNotNull(myItems);
List<String> actual = ContainerUtil.map(myItems, new Function<LookupElement, String>() {
@Override
public String fun(LookupElement element) {
return element.getLookupString();
}
});
assertOrderedEquals(actual, strings);
}
}
@@ -49,10 +49,10 @@ public class CamelHumpMatcher extends PrefixMatcher {
ourLastCompletionCaseSetting = currentSetting;
}
myMatcher = obtainMatcher(myRelaxedMatching);
myMatcher = obtainMatcher(myRelaxedMatching, myCaseSensitive);
}
if (myMatcher.matches(name)) {
if (myRelaxedMatching && obtainMatcher(false).matches(name)) {
if (myRelaxedMatching && obtainMatcher(false, myCaseSensitive).matches(name)) {
return false;
}
@@ -62,11 +62,11 @@ public class CamelHumpMatcher extends PrefixMatcher {
}
}
private NameUtil.Matcher obtainMatcher(final boolean relax) {
String key = relax + myPrefix;
private NameUtil.Matcher obtainMatcher(final boolean relax, final boolean caseSensitive) {
String key = relax + myPrefix + caseSensitive;
NameUtil.Matcher pattern = ourPatternCache.get(key);
if (pattern == null) {
pattern = createCamelHumpsMatcher(relax);
pattern = createCamelHumpsMatcher(relax, caseSensitive);
ourPatternCache.put(key, pattern);
}
return pattern;
@@ -86,12 +86,8 @@ public class CamelHumpMatcher extends PrefixMatcher {
if (itemCaseInsensitive && StringUtil.startsWithIgnoreCase(name, myPrefix) || prefixMatches(name)) {
return true;
}
if (itemCaseInsensitive &&
CodeInsightSettings.ALL != CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE &&
name.length() > 0) {
final char c = name.charAt(0);
String swappedCase = (Character.isUpperCase(c) ? Character.toLowerCase(c) : Character.toUpperCase(c)) + name.substring(1);
if (prefixMatches(swappedCase)) {
if (itemCaseInsensitive && CodeInsightSettings.ALL != CodeInsightSettings.getInstance().COMPLETION_CASE_SENSITIVE) {
if (obtainMatcher(false, false).matches(name)) {
return true;
}
}
@@ -104,8 +100,8 @@ public class CamelHumpMatcher extends PrefixMatcher {
return new CamelHumpMatcher(prefix, myCaseSensitive, myRelaxedMatching);
}
private NameUtil.Matcher createCamelHumpsMatcher(final boolean relaxedMatching) {
if (!myCaseSensitive) {
private NameUtil.Matcher createCamelHumpsMatcher(final boolean relaxedMatching, final boolean caseSensitive) {
if (!caseSensitive) {
return NameUtil.buildCompletionMatcher(myPrefix, 0, true, true);
}
@@ -126,6 +126,7 @@ public class LookupImpl extends LightweightHint implements Lookup, Disposable {
public LookupImpl(Project project, Editor editor, @NotNull LookupArranger arranger){
super(new JPanel(new BorderLayout()));
setForceShowAsPopup(true);
setCancelOnClickOutside(false);
myProject = project;
myEditor = editor;
@@ -121,7 +121,7 @@ navbar.userActivityMergeTime=500
navbar.newpopup=true
inspectionGadgets.telemetry.enabled=false
minuscule.humps.matching=false
minuscule.humps.matching=true
minuscule.humps.matching.description=Camel Case without holding Shift in Ctrl+N/Ctrl+Shift+N etc
jvmbugfix.mac.caccessibleLeak=true
@@ -317,6 +317,10 @@ public class NameUtil {
private enum WordState { NO_WORD, PREV_UC, WORD }
private static boolean isWordStart(char p) {
return Character.isUpperCase(p) || Character.isDigit(p);
}
private static void addAllWords(String word, List<String> result) {
CharacterIterator it = new StringCharacterIterator(word);
StringBuffer b = new StringBuffer();
@@ -325,7 +329,7 @@ public class NameUtil {
for (char c = it.first(); c != CharacterIterator.DONE; c = it.next()) {
switch (state) {
case NO_WORD:
if (!Character.isUpperCase(c)) {
if (!isWordStart(c)) {
b.append(c);
state = WordState.WORD;
}
@@ -335,7 +339,7 @@ public class NameUtil {
}
break;
case PREV_UC:
if (!Character.isUpperCase(c)) {
if (!isWordStart(c)) {
b = startNewWord(result, b, curPrevUC);
b.append(c);
state = WordState.WORD;
@@ -347,7 +351,7 @@ public class NameUtil {
}
break;
case WORD:
if (Character.isUpperCase(c)) {
if (isWordStart(c)) {
startNewWord(result, b, c);
b.setLength(0);
state = WordState.PREV_UC;
@@ -475,14 +479,13 @@ public class NameUtil {
}
}
public static class MinusculeMatcher implements Matcher {
private final char[] myPattern;
private final boolean myFirstLetterCaseMatters;
public MinusculeMatcher(String pattern, boolean firstLetterCaseMatters) {
myFirstLetterCaseMatters = firstLetterCaseMatters;
myPattern = pattern.replaceAll(":", "\\*:").replaceAll("\\.", "\\*\\.").toCharArray();
myPattern = StringUtil.trimEnd(pattern, "* ").replaceAll(":", "\\*:").replaceAll("\\.", "\\*\\.").toCharArray();
}
private boolean matches(int patternIndex, List<String> words, int wordIndex) {
@@ -501,7 +504,14 @@ public class NameUtil {
if (isWordSeparator(w.charAt(0))) {
assert w.length() == 1 : "'" + w + "'";
return matches(isWordSeparator(myPattern[patternIndex]) ? patternIndex + 1 : patternIndex, words, wordIndex + 1);
if (isWordSeparator(myPattern[patternIndex])) {
return matches(patternIndex + 1, words, wordIndex + 1);
}
if (patternIndex == 0 && myFirstLetterCaseMatters) {
return false;
}
return matches(patternIndex, words, wordIndex + 1);
}
if (patternIndex == 0 && myFirstLetterCaseMatters && w.charAt(0) != myPattern[0]) {
@@ -512,7 +522,7 @@ public class NameUtil {
return false;
}
boolean uppers = Character.isUpperCase(myPattern[patternIndex]);
boolean uppers = isWordStart(myPattern[patternIndex]);
int i = 1;
while (true) {
@@ -523,7 +533,7 @@ public class NameUtil {
break;
}
char p = myPattern[patternIndex + i];
if (uppers && Character.isUpperCase(p)) {
if (uppers && isWordStart(p)) {
p = StringUtil.toLowerCase(p);
} else {
uppers = false;
@@ -570,7 +580,7 @@ public class NameUtil {
break;
}
List<String> newWords = new ArrayList<String>();
newWords.add(s.substring(fromIndex));
newWords.add(s.substring(next));
newWords.addAll(words.subList(i + 1, words.size()));
if (matches(patternIndex, newWords, 0)) {
return true;
@@ -587,7 +597,7 @@ public class NameUtil {
@Override
public boolean matches(String name) {
StringTokenizer tokenizer = new StringTokenizer(name, " -_.:", true);
StringTokenizer tokenizer = new StringTokenizer(name, " -_.:/", true);
List<String> words = new ArrayList<String>();
while (tokenizer.hasMoreTokens()) {
String token = tokenizer.nextToken();
@@ -167,6 +167,7 @@ import.what=Import ''{0}''
import.class=Import Class
create.class.family.name=Create Class
create.class.text=Create Class ''{0}''
create.interface.text=Create Interface ''{0}''
dynamize.invalid.interface.text=Dynamize invalid interface instantiation
dynamize.invalid.interface.family.name=Dynamize invalid code
class.to.import.chooser.title=Choose class to import
@@ -33,6 +33,9 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyBundle;
import org.jetbrains.plugins.groovy.actions.GroovyTemplatesFactory;
import org.jetbrains.plugins.groovy.lang.psi.GrReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrExtendsClause;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrImplementsClause;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrInterfaceDefinition;
/**
* @author ilyas
@@ -47,7 +50,8 @@ public abstract class CreateClassActionBase implements IntentionAction {
@NotNull
public String getText() {
return GroovyBundle.message("create.class.text", myRefElement.getReferenceName());
String referenceName = myRefElement.getReferenceName();
return shouldCreateInterface() ? GroovyBundle.message("create.interface.text", referenceName) : GroovyBundle.message("create.class.text", referenceName);
}
@NotNull
@@ -63,6 +67,11 @@ public abstract class CreateClassActionBase implements IntentionAction {
return true;
}
protected boolean shouldCreateInterface() {
PsiElement parent = myRefElement.getParent();
return parent instanceof GrImplementsClause || parent instanceof GrExtendsClause && parent.getParent() instanceof GrInterfaceDefinition;
}
public static PsiClass createClassByType(final PsiDirectory directory,
final String name,
final PsiManager manager,
@@ -35,7 +35,6 @@ import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrNewExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrImplementsClause;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrMemberOwner;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.expectedTypes.SupertypeConstraint;
@@ -111,7 +110,7 @@ public abstract class CreateClassFix {
PsiDirectory targetDirectory = getTargetDirectory(project, qualifier, name, module);
if (targetDirectory == null) return;
String templateName = myRefElement.getParent() instanceof GrImplementsClause ? NewGroovyClassAction.GROOVY_INTERFACE : NewGroovyClassAction.GROOVY_CLASS;
String templateName = shouldCreateInterface() ? NewGroovyClassAction.GROOVY_INTERFACE : NewGroovyClassAction.GROOVY_CLASS;
PsiClass targetClass = createClassByType(targetDirectory, name, manager, myRefElement, templateName);
if (targetClass != null) {
addImportForClass(groovyFile, qualifier, targetClass);
@@ -47,7 +47,9 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinary
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrCommandArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameterList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Block implementation for Groovy formatter
@@ -61,15 +63,23 @@ public class GroovyBlock implements Block, GroovyElementTypes {
final protected Indent myIndent;
final protected Wrap myWrap;
final protected CodeStyleSettings mySettings;
final protected Map<GrBinaryExpression, Alignment> myInnerAlignments;
protected List<Block> mySubBlocks = null;
public GroovyBlock(@NotNull final ASTNode node, @Nullable final Alignment alignment, @NotNull final Indent indent, @Nullable final Wrap wrap, final CodeStyleSettings settings) {
this(node, alignment, indent, wrap, settings, Collections.<GrBinaryExpression, Alignment>emptyMap());
}
public GroovyBlock(@NotNull final ASTNode node, @Nullable final Alignment alignment, @NotNull final Indent indent, @Nullable final Wrap wrap, final CodeStyleSettings settings,
@NotNull Map<GrBinaryExpression, Alignment> innerAlignments) {
myNode = node;
myAlignment = alignment;
myIndent = indent;
myWrap = wrap;
mySettings = settings;
myInnerAlignments = innerAlignments;
}
@NotNull
@@ -27,6 +27,7 @@ import com.intellij.psi.PsiFile;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.templateLanguages.OuterLanguageElement;
import com.intellij.psi.tree.TokenSet;
import com.intellij.util.containers.CollectionFactory;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.GroovyFileType;
import org.jetbrains.plugins.groovy.formatter.processors.GroovyIndentProcessor;
@@ -34,6 +35,8 @@ import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.parser.GroovyElementTypes;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.api.auxiliary.GrThrowsClause;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrLabeledStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.arguments.GrArgumentList;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrBinaryExpression;
@@ -42,7 +45,9 @@ import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameterLi
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrExtendsClause;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* Utility class to generate myBlock hierarchy
@@ -66,7 +71,7 @@ public class GroovyBlockGenerator implements GroovyElementTypes {
PsiElement blockPsi = block.getNode().getPsi();
if (blockPsi instanceof GrBinaryExpression &&
!(blockPsi.getParent() instanceof GrBinaryExpression)) {
return generateForBinaryExpr(node, myWrap, mySettings);
return generateForBinaryExpr(node, myWrap, mySettings, block.myInnerAlignments);
}
//For multiline strings
@@ -119,33 +124,93 @@ public class GroovyBlockGenerator implements GroovyElementTypes {
// For Parameter lists
if (isListLikeClause(blockPsi)) {
final ArrayList<Block> subBlocks = new ArrayList<Block>();
ASTNode[] children = node.getChildren(null);
ASTNode prevChildNode = null;
final Alignment alignment = mustAlign(blockPsi, mySettings, children) ? Alignment.createAlignment() : null;
final Alignment alignment = mustAlign(blockPsi, mySettings, node.getChildren(null)) ? Alignment.createAlignment() : null;
for (ASTNode childNode : visibleChildren(node)) {
final Indent indent = GroovyIndentProcessor.getChildIndent(block, prevChildNode, childNode);
subBlocks.add(new GroovyBlock(childNode, isKeyword(childNode) ? null : alignment, indent, myWrap, mySettings));
prevChildNode = childNode;
}
return subBlocks;
}
if (blockPsi instanceof GrCodeBlock) {
List<ASTNode> children = visibleChildren(node);
Map<GrBinaryExpression, Alignment> innerAlignments = calculateInnerAlignments(children);
final ArrayList<Block> subBlocks = new ArrayList<Block>();
ASTNode prevChildNode = null;
for (ASTNode childNode : children) {
if (canBeCorrectBlock(childNode)) {
final Indent indent = GroovyIndentProcessor.getChildIndent(block, prevChildNode, childNode);
subBlocks.add(new GroovyBlock(childNode, isKeyword(childNode) ? null : alignment, indent, myWrap, mySettings));
prevChildNode = childNode;
}
final Indent indent = GroovyIndentProcessor.getChildIndent(block, prevChildNode, childNode);
subBlocks.add(new GroovyBlock(childNode, null, indent, myWrap, mySettings, innerAlignments));
prevChildNode = childNode;
}
return subBlocks;
}
// For other cases
final ArrayList<Block> subBlocks = new ArrayList<Block>();
ASTNode[] children = getGroovyChildren(node);
ASTNode prevChildNode = null;
for (ASTNode childNode : children) {
if (canBeCorrectBlock(childNode)) {
final Indent indent = GroovyIndentProcessor.getChildIndent(block, prevChildNode, childNode);
subBlocks.add(new GroovyBlock(childNode, blockPsi instanceof GrCodeBlock ? null : myAlignment, indent, myWrap, mySettings));
prevChildNode = childNode;
}
for (ASTNode childNode : visibleChildren(node)) {
final Indent indent = GroovyIndentProcessor.getChildIndent(block, prevChildNode, childNode);
subBlocks.add(new GroovyBlock(childNode, myAlignment, indent, myWrap, mySettings, block.myInnerAlignments));
prevChildNode = childNode;
}
return subBlocks;
}
private static Map<GrBinaryExpression, Alignment> calculateInnerAlignments(List<ASTNode> children) {
Map<GrBinaryExpression, Alignment> innerAlignments = CollectionFactory.hashMap();
List<Alignment> currentGroup = null;
for (ASTNode child : children) {
PsiElement psi = child.getPsi();
if (psi instanceof GrLabeledStatement) {
List<GrBinaryExpression> table = getTable(((GrLabeledStatement)psi).getStatement());
if (table.isEmpty()) {
currentGroup = null;
}
else {
currentGroup = new ArrayList<Alignment>();
for (GrBinaryExpression expression : table) {
Alignment alignment = Alignment.createAlignment(true);
currentGroup.add(alignment);
innerAlignments.put(expression, alignment);
}
}
} else if (currentGroup != null && isTablePart(psi)) {
List<GrBinaryExpression> table = getTable((GrStatement)psi);
for (int i = 0; i < Math.min(table.size(), currentGroup.size()); i++) {
innerAlignments.put(table.get(i), currentGroup.get(i));
}
} else {
currentGroup = null;
}
}
return innerAlignments;
}
private static List<GrBinaryExpression> getTable(GrStatement statement) {
LinkedList<GrBinaryExpression> result = new LinkedList<GrBinaryExpression>();
while (isTablePart(statement)) {
result.addFirst((GrBinaryExpression)statement);
statement = ((GrBinaryExpression)statement).getLeftOperand();
}
return result;
}
private static boolean isTablePart(PsiElement psi) {
return psi instanceof GrBinaryExpression && mBOR == ((GrBinaryExpression)psi).getOperationTokenType();
}
private static List<ASTNode> visibleChildren(ASTNode node) {
ArrayList<ASTNode> list = new ArrayList<ASTNode>();
for (ASTNode astNode : getGroovyChildren(node)) {
if (canBeCorrectBlock(astNode)) {
list.add(astNode);
}
}
return list;
}
private static boolean mustAlign(PsiElement blockPsi, CodeStyleSettings mySettings, ASTNode[] children) {
// We don't want to align single call argument if it's a closure. The reason is that it looks better to have call like
//
@@ -276,23 +341,14 @@ public class GroovyBlockGenerator implements GroovyElementTypes {
* @return
* @param node
*/
private static List<Block> generateForBinaryExpr(final ASTNode node, Wrap myWrap, CodeStyleSettings mySettings) {
private static List<Block> generateForBinaryExpr(final ASTNode node, Wrap myWrap, CodeStyleSettings mySettings, Map<GrBinaryExpression, Alignment> inner) {
final ArrayList<Block> subBlocks = new ArrayList<Block>();
Alignment alignment = mySettings.ALIGN_MULTILINE_BINARY_OPERATION ? Alignment.createAlignment() : null;
GrBinaryExpression myExpr = (GrBinaryExpression) node.getPsi();
ASTNode[] children = node.getChildren(null);
if (myExpr.getLeftOperand() instanceof GrBinaryExpression) {
addBinaryChildrenRecursively(myExpr.getLeftOperand(), subBlocks, Indent.getContinuationWithoutFirstIndent(), alignment, myWrap, mySettings);
}
for (ASTNode childNode : children) {
if (canBeCorrectBlock(childNode) &&
!(childNode.getPsi() instanceof GrBinaryExpression)) {
subBlocks.add(new GroovyBlock(childNode, alignment, Indent.getContinuationWithoutFirstIndent(), myWrap, mySettings));
}
}
if (myExpr.getRightOperand() instanceof GrBinaryExpression) {
addBinaryChildrenRecursively(myExpr.getRightOperand(), subBlocks, Indent.getContinuationWithoutFirstIndent(), alignment, myWrap, mySettings);
}
GrBinaryExpression binary = (GrBinaryExpression) node.getPsi();
assert binary != null;
addBinaryChildrenRecursively(binary, subBlocks, Indent.getContinuationWithoutFirstIndent(), alignment, myWrap, mySettings, inner);
return subBlocks;
}
@@ -307,23 +363,23 @@ public class GroovyBlockGenerator implements GroovyElementTypes {
private static void addBinaryChildrenRecursively(PsiElement elem,
List<Block> list,
Indent indent,
Alignment alignment, Wrap myWrap, CodeStyleSettings mySettings) {
Alignment alignment, Wrap myWrap, CodeStyleSettings mySettings, Map<GrBinaryExpression, Alignment> inner) {
if (elem == null) return;
ASTNode[] children = elem.getNode().getChildren(null);
// For binary expressions
if ((elem instanceof GrBinaryExpression)) {
GrBinaryExpression myExpr = ((GrBinaryExpression) elem);
if (myExpr.getLeftOperand() instanceof GrBinaryExpression) {
addBinaryChildrenRecursively(myExpr.getLeftOperand(), list, Indent.getContinuationWithoutFirstIndent(), alignment, myWrap, mySettings);
addBinaryChildrenRecursively(myExpr.getLeftOperand(), list, Indent.getContinuationWithoutFirstIndent(), alignment, myWrap, mySettings, inner);
}
for (ASTNode childNode : children) {
if (canBeCorrectBlock(childNode) &&
!(childNode.getPsi() instanceof GrBinaryExpression)) {
list.add(new GroovyBlock(childNode, alignment, indent, myWrap, mySettings));
PsiElement op = ((GrBinaryExpression)elem).getOperationToken();
for (ASTNode childNode : visibleChildren(elem.getNode())) {
PsiElement psi = childNode.getPsi();
if (!(psi instanceof GrBinaryExpression)) {
list.add(new GroovyBlock(childNode, op == psi ? inner.get(myExpr) : alignment, indent, myWrap, mySettings));
}
}
if (myExpr.getRightOperand() instanceof GrBinaryExpression) {
addBinaryChildrenRecursively(myExpr.getRightOperand(), list, Indent.getContinuationWithoutFirstIndent(), alignment, myWrap, mySettings);
addBinaryChildrenRecursively(myExpr.getRightOperand(), list, Indent.getContinuationWithoutFirstIndent(), alignment, myWrap, mySettings, inner);
}
}
}
@@ -112,6 +112,8 @@ public class FormatterTest extends GroovyFormatterTestCase {
public void testClosureAfterLineComment() throws Throwable { doTest(); }
public void testAnnotationOnSeparateLine() throws Throwable { doTest(); }
public void testSpockTable() throws Throwable { doTest(); }
public void testElseIfs() throws Throwable {
myTempSettings.SPECIAL_ELSE_IF_TREATMENT = false;
doTest();
@@ -0,0 +1,31 @@
class HelloSpock extends spock.lang.Specification {
def "length of Spock's and his friends' names"() {
expect:
name.size() == length
where:
name | length | foo
"Spock" | 5
"Kirk" | 4 | xxx | yyy
"Scotty" | 6 |dddddddddd | fff
//aaa
a | b | c
}
}
-----
class HelloSpock extends spock.lang.Specification {
def "length of Spock's and his friends' names"() {
expect:
name.size() == length
where:
name | length | foo
"Spock" | 5
"Kirk" | 4 | xxx | yyy
"Scotty" | 6 | dddddddddd | fff
//aaa
a | b | c
}
}