blob: 5a3e81d77a76d6ccd4f431f8ed8c782be9cf19f7 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.plugins.groovy.codeStyle;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.PackageEntry;
import com.intellij.psi.codeStyle.PackageEntryTable;
import com.intellij.util.IncorrectOperationException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.editor.GroovyImportOptimizer;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFile;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.GrTopStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.imports.GrImportStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.toplevel.packaging.GrPackageDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.types.GrCodeReferenceElement;
import org.jetbrains.plugins.groovy.lang.psi.impl.GroovyCodeStyleManager;
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
import java.util.Comparator;
public class GroovyCodeStyleManagerImpl extends GroovyCodeStyleManager {
@NotNull
@Override
public GrImportStatement addImport(@NotNull GroovyFile psiFile, @NotNull GrImportStatement statement) throws IncorrectOperationException {
PsiElement anchor = getAnchorToInsertImportAfter(psiFile, statement);
final PsiElement result = psiFile.addAfter(statement, anchor);
final GrImportStatement gImport = (GrImportStatement)result;
addLineFeedBefore(psiFile, gImport);
addLineFeedAfter(psiFile, gImport);
return gImport;
}
@Nullable
private PsiElement getShellComment(@NotNull PsiElement psiFile) {
final ASTNode node = psiFile.getNode().findChildByType(GroovyTokenTypes.mSH_COMMENT);
return node == null ? null : node.getPsi();
}
@Nullable
private PsiElement getAnchorToInsertImportAfter(@NotNull GroovyFile psiFile, @NotNull GrImportStatement statement) {
final GroovyCodeStyleSettings settings = CodeStyleSettingsManager.getInstance(psiFile.getProject()).getCurrentSettings().getCustomSettings(
GroovyCodeStyleSettings.class);
final PackageEntryTable layoutTable = settings.IMPORT_LAYOUT_TABLE;
final PackageEntry[] entries = layoutTable.getEntries();
GrImportStatement[] importStatements = psiFile.getImportStatements();
if (importStatements.length == 0) {
final GrPackageDefinition definition = psiFile.getPackageDefinition();
if (definition != null) {
return definition;
}
return getShellComment(psiFile);
}
final Comparator<GrImportStatement> comparator = GroovyImportOptimizer.getComparator(settings);
final int idx = getPackageEntryIdx(entries, statement);
PsiElement anchor = null;
for (GrImportStatement importStatement : importStatements) {
final int i = getPackageEntryIdx(entries, importStatement);
if (i < idx) {
anchor = importStatement;
}
else if (i > idx) {
break;
}
else if (comparator.compare(statement, importStatement) > 0) {
anchor = importStatement;
}
else {
break;
}
}
if (anchor == null) anchor = psiFile.getPackageDefinition();
if (anchor == null) anchor = getShellComment(psiFile);
if (anchor == null && importStatements.length > 0) anchor = importStatements[0].getPrevSibling();
return anchor;
}
protected static int getPackageEntryIdx(@NotNull PackageEntry[] entries, @NotNull GrImportStatement statement) {
final GrCodeReferenceElement reference = statement.getImportReference();
if (reference == null) return -1;
final String packageName = StringUtil.getPackageName(reference.getCanonicalText());
final boolean isStatic = statement.isStatic();
int best = -1;
int allOtherStatic = -1;
int allOther = -1;
PackageEntry bestEntry = null;
for (int i = 0, length = entries.length; i < length; i++) {
PackageEntry entry = entries[i];
if (entry.isBetterMatchForPackageThan(bestEntry, packageName, isStatic)) {
best = i;
bestEntry = entry;
}
else if (entry == PackageEntry.ALL_OTHER_STATIC_IMPORTS_ENTRY) {
allOtherStatic = i;
}
else if (entry == PackageEntry.ALL_OTHER_IMPORTS_ENTRY) {
allOther = i;
}
}
if (best >= 0) return best;
if (isStatic && allOtherStatic != -1) return allOtherStatic;
return allOther;
}
protected void addLineFeedBefore(@NotNull PsiElement psiFile, @NotNull GrImportStatement result) {
final GroovyCodeStyleSettings settings =
CodeStyleSettingsManager.getInstance(psiFile.getProject()).getCurrentSettings().getCustomSettings(GroovyCodeStyleSettings.class);
final PackageEntryTable layoutTable = settings.IMPORT_LAYOUT_TABLE;
final PackageEntry[] entries = layoutTable.getEntries();
PsiElement prev = result.getPrevSibling();
while (PsiImplUtil.isWhiteSpaceOrNls(prev)) {
prev = prev.getPrevSibling();
}
if (PsiImplUtil.hasElementType(prev, GroovyTokenTypes.mSEMI)) prev = prev.getPrevSibling();
if (PsiImplUtil.isWhiteSpaceOrNls(prev)) prev = prev.getPrevSibling();
if (prev instanceof GrImportStatement) {
final int idx_before = getPackageEntryIdx(entries, (GrImportStatement)prev);
final int idx = getPackageEntryIdx(entries, result);
final int spaceCount = getMaxSpaceCount(entries, idx_before, idx);
//skip space and semicolon after import
if (PsiImplUtil.isWhiteSpaceOrNls(prev.getNextSibling()) && PsiImplUtil
.hasElementType(prev.getNextSibling().getNextSibling(), GroovyTokenTypes.mSEMI)) prev = prev.getNextSibling().getNextSibling();
ASTNode node = psiFile.getNode();
while (PsiImplUtil.isWhiteSpaceOrNls(prev.getNextSibling())) {
node.removeChild(prev.getNextSibling().getNode());
}
node.addLeaf(GroovyTokenTypes.mNLS, StringUtil.repeat("\n", spaceCount + 1), result.getNode());
}
}
protected void addLineFeedAfter(@NotNull PsiElement psiFile, GrImportStatement result) {
final GroovyCodeStyleSettings settings = CodeStyleSettingsManager.getInstance(psiFile.getProject()).getCurrentSettings().getCustomSettings(GroovyCodeStyleSettings.class);
final PackageEntryTable layoutTable = settings.IMPORT_LAYOUT_TABLE;
final PackageEntry[] entries = layoutTable.getEntries();
PsiElement next = result.getNextSibling();
if (PsiImplUtil.isWhiteSpaceOrNls(next)) next = next.getNextSibling();
if (PsiImplUtil.hasElementType(next, GroovyTokenTypes.mSEMI)) next = next.getNextSibling();
while (PsiImplUtil.isWhiteSpaceOrNls(next)) {
next = next.getNextSibling();
}
if (next instanceof GrImportStatement) {
final int idx_after = getPackageEntryIdx(entries, (GrImportStatement)next);
final int idx = getPackageEntryIdx(entries, result);
final int spaceCount = getMaxSpaceCount(entries, idx, idx_after);
ASTNode node = psiFile.getNode();
while (PsiImplUtil.isWhiteSpaceOrNls(next.getPrevSibling())) {
node.removeChild(next.getPrevSibling().getNode());
}
node.addLeaf(GroovyTokenTypes.mNLS, StringUtil.repeat("\n", spaceCount + 1), next.getNode());
}
}
private static int getMaxSpaceCount(PackageEntry[] entries, int b1, int b2) {
int start = Math.min(b1, b2);
int end = Math.max(b1, b2);
if (start == -1) return 0;
int max = 0;
int cur = 0;
for (int i = start; i < end; i++) {
if (entries[i] == PackageEntry.BLANK_LINE_ENTRY) {
cur++;
}
else {
max = Math.max(max, cur);
cur = 0;
}
}
max = Math.max(max, cur);
return max;
}
@Override
public void removeImport(@NotNull GroovyFileBase psiFile, @NotNull GrImportStatement importStatement) throws IncorrectOperationException {
PsiElement psiElement = psiFile;
PsiElement before = importStatement;
while (PsiImplUtil.isWhiteSpaceOrNls(before.getPrevSibling())) {
before = before.getPrevSibling();
}
if (PsiImplUtil.hasElementType(before.getPrevSibling(), GroovyTokenTypes.mSEMI)) before = before.getPrevSibling();
if (PsiImplUtil.isWhiteSpaceOrNls(before.getPrevSibling())) before = before.getPrevSibling();
PsiElement after = importStatement;
if (PsiImplUtil.isWhiteSpaceOrNls(after.getNextSibling())) after = after.getNextSibling();
if (PsiImplUtil.hasElementType(after.getNextSibling(), GroovyTokenTypes.mSEMI)) after = after.getNextSibling();
while (PsiImplUtil.isWhiteSpaceOrNls(after.getNextSibling())) after = after.getNextSibling();
if (before == null) before = importStatement;
PsiElement anchor_before = before.getPrevSibling();
PsiElement anchor_after = after.getNextSibling();
if (before == after) {
importStatement.delete();
}
else {
psiFile.deleteChildRange(before, after);
}
if (anchor_before instanceof GrImportStatement && anchor_after instanceof GrImportStatement) {
addLineFeedAfter(psiFile, (GrImportStatement)anchor_before);
}
else if (anchor_before != null && anchor_after != null) {
String text = anchor_after instanceof GrTopStatement && anchor_before instanceof GrTopStatement ? "\n\n" : "\n";
psiElement.getNode().addLeaf(GroovyTokenTypes.mNLS, text, anchor_after.getNode());
}
}
}