IDEA-19061 Integrate the Rearranger-plugin into core-IDEA

1. Arrangement core engine is provided;
2. Arrangement tests infrastructure is provided;
3. Added java-specific support for arrangement by type, name and modifier;
4. Corresponding tests are added;
This commit is contained in:
Denis.Zhdanov
2012-07-24 10:46:44 +04:00
parent 6f3abb799d
commit 7a9eddae63
18 changed files with 1038 additions and 23 deletions
@@ -0,0 +1,195 @@
/*
* Copyright 2000-2012 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.codeStyle.arrangement;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
import com.intellij.util.containers.Stack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JavaArrangementVisitor extends JavaElementVisitor {
private static final Map<String, ArrangementModifier> MODIFIERS = new HashMap<String, ArrangementModifier>();
static {
MODIFIERS.put(PsiModifier.PUBLIC, ArrangementModifier.PUBLIC);
MODIFIERS.put(PsiModifier.PROTECTED, ArrangementModifier.PROTECTED);
MODIFIERS.put(PsiModifier.PRIVATE, ArrangementModifier.PRIVATE);
MODIFIERS.put(PsiModifier.PACKAGE_LOCAL, ArrangementModifier.PACKAGE_PRIVATE);
MODIFIERS.put(PsiModifier.STATIC, ArrangementModifier.STATIC);
MODIFIERS.put(PsiModifier.FINAL, ArrangementModifier.FINAL);
}
private final Stack<JavaElementArrangementEntry> myStack = new Stack<JavaElementArrangementEntry>();
@NotNull private final List<JavaElementArrangementEntry> myRootEntries;
@NotNull private Document myDocument;
@NotNull private Collection<TextRange> myRanges;
public JavaArrangementVisitor(@NotNull List<JavaElementArrangementEntry> entries,
@NotNull Document document,
@NotNull Collection<TextRange> ranges)
{
myRootEntries = entries;
myDocument = document;
myRanges = ranges;
}
@Override
public void visitClass(PsiClass aClass) {
JavaElementArrangementEntry entry = createNewEntry(aClass.getTextRange(), ArrangementEntryType.CLASS, aClass.getName(), true);
processEntry(entry, aClass, aClass);
}
@Override
public void visitAnonymousClass(PsiAnonymousClass aClass) {
JavaElementArrangementEntry entry = createNewEntry(aClass.getTextRange(), ArrangementEntryType.CLASS, aClass.getName(), false);
processEntry(entry, null, aClass);
}
@Override
public void visitJavaFile(PsiJavaFile file) {
for (PsiClass psiClass : file.getClasses()) {
visitClass(psiClass);
}
}
@Override
public void visitField(PsiField field) {
JavaElementArrangementEntry entry = createNewEntry(field.getTextRange(), ArrangementEntryType.FIELD, field.getName(), true);
processEntry(entry, field, field.getInitializer());
}
@Override
public void visitMethod(PsiMethod method) {
JavaElementArrangementEntry entry = createNewEntry(method.getTextRange(), ArrangementEntryType.METHOD, method.getName(), true);
processEntry(entry, method, method.getBody());
}
@Override
public void visitExpressionStatement(PsiExpressionStatement statement) {
statement.getExpression().acceptChildren(this);
}
@Override
public void visitNewExpression(PsiNewExpression expression) {
PsiAnonymousClass anonymousClass = expression.getAnonymousClass();
if (anonymousClass == null) {
return;
}
JavaElementArrangementEntry entry =
createNewEntry(anonymousClass.getTextRange(), ArrangementEntryType.CLASS, anonymousClass.getName(), false);
processEntry(entry, null, anonymousClass);
}
@Override
public void visitExpressionList(PsiExpressionList list) {
for (PsiExpression expression : list.getExpressions()) {
expression.acceptChildren(this);
}
}
@Override
public void visitDeclarationStatement(PsiDeclarationStatement statement) {
for (PsiElement element : statement.getDeclaredElements()) {
element.acceptChildren(this);
}
}
private void processEntry(@Nullable JavaElementArrangementEntry entry,
@Nullable PsiModifierListOwner modifier,
@Nullable PsiElement nextPsiRoot)
{
if (entry == null) {
return;
}
if (modifier != null) {
parseModifiers(modifier.getModifierList(), entry);
}
if (nextPsiRoot == null) {
return;
}
myStack.push(entry);
try {
nextPsiRoot.acceptChildren(this);
}
finally {
myStack.pop();
}
}
@Nullable
private JavaElementArrangementEntry createNewEntry(@NotNull TextRange range,
@NotNull ArrangementEntryType type,
@Nullable String name,
boolean canArrange)
{
if (!isWithinBounds(range)) {
return null;
}
DefaultArrangementEntry current = getCurrent();
JavaElementArrangementEntry entry;
if (canArrange) {
TextRange expandedRange = ArrangementUtil.expandToLine(range, myDocument.getCharsSequence());
TextRange rangeToUse = expandedRange == null ? range : expandedRange;
entry = new JavaElementArrangementEntry(current, rangeToUse, type, name, expandedRange != null);
}
else {
entry = new JavaElementArrangementEntry(current, range, type, name, false);
}
if (current == null) {
myRootEntries.add(entry);
}
else {
current.addChild(entry);
}
return entry;
}
private boolean isWithinBounds(@NotNull TextRange range) {
for (TextRange textRange : myRanges) {
if (textRange.intersects(range)) {
return true;
}
}
return false;
}
@Nullable
private DefaultArrangementEntry getCurrent() {
return myStack.isEmpty() ? null : myStack.peek();
}
private static void parseModifiers(@Nullable PsiModifierList modifierList, @NotNull JavaElementArrangementEntry entry) {
if (modifierList == null) {
return;
}
for (String modifier : PsiModifier.MODIFIERS) {
if (modifierList.hasModifierProperty(modifier)) {
entry.addModifier(MODIFIERS.get(modifier));
}
}
}
}
@@ -0,0 +1,93 @@
/*
* Copyright 2000-2012 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.codeStyle.arrangement;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType;
import com.intellij.psi.codeStyle.arrangement.match.ArrangementModifier;
import com.intellij.psi.codeStyle.arrangement.match.ModifierAwareArrangementEntry;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.EnumSet;
import java.util.Set;
/**
* @author Denis Zhdanov
* @since 7/20/12 4:50 PM
*/
public class JavaElementArrangementEntry extends DefaultArrangementEntry
implements TypeAwareArrangementEntry, NameAwareArrangementEntry,ModifierAwareArrangementEntry
{
private final Set<ArrangementModifier> myModifiers = EnumSet.noneOf(ArrangementModifier.class);
@NotNull private final ArrangementEntryType myType;
@Nullable private final String myName;
public JavaElementArrangementEntry(@Nullable ArrangementEntry parent,
@NotNull TextRange range,
@NotNull ArrangementEntryType type,
@Nullable String name,
boolean canBeMatched)
{
this(parent, range.getStartOffset(), range.getEndOffset(), type, name, canBeMatched);
}
public JavaElementArrangementEntry(@Nullable ArrangementEntry parent,
int startOffset,
int endOffset,
@NotNull ArrangementEntryType type,
@Nullable String name,
boolean canBeArranged)
{
super(parent, startOffset, endOffset, canBeArranged);
myType = type;
myName = name;
}
@NotNull
@Override
public Set<ArrangementModifier> getModifiers() {
return myModifiers;
}
public void addModifier(@NotNull ArrangementModifier modifier) {
myModifiers.add(modifier);
}
@Nullable
@Override
public String getName() {
return myName;
}
@NotNull
@Override
public ArrangementEntryType getType() {
return myType;
}
@Override
public String toString() {
return String.format(
"[%d; %d): %s %s %s",
getStartOffset(), getEndOffset(), StringUtil.join(myModifiers, " ").toLowerCase(), myType.toString().toLowerCase(),
myName == null ? "<no name>" : myName
);
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2000-2012 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.codeStyle.arrangement;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author Denis Zhdanov
* @since 7/20/12 2:31 PM
*/
public class JavaRearranger implements Rearranger<JavaElementArrangementEntry> {
@NotNull
@Override
public Collection<JavaElementArrangementEntry> parse(@NotNull PsiElement root,
@NotNull Document document,
@NotNull Collection<TextRange> ranges)
{
// Following entries are subject to arrangement: class, interface, field, method.
List<JavaElementArrangementEntry> result = new ArrayList<JavaElementArrangementEntry>();
root.accept(new JavaArrangementVisitor(result, document, ranges));
return result;
}
}