Skip to content

Commit

Permalink
Rebased and fixed conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
adamwojs committed May 22, 2022
1 parent daca2db commit e5b4001
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 98 deletions.
27 changes: 27 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ plugins {
id("org.jetbrains.changelog") version "1.3.1"
// Gradle Qodana Plugin
id("org.jetbrains.qodana") version "0.1.13"
// Gradle Grammar-Kit Plugin
id("org.jetbrains.grammarkit") version "2021.2.2"
}

group = properties("pluginGroup")
Expand Down Expand Up @@ -112,4 +114,29 @@ tasks {
includeEngines("junit-vintage")
}
}

generateLexer {
source.set("src/main/java/fr/adrienbrault/idea/symfony2plugin/expressionLanguage/ExpressionLanguage.flex")
targetDir.set("src/main/gen/fr/adrienbrault/idea/symfony2plugin/expressionLanguage/")
targetClass.set("ExpressionLanguageLexer")
purgeOldFiles.set(true)
}

generateParser {
source.set("src/main/java/fr/adrienbrault/idea/symfony2plugin/expressionLanguage/ExpressionLanguage.bnf")
targetRoot.set("src/main/gen")
pathToParser.set("fr/adrienbrault/idea/symfony2plugin/expressionLanguage/ExpressionLanguageParser.java")
pathToPsiRoot.set("fr/adrienbrault/idea/symfony2plugin/expressionLanguage/psi")
purgeOldFiles.set(true)
}

compileJava {
dependsOn("generateLexer")
dependsOn("generateParser")
}
}

