src/org/intellij/grammar/refactor/BnfInlineRuleProcessor.java (146 lines of code) (raw):
/*
* Copyright 2011-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package org.intellij.grammar.refactor;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
import com.intellij.psi.PsiReference;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewDescriptor;
import com.intellij.util.IncorrectOperationException;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.intellij.grammar.generator.ParserGeneratorUtil;
import org.intellij.grammar.psi.*;
import org.intellij.grammar.psi.impl.BnfElementFactory;
import org.intellij.grammar.psi.impl.GrammarUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* Created by IntelliJ IDEA.
* Date: 8/11/11
* Time: 4:19 PM
*
* @author Vadim Romansky
*/
public class BnfInlineRuleProcessor extends BaseRefactoringProcessor {
private static final Logger LOG = Logger.getInstance(BnfInlineRuleProcessor.class);
private BnfRule myRule;
private final PsiReference myReference;
private final boolean myInlineThisOnly;
public BnfInlineRuleProcessor(BnfRule rule, Project project, PsiReference ref, boolean isInlineThisOnly) {
super(project);
myRule = rule;
myReference = ref;
myInlineThisOnly = isInlineThisOnly;
}
@Override
protected @NotNull UsageViewDescriptor createUsageViewDescriptor(UsageInfo @NotNull [] usages) {
return new BnfInlineViewDescriptor(myRule);
}
@Override
protected @NotNull String getCommandName() {
return "Inline rule '" + myRule.getName() + "'";
}
@Override
protected UsageInfo @NotNull [] findUsages() {
if (myInlineThisOnly) return new UsageInfo[]{new UsageInfo(myReference.getElement())};
List<UsageInfo> result = new ArrayList<>();
for (PsiReference reference : ReferencesSearch.search(myRule, myRule.getUseScope(), false)) {
PsiElement element = reference.getElement();
if (GrammarUtil.isInAttributesReference(element)) continue;
result.add(new UsageInfo(element));
}
return result.toArray(UsageInfo.EMPTY_ARRAY);
}
@Override
protected void refreshElements(PsiElement @NotNull [] elements) {
LOG.assertTrue(elements.length == 1 && elements[0] instanceof BnfRule);
myRule = (BnfRule)elements[0];
}
@Override
protected void performRefactoring(UsageInfo @NotNull [] usages) {
BnfExpression expression = myRule.getExpression();
boolean meta = ParserGeneratorUtil.Rule.isMeta(myRule);
CommonRefactoringUtil.sortDepthFirstRightLeftOrder(usages);
for (UsageInfo info : usages) {
try {
BnfExpression element = (BnfExpression)info.getElement();
boolean metaRuleRef = GrammarUtil.isExternalReference(element);
if (meta && metaRuleRef) {
inlineMetaRuleUsage(myProject, element, expression);
}
else if (!meta && !metaRuleRef) {
inlineExpressionUsage(myProject, element, expression);
}
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
if (!myInlineThisOnly) {
try {
myRule.delete();
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
}
private static void inlineExpressionUsage(Project project, BnfExpression place, BnfExpression ruleExpr) throws IncorrectOperationException {
BnfExpression replacement = BnfElementFactory.createExpressionFromText(project, '(' + ruleExpr.getText() + ')');
BnfExpressionOptimizer.optimize(project, place.replace(replacement));
}
private static void inlineMetaRuleUsage(Project project, BnfExpression place, BnfExpression expression) {
BnfRule rule = PsiTreeUtil.getParentOfType(place, BnfRule.class);
PsiElement parent = place.getParent();
List<BnfExpression> expressionList;
if (parent instanceof BnfExternalExpression) {
expressionList = ((BnfExternalExpression)parent).getExpressionList();
}
else if (parent instanceof BnfSequence) {
expressionList = ((BnfSequence)parent).getExpressionList();
}
else if (parent instanceof BnfRule) {
expressionList = Collections.emptyList();
}
else {
LOG.error("Unexpected element: " + (parent == null ? "null" : parent.getClass().getName()));
return;
}
Object2IntMap<String> visited = new Object2IntOpenHashMap<>();
LinkedList<Pair<PsiElement, PsiElement>> work = new LinkedList<>();
(expression = (BnfExpression)expression.copy()).acceptChildren(new PsiRecursiveElementWalkingVisitor() {
@Override
public void visitElement(@NotNull PsiElement element) {
if (element instanceof BnfExternalExpression) {
List<BnfExpression> list = ((BnfExternalExpression)element).getExpressionList();
if (list.size() == 1) {
String text = list.get(0).getText();
int idx = visited.getInt(text);
if (idx == 0) visited.put(text, idx = visited.size() + 1);
if (idx < expressionList.size()) {
work.addFirst(Pair.create(element, expressionList.get(idx)));
}
}
}
else {
super.visitElement(element);
}
}
});
for (Pair<PsiElement, PsiElement> pair : work) {
BnfExpressionOptimizer.optimize(project, pair.first.replace(pair.second));
}
inlineExpressionUsage(project, (BnfExpression)parent, expression);
if (!(parent instanceof BnfExternalExpression)) {
for (BnfModifier modifier : rule.getModifierList()) {
if (modifier.getText().equals("external")) {
modifier.getNextSibling().delete(); // whitespace
modifier.delete();
break;
}
}
}
}
}