Skip to content

Commit

Permalink
support basic plugin aware
Browse files Browse the repository at this point in the history
  • Loading branch information
CsCherrYY committed Sep 14, 2021
1 parent 8dd2c86 commit 183be78
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,15 @@ public CompletableFuture<Either<List<CompletionItem>, CompletionList>> completio
containingCall = call;
}
}
boolean javaPluginsIncluded = this.libraryResolver.isJavaPluginsIncluded(this.completionVisitor.getPlugins(uri));
CompletionHandler handler = new CompletionHandler();
// check again
if (containingCall == null && isGradleRoot(uri, params.getPosition())) {
return CompletableFuture.completedFuture(Either
.forLeft(handler.getCompletionItems(null, Paths.get(uri).getFileName().toString(), this.libraryResolver)));
.forLeft(handler.getCompletionItems(null, Paths.get(uri).getFileName().toString(), this.libraryResolver, javaPluginsIncluded)));
}
return CompletableFuture.completedFuture(Either.forLeft(
handler.getCompletionItems(containingCall, Paths.get(uri).getFileName().toString(), this.libraryResolver)));
handler.getCompletionItems(containingCall, Paths.get(uri).getFileName().toString(), this.libraryResolver, javaPluginsIncluded)));
}

private boolean isGradleRoot(URI uri, Position position) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
import org.codehaus.groovy.ast.expr.ConstantExpression;
import org.codehaus.groovy.ast.expr.Expression;
import org.codehaus.groovy.ast.expr.GStringExpression;
import org.codehaus.groovy.ast.expr.MapEntryExpression;
import org.codehaus.groovy.ast.expr.MethodCallExpression;
import org.codehaus.groovy.ast.expr.NamedArgumentListExpression;
import org.codehaus.groovy.ast.expr.TupleExpression;
import org.codehaus.groovy.ast.stmt.BlockStatement;
import org.codehaus.groovy.ast.stmt.ExpressionStatement;
import org.codehaus.groovy.ast.stmt.Statement;
Expand Down Expand Up @@ -60,6 +63,7 @@ public Range getRange() {
private Map<URI, Set<MethodCallExpression>> methodCalls = new HashMap<>();
private Map<URI, List<Statement>> statements = new HashMap<>();
private Map<URI, List<Expression>> constants = new HashMap<>();
private Map<URI, Set<String>> plugins = new HashMap<>();

public List<DependencyItem> getDependencies(URI uri) {
return this.dependencies.get(uri);
Expand All @@ -77,6 +81,10 @@ public List<Expression> getConstants(URI uri) {
return this.constants.get(uri);
}

public Set<String> getPlugins(URI uri) {
return this.plugins.get(uri);
}

public void visitCompilationUnit(URI uri, GradleCompilationUnit compilationUnit) {
this.currentUri = uri;
compilationUnit.iterator().forEachRemaining(unit -> visitSourceUnit(unit));
Expand All @@ -89,6 +97,7 @@ public void visitSourceUnit(SourceUnit unit) {
this.methodCalls.put(this.currentUri, new HashSet<>());
this.statements.put(this.currentUri, new ArrayList<>());
this.constants.put(this.currentUri, new ArrayList<>());
this.plugins.put(this.currentUri, new HashSet<>());
visitModule(moduleNode);
}
}
Expand All @@ -106,6 +115,16 @@ public void visitMethodCallExpression(MethodCallExpression node) {
this.methodCalls.get(this.currentUri).add(node);
if (node.getMethodAsString().equals("dependencies")) {
this.dependencies.get(this.currentUri).addAll(getDependencies(node));
} else if (node.getMethodAsString().equals("plugins")) {
// match plugins { id: ${id} }
List<String> plugins = getPluginFromPlugins(node);
this.plugins.get(this.currentUri).addAll(plugins);
} else if (node.getMethodAsString().equals("apply")) {
// match apply plugins: '${id}'
String plugin = getPluginFromApply(node);
if (plugin != null) {
this.plugins.get(this.currentUri).add(plugin);
}
}
super.visitMethodCallExpression(node);
}
Expand Down Expand Up @@ -162,6 +181,62 @@ private List<DependencyItem> getDependencies(ExpressionStatement expressionState
return Collections.emptyList();
}

private String getPluginFromApply(MethodCallExpression node) {
Expression argument = node.getArguments();
if (argument instanceof TupleExpression) {
List<Expression> expressions = ((TupleExpression)argument).getExpressions();
for (Expression expression : expressions) {
if (expression instanceof NamedArgumentListExpression) {
List<MapEntryExpression> mapEntryExpressions = ((NamedArgumentListExpression)expression).getMapEntryExpressions();
for (MapEntryExpression mapEntryExp: mapEntryExpressions) {
Expression keyExpression = mapEntryExp.getKeyExpression();
if (keyExpression instanceof ConstantExpression && keyExpression.getText().equals("plugin")) {
return mapEntryExp.getValueExpression().getText();
}
}
}
}
}
return null;
}

private List<String> getPluginFromPlugins(MethodCallExpression node) {
Expression objectExpression = node.getObjectExpression();
if (objectExpression instanceof MethodCallExpression) {
return getPluginFromPlugins((MethodCallExpression)objectExpression);
}
List<String> results = new ArrayList<>();
Expression argument = node.getArguments();
if (argument instanceof ArgumentListExpression) {
List<Expression> expressions = ((ArgumentListExpression)argument).getExpressions();
for (Expression expression : expressions) {
if (expression instanceof ConstantExpression && node.getMethodAsString().equals("id")) {
results.add(expression.getText());
} else if (expression instanceof ClosureExpression) {
Statement code = ((ClosureExpression)expression).getCode();
if (code instanceof BlockStatement) {
results.addAll(getPluginFromPlugins((BlockStatement) code));
}
}
}
}
return results;
}

private List<String> getPluginFromPlugins(BlockStatement code) {
List<String> results = new ArrayList<>();
List<Statement> statements = code.getStatements();
for (Statement statement : statements) {
if (statement instanceof ExpressionStatement) {
Expression expression = ((ExpressionStatement)statement).getExpression();
if (expression instanceof MethodCallExpression) {
results.addAll(getPluginFromPlugins((MethodCallExpression) expression));
}
}
}
return results;
}

@Override
public void visitConstantExpression(ConstantExpression expression) {
this.constants.get(currentUri).add(expression);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ public class CompletionHandler {

private static String BUILD_GRADLE = "build.gradle";
private static String SETTING_GRADLE = "settings.gradle";
private static String DEPENDENCYHANDLER_CLASS = "org.gradle.api.artifacts.dsl.DependencyHandler";

public List<CompletionItem> getCompletionItems(MethodCallExpression containingCall, String fileName, GradleLibraryResolver resolver) {
public List<CompletionItem> getCompletionItems(MethodCallExpression containingCall, String fileName, GradleLibraryResolver resolver, boolean javaPluginsIncluded) {
String delegateClassName = null;
if (containingCall == null) {
if (fileName.equals(BUILD_GRADLE)) {
Expand All @@ -51,22 +52,22 @@ public List<CompletionItem> getCompletionItems(MethodCallExpression containingCa
if (delegateClass == null) {
return Collections.emptyList();
}
return getCompletionItemsFromClass(delegateClass, resolver, new HashSet<>());
return getCompletionItemsFromClass(delegateClass, resolver, javaPluginsIncluded, new HashSet<>());
}

private List<CompletionItem> getCompletionItemsFromClass(JavaClass javaClass, GradleLibraryResolver resolver, Set<String> resultSet) {
private List<CompletionItem> getCompletionItemsFromClass(JavaClass javaClass, GradleLibraryResolver resolver, boolean javaPluginsIncluded, Set<String> resultSet) {
if (javaClass == null) {
return Collections.emptyList();
}
List<CompletionItem> results = new ArrayList<>();
for (String superInterface : javaClass.getInterfaceNames()) {
if (resolver.getGradleLibraries().containsKey(superInterface)) {
results.addAll(getCompletionItemsFromClass(resolver.getGradleLibraries().get(superInterface), resolver, resultSet));
results.addAll(getCompletionItemsFromClass(resolver.getGradleLibraries().get(superInterface), resolver, javaPluginsIncluded, resultSet));
}
}
String superClass = javaClass.getSuperclassName();
if (resolver.getGradleLibraries().containsKey(superClass)) {
results.addAll(getCompletionItemsFromClass(resolver.getGradleLibraries().get(superClass), resolver, resultSet));
results.addAll(getCompletionItemsFromClass(resolver.getGradleLibraries().get(superClass), resolver, javaPluginsIncluded, resultSet));
}
List<String> methodNames = new ArrayList<>();
Method[] methods = javaClass.getMethods();
Expand Down Expand Up @@ -128,6 +129,22 @@ private List<CompletionItem> getCompletionItemsFromClass(JavaClass javaClass, Gr
}
}
}
if (javaPluginsIncluded && javaClass.getClassName().equals(DEPENDENCYHANDLER_CLASS)) {
// for dependency {}, we offer java configurations if there is any applied java plugin
for (String plugin : resolver.getJavaConfigurations()) {
StringBuilder builder = new StringBuilder();
builder.append(plugin);
builder.append("(Object... o)");
StringBuilder insertBuilder = new StringBuilder();
insertBuilder.append(plugin);
insertBuilder.append("($0)");
CompletionItem item = new CompletionItem(builder.toString());
item.setKind(CompletionItemKind.Function);
item.setInsertTextFormat(InsertTextFormat.Snippet);
item.setInsertText(insertBuilder.toString());
results.add(item);
}
}
return results;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,39 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.bcel.classfile.ClassFormatException;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.JavaClass;

public class GradleLibraryResolver {

private static String JAVA_PLUGIN = "org.gradle.api.plugins.JavaPlugin";

private Map<String, JavaClass> gradleLibraries = new HashMap<>();
private Set<String> javaConfigurations = new HashSet<>();
private Set<String> javaPlugins = new HashSet<>();
private String gradleHome;
private String gradleVersion;
private boolean gradleWrapperEnabled;
private String gradleUserHome;
private Path workspacePath;

public GradleLibraryResolver() {
this.javaPlugins.addAll(Arrays.asList("java", "application", "groovy", "java-library", "war"));
}

public void setGradleHome(String gradleHome) {
this.gradleHome = gradleHome;
}
Expand All @@ -60,6 +73,10 @@ public Map<String, JavaClass> getGradleLibraries() {
return this.gradleLibraries;
}

public Set<String> getJavaConfigurations() {
return this.javaConfigurations;
}

public void resolve() {
Path gradleUserHomePath = (this.gradleUserHome == null) ? Path.of(System.getProperty("user.home"), ".gradle")
: Path.of(this.gradleUserHome);
Expand All @@ -86,6 +103,7 @@ public void resolve() {
}
JarFile pluginLibJar = new JarFile(pluginLibFile);
getGradleLibraries(pluginLibFile.toPath(), pluginLibJar);
resolveJavaConfigurations();
} catch (Exception e) {
// Do Nothing
}
Expand Down Expand Up @@ -190,4 +208,33 @@ private void getGradleLibraries(Path jarPath, JarFile jarFile) {
}
}
}

private void resolveJavaConfigurations() {
JavaClass javaPluginClass = this.gradleLibraries.get(GradleLibraryResolver.JAVA_PLUGIN);
if (javaPluginClass == null) {
return;
}
for (Field field : javaPluginClass.getFields()) {
if (field.getName().endsWith("CONFIGURATION_NAME")) {
this.javaConfigurations.add(removeQuotes(field.getConstantValue().toString()));
}
}
}

private static String removeQuotes(String original) {
// for those fields parsed from class files, we get ""values"", so we remove the starting and ending quotes here
if (original.length() < 3) {
return original;
}
return original.substring(1, original.length() - 1);
}

public boolean isJavaPluginsIncluded(Set<String> plugins) {
for (String plugin : plugins) {
if (this.javaPlugins.contains(plugin)) {
return true;
}
}
return false;
}
}

0 comments on commit 183be78

Please sign in to comment.