java.sourceSets["main"].java {
// Include the generated files in the source set
srcDir("src/main/gen")
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
import com.intellij.patterns.PatternCondition;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.patterns.PsiElementPattern;
import com.intellij.patterns.StandardPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.util.ProcessingContext;
import com.jetbrains.php.lang.documentation.phpdoc.lexer.PhpDocTokenTypes;
import com.jetbrains.php.lang.documentation.phpdoc.parser.PhpDocElementTypes;
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag;
import com.jetbrains.php.lang.lexer.PhpTokenTypes;
import com.jetbrains.php.lang.psi.elements.ParameterList;
Expand All @@ -18,16 +17,17 @@
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionProviderLookupArguments;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrar;
import fr.adrienbrault.idea.symfony2plugin.codeInsight.GotoCompletionRegistrarParameter;
import fr.adrienbrault.idea.symfony2plugin.expressionLanguage.psi.ExpressionLanguageCallExpr;
import fr.adrienbrault.idea.symfony2plugin.expressionLanguage.psi.ExpressionLanguageLiteralExpr;
import fr.adrienbrault.idea.symfony2plugin.expressionLanguage.psi.ExpressionLanguageRefExpr;
import fr.adrienbrault.idea.symfony2plugin.expressionLanguage.psi.ExpressionLanguageStringLiteral;
import fr.adrienbrault.idea.symfony2plugin.security.utils.VoterUtil;
import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.NotNull;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* @author Daniel Espendiller <[email protected]>
Expand All @@ -40,39 +40,24 @@ public class AnnotationExpressionGotoCompletionRegistrar implements GotoCompleti
public void register(@NotNull GotoCompletionRegistrarParameter registrar) {
// "@Security("is_granted('POST_SHOW', post) and has_role('ROLE_ADMIN')")"
registrar.register(
PlatformPatterns.or(getDocTagStringPattern(), getAttributeStringPattern()),
PlatformPatterns.psiElement()
.withParent(PlatformPatterns
.psiElement(ExpressionLanguageStringLiteral.class)
.withParent(PlatformPatterns
.psiElement(ExpressionLanguageLiteralExpr.class)
.withParent(PlatformPatterns
.psiElement(ExpressionLanguageCallExpr.class)
.withFirstChild(PlatformPatterns
.psiElement(ExpressionLanguageRefExpr.class)
.withText(StandardPatterns.string().oneOf("has_role", "is_granted"))
)
)
)
),
MyGotoCompletionProvider::new
);
}

@NotNull
private PsiElementPattern.Capture<PsiElement> getAttributeStringPattern() {
// #[Security("is_granted('POST_SHOW')")]
return PlatformPatterns.psiElement().withElementType(PlatformPatterns.elementType().or(
PhpTokenTypes.STRING_LITERAL_SINGLE_QUOTE,
PhpTokenTypes.STRING_LITERAL
))
.withParent(PlatformPatterns.psiElement(StringLiteralExpression.class)
.withParent(PlatformPatterns.psiElement(ParameterList.class)
.withParent(PlatformPatterns.psiElement(PhpAttribute.class)
.with(PhpDocInstancePatternCondition.INSTANCE)
)
)
);
}

@NotNull
private PsiElementPattern.Capture<PsiElement> getDocTagStringPattern() {
return PlatformPatterns.psiElement(PhpDocTokenTypes.DOC_STRING)
.withParent(PlatformPatterns.psiElement(StringLiteralExpression.class)
.withParent(PlatformPatterns.psiElement(PhpDocElementTypes.phpDocAttributeList)
.withParent(PlatformPatterns.psiElement(PhpDocTag.class)
.with(PhpDocInstancePatternCondition.INSTANCE)
)
)
);
}

/**
* "@Security("has_role('ROLE_FOOBAR')")"
* "@Security("is_granted('POST_SHOW', post) and has_role('ROLE_ADMIN')")"
Expand All @@ -87,16 +72,7 @@ public void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arg
final CompletionResultSet resultSet = arguments.getResultSet();
String blockNamePrefix = resultSet.getPrefixMatcher().getPrefix();

// find caret position:
// - "has_role('"
// - "has_role('YAML_ROLE_"
if(!blockNamePrefix.matches("^.*(has_role|is_granted)\\s*\\(\\s*['|\"][\\w-]*$")) {
return;
}

// clear prefix caret string; for a clean completion independent from inside content
String substring = blockNamePrefix.replaceAll("^(.*(has_role|is_granted)\\s*\\(\\s*['|\"])", "");
CompletionResultSet myResultSet = resultSet.withPrefixMatcher(substring);
CompletionResultSet myResultSet = resultSet.withPrefixMatcher(blockNamePrefix);

VoterUtil.LookupElementPairConsumer consumer = new VoterUtil.LookupElementPairConsumer();
VoterUtil.visitAttribute(getProject(), consumer);
Expand All @@ -106,70 +82,22 @@ public void getLookupElements(@NotNull GotoCompletionProviderLookupArguments arg
@NotNull
@Override
public Collection<PsiElement> getPsiTargets(PsiElement element) {
String contents = null;
if(getElement().getNode().getElementType() == PhpDocTokenTypes.DOC_STRING) {
// @Security
PsiElement parent = getElement().getParent();
if(!(parent instanceof StringLiteralExpression)) {
return Collections.emptyList();
}

contents = ((StringLiteralExpression) parent).getContents();
} else {
// @Security
PsiElement parent = getElement().getParent();
if (parent instanceof StringLiteralExpression) {
contents = ((StringLiteralExpression) parent).getContents();
}
}

if (StringUtils.isBlank(contents)) {
var text = getElement().getText();
if (text.length() < 2) {
return Collections.emptyList();
}

Collection<String> roles = new HashSet<>();
for (String regex : new String[]{"is_granted\\s*\\(\\s*['|\"]([^'\"]+)['|\"]\\s*[\\)|,]", "has_role\\s*\\(\\s*['|\"]([^'\"]+)['|\"]\\s*\\)"}) {
Matcher matcher = Pattern.compile(regex).matcher(contents);
while(matcher.find()){
roles.add(matcher.group(1));
}
}

if(roles.size() == 0) {
return Collections.emptyList();
}

Collection<PsiElement> targets = new HashSet<>();
// Strip quotes
var role = text.substring(1, text.length() - 1);

var targets = new HashSet<PsiElement>();
VoterUtil.visitAttribute(getProject(), pair -> {
if(roles.contains(pair.getFirst())) {
if(pair.getFirst().equals(role)) {
targets.add(pair.getSecond());
}
});

return targets;
}
}

/**
* Check if given PhpDocTag is instance of given Annotation class
*/
private static class PhpDocInstancePatternCondition extends PatternCondition<PsiElement> {
private static final PhpDocInstancePatternCondition INSTANCE = new PhpDocInstancePatternCondition();

PhpDocInstancePatternCondition() {
super("PhpDoc/Attribute Instance");
}

@Override
public boolean accepts(@NotNull PsiElement psiElement, ProcessingContext processingContext) {
if (psiElement instanceof PhpDocTag) {
return PhpElementsUtil.isEqualClassName(AnnotationUtil.getAnnotationReference((PhpDocTag) psiElement), SECURITY_ANNOTATION);
} else if (psiElement instanceof PhpAttribute) {
return SECURITY_ANNOTATION.equals(((PhpAttribute) psiElement).getFQN());
}

return false;
}
}
}

0 comments on commit e5b4001

Please sign in to comment.