/* * Copyright 2013-2016 consulo.io * * 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 consulo.unity3d.shaderlab.lang.psi; import java.util.Collection; import java.util.List; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.icons.AllIcons; import com.intellij.lang.ASTNode; import com.intellij.openapi.util.TextRange; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiQualifiedReferenceElement; import com.intellij.psi.PsiReference; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.tree.TokenSet; import com.intellij.util.Consumer; import com.intellij.util.IncorrectOperationException; import com.intellij.util.SmartList; import com.intellij.util.containers.ContainerUtil; import consulo.annotations.RequiredReadAction; import consulo.csharp.lang.psi.impl.msil.CSharpTransform; import consulo.dotnet.psi.DotNetTypeDeclaration; import consulo.dotnet.resolve.DotNetPsiSearcher; import consulo.unity3d.shaderlab.ide.refactoring.ShaderRefactorUtil; import consulo.unity3d.shaderlab.lang.ShaderMaterialAttribute; import consulo.unity3d.shaderlab.lang.parser.roles.ShaderLabRole; import consulo.unity3d.shaderlab.lang.psi.stub.index.ShaderDefIndex; /** * @author VISTALL * @since 08.05.2015 */ public class ShaderReference extends ShaderLabElement implements PsiQualifiedReferenceElement { public static enum ResolveKind { ATTRIBUTE, ANOTHER_SHADER, PROPERTY } private static final TokenSet ourTokens = TokenSet.create(ShaderLabTokens.IDENTIFIER, ShaderLabTokens.STRING_LITERAL); public ShaderReference(@NotNull ASTNode node) { super(node); } @NotNull public ResolveKind kind() { PsiElement parent = getParent(); if(parent instanceof ShaderPropertyAttribute) { return ResolveKind.ATTRIBUTE; } else if(parent instanceof ShaderSetTexture) { return ResolveKind.PROPERTY; } else if(parent instanceof ShaderSimpleValue) { ShaderLabRole role = ((ShaderSimpleValue) parent).getRole(); if(role == ShaderLabRole.Fallback || role == ShaderLabRole.UsePass) { return ResolveKind.ANOTHER_SHADER; } } return ResolveKind.PROPERTY; } @Override public PsiReference getReference() { return this; } @Override public PsiElement getElement() { return this; } @NotNull public PsiElement getReferenceElement() { return findNotNullChildByType(ourTokens); } @Override public TextRange getRangeInElement() { PsiElement referenceElement = getReferenceElement(); return new TextRange(0, referenceElement.getTextLength()); } @Nullable @Override @RequiredReadAction public PsiElement resolve() { GlobalSearchScope scope = GlobalSearchScope.allScope(getProject()); ResolveKind kind = kind(); String referenceName = getReferenceName(); switch(kind) { case ATTRIBUTE: try { ShaderMaterialAttribute attribute = ShaderMaterialAttribute.valueOf(referenceName); DotNetTypeDeclaration type = DotNetPsiSearcher.getInstance(getProject()).findType(attribute.getType(), scope, CSharpTransform.INSTANCE); if(type != null) { return type; } } catch(IllegalArgumentException ignored) { // } break; case ANOTHER_SHADER: Collection<ShaderDef> shaderDefs = ShaderDefIndex.getInstance().get(referenceName, getProject(), scope); return ContainerUtil.getFirstItem(shaderDefs); case PROPERTY: PsiFile containingFile = getContainingFile(); if(!(containingFile instanceof ShaderLabFile)) { return null; } for(ShaderProperty shaderProperty : ((ShaderLabFile) containingFile).getProperties()) { if(referenceName.equals(shaderProperty.getName())) { return shaderProperty; } } break; } return null; } @NotNull @Override public String getCanonicalText() { return getText(); } @Override public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException { ShaderRefactorUtil.replaceIdentifier(getReferenceElement(), newElementName); return this; } @Override public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException { return null; } @Override @RequiredReadAction public boolean isReferenceTo(PsiElement element) { return element.isEquivalentTo(resolve()); } @NotNull @Override public Object[] getVariants() { ResolveKind kind = kind(); final List<LookupElement> values = new SmartList<LookupElement>(); switch(kind) { case ATTRIBUTE: for(ShaderMaterialAttribute attribute : ShaderMaterialAttribute.values()) { LookupElementBuilder builder = LookupElementBuilder.create(attribute.name()); builder = builder.withIcon(AllIcons.Nodes.Class); builder = builder.withTypeText(attribute.getType(), true); values.add(builder); } case PROPERTY: PsiFile containingFile = getContainingFile(); if(containingFile instanceof ShaderLabFile) { consumeProperties((ShaderLabFile) containingFile, new Consumer<LookupElement>() { @Override public void consume(LookupElement lookupElement) { values.add(lookupElement); } }); } break; } return values.toArray(); } public static void consumeProperties(@NotNull ShaderLabFile file, @NotNull Consumer<LookupElement> consumer) { for(ShaderProperty shaderProperty : file.getProperties()) { String name = shaderProperty.getName(); if(name == null) { continue; } LookupElementBuilder builder = LookupElementBuilder.create(name); builder = builder.withIcon(AllIcons.Nodes.Property); ShaderPropertyType type = shaderProperty.getType(); if(type != null) { builder = builder.withTypeText(type.getTargetText(), true); } consumer.consume(builder); } } @Override public boolean isSoft() { return false; } @Override public void accept(SharpLabElementVisitor visitor) { visitor.visitReference(this); } @Nullable @Override public PsiElement getQualifier() { return findChildByClass(ShaderReference.class); } @Nullable @Override public String getReferenceName() { PsiElement referenceElement = getReferenceElement(); return referenceElement.getText(); } }