diff --git a/src/main/java/glslplugin/extensions/GLSLCompletionContributor.java b/src/main/java/glslplugin/extensions/GLSLCompletionContributor.java index a6a7a1d..86a67f8 100644 --- a/src/main/java/glslplugin/extensions/GLSLCompletionContributor.java +++ b/src/main/java/glslplugin/extensions/GLSLCompletionContributor.java @@ -27,30 +27,35 @@ import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.codeInsight.lookup.LookupElementBuilder; import com.intellij.codeInsight.lookup.LookupElementPresentation; -import com.intellij.openapi.fileTypes.LanguageFileType; +import com.intellij.codeInsight.lookup.LookupElementRenderer; import com.intellij.patterns.ElementPattern; -import com.intellij.patterns.StandardPatterns; import com.intellij.psi.PsiElement; +import com.intellij.psi.ResolveState; +import com.intellij.psi.scope.PsiScopeProcessor; +import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.util.ProcessingContext; -import glslplugin.lang.GLSLFileType; import glslplugin.lang.elements.GLSLTokenTypes; import glslplugin.lang.elements.declarations.GLSLDeclarator; import glslplugin.lang.elements.declarations.GLSLFunctionDeclaration; +import glslplugin.lang.elements.declarations.GLSLParameterDeclaration; import glslplugin.lang.elements.declarations.GLSLStructDefinition; -import glslplugin.lang.elements.expressions.GLSLAssignmentExpression; import glslplugin.lang.elements.expressions.GLSLExpression; import glslplugin.lang.elements.expressions.GLSLFieldSelectionExpression; -import glslplugin.lang.elements.expressions.GLSLFunctionOrConstructorCallExpression; -import glslplugin.lang.elements.expressions.GLSLVariableExpression; -import glslplugin.lang.elements.types.GLSLScalarType; +import glslplugin.lang.elements.preprocessor.GLSLDefineDirective; +import glslplugin.lang.elements.reference.GLSLBuiltInPsiUtilService; +import glslplugin.lang.elements.statements.GLSLCompoundStatement; +import glslplugin.lang.elements.types.GLSLArrayType; +import glslplugin.lang.elements.types.GLSLMatrixType; import glslplugin.lang.elements.types.GLSLStructType; import glslplugin.lang.elements.types.GLSLType; import glslplugin.lang.elements.types.GLSLVectorType; +import glslplugin.util.TreeIterator; import glslplugin.util.VectorComponents; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import java.util.ArrayList; +import java.util.HashMap; import java.util.Map; import static com.intellij.patterns.PlatformPatterns.psiElement; @@ -59,114 +64,158 @@ * Provides more advanced contextual autocompletion * * @author Wyozi + * @author Jan Polák */ public class GLSLCompletionContributor extends DefaultCompletionContributor { private static final ElementPattern FIELD_SELECTION = psiElement(GLSLTokenTypes.IDENTIFIER).withParent(GLSLFieldSelectionExpression.class); - private static final ElementPattern VARIABLE_REFERENCE = psiElement(GLSLTokenTypes.IDENTIFIER).withParent(GLSLVariableExpression.class); + private static final ElementPattern GENERIC_REFERENCE = psiElement(GLSLTokenTypes.IDENTIFIER); public GLSLCompletionContributor() { // Add field selection completion extend(CompletionType.BASIC, FIELD_SELECTION, new CompletionProvider<>() { @Override protected void addCompletions(@NotNull CompletionParameters completionParameters, @NotNull ProcessingContext processingContext, @NotNull CompletionResultSet completionResultSet) { - GLSLFieldSelectionExpression fieldSelection = (GLSLFieldSelectionExpression) completionParameters.getPosition().getParent().getParent(); + final PsiElement position = completionParameters.getPosition(); + GLSLFieldSelectionExpression fieldSelection = (GLSLFieldSelectionExpression) position.getParent(); GLSLExpression leftHandExpression = fieldSelection.getLeftHandExpression(); if (leftHandExpression == null) return; // Struct member completion GLSLType type = leftHandExpression.getType(); - if (type instanceof GLSLStructType) { - completeStructTypes(((GLSLStructType) type), completionResultSet); - } - // Vector completion that infers component count from the context - if (type instanceof GLSLVectorType) { - // if it's a declaration (eg. `vec3 v3 = v4.`) the context type is inferred from declarator type (here vec3) - GLSLDeclarator declarator = PsiTreeUtil.getParentOfType(fieldSelection, GLSLDeclarator.class); - if (declarator != null) { - completeVectorTypes(((GLSLVectorType) type), declarator.getType(), completionResultSet); + if (type instanceof GLSLVectorType vec) { + completionResultSet.addElement(LookupElementBuilder.create("length()").withTypeText("int")); + + final int textStart = position.getTextOffset(); + final int textEnd = completionParameters.getOffset(); + if (textStart < textEnd) { + String prefix = position.getText().substring(0, textEnd - textStart); + if (prefix.length() > 0 && prefix.length() < 4) { + final String newType = GLSLVectorType.getType(vec.getBaseType(), prefix.length() + 1).getTypename(); + + final char firstComponent = prefix.charAt(0); + for (String set : VectorComponents.SETS) { + if (set.indexOf(firstComponent) == -1) { + continue; + } + for (int i = 0; i < Math.min(vec.getNumComponents(), set.length()); i++) { + completionResultSet.addElement(LookupElementBuilder.create(prefix + set.charAt(i)).withTypeText(newType)); + } + break; + } + } + + } else { + String baseType = vec.getBaseType().getTypename(); + for (String set : VectorComponents.SETS) { + for (int i = 0; i < Math.min(vec.getNumComponents(), set.length()); i++) { + completionResultSet.addElement(LookupElementBuilder.create(set.charAt(i)).withTypeText(baseType)); + } + } } - - // if it's an assignment (eg. `vec3 v3; v3 = v4.`) the context type is inferred from variable type (if known) - GLSLAssignmentExpression assignment = PsiTreeUtil.getParentOfType(fieldSelection, GLSLAssignmentExpression.class); - if (assignment != null) { - completeVectorTypes(((GLSLVectorType) type), assignment.getType(), completionResultSet); + } else if (type instanceof GLSLMatrixType || type instanceof GLSLArrayType) { + completionResultSet.addElement(LookupElementBuilder.create("length()").withTypeText("int")); + } else if (type instanceof GLSLStructType struct) { + for (Map.Entry entry : struct.getMembers().entrySet()) { + completionResultSet.addElement(LookupElementBuilder.create(entry.getKey()).withTypeText(entry.getValue().getTypename())); } } } }); - // Functions often start like this - extend(CompletionType.BASIC, VARIABLE_REFERENCE, new CompletionProvider() { + // This can be anything, types, tokens... + extend(CompletionType.BASIC, GENERIC_REFERENCE, new CompletionProvider<>() { @Override protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) { - final GLSLFunctionOrConstructorCallExpression.WalkResult walk = GLSLFunctionOrConstructorCallExpression.WalkResult.walkPossibleReferences(parameters.getOriginalPosition(), null); - for (GLSLFunctionDeclaration value : walk.functionDeclarations.values()) { - result.addElement(LookupElementBuilder.create(value)); + final PsiElement element = parameters.getOriginalPosition(); + if (element == null) return; + + final PsiElement prevLeaf = PsiTreeUtil.prevLeaf(parameters.getPosition()); + if (prevLeaf != null) { + final IElementType prevToken = prevLeaf.getNode().getElementType(); + if (prevToken == GLSLTokenTypes.DOT) { + // Field selection handles this + return; + } + if (GLSLTokenTypes.CONSTANT_TOKENS.contains(prevToken)) { + // No complete right after a number, it is confusing and annoying + return; + } } - for (GLSLStructDefinition value : walk.structDefinitions) { - result.addElement(LookupElementBuilder.create(value)); + + // Walk declarations + boolean includeFunctions = PsiTreeUtil.getParentOfType(element, GLSLCompoundStatement.class) != null; + final DeclarationWalk walk = new DeclarationWalk(result, includeFunctions); + PsiTreeUtil.treeWalkUp(walk, element, null, ResolveState.initial()); + + // Walk preprocessor tokens + { + GLSLDefineDirective prev = TreeIterator.previous(element, GLSLDefineDirective.class); + while (prev != null) { + System.out.println("Got one: "+prev); + result.addElement(LookupElementBuilder.create(prev)); + prev = TreeIterator.previous(prev, GLSLDefineDirective.class); + } } + + // Add all built-ins + final GLSLBuiltInPsiUtilService bipus = element.getProject().getService(GLSLBuiltInPsiUtilService.class); + result.addAllElements(bipus.builtinTypeLookup); } }); } - private void completeStructTypes(GLSLStructType type, CompletionResultSet completionResultSet) { - for (Map.Entry entry : type.getMembers().entrySet()) { - completionResultSet.addElement(new GLSLLookupElement(entry.getKey(), entry.getValue())); - } - completionResultSet.stopHere(); - } + /** Combination of all reference walks into one, for efficiency. */ + private static final class DeclarationWalk implements PsiScopeProcessor { - /** - * Vector type completion implementation. Uses values from {@link VectorComponents} in order. - * - * @param type the vector type we're completing - * @param contextNumComponents how many components does the context expect - */ - private void completeVectorTypes(GLSLVectorType type, int contextNumComponents, CompletionResultSet completionResultSet) { - int componentCount = Math.min(contextNumComponents, type.getNumComponents()); - - for (VectorComponents components : VectorComponents.values()) { - completionResultSet.addElement(new GLSLLookupElement(components.getComponentRange(componentCount), type)); - } + public final @NotNull CompletionResultSet result; + public final boolean includeFunctions; - completionResultSet.stopHere(); - } - - private void completeVectorTypes(GLSLVectorType type, @Nullable GLSLType contextType, CompletionResultSet completionResultSet) { - if (contextType instanceof GLSLScalarType) { - completeVectorTypes(type, 1, completionResultSet); - } else if (contextType instanceof GLSLVectorType) { - completeVectorTypes(type, ((GLSLVectorType) contextType).getNumComponents(), completionResultSet); + private DeclarationWalk(@NotNull CompletionResultSet result, boolean includeFunctions) { + this.result = result; + this.includeFunctions = includeFunctions; } - } - public static class GLSLLookupElement extends LookupElement { - private final String str; - private final GLSLType type; - - public GLSLLookupElement(String str, GLSLType type) { - this.str = str; - this.type = type; - } + private final HashMap> encounteredFunctions = new HashMap<>(); @Override - public void renderElement(LookupElementPresentation presentation) { - super.renderElement(presentation); - presentation.setIcon(((LanguageFileType) GLSLFileType.INSTANCE).getIcon()); - - if (type != null) { - presentation.setTypeText(this.type.getTypename()); + public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) { + if (element instanceof final GLSLDeclarator declarator) { + result.addElement(LookupElementBuilder.create(declarator).withTypeText(declarator.getType().getTypename())); + } else if (element instanceof GLSLStructDefinition def) { + result.addElement(LookupElementBuilder.create(def).withTypeText("struct")); + } else if (includeFunctions && element instanceof GLSLFunctionDeclaration dec) { + final String funcName = dec.getFunctionName(); + ArrayList all = encounteredFunctions.get(funcName); + if (all == null) { + all = new ArrayList<>(); + result.addElement(LookupElementBuilder.create(dec).withExpensiveRenderer(new LookupElementRenderer<>() { + @Override + public void renderElement(LookupElement element, LookupElementPresentation presentation) { + presentation.setItemText(dec.getFunctionName()); + presentation.appendTailText("(", true); + boolean first = true; + for (GLSLParameterDeclaration parameter : dec.getParameters()) { + if (!first) { + presentation.appendTailText(", ", true); + } else first = false; + + presentation.appendTailText(parameter.getTypeSpecifierNodeTypeName(), false); + } + presentation.appendTailText(")", true); + presentation.setTypeText(dec.getReturnType().getTypename()); + } + })); + all.add(dec); + encounteredFunctions.put(funcName, all); + } else { + all.add(dec); + } } - } - @NotNull - @Override - public String getLookupString() { - return this.str; + return true; } } } diff --git a/src/main/java/glslplugin/lang/elements/declarations/GLSLFunctionDeclarationImpl.java b/src/main/java/glslplugin/lang/elements/declarations/GLSLFunctionDeclarationImpl.java index 0159d96..40f3145 100755 --- a/src/main/java/glslplugin/lang/elements/declarations/GLSLFunctionDeclarationImpl.java +++ b/src/main/java/glslplugin/lang/elements/declarations/GLSLFunctionDeclarationImpl.java @@ -129,16 +129,6 @@ public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) } return true;// Continue } - - @Override - public Object @NotNull [] getVariants() { - try { - PsiTreeUtil.treeWalkUp(this, getElement(), null, ResolveState.initial()); - return functionDefinitions.toArray(); - } finally { - functionDefinitions.clear(); - } - } } @Override diff --git a/src/main/java/glslplugin/lang/elements/declarations/GLSLTypename.java b/src/main/java/glslplugin/lang/elements/declarations/GLSLTypename.java index d647c89..02473e3 100755 --- a/src/main/java/glslplugin/lang/elements/declarations/GLSLTypename.java +++ b/src/main/java/glslplugin/lang/elements/declarations/GLSLTypename.java @@ -73,63 +73,50 @@ public String getReferencedTypeName() { /** If this refers to a struct, return its definition. */ @Nullable public GLSLStructDefinition getStructDefinition() { - final GLSLType type = getReference().resolveType(); + final GLSLType type = getType(); if (type instanceof GLSLStructType) { return ((GLSLStructType) type).getDefinition(); } return null; } + private GLSLType builtinTypeCache = null; + + @Override + public void subtreeChanged() { + super.subtreeChanged(); + builtinTypeCache = null; + } + @NotNull public GLSLType getType() { - return getReference().resolveType(); - } + final GLSLType cached = builtinTypeCache; + if (cached != null) return cached; - public static final class TypeReference extends GLSLAbstractReference implements PsiScopeProcessor { + final String typeName = GLSLElement.text(this); - public TypeReference(@NotNull GLSLTypename element) { - super(element); + GLSLType builtIn = GLSLTypes.getTypeFromName(typeName); + if (builtIn != null) { + return builtinTypeCache = builtIn; } - private String onlyNamed = null; - private final ArrayList definitions = new ArrayList<>(); - - @Override - public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) { - if (element instanceof final GLSLStructDefinition def) { - if (onlyNamed == null || onlyNamed.equals(def.getStructName())) { - definitions.add(def); - return onlyNamed == null;// Done if we found the one named - } - } - return true;// Continue + final StructDefinitionWalk result = StructDefinitionWalk.walk(this, typeName); + if (!result.definitions.isEmpty()) { + return result.definitions.get(0).getType(); } - public @NotNull GLSLType resolveType() { - final String typeName = GLSLElement.text(getElement()); - - GLSLType builtIn = GLSLTypes.getTypeFromName(typeName); - if (builtIn != null) { - return builtIn; - } + return GLSLTypes.getUndefinedType(typeName); + } - try { - onlyNamed = typeName; - PsiTreeUtil.treeWalkUp(this, getElement(), null, ResolveState.initial()); - if (!definitions.isEmpty()) { - return definitions.get(0).getType(); - } - } finally { - definitions.clear(); - } + public static final class TypeReference extends GLSLAbstractReference { - // TODO: Check built-in structures - return GLSLTypes.getUndefinedType(typeName); + public TypeReference(@NotNull GLSLTypename element) { + super(element); } @Override public @Nullable GLSLStructDefinition resolve() { - final GLSLType type = resolveType(); + final GLSLType type = element.getType(); if (type instanceof GLSLStructType) { return ((GLSLStructType) type).getDefinition(); } @@ -151,6 +138,33 @@ public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) } } + public static final class StructDefinitionWalk implements PsiScopeProcessor { + + public static StructDefinitionWalk walk(PsiElement from, @Nullable String onlyNamed) { + final StructDefinitionWalk walk = new StructDefinitionWalk(onlyNamed); + PsiTreeUtil.treeWalkUp(walk, from, null, ResolveState.initial()); + return walk; + } + + private final String onlyNamed; + public final ArrayList definitions = new ArrayList<>(); + + private StructDefinitionWalk(String onlyNamed) { + this.onlyNamed = onlyNamed; + } + + @Override + public boolean execute(@NotNull PsiElement element, @NotNull ResolveState state) { + if (element instanceof final GLSLStructDefinition def) { + if (onlyNamed == null || onlyNamed.equals(def.getStructName())) { + definitions.add(def); + return onlyNamed == null;// Done if we found the one named + } + } + return true;// Continue + } + } + @NotNull @Override public TypeReference getReference() { diff --git a/src/main/java/glslplugin/lang/elements/expressions/GLSLFieldSelectionExpression.java b/src/main/java/glslplugin/lang/elements/expressions/GLSLFieldSelectionExpression.java index c1853c5..893934d 100755 --- a/src/main/java/glslplugin/lang/elements/expressions/GLSLFieldSelectionExpression.java +++ b/src/main/java/glslplugin/lang/elements/expressions/GLSLFieldSelectionExpression.java @@ -141,15 +141,6 @@ public FieldReference(@NotNull GLSLFieldSelectionExpression element, TextRange t return fieldStruct.getDeclarator(fieldName); } - @Override - public Object @NotNull [] getVariants() { - final ArrayList declarators = new ArrayList<>(); - for (GLSLStructMemberDeclaration declaration : fieldStruct.getDeclarations()) { - Collections.addAll(declarators, declaration.getDeclarators()); - } - return declarators.toArray(GLSLDeclarator.NO_DECLARATORS); - } - @Override public @NotNull String getCanonicalText() { final GLSLDeclarator resolve = resolve(); diff --git a/src/main/java/glslplugin/lang/elements/expressions/GLSLFunctionOrConstructorCallExpression.java b/src/main/java/glslplugin/lang/elements/expressions/GLSLFunctionOrConstructorCallExpression.java index b96fedf..b52f9b2 100755 --- a/src/main/java/glslplugin/lang/elements/expressions/GLSLFunctionOrConstructorCallExpression.java +++ b/src/main/java/glslplugin/lang/elements/expressions/GLSLFunctionOrConstructorCallExpression.java @@ -291,35 +291,6 @@ public ResolveResult(@NotNull PsiElement element, return ResolveResult.EMPTY_ARRAY; } } - - - - @Override - public Object @NotNull [] getVariants() { - final WalkResult walk = WalkResult.walkPossibleReferences(element, null); - - final ArrayList variants = new ArrayList<>(); - - for (GLSLScalarType scalar : GLSLScalarType.SCALARS) { - variants.add(scalar.getTypename()); - } - for (GLSLVectorType[] sharedBaseType : GLSLVectorType.VECTOR_TYPES.values()) { - for (GLSLVectorType type : sharedBaseType) { - variants.add(type.getTypename()); - } - } - for (GLSLMatrixType[][] sharedBaseType : GLSLMatrixType.MATRIX_TYPES.values()) { - for (GLSLMatrixType[] column : sharedBaseType) { - for (GLSLMatrixType type : column) { - variants.add(type.getTypename()); - } - } - } - variants.addAll(walk.functionDeclarations.values()); - variants.addAll(walk.structDefinitions); - - return variants.toArray(); - } } public static final class WalkResult implements PsiScopeProcessor { diff --git a/src/main/java/glslplugin/lang/elements/expressions/GLSLParameterList.java b/src/main/java/glslplugin/lang/elements/expressions/GLSLParameterList.java index 97c06bf..5fcb584 100755 --- a/src/main/java/glslplugin/lang/elements/expressions/GLSLParameterList.java +++ b/src/main/java/glslplugin/lang/elements/expressions/GLSLParameterList.java @@ -52,14 +52,6 @@ public GLSLExpression[] getParameters() { for (PsiElement child : children) { if (child instanceof GLSLExpression) { result.add((GLSLExpression) child); - } else { - final ASTNode node = child.getNode(); - if (node != null) { - final IElementType type = node.getElementType(); - if (!GLSLTokenTypes.COMMENTS.contains(type)) { - Logger.getLogger("GLSLParameterList").warning("Parameter list contains non-comment, non-expression element."); - } - } } } return result.toArray(GLSLExpression.EMPTY_ARRAY); diff --git a/src/main/java/glslplugin/lang/elements/expressions/GLSLVariableExpression.java b/src/main/java/glslplugin/lang/elements/expressions/GLSLVariableExpression.java index 6daf6b9..b3429dd 100755 --- a/src/main/java/glslplugin/lang/elements/expressions/GLSLVariableExpression.java +++ b/src/main/java/glslplugin/lang/elements/expressions/GLSLVariableExpression.java @@ -108,12 +108,6 @@ public VariableReference(@NotNull GLSLVariableExpression element) { } return null; } - - @Override - public synchronized Object @NotNull [] getVariants() { - final VariableWalkResult result = VariableWalkResult.walkPossibleReferences(element, null); - return result.visitedDeclarations.toArray(PsiElement.EMPTY_ARRAY); - } } public static final class VariableWalkResult implements PsiScopeProcessor { diff --git a/src/main/java/glslplugin/lang/elements/preprocessor/GLSLRedefinedToken.java b/src/main/java/glslplugin/lang/elements/preprocessor/GLSLRedefinedToken.java index 180e80f..3c9185a 100644 --- a/src/main/java/glslplugin/lang/elements/preprocessor/GLSLRedefinedToken.java +++ b/src/main/java/glslplugin/lang/elements/preprocessor/GLSLRedefinedToken.java @@ -88,25 +88,15 @@ public RedefinedTokenReference(@NotNull GLSLRedefinedToken element) { return null; } - - @Override - public Object @NotNull [] getVariants() { - final ArrayList results = new ArrayList<>(); - - GLSLDefineDirective prev = TreeIterator.previous(element, GLSLDefineDirective.class); - while (prev != null) { - results.add(prev); - prev = TreeIterator.previous(prev, GLSLDefineDirective.class); - } - return results.toArray(); - } } + private final RedefinedTokenReference reference = new RedefinedTokenReference(this); + /** * @return reference to the #define which caused the redefinition of this token */ @Override public RedefinedTokenReference getReference() { - return new RedefinedTokenReference(this); + return reference; } } diff --git a/src/main/java/glslplugin/lang/elements/reference/GLSLAbstractReference.java b/src/main/java/glslplugin/lang/elements/reference/GLSLAbstractReference.java index 79b3e45..0097584 100755 --- a/src/main/java/glslplugin/lang/elements/reference/GLSLAbstractReference.java +++ b/src/main/java/glslplugin/lang/elements/reference/GLSLAbstractReference.java @@ -10,6 +10,7 @@ import com.intellij.psi.PsiReference; import com.intellij.psi.PsiReferenceBase; import com.intellij.psi.ResolveResult; +import com.intellij.util.ArrayUtilRt; import com.intellij.util.IncorrectOperationException; import glslplugin.lang.elements.GLSLElement; import glslplugin.lang.elements.preprocessor.GLSLRedefinedToken; @@ -116,6 +117,15 @@ public boolean isReferenceTo(@NotNull PsiElement element) { return getElement().getManager().areElementsEquivalent(resolve(), element); } + /** This is used for autocomplete, but it is meaningless for most of the types, + * because the typed text must first resolve to the {@link #element} type before + * it is even called. Therefore, we don't ever implement this and instead provide + * all completion variants in {@link glslplugin.extensions.GLSLCompletionContributor} */ + @Override + public final Object @NotNull [] getVariants() { + return ArrayUtilRt.EMPTY_OBJECT_ARRAY; + } + @Override public boolean isSoft() { return false; diff --git a/src/main/java/glslplugin/lang/elements/reference/GLSLBuiltInPsiUtilService.java b/src/main/java/glslplugin/lang/elements/reference/GLSLBuiltInPsiUtilService.java index 0279fac..139f8a7 100755 --- a/src/main/java/glslplugin/lang/elements/reference/GLSLBuiltInPsiUtilService.java +++ b/src/main/java/glslplugin/lang/elements/reference/GLSLBuiltInPsiUtilService.java @@ -1,5 +1,8 @@ package glslplugin.lang.elements.reference; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.codeInsight.lookup.LookupItem; import com.intellij.openapi.components.Service; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.LocalFileSystem; @@ -18,6 +21,7 @@ import glslplugin.lang.elements.types.GLSLOpaqueType; import glslplugin.lang.elements.types.GLSLScalarType; import glslplugin.lang.elements.types.GLSLType; +import glslplugin.lang.elements.types.GLSLTypes; import glslplugin.lang.elements.types.GLSLVectorType; import glslplugin.lang.parser.GLSLFile; import glslplugin.util.VectorComponents; @@ -26,7 +30,9 @@ import java.io.IOException; import java.net.URL; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; @Service public final class GLSLBuiltInPsiUtilService { @@ -156,4 +162,21 @@ private GLSLFile createImmutableDummyFile(String content) { } return null; } + + + public final ArrayList builtinTypeLookup; + + { + final List types = GLSLTypes.builtinTypes(); + builtinTypeLookup = new ArrayList<>(types.size()); + for (GLSLType type : types) { + LookupElementBuilder lookup = LookupElementBuilder.create(type, type.getTypename()); + + if (type instanceof GLSLMatrixType mat && !mat.shortName.equals(mat.fullName)) { + lookup = lookup.withLookupString(mat.fullName); + } + + builtinTypeLookup.add(lookup); + } + } } diff --git a/src/main/java/glslplugin/lang/elements/types/GLSLTypes.java b/src/main/java/glslplugin/lang/elements/types/GLSLTypes.java index 7d470aa..ff0c6d7 100755 --- a/src/main/java/glslplugin/lang/elements/types/GLSLTypes.java +++ b/src/main/java/glslplugin/lang/elements/types/GLSLTypes.java @@ -19,9 +19,14 @@ package glslplugin.lang.elements.types; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.codeInsight.lookup.LookupElementBuilder; +import com.intellij.codeInsight.lookup.LookupItem; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -35,19 +40,23 @@ @SuppressWarnings("unused") public class GLSLTypes { - private static final Map types = new HashMap<>(); + private static final ArrayList builtinTypes = new ArrayList<>(); + private static final Map namesToTypes = new HashMap<>(); + private static final ArrayList typeLookupElements = new ArrayList<>(); static { // Register all built-in types // Scalars for (GLSLScalarType scalar : GLSLScalarType.SCALARS) { + builtinTypes.add(scalar); register(scalar); } // Vectors for (GLSLVectorType[] byBaseType : GLSLVectorType.VECTOR_TYPES.values()) { for (GLSLVectorType vectorType : byBaseType) { + builtinTypes.add(vectorType); register(vectorType); } } @@ -56,10 +65,9 @@ public class GLSLTypes { for (GLSLMatrixType[][] byBaseType : GLSLMatrixType.MATRIX_TYPES.values()) { for (GLSLMatrixType[] byFirstDim : byBaseType) { for (GLSLMatrixType matrixType : byFirstDim) { + builtinTypes.add(matrixType); register(matrixType.fullName, matrixType); - // The instance is guaranteed to be actually identical if it is the same - //noinspection StringEquality - if (matrixType.shortName != matrixType.fullName) { + if (!matrixType.shortName.equals(matrixType.fullName)) { register(matrixType.shortName, matrixType); } } @@ -68,15 +76,19 @@ public class GLSLTypes { // Opaque types for (GLSLOpaqueType opaqueType : GLSLOpaqueType.ALL) { + builtinTypes.add(opaqueType); register(opaqueType); } for (GLSLOpaqueType.Sampler opaqueType : GLSLOpaqueType.Sampler.ALL) { + builtinTypes.add(opaqueType); register(opaqueType); } for (GLSLOpaqueType.ShadowSampler opaqueType : GLSLOpaqueType.ShadowSampler.ALL) { + builtinTypes.add(opaqueType); register(opaqueType); } for (GLSLOpaqueType.Image opaqueType : GLSLOpaqueType.Image.ALL) { + builtinTypes.add(opaqueType); register(opaqueType); } } @@ -126,15 +138,18 @@ public String getTypename() { } private static void register(GLSLType type) { - register(type.getTypename(), type); + namesToTypes.put(type.getTypename(), type); } private static void register(String name, GLSLType type) { - types.put(name, type); + namesToTypes.put(name, type); } public static GLSLType getTypeFromName(String name) { - return types.get(name); + return namesToTypes.get(name); } + public static List builtinTypes() { + return builtinTypes; + } } diff --git a/src/main/java/glslplugin/lang/parser/GLSLFile.java b/src/main/java/glslplugin/lang/parser/GLSLFile.java index 0204fa0..e113ae3 100755 --- a/src/main/java/glslplugin/lang/parser/GLSLFile.java +++ b/src/main/java/glslplugin/lang/parser/GLSLFile.java @@ -68,7 +68,7 @@ public FileType getFileType() { public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, @Nullable PsiElement lastParent, @NotNull PsiElement place) { for (PsiElement child = getFirstChild(); child != null; child = child.getNextSibling()) { - if (child == lastParent || PsiTreeUtil.isAncestor(child, place, false)) { + if (child == lastParent || (lastParent != null && PsiTreeUtil.isAncestor(child, place, false))) { // Intentionally show even later declarations. // Inspection checks that everything is ok. continue; diff --git a/src/main/java/glslplugin/util/TreeIterator.java b/src/main/java/glslplugin/util/TreeIterator.java index 2210382..26cf305 100755 --- a/src/main/java/glslplugin/util/TreeIterator.java +++ b/src/main/java/glslplugin/util/TreeIterator.java @@ -2,6 +2,7 @@ import com.intellij.lang.ASTNode; import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -58,7 +59,7 @@ public class TreeIterator { // Then we have to go up and to the right origin = origin.getParent(); - if (origin == null) { + if (origin == null || origin instanceof PsiFile) { // Can't go on return null; } @@ -83,7 +84,7 @@ public class TreeIterator { // Then we have to go up and to the right origin = origin.getParent(); - if (origin == null) { + if (origin == null || origin instanceof PsiFile) { // Can't go on return null; }