mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
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:
+195
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+93
@@ -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;
|
||||
}
|
||||
}
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* 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.lang.java.JavaLanguage
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.fileTypes.FileType
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager
|
||||
import com.intellij.psi.codeStyle.arrangement.engine.ArrangementEngine
|
||||
import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.annotations.NotNull
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 7/20/12 2:54 PM
|
||||
*/
|
||||
abstract class AbstractRearrangerTest extends LightCodeInsightFixtureTestCase {
|
||||
|
||||
def FileType fileType
|
||||
|
||||
@Override
|
||||
protected void setUp() {
|
||||
super.setUp()
|
||||
CodeStyleSettingsManager.getInstance(myFixture.project).temporarySettings = new CodeStyleSettings()
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() {
|
||||
CodeStyleSettingsManager.getInstance(myFixture.project).dropTemporarySettings()
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
protected void doTest(@NotNull String initial,
|
||||
@NotNull String expected,
|
||||
@NotNull List<ArrangementRule> rules,
|
||||
@NotNull Collection<TextRange> ranges = null)
|
||||
{
|
||||
def (String textToUse, List<TextRange> rangesToUse) = parseRanges(initial)
|
||||
if (rangesToUse && ranges) {
|
||||
fail("Duplicate ranges info detected: explicitly given: $ranges, derived from markup: $rangesToUse. Text:\n$initial")
|
||||
}
|
||||
if (!rangesToUse) {
|
||||
rangesToUse = ranges ?: [TextRange.from(0, initial.length())]
|
||||
}
|
||||
|
||||
myFixture.configureByText(fileType, textToUse)
|
||||
def settings = CodeStyleSettingsManager.getInstance(myFixture.project).currentSettings.getCommonSettings(JavaLanguage.INSTANCE)
|
||||
settings.arrangementRules = rules
|
||||
ArrangementEngine engine = ServiceManager.getService(myFixture.project, ArrangementEngine)
|
||||
engine.arrange(myFixture.file, rangesToUse);
|
||||
assertEquals(expected, myFixture.editor.document.text);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static def parseRanges(@NotNull String text) {
|
||||
def clearText = new StringBuilder(text)
|
||||
def ranges = []
|
||||
int shift = 0
|
||||
int shiftIncrease = '<range>'.length() * 2 + 1
|
||||
def match = text =~ '(?is)<range>.*?</range>'
|
||||
match.each {
|
||||
ranges << TextRange.create(match.start() - shift, match.end() - shift - shiftIncrease)
|
||||
clearText.delete(match.end() - '</range>'.length() - shift, match.end() - shift)
|
||||
clearText.delete(match.start() - shift, match.start() + '<range>'.length() - shift)
|
||||
shift += shiftIncrease
|
||||
}
|
||||
|
||||
[clearText.toString(), ranges]
|
||||
}
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* 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.ide.highlighter.JavaFileType
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryType
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ByTypeArrangementEntryMatcher
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 7/20/12 2:45 PM
|
||||
*/
|
||||
class JavaRearrangerByTypeTest extends AbstractRearrangerTest {
|
||||
|
||||
JavaRearrangerByTypeTest() {
|
||||
fileType = JavaFileType.INSTANCE
|
||||
}
|
||||
|
||||
void testFieldsBeforeMethods() {
|
||||
doTest(
|
||||
'''\
|
||||
class Test {
|
||||
public void test() {}
|
||||
private int i;
|
||||
}
|
||||
class Test2 {
|
||||
public void test() {
|
||||
}
|
||||
private int i;
|
||||
private int j;
|
||||
}''',
|
||||
'''\
|
||||
class Test {
|
||||
private int i;
|
||||
public void test() {}
|
||||
}
|
||||
class Test2 {
|
||||
private int i;
|
||||
private int j;
|
||||
public void test() {
|
||||
}
|
||||
}''',
|
||||
[new ArrangementRule(new ByTypeArrangementEntryMatcher(ArrangementEntryType.FIELD))]
|
||||
)
|
||||
}
|
||||
|
||||
void testAnonymousClassAtFieldInitializer() {
|
||||
doTest(
|
||||
'''\
|
||||
class Test {
|
||||
private Object first = new Object() {
|
||||
int inner1;
|
||||
public String toString() { return "test"; }
|
||||
int inner2;
|
||||
};
|
||||
public Object test(Object ... args) {
|
||||
return null;
|
||||
}
|
||||
private Object second = test(test(new Object() {
|
||||
public String toString() {
|
||||
return "test";
|
||||
}
|
||||
private Object inner = new Object() {
|
||||
public String toString() { return "innerTest"; }
|
||||
};
|
||||
}));
|
||||
}''',
|
||||
'''\
|
||||
class Test {
|
||||
private Object first = new Object() {
|
||||
int inner1;
|
||||
int inner2;
|
||||
public String toString() { return "test"; }
|
||||
};
|
||||
private Object second = test(test(new Object() {
|
||||
private Object inner = new Object() {
|
||||
public String toString() { return "innerTest"; }
|
||||
};
|
||||
public String toString() {
|
||||
return "test";
|
||||
}
|
||||
}));
|
||||
public Object test(Object ... args) {
|
||||
return null;
|
||||
}
|
||||
}''',
|
||||
[new ArrangementRule(new ByTypeArrangementEntryMatcher(ArrangementEntryType.FIELD))]
|
||||
)
|
||||
}
|
||||
|
||||
void testAnonymousClassAtMethod() {
|
||||
doTest(
|
||||
'''\
|
||||
class Test {
|
||||
void declaration() {
|
||||
Object o = new Object() {
|
||||
private int test() { return 1; }
|
||||
String s;
|
||||
}
|
||||
}
|
||||
double d;
|
||||
void call() {
|
||||
test(test(1, new Object() {
|
||||
public void test() {}
|
||||
int i;
|
||||
});
|
||||
}
|
||||
}''',
|
||||
'''\
|
||||
class Test {
|
||||
double d;
|
||||
void declaration() {
|
||||
Object o = new Object() {
|
||||
String s;
|
||||
private int test() { return 1; }
|
||||
}
|
||||
}
|
||||
void call() {
|
||||
test(test(1, new Object() {
|
||||
int i;
|
||||
public void test() {}
|
||||
});
|
||||
}
|
||||
}''',
|
||||
[new ArrangementRule(new ByTypeArrangementEntryMatcher(ArrangementEntryType.FIELD))]
|
||||
)
|
||||
}
|
||||
|
||||
void testRanges() {
|
||||
doTest(
|
||||
'''\
|
||||
class Test {
|
||||
void outer1() {}
|
||||
<range> String outer2() {}
|
||||
int i;</range>
|
||||
void test() {
|
||||
method(new Object() {
|
||||
void inner1() {}
|
||||
Object field = new Object() {
|
||||
<range> void inner2() {}
|
||||
String s;</range>
|
||||
Integer i;
|
||||
}
|
||||
});
|
||||
}
|
||||
}''',
|
||||
'''\
|
||||
class Test {
|
||||
void outer1() {}
|
||||
int i;
|
||||
String outer2() {}
|
||||
void test() {
|
||||
method(new Object() {
|
||||
void inner1() {}
|
||||
Object field = new Object() {
|
||||
String s;
|
||||
void inner2() {}
|
||||
Integer i;
|
||||
}
|
||||
});
|
||||
}
|
||||
}''',
|
||||
[new ArrangementRule(new ByTypeArrangementEntryMatcher(ArrangementEntryType.FIELD))]
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ import com.intellij.openapi.extensions.Extensions;
|
||||
import com.intellij.openapi.fileTypes.FileType;
|
||||
import com.intellij.openapi.util.*;
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementRule;
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementRuleUtil;
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementUtil;
|
||||
import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters;
|
||||
import com.intellij.util.xmlb.XmlSerializer;
|
||||
import org.intellij.lang.annotations.MagicConstant;
|
||||
@@ -120,6 +120,16 @@ public class CommonCodeStyleSettings {
|
||||
return myIndentOptions;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public List<ArrangementRule> getArrangementRules() {
|
||||
return myArrangementRules;
|
||||
}
|
||||
|
||||
public void setArrangementRules(@NotNull List<ArrangementRule> rules) {
|
||||
myArrangementRules.clear();
|
||||
myArrangementRules.addAll(rules);
|
||||
}
|
||||
|
||||
public CommonCodeStyleSettings clone(CodeStyleSettings rootSettings) {
|
||||
assert rootSettings != null;
|
||||
CommonCodeStyleSettings commonSettings = new CommonCodeStyleSettings(myLanguage, getFileType());
|
||||
@@ -168,10 +178,7 @@ public class CommonCodeStyleSettings {
|
||||
private static void copyFieldValue(final Object from, Object to, final Field field)
|
||||
throws IllegalAccessException {
|
||||
Class<?> fieldType = field.getType();
|
||||
if (fieldType.isPrimitive()) {
|
||||
field.set(to, field.get(from));
|
||||
}
|
||||
else if (fieldType.equals(String.class)) {
|
||||
if (fieldType.isPrimitive() || fieldType.equals(String.class)) {
|
||||
field.set(to, field.get(from));
|
||||
}
|
||||
else {
|
||||
@@ -202,7 +209,7 @@ public class CommonCodeStyleSettings {
|
||||
}
|
||||
Element arrangementRulesContainer = element.getChild(ARRANGEMENT_ELEMENT_NAME);
|
||||
if (arrangementRulesContainer != null) {
|
||||
myArrangementRules.addAll(ArrangementRuleUtil.readExternal(arrangementRulesContainer, myLanguage));
|
||||
myArrangementRules.addAll(ArrangementUtil.readExternal(arrangementRulesContainer, myLanguage));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,7 +231,7 @@ public class CommonCodeStyleSettings {
|
||||
|
||||
if (!myArrangementRules.isEmpty()) {
|
||||
Element container = new Element(ARRANGEMENT_ELEMENT_NAME);
|
||||
ArrangementRuleUtil.writeExternal(container, myArrangementRules, myLanguage);
|
||||
ArrangementUtil.writeExternal(container, myArrangementRules, myLanguage);
|
||||
if (!container.getChildren().isEmpty()) {
|
||||
element.addContent(container);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.psi.codeStyle.arrangement;
|
||||
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryMatcher;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -66,4 +67,15 @@ public interface ArrangementEntry {
|
||||
* to move rearranged entries
|
||||
*/
|
||||
int getEndOffset();
|
||||
|
||||
/**
|
||||
* Sometimes we want particular entry to serve just as another entries holder. For example, we might want to arrange
|
||||
* anonymous class entries but don't want the class itself, say, to be arranged with normal inner classes.
|
||||
* <p/>
|
||||
* That is achieved for entries which return <code>'false'</code> from this method call.
|
||||
*
|
||||
* @return <code>true</code> if current entry can be {@link ArrangementEntryMatcher#isMatched(ArrangementEntry) matched};
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
boolean canBeMatched();
|
||||
}
|
||||
|
||||
+79
-2
@@ -16,6 +16,8 @@
|
||||
package com.intellij.psi.codeStyle.arrangement;
|
||||
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.ArrangementEntryMatcher;
|
||||
import com.intellij.psi.codeStyle.arrangement.match.CompositeArrangementEntryMatcher;
|
||||
import org.jdom.Element;
|
||||
@@ -30,9 +32,9 @@ import java.util.List;
|
||||
* @author Denis Zhdanov
|
||||
* @since 7/17/12 11:24 AM
|
||||
*/
|
||||
public class ArrangementRuleUtil {
|
||||
public class ArrangementUtil {
|
||||
|
||||
private ArrangementRuleUtil() {
|
||||
private ArrangementUtil() {
|
||||
}
|
||||
|
||||
//region Serialization
|
||||
@@ -141,4 +143,79 @@ public class ArrangementRuleUtil {
|
||||
return composite;
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region ArrangementEntry
|
||||
|
||||
/**
|
||||
* Tries to build a text range on the given arguments basis. It should conform to the criteria below:
|
||||
* <pre>
|
||||
* <ul>
|
||||
* <li>it's start offset is located at the start of the same line where given range starts;</li>
|
||||
* <li>it's end offset is located at the end of the same line where given range ends;</li>
|
||||
* <li>all symbols between the resulting range start offset and given range's start offset are white spaces or tabulations;</li>
|
||||
* <li>all symbols between the given range's end offset and resulting range's end offset are white spaces or tabulations;</li>
|
||||
* </ul>
|
||||
* </pre>
|
||||
* This method is expected to be used in a situation when we want to arrange complete rows.
|
||||
* Example:
|
||||
* <pre>
|
||||
* class Test {
|
||||
* void test() {
|
||||
* }
|
||||
* int i;
|
||||
* }
|
||||
* </pre>
|
||||
* Suppose, we want to locate fields before methods. We can move the exact field and method range then but indent will be broken,
|
||||
* i.e. we'll get the result below:
|
||||
* <pre>
|
||||
* class Test {
|
||||
* int i;
|
||||
* void test() {
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
* We can expand field and method range to the whole lines and that would allow to achieve the desired result:
|
||||
* <pre>
|
||||
* class Test {
|
||||
* int i;
|
||||
* void test() {
|
||||
* }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param initialRange anchor range
|
||||
* @param text target text against which the ranges are built
|
||||
* @return expanded range if possible; <code>null</code> otherwise
|
||||
*/
|
||||
@Nullable
|
||||
public static TextRange expandToLine(@NotNull TextRange initialRange, @NotNull CharSequence text) {
|
||||
int startOffsetToUse = initialRange.getStartOffset();
|
||||
for (int i = startOffsetToUse - 1; i >= 0; i--) {
|
||||
char c = text.charAt(i);
|
||||
if (!StringUtil.isWhiteSpace(c)) {
|
||||
return null;
|
||||
}
|
||||
else if (c == '\n') {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
startOffsetToUse = i;
|
||||
}
|
||||
}
|
||||
|
||||
int endOffsetToUse = initialRange.getEndOffset();
|
||||
for (int i = endOffsetToUse; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if (!StringUtil.isWhiteSpace(c)) {
|
||||
return null;
|
||||
}
|
||||
else if (c == '\n') {
|
||||
endOffsetToUse = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return TextRange.create(startOffsetToUse, endOffsetToUse);
|
||||
}
|
||||
//endregion
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* 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 org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 7/20/12 4:53 PM
|
||||
*/
|
||||
public class DefaultArrangementEntry implements ArrangementEntry {
|
||||
|
||||
private final List<ArrangementEntry> myChildren = new ArrayList<ArrangementEntry>();
|
||||
|
||||
@Nullable ArrangementEntry myParent;
|
||||
private final int myStartOffset;
|
||||
private final int myEndOffset;
|
||||
private final boolean myCanBeMatched;
|
||||
|
||||
public DefaultArrangementEntry(@Nullable ArrangementEntry parent, int startOffset, int endOffset, boolean canBeMatched) {
|
||||
myCanBeMatched = canBeMatched;
|
||||
assert startOffset < endOffset;
|
||||
myParent = parent;
|
||||
myStartOffset = startOffset;
|
||||
myEndOffset = endOffset;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ArrangementEntry getParent() {
|
||||
return myParent;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<? extends ArrangementEntry> getChildren() {
|
||||
return myChildren;
|
||||
}
|
||||
|
||||
public void addChild(@NotNull ArrangementEntry entry) {
|
||||
myChildren.add(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStartOffset() {
|
||||
return myStartOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEndOffset() {
|
||||
return myEndOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeMatched() {
|
||||
return myCanBeMatched;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.intellij.psi.codeStyle.arrangement;
|
||||
|
||||
import com.intellij.lang.LanguageExtension;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -33,15 +34,16 @@ import java.util.Collection;
|
||||
*/
|
||||
public interface Rearranger<E extends ArrangementEntry> {
|
||||
|
||||
LanguageExtension<Rearranger<?>> EXTENSION = new LanguageExtension<Rearranger<?>>("com.intellij.rearranger.facade");
|
||||
LanguageExtension<Rearranger<?>> EXTENSION = new LanguageExtension<Rearranger<?>>("com.intellij.lang.rearranger");
|
||||
|
||||
/**
|
||||
* Allows to build rearranger-interested data for the given element.
|
||||
*
|
||||
* @param root root element which children should be parsed for the rearrangement
|
||||
* @param range target offsets range to use for filtering given root's children
|
||||
* @return given root's children that are subject for further rearrangement
|
||||
* @param root root element which children should be parsed for the rearrangement
|
||||
* @param document document which corresponds to the target PSI tree
|
||||
* @param ranges target offsets ranges to use for filtering given root's children
|
||||
* @return given root's children which are subject for further rearrangement
|
||||
*/
|
||||
@NotNull
|
||||
Collection<E> parse(@NotNull PsiElement root, @NotNull TextRange range);
|
||||
Collection<E> parse(@NotNull PsiElement root, @NotNull Document document, @NotNull Collection<TextRange> ranges);
|
||||
}
|
||||
|
||||
+1
-3
@@ -35,9 +35,7 @@ public class ByNameArrangementEntryMatcher implements ArrangementEntryMatcher {
|
||||
public boolean isMatched(@NotNull ArrangementEntry entry) {
|
||||
if (entry instanceof NameAwareArrangementEntry) {
|
||||
String name = ((NameAwareArrangementEntry)entry).getName();
|
||||
if (name != null) {
|
||||
return name.matches(myPattern);
|
||||
}
|
||||
return name != null && name.matches(myPattern);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
+3
-1
@@ -18,6 +18,8 @@ package com.intellij.psi.codeStyle.arrangement.match;
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementEntry;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 7/17/12 8:57 PM
|
||||
@@ -25,5 +27,5 @@ import org.jetbrains.annotations.NotNull;
|
||||
public interface ModifierAwareArrangementEntry extends ArrangementEntry {
|
||||
|
||||
@NotNull
|
||||
ArrangementModifier getModifier();
|
||||
Set<ArrangementModifier> getModifiers();
|
||||
}
|
||||
|
||||
+4
-4
@@ -17,7 +17,7 @@ package com.intellij.psi.codeStyle.arrangement.match;
|
||||
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementRule;
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementRuleSerializer;
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementRuleUtil;
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementUtil;
|
||||
import com.intellij.psi.codeStyle.arrangement.DefaultArrangementRuleSerializer;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -48,13 +48,13 @@ public class DefaultArrangementRuleSerializerTest {
|
||||
|
||||
@Test
|
||||
public void compositeMatchers() {
|
||||
doTest(ArrangementRuleUtil.or(
|
||||
doTest(ArrangementUtil.or(
|
||||
new ByTypeArrangementEntryMatcher(ArrangementEntryType.FIELD),
|
||||
new ByTypeArrangementEntryMatcher(ArrangementEntryType.METHOD))
|
||||
);
|
||||
|
||||
doTest(ArrangementRuleUtil.and(
|
||||
ArrangementRuleUtil.or(
|
||||
doTest(ArrangementUtil.and(
|
||||
ArrangementUtil.or(
|
||||
new ByTypeArrangementEntryMatcher(ArrangementEntryType.METHOD),
|
||||
new ByNameArrangementEntryMatcher("get*")
|
||||
)
|
||||
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
* 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.engine;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.PsiDocumentManager;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettings;
|
||||
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementEntry;
|
||||
import com.intellij.psi.codeStyle.arrangement.ArrangementRule;
|
||||
import com.intellij.psi.codeStyle.arrangement.Rearranger;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Denis Zhdanov
|
||||
* @since 7/20/12 1:56 PM
|
||||
*/
|
||||
public class ArrangementEngine {
|
||||
|
||||
/**
|
||||
* Arranges given PSI root contents that belong to the given ranges.
|
||||
*
|
||||
* @param file target PSI root
|
||||
* @param ranges target ranges to use within the given root
|
||||
*/
|
||||
@SuppressWarnings("MethodMayBeStatic")
|
||||
public void arrange(@NotNull PsiFile file, @NotNull Collection<TextRange> ranges) {
|
||||
final Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
|
||||
if (document == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
CodeStyleSettings settings = CodeStyleSettingsManager.getInstance(file.getProject()).getCurrentSettings();
|
||||
final List<ArrangementRule> arrangementRules = settings.getCommonSettings(file.getLanguage()).getArrangementRules();
|
||||
if (arrangementRules.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Rearranger<?> rearranger = Rearranger.EXTENSION.forLanguage(file.getLanguage());
|
||||
if (rearranger == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Collection<? extends ArrangementEntry> entriesToProcess = rearranger.parse(file, document, ranges);
|
||||
final DocumentEx documentEx;
|
||||
if (document instanceof DocumentEx && !((DocumentEx)document).isInBulkUpdate()) {
|
||||
documentEx = (DocumentEx)document;
|
||||
}
|
||||
else {
|
||||
documentEx = null;
|
||||
}
|
||||
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (documentEx != null) {
|
||||
documentEx.setInBulkUpdate(true);
|
||||
}
|
||||
try {
|
||||
doArrange(document, arrangementRules, entriesToProcess);
|
||||
}
|
||||
finally {
|
||||
if (documentEx != null) {
|
||||
documentEx.setInBulkUpdate(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void doArrange(@NotNull final Document document,
|
||||
@NotNull List<ArrangementRule> arrangementRules,
|
||||
@NotNull Collection<? extends ArrangementEntry> entriesToProcess)
|
||||
{
|
||||
// The general idea is to process entries bottom-up where every processed group belongs to the same parent. We may not bother
|
||||
// with entries text ranges then. We use a list and a stack for achieving that than.
|
||||
//
|
||||
// Example:
|
||||
// Entry1 Entry2
|
||||
// / \ / \
|
||||
// Entry11 Entry12 Entry21 Entry22
|
||||
//
|
||||
// --------------------------
|
||||
// Stage 1:
|
||||
// list: Entry1 Entry2
|
||||
// stack: [0, 0, 2]
|
||||
// --------------------------
|
||||
// Stage 2:
|
||||
// list: Entry1 Entry2 Entry11 Entry12
|
||||
// stack: [0, 1, 2]
|
||||
// [2, 2, 4]
|
||||
// --------------------------
|
||||
// Stage 3:
|
||||
// list: Entry1 Entry2 Entry11 Entry12
|
||||
// stack: [0, 1, 2]
|
||||
// [2, 3, 4]
|
||||
// --------------------------
|
||||
// Stage 4:
|
||||
// list: Entry1 Entry2 Entry11 Entry12
|
||||
// stack: [0, 1, 2]
|
||||
// [2, 4, 4]
|
||||
// --------------------------
|
||||
// arrange 'Entry11 Entry12'
|
||||
// --------------------------
|
||||
// Stage 5:
|
||||
// list: Entry1 Entry2
|
||||
// stack: [0, 1, 2]
|
||||
// --------------------------
|
||||
// Stage 6:
|
||||
// list: Entry1 Entry2 Entry21 Entry22
|
||||
// stack: [0, 2, 2]
|
||||
// [2, 2, 4]
|
||||
// --------------------------
|
||||
// Stage 7:
|
||||
// list: Entry1 Entry2 Entry21 Entry22
|
||||
// stack: [0, 2, 2]
|
||||
// [2, 3, 4]
|
||||
// --------------------------
|
||||
// Stage 8:
|
||||
// list: Entry1 Entry2 Entry21 Entry22
|
||||
// stack: [0, 2, 2]
|
||||
// [2, 4, 4]
|
||||
// --------------------------
|
||||
// arrange 'Entry21 Entry22'
|
||||
// --------------------------
|
||||
// Stage 9:
|
||||
// list: Entry1 Entry2
|
||||
// stack: [0, 2, 2]
|
||||
// --------------------------
|
||||
// arrange 'Entry1 Entry2'
|
||||
|
||||
List<ArrangementEntry> entries = new ArrayList<ArrangementEntry>();
|
||||
Stack<StackEntry> stack = new Stack<StackEntry>();
|
||||
entries.addAll(entriesToProcess);
|
||||
stack.push(new StackEntry(0, entriesToProcess.size()));
|
||||
while (!stack.isEmpty()) {
|
||||
StackEntry stackEntry = stack.peek();
|
||||
if (stackEntry.current >= stackEntry.end) {
|
||||
List<ArrangementEntry> subEntries = entries.subList(stackEntry.start, stackEntry.end);
|
||||
if (subEntries.size() > 1) {
|
||||
doArrange(arrangementRules, subEntries, document);
|
||||
}
|
||||
subEntries.clear();
|
||||
stack.pop();
|
||||
}
|
||||
else {
|
||||
ArrangementEntry entry = entries.get(stackEntry.current++);
|
||||
Collection<? extends ArrangementEntry> children = entry.getChildren();
|
||||
if (!children.isEmpty()) {
|
||||
entries.addAll(children);
|
||||
stack.push(new StackEntry(stackEntry.end, children.size()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void doArrange(@NotNull List<ArrangementRule> rules,
|
||||
@NotNull List<ArrangementEntry> entries,
|
||||
@NotNull Document document)
|
||||
{
|
||||
List<ArrangementEntry> arranged = new ArrayList<ArrangementEntry>();
|
||||
Set<ArrangementEntry> unprocessed = new LinkedHashSet<ArrangementEntry>(entries);
|
||||
|
||||
for (ArrangementRule rule : rules) {
|
||||
for (ArrangementEntry entry : entries) {
|
||||
if (entry.canBeMatched() && unprocessed.contains(entry) && rule.getMatcher().isMatched(entry)) {
|
||||
arranged.add(entry);
|
||||
unprocessed.remove(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
arranged.addAll(unprocessed);
|
||||
|
||||
if (arranged.equals(entries)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We apply changes from the last position to the first position in order not to bother with offsets shifts.
|
||||
ArrangementEntry parent = entries.get(0).getParent();
|
||||
final String initial;
|
||||
final int shift;
|
||||
if (parent == null) {
|
||||
initial = document.getCharsSequence().toString();
|
||||
shift = 0;
|
||||
}
|
||||
else {
|
||||
initial = document.getCharsSequence().subSequence(parent.getStartOffset(), parent.getEndOffset()).toString();
|
||||
shift = parent.getStartOffset();
|
||||
}
|
||||
for (int i = arranged.size() - 1; i >= 0; i--) {
|
||||
ArrangementEntry arrangedEntry = arranged.get(i);
|
||||
ArrangementEntry initialEntry = entries.get(i);
|
||||
if (!arrangedEntry.equals(initialEntry)) {
|
||||
String text = initial.substring(arrangedEntry.getStartOffset() - shift, arrangedEntry.getEndOffset() - shift);
|
||||
document.replaceString(initialEntry.getStartOffset(), initialEntry.getEndOffset(), text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class StackEntry {
|
||||
|
||||
public int start;
|
||||
public int current;
|
||||
public int end;
|
||||
|
||||
StackEntry(int start, int count) {
|
||||
this.start = start;
|
||||
current = start;
|
||||
end = start + count;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,6 +123,7 @@
|
||||
beanClass="com.intellij.lang.LanguageExtensionPoint"/>
|
||||
<extensionPoint name="lang.formatter" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
|
||||
<extensionPoint name="lang.importOptimizer" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
|
||||
<extensionPoint name="lang.rearranger" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
|
||||
<extensionPoint name="lang.surroundDescriptor" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
|
||||
<extensionPoint name="lang.unwrapDescriptor" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
|
||||
<extensionPoint name="lang.parserDefinition" beanClass="com.intellij.lang.LanguageExtensionPoint"/>
|
||||
|
||||
@@ -177,6 +177,8 @@
|
||||
<applicationService serviceInterface="com.intellij.openapi.roots.impl.libraries.JarDirectoryWatcherFactory"
|
||||
serviceImplementation="com.intellij.openapi.roots.impl.libraries.JarDirectoryWatcherFactoryImpl"/>
|
||||
|
||||
<applicationService serviceImplementation="com.intellij.psi.codeStyle.arrangement.engine.ArrangementEngine"/>
|
||||
|
||||
<projectService serviceInterface="com.intellij.ui.EditorTextFieldProvider"
|
||||
serviceImplementation="com.intellij.ui.EditorTextFieldProviderImpl"/>
|
||||
|
||||
|
||||
@@ -1163,6 +1163,17 @@ public class StringUtil extends StringUtilRt {
|
||||
return join((Iterable<T>)items, f, separator);
|
||||
}
|
||||
|
||||
public static String join(@NotNull Iterable<?> items, @NotNull @NonNls String separator) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (Object item : items) {
|
||||
result.append(item).append(separator);
|
||||
}
|
||||
if (result.length() > 0) {
|
||||
result.setLength(result.length() - 1);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T> String join(@NotNull Iterable<T> items, @NotNull Function<T, String> f, @NotNull @NonNls String separator) {
|
||||
final StringBuilder result = new StringBuilder();
|
||||
|
||||
@@ -764,7 +764,8 @@
|
||||
<lang.formatter language="JAVA" implementationClass="com.intellij.lang.java.JavaFormattingModelBuilder"/>
|
||||
<lang.whiteSpaceFormattingStrategy language="JAVA"
|
||||
implementationClass="com.intellij.psi.formatter.JavadocWhiteSpaceFormattingStrategy"/>
|
||||
|
||||
<lang.rearranger language="JAVA" implementationClass="com.intellij.psi.codeStyle.arrangement.JavaRearranger"/>
|
||||
|
||||
<lang.documentationProvider language="JAVA" implementationClass="com.intellij.lang.java.JavaDocumentationProvider"/>
|
||||
<documentationProvider implementation="com.intellij.lang.java.FileDocumentationProvider" order="last"/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user