Skip to content

Commit

Permalink
Implement basic support for include, closes #109
Browse files Browse the repository at this point in the history
  • Loading branch information
Jan Polák committed Oct 24, 2022
1 parent d591227 commit f738c35
Show file tree
Hide file tree
Showing 8 changed files with 1,113 additions and 963 deletions.
10 changes: 10 additions & 0 deletions src/main/java/glslplugin/lang/elements/GLSLPsiElementFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ public GLSLElement create(ASTNode node) {
if (type instanceof GLSLElementTypes.RedefinedTokenElementType /*== GLSLElementTypes.REDEFINED_TOKEN*/) return new GLSLRedefinedToken(node);
if (type == GLSLTokenTypes.PREPROCESSOR_DEFINE) return new GLSLDefineDirective(node);
if (type == GLSLTokenTypes.PREPROCESSOR_VERSION) return new GLSLVersionDirective(node);
if (type == GLSLTokenTypes.PREPROCESSOR_INCLUDE) return new GLSLPreprocessorInclude(node);
if (GLSLTokenTypes.PREPROCESSOR_DIRECTIVES.contains(type)) return new GLSLPreprocessorDirective(node);

// primary expressions
Expand Down Expand Up @@ -155,4 +156,13 @@ public static ASTNode createIdentifier(Project project, String name) throws Inco
}
throw new IncorrectOperationException("'"+name+"' is not a valid identifier");
}

public static ASTNode createPreprocessorString(Project project, String name) throws IncorrectOperationException {
PsiElement element = PsiFileFactory.getInstance(project).createFileFromText("dummy.glsl", GLSLFileType.INSTANCE, "# "+name);
final ASTNode node = element.getNode().getLastChildNode();
if (node != null && node.getElementType() == GLSLTokenTypes.PREPROCESSOR_STRING) {
return node;
}
throw new IncorrectOperationException("'"+name+"' is not a valid preprocessor string");
}
}
2 changes: 2 additions & 0 deletions src/main/java/glslplugin/lang/elements/GLSLTokenTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ public class GLSLTokenTypes {
public static final IElementType PREPROCESSOR_LINE = new GLSLElementType("PREPROCESSOR_LINE");
public static final IElementType PREPROCESSOR_DEFINED = new GLSLElementType("PREPROCESSOR_DEFINED");
public static final IElementType PREPROCESSOR_CONCAT = new GLSLElementType("PREPROCESSOR_CONCAT");
public static final IElementType PREPROCESSOR_INCLUDE = new GLSLElementType("PREPROCESSOR_INCLUDE");
public static final IElementType PREPROCESSOR_STRING = new GLSLElementType("PREPROCESSOR_STRING");
public static final IElementType PREPROCESSOR_RAW = new GLSLElementType("PREPROCESSOR_RAW");

Expand All @@ -249,6 +250,7 @@ public class GLSLTokenTypes {
PREPROCESSOR_LINE,
PREPROCESSOR_DEFINED,
PREPROCESSOR_CONCAT,
PREPROCESSOR_INCLUDE,
PREPROCESSOR_OTHER);

// Type specifiers
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package glslplugin.lang.elements.preprocessor;

import com.intellij.lang.ASTNode;
import com.intellij.psi.PsiElement;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import glslplugin.lang.elements.GLSLElementImpl;
import glslplugin.lang.elements.GLSLTokenTypes;
import org.jetbrains.annotations.NotNull;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package glslplugin.lang.elements.preprocessor;

import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.psi.PsiReferenceBase;
import com.intellij.psi.ResolveState;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.util.IncorrectOperationException;
import glslplugin.lang.elements.GLSLTokenTypes;
import glslplugin.lang.elements.reference.GLSLReferencableDeclaration;
import glslplugin.lang.elements.reference.GLSLReferenceUtil;
import glslplugin.lang.parser.GLSLFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* When the directive is in the top-level and contains a string literal (surrounded by whatever),
* and that string can be evaluated to a valid GLSL file, declarations from that file will be included.
*
* Preprocessor redefinitions can't be included in this way.
* Syntactical correctness of the file inclusion is not checked.
*/
public class GLSLPreprocessorInclude extends GLSLPreprocessorDirective {
public GLSLPreprocessorInclude(@NotNull ASTNode astNode) {
super(astNode);
}

public @Nullable GLSLFile includedFile() {
final PsiElement stringElement = findChildByType(GLSLTokenTypes.PREPROCESSOR_STRING);
final String pathStringRaw = stringElement == null ? null : stringElement.getText();
final String pathString = pathStringRaw == null
|| !pathStringRaw.startsWith("\"")
|| !pathStringRaw.endsWith("\"")
|| pathStringRaw.length() <= 2
? null : pathStringRaw.substring(1, pathStringRaw.length() - 1);
if (pathString == null) return null;

final String[] parts = pathString.split("[\\\\/]");

PsiDirectory dir = this.getContainingFile().getContainingDirectory();
for (int i = 0; i < parts.length - 1; i++) {
String s = parts[i];
if (s.isEmpty() || ".".equals(s)) {
continue;// Nothing to do
}
if ("..".equals(s)) {
dir = dir.getParentDirectory();
} else {
dir = dir.findSubdirectory(s);
}
if (dir == null) {
return null;
}
}

final PsiFile includedFile = dir.findFile(parts[parts.length - 1]);
if (!(includedFile instanceof GLSLFile glslFile)) {
return null;
}

return glslFile;
}

@Override
public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state, PsiElement lastParent, @NotNull PsiElement place) {
final GLSLFile glslFile = includedFile();
if (glslFile == null) return true;

return glslFile.processDeclarations(processor, state, lastParent, place);
}

@Override
public PsiReference getReference() {
final PsiElement fileString = findChildByType(GLSLTokenTypes.PREPROCESSOR_STRING);
if (fileString == null) return null;
final TextRange textRange = GLSLReferenceUtil.rangeOfIn(fileString, this);
return new PsiReferenceBase<>(this, textRange, true) {

@Override
public @Nullable PsiElement resolve() {
return includedFile();
}

@Override
public PsiElement handleElementRename(@NotNull String newElementName) throws IncorrectOperationException {
newElementName = "\"" + newElementName.replace("\"", "").replace("\n", "").replace("\r", "") + "\"";

final GLSLPreprocessorInclude element = getElement();
final PsiElement string = element.findChildByType(GLSLTokenTypes.PREPROCESSOR_STRING);
if (string == null) {
throw new IncorrectOperationException(element+" can't be renamed");
}

GLSLReferencableDeclaration.replacePreprocessorString(string, newElementName);
setRangeInElement(GLSLReferenceUtil.rangeOfIn(element.findChildByType(GLSLTokenTypes.PREPROCESSOR_STRING), element));
return element;
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ static void replaceIdentifier(PsiElement oldIdentifier, @NotNull String name) th
oldIdentifierNode.getTreeParent().replaceChild(oldIdentifierNode, newNameNode);
}

static void replacePreprocessorString(PsiElement oldPreprocessorString, @NotNull String name) throws IncorrectOperationException {
if (oldPreprocessorString == null) throw new IncorrectOperationException("No name to rename");
ASTNode newNameNode = GLSLPsiElementFactory.createPreprocessorString(oldPreprocessorString.getProject(), name);
final ASTNode oldIdentifierNode = oldPreprocessorString.getNode();
oldIdentifierNode.getTreeParent().replaceChild(oldIdentifierNode, newNameNode);
}

static void reformat(PsiElement statement) {
if (statement.getContainingFile() instanceof GLSLFile) {
CodeStyleManager.getInstance(statement.getManager()).reformat(statement);
Expand Down
14 changes: 11 additions & 3 deletions src/main/java/glslplugin/lang/parser/GLSLParsing.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ protected void parsePreprocessor() {

IElementType directiveType = b.getTokenType();

if(directiveType == PREPROCESSOR_DEFINE){
if (directiveType == PREPROCESSOR_DEFINE) {
//Parse define
b.advanceLexer(false, false);//Get past DEFINE

Expand Down Expand Up @@ -119,7 +119,7 @@ protected void parsePreprocessor() {
b.advanceLexer();
}
}
}else if(directiveType == PREPROCESSOR_UNDEF){
} else if (directiveType == PREPROCESSOR_UNDEF) {
//Parse undefine
b.advanceLexer(false, false);//Get past UNDEF

Expand All @@ -143,7 +143,15 @@ protected void parsePreprocessor() {
}
b.advanceLexer();
}
}else{
} else if (directiveType == PREPROCESSOR_INCLUDE) {
// Since there is no standard, just eat whatever and hope that there is a string with path somewhere in there
while (!b.eof()) {
if (b.getTokenType() == PREPROCESSOR_END) {
break;
}
b.advanceLexer();
}
} else {
//Some other directive, no work here
while (!b.eof()) {
if (b.getTokenType() == PREPROCESSOR_END) {
Expand Down
1 change: 1 addition & 0 deletions src/main/java/glslplugin/lang/scanner/GLSL.flex
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ BLOCK_COMMENT = "/*"([^"*"]|("*"+[^"*""/"]))*("*"+"/")?
line { return PREPROCESSOR_LINE; }
defined { return PREPROCESSOR_DEFINED; }
## { return PREPROCESSOR_CONCAT; }
include { return PREPROCESSOR_INCLUDE; }
}

<PREPROCESSOR_RAW_MODE> {
Expand Down
Loading

0 comments on commit f738c35

Please sign in to comment.