blob: cffc06c68a8dab10e479579cc62f92fd0d7eda21 [file] [log] [blame]
/*
* Copyright 2007 Sascha Weinreuter
*
* 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.plugins.relaxNG.compact.psi.impl;
import com.intellij.codeInsight.daemon.EmptyResolveMessageProvider;
import com.intellij.codeInspection.LocalQuickFix;
import com.intellij.codeInspection.LocalQuickFixProvider;
import com.intellij.codeInspection.ProblemDescriptor;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import org.intellij.plugins.relaxNG.compact.RncFileType;
import org.intellij.plugins.relaxNG.compact.RncTokenTypes;
import org.intellij.plugins.relaxNG.compact.psi.*;
import org.intellij.plugins.relaxNG.compact.psi.util.EscapeUtil;
import org.intellij.plugins.relaxNG.compact.psi.util.RenameUtil;
import org.intellij.plugins.relaxNG.model.Define;
import org.intellij.plugins.relaxNG.model.resolve.DefinitionResolver;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import java.util.Set;
/**
* Created by IntelliJ IDEA.
* User: sweinreuter
* Date: 13.08.2007
*/
class PatternReference extends PsiReferenceBase.Poly<RncRef> implements Function<Define, ResolveResult>,
LocalQuickFixProvider, EmptyResolveMessageProvider {
public PatternReference(RncRef ref) {
super(ref);
}
@Override
public TextRange getRangeInElement() {
final ASTNode node = findNameNode();
if (node == null) return TextRange.from(0, 0);
final int offset = myElement.getTextOffset();
return TextRange.from(offset - myElement.getTextRange().getStartOffset(), node.getTextLength());
}
private ASTNode findNameNode() {
final ASTNode node = myElement.getNode();
assert node != null;
return node.findChildByType(RncTokenTypes.IDENTIFIERS);
}
@Override
@Nullable
public PsiElement resolve() {
final ResolveResult[] results = multiResolve(false);
return results.length == 1 ? results[0].getElement() : null;
}
@Override
@NotNull
public ResolveResult[] multiResolve(boolean incompleteCode) {
final RncGrammar scope = getScope();
if (scope == null) {
return ResolveResult.EMPTY_ARRAY;
}
final Set<Define> set = DefinitionResolver.resolve(scope, getCanonicalText());
if (set == null || set.size() == 0) return ResolveResult.EMPTY_ARRAY;
return ContainerUtil.map2Array(set, ResolveResult.class, this);
}
@Override
public ResolveResult fun(Define rncDefine) {
final PsiElement element = rncDefine.getPsiElement();
return element != null ? new PsiElementResolveResult(element) : new ResolveResult() {
@Override
@Nullable
public PsiElement getElement() {
return null;
}
@Override
public boolean isValidResult() {
return false;
}
};
}
@Nullable
protected RncGrammar getScope() {
return PsiTreeUtil.getParentOfType(myElement, RncGrammar.class, true, PsiFile.class);
}
@Override
@NotNull
public String getCanonicalText() {
final ASTNode node = findNameNode();
return node != null ? EscapeUtil.unescapeText(node) : "";
}
@Override
public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
final ASTNode newNode = RenameUtil.createIdentifierNode(getElement().getManager(), newElementName);
final ASTNode nameNode = findNameNode();
nameNode.getTreeParent().replaceChild(nameNode, newNode);
return getElement();
}
@Override
public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
throw new UnsupportedOperationException();
}
@Override
@NotNull
public Object[] getVariants() {
final RncGrammar scope = getScope();
if (scope == null) {
return ResolveResult.EMPTY_ARRAY;
}
final Map<String, Set<Define>> map = DefinitionResolver.getAllVariants(scope);
if (map == null || map.size() == 0) return ArrayUtil.EMPTY_OBJECT_ARRAY;
return ContainerUtil.mapNotNull(map.values(), new Function<Set<Define>, Object>() {
@Override
public Object fun(Set<Define> defines) {
return defines.size() == 0 ? null : defines.iterator().next().getPsiElement();
}
}).toArray();
}
@Override
public boolean isSoft() {
return false;
}
@Override
@NotNull
public String getUnresolvedMessagePattern() {
return "Unresolved pattern reference ''{0}''";
}
@Nullable
@Override
public LocalQuickFix[] getQuickFixes() {
if (getScope() != null) {
return new LocalQuickFix[] { new CreatePatternFix(this) };
}
return LocalQuickFix.EMPTY_ARRAY;
}
static class CreatePatternFix implements LocalQuickFix {
private final PatternReference myReference;
public CreatePatternFix(PatternReference reference) {
myReference = reference;
}
@NotNull
@Override
public String getName() {
return "Create Pattern '" + myReference.getCanonicalText() + "'";
}
@Override
@NotNull
public String getFamilyName() {
return "Create Pattern";
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final RncFile rncfile = (RncFile)PsiFileFactory.getInstance(myReference.getElement().getProject()).createFileFromText("dummy.rnc", RncFileType.getInstance(), "dummy = xxx");
final RncGrammar grammar = rncfile.getGrammar();
assert grammar != null;
final RncDefine def = (RncDefine)grammar.getFirstChild();
final RncGrammar scope = myReference.getScope();
assert scope != null;
assert def != null;
final RncDefine e = (RncDefine)scope.add(def);
// ensures proper escaping (start -> \start)
def.setName(myReference.getCanonicalText());
final SmartPsiElementPointer<RncDefine> p = SmartPointerManager.getInstance(project).createSmartPsiElementPointer(e);
final ASTNode blockNode = e.getParent().getNode();
assert blockNode != null;
final ASTNode newNode = e.getNode();
assert newNode != null;
CodeStyleManager.getInstance(e.getManager().getProject()).reformatNewlyAddedElement(blockNode, newNode);
final RncDefine d = p.getElement();
assert d != null;
final RncElement definition = d.getPattern();
assert definition != null;
final int offset = definition.getTextRange().getStartOffset();
definition.delete();
VirtualFile virtualFile = myReference.getElement().getContainingFile().getVirtualFile();
if (virtualFile != null) {
FileEditorManager.getInstance(project).openTextEditor(new OpenFileDescriptor(project, virtualFile, offset), true);
}
}
}
}