/*
* Copyright 2011-present Greg Shrago
*
* 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.intellij.grammar.refactor;
import com.intellij.codeInsight.unwrap.UnwrapDescriptor;
import com.intellij.codeInsight.unwrap.Unwrapper;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.SelectionModel;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiWhiteSpace;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.IncorrectOperationException;
import org.intellij.grammar.psi.*;
import org.intellij.grammar.psi.impl.BnfElementFactory;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* @author gregsh
*/
public class BnfUnwrapDescriptor implements UnwrapDescriptor, Unwrapper {
@Override
public List<Pair<PsiElement, Unwrapper>> collectUnwrappers(Project project, Editor editor, PsiFile file) {
PsiElement element = findTargetElement(editor, file);
List<Pair<PsiElement, Unwrapper>> result = new ArrayList<>();
while (element != null) {
if (element instanceof BnfParenthesized) {
result.add(new Pair<>(element, this));
}
element = element.getParent();
}
return result;
}
@Override
public boolean showOptionsDialog() {
return true;
}
@Override
public boolean shouldTryToRestoreCaretPosition() {
return true;
}
@Override
public boolean isApplicableTo(PsiElement e) {
return e instanceof BnfParenthesized && !PsiUtil.hasErrorElementChild(e);
}
@Override
public void collectElementsToIgnore(PsiElement element, Set<PsiElement> result) {
}
@Override
public String getDescription(PsiElement e) {
PsiElement parent = e.getParent();
BnfQuantifier quantifier = parent instanceof BnfQuantified ? ((BnfQuantified)parent).getQuantifier() : null;
BnfPredicateSign sign = parent instanceof BnfPredicate ? ((BnfPredicate)parent).getPredicateSign() : null;
String prefix = sign == null? "" : sign.getText();
String suffix = quantifier == null? "" : quantifier.getText();
return "Unwrap " + prefix + e.getFirstChild().getText() + "..." + e.getLastChild().getText() + suffix;
}
@Override
public PsiElement collectAffectedElements(PsiElement element, List<PsiElement> toExtract) {
PsiElement last = element.getLastChild();
PsiElement first = element.getFirstChild();
if (element instanceof BnfParenthesized) {
last = last.getPrevSibling();
first = first.getNextSibling();
}
while (first != last && first instanceof PsiWhiteSpace) {
first = first.getNextSibling();
}
while (last != first && last instanceof PsiWhiteSpace) {
last = last.getPrevSibling();
}
if (first == null || last == null || first == last && last instanceof PsiWhiteSpace) return null;
for (PsiElement c = first; c != last && c != null; c = c.getNextSibling()) {
toExtract.add(c);
}
PsiElement parent = element.getParent();
PsiElement target = parent instanceof BnfQuantified || parent instanceof BnfPredicate? parent : element;
return target;
}
@Override
public List<PsiElement> unwrap(Editor editor, PsiElement element) throws IncorrectOperationException {
PsiElement last = element.getLastChild();
PsiElement first = element.getFirstChild();
if (element instanceof BnfParenthesized) {
last = last.getPrevSibling();
first = first.getNextSibling();
}
while (first != last && first instanceof PsiWhiteSpace) {
first = first.getNextSibling();
}
while (last != first && last instanceof PsiWhiteSpace) {
last = last.getPrevSibling();
}
if (first == null || last == null || first == last && last instanceof PsiWhiteSpace) return null;
PsiElement parent = element.getParent();
PsiElement target = parent instanceof BnfQuantified || parent instanceof BnfPredicate? parent : element;
return Collections.singletonList(target.replace(BnfElementFactory.createExpressionFromText(
editor.getProject(), element.getContainingFile().getText().substring(first.getTextRange().getStartOffset(), last.getTextRange().getEndOffset()))));
}
@Nullable
private static PsiElement findTargetElement(Editor editor, PsiFile file) {
int offset = editor.getCaretModel().getOffset();
PsiElement endElement = file.findElementAt(offset);
SelectionModel selectionModel = editor.getSelectionModel();
if (selectionModel.hasSelection() && selectionModel.getSelectionStart() < offset) {
PsiElement startElement = file.findElementAt(selectionModel.getSelectionStart());
if (startElement != null && startElement != endElement && startElement.getTextRange().getEndOffset() == offset) {
return startElement;
}
}
return endElement;
}
}