Skip to content

Commit

Permalink
Improve GLSLLineMarkerProvider to provide correct function declaratio…
Browse files Browse the repository at this point in the history
…n-definition navigation

Took 48 minutes
  • Loading branch information
Jan Polák committed Dec 15, 2021
1 parent ac8b0a5 commit 2c5dac0
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 12 deletions.
69 changes: 58 additions & 11 deletions src/main/java/glslplugin/extensions/GLSLLineMarkerProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,22 @@

package glslplugin.extensions;

import com.intellij.codeInsight.daemon.DefaultGutterIconNavigationHandler;
import com.intellij.codeInsight.daemon.LineMarkerInfo;
import com.intellij.codeInsight.daemon.LineMarkerProvider;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.editor.markup.GutterIconRenderer;
import com.intellij.psi.PsiElement;
import glslplugin.lang.elements.declarations.GLSLFunctionDeclarationImpl;
import glslplugin.lang.elements.declarations.GLSLFunctionDefinitionImpl;
import com.intellij.psi.util.PsiTreeUtil;
import glslplugin.lang.elements.GLSLIdentifier;
import glslplugin.lang.elements.declarations.GLSLFunctionDeclaration;
import glslplugin.lang.elements.declarations.GLSLFunctionDefinition;
import org.jetbrains.annotations.NotNull;

import javax.swing.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Supplier;

/**
* This annotation will show gutter icons for prototypes that are implemented and implementations of prototypes.
Expand All @@ -37,16 +43,57 @@ public class GLSLLineMarkerProvider implements LineMarkerProvider {
private static final Icon implemented = AllIcons.Gutter.ImplementedMethod;
private static final Icon implementing = AllIcons.Gutter.ImplementingMethod;

private static Collection<GLSLFunctionDeclaration> findOtherFunctionDeclarations(GLSLFunctionDeclaration basedOn, boolean declarations, boolean definitions) {
final Collection<GLSLFunctionDeclaration> possibilities = PsiTreeUtil.findChildrenOfType(basedOn.getContainingFile(), GLSLFunctionDeclaration.class);
final String targetSignature = basedOn.getSignature();
final ArrayList<GLSLFunctionDeclaration> result = new ArrayList<>(possibilities.size() - 1);
for (GLSLFunctionDeclaration possibility : possibilities) {
if (possibility == basedOn) {
continue;
}
final boolean isDefinition = possibility instanceof GLSLFunctionDefinition;
if (isDefinition && !definitions || !isDefinition && !declarations) {
continue;
}
if (targetSignature.equals(possibility.getSignature())) {
result.add(possibility);
}
}
return result;
}

private static final Supplier<String> implementedAccessibilityName = () -> "implemented";
private static final Supplier<String> implementingAccessibilityName = () -> "implementing";

public LineMarkerInfo<?> getLineMarkerInfo(@NotNull PsiElement expr) {
//todo: add navigation support for guttericons and tooltips
if (expr instanceof GLSLFunctionDefinitionImpl) {
//todo: check if a prototype exists
return new LineMarkerInfo<>(expr, expr.getTextRange(), implementing, null, null, GutterIconRenderer.Alignment.RIGHT, () -> "implementing");
} else if (expr instanceof GLSLFunctionDeclarationImpl) {
//todo: check if it is implemented
return new LineMarkerInfo<>((GLSLFunctionDeclarationImpl) expr, expr.getTextRange(),
implemented,null, null, GutterIconRenderer.Alignment.RIGHT, () -> "implemented");
if (!(expr instanceof GLSLIdentifier)) {
return null;
}

final GLSLFunctionDeclaration functionDeclaration = ((GLSLIdentifier) expr).findParentByClass(GLSLFunctionDeclaration.class);
if (functionDeclaration == null || functionDeclaration.getNameIdentifier() != expr) {
return null;
}
return null;

final boolean isDefinition = functionDeclaration instanceof GLSLFunctionDefinition;
final Collection<GLSLFunctionDeclaration> navigationTargets = findOtherFunctionDeclarations(functionDeclaration, isDefinition, !isDefinition);
if (navigationTargets.isEmpty()) {
return null;
}

// GLSLIdentifier wraps PsiElement(IDENTIFIER), which is what we should pass in
PsiElement targetElement = ((GLSLIdentifier) expr).getNameIdentifier();
if (targetElement == null) {
targetElement = expr;
}

final String signature = functionDeclaration.getSignature();
return new LineMarkerInfo<>(targetElement,
targetElement.getTextRange(),
isDefinition ? implementing : implemented,
(element) -> signature + (isDefinition ? " has forward declaration" : " has definition"),
new DefaultGutterIconNavigationHandler<>(navigationTargets, (isDefinition ? "Declarations of " : "Definitions of ") + signature),
GutterIconRenderer.Alignment.RIGHT,
isDefinition ? implementingAccessibilityName : implementedAccessibilityName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

package glslplugin.lang.elements.declarations;

import com.intellij.psi.PsiNameIdentifierOwner;
import glslplugin.lang.elements.types.GLSLFunctionType;
import glslplugin.lang.elements.types.GLSLType;
import org.jetbrains.annotations.NotNull;
Expand All @@ -28,7 +29,7 @@
* It inherits the name, qualifier and (return) type from {@link glslplugin.lang.elements.declarations.GLSLDeclaration}
* and adds the parameter list.
*/
public interface GLSLFunctionDeclaration extends GLSLSingleDeclaration {
public interface GLSLFunctionDeclaration extends GLSLSingleDeclaration, PsiNameIdentifierOwner {

@NotNull
GLSLParameterDeclaration[] getParameters();
Expand Down

0 comments on commit 2c5dac0

Please sign in to comment.