mirror of
https://github.com/tiennm99/lombok.git
synced 2026-10-13 22:16:11 +00:00
Fixed Help Content in Eclipse / ecj support.
- Moved Completion proposal code to the PatchExtensionMethodCompletionProposal class - Moved out error reporting from EclipseAST. - Fixed error reporting of the portals.
This commit is contained in:
1 parent
2af094030f
commit
3ed67fc8ae
8 files changed
+391
-199
No files matched your search
@@ -21,15 +21,16 @@
|
||||
*/
|
||||
package lombok.eclipse;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.Lombok;
|
||||
import lombok.core.AST;
|
||||
|
||||
import org.eclipse.jdt.core.compiler.CategorizedProblem;
|
||||
import org.eclipse.jdt.internal.compiler.CompilationResult;
|
||||
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
|
||||
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.ast.Annotation;
|
||||
@@ -42,9 +43,6 @@ import org.eclipse.jdt.internal.compiler.ast.Initializer;
|
||||
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.ast.Statement;
|
||||
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
|
||||
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
|
||||
import org.eclipse.jdt.internal.compiler.util.Util;
|
||||
|
||||
/**
|
||||
* Wraps around Eclipse's internal AST view to add useful features as well as the ability to visit parents from children,
|
||||
@@ -147,44 +145,25 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
|
||||
public static void addProblemToCompilationResult(CompilationUnitDeclaration ast,
|
||||
boolean isWarning, String message, int sourceStart, int sourceEnd) {
|
||||
if (ast.compilationResult == null) return;
|
||||
char[] fileNameArray = ast.getFileName();
|
||||
if (fileNameArray == null) fileNameArray = "(unknown).java".toCharArray();
|
||||
int lineNumber = 0;
|
||||
int columnNumber = 1;
|
||||
CompilationResult result = ast.compilationResult;
|
||||
int[] lineEnds = null;
|
||||
lineNumber = sourceStart >= 0
|
||||
? Util.getLineNumber(sourceStart, lineEnds = result.getLineSeparatorPositions(), 0, lineEnds.length-1)
|
||||
: 0;
|
||||
columnNumber = sourceStart >= 0
|
||||
? Util.searchColumnNumber(result.getLineSeparatorPositions(), lineNumber,sourceStart)
|
||||
: 0;
|
||||
|
||||
CategorizedProblem ecProblem = new LombokProblem(
|
||||
fileNameArray, message, 0, new String[0],
|
||||
isWarning ? ProblemSeverities.Warning : ProblemSeverities.Error,
|
||||
sourceStart, sourceEnd, lineNumber, columnNumber);
|
||||
ast.compilationResult.record(ecProblem, null);
|
||||
}
|
||||
|
||||
private static class LombokProblem extends DefaultProblem {
|
||||
private static final String MARKER_ID = "org.eclipse.jdt.apt.pluggable.core.compileProblem"; //$NON-NLS-1$
|
||||
|
||||
public LombokProblem(char[] originatingFileName, String message, int id,
|
||||
String[] stringArguments, int severity,
|
||||
int startPosition, int endPosition, int line, int column) {
|
||||
super(originatingFileName, message, id, stringArguments, severity, startPosition, endPosition, line, column);
|
||||
}
|
||||
|
||||
@Override public int getCategoryID() {
|
||||
return CAT_UNSPECIFIED;
|
||||
}
|
||||
|
||||
@Override public String getMarkerType() {
|
||||
return MARKER_ID;
|
||||
try {
|
||||
EcjReflectionCheck.addProblemToCompilationResult.invoke(null, ast, isWarning, message, sourceStart, sourceEnd);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
} catch (IllegalAccessException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (NullPointerException e) {
|
||||
if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) {
|
||||
e.initCause(EcjReflectionCheck.problem);
|
||||
throw e;
|
||||
}
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final CompilationUnitDeclaration compilationUnitDeclaration;
|
||||
private boolean completeParse;
|
||||
|
||||
@@ -372,4 +351,25 @@ public class EclipseAST extends AST<EclipseAST, EclipseNode, ASTNode> {
|
||||
@Override protected Collection<Class<? extends ASTNode>> getStatementTypes() {
|
||||
return Collections.<Class<? extends ASTNode>>singleton(Statement.class);
|
||||
}
|
||||
|
||||
private static class EcjReflectionCheck {
|
||||
private static final String CUD_TYPE = "org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration";
|
||||
|
||||
public static Method addProblemToCompilationResult;
|
||||
public static final Throwable problem;
|
||||
|
||||
static {
|
||||
Throwable problem_ = null;
|
||||
Method m = null;
|
||||
try {
|
||||
m = EclipseAstProblemView.class.getMethod("addProblemToCompilationResult", Class.forName(CUD_TYPE), boolean.class, String.class, int.class, int.class);
|
||||
} catch (Throwable t) {
|
||||
// That's problematic, but as long as no local classes are used we don't actually need it.
|
||||
// Better fail on local classes than crash altogether.
|
||||
problem_ = t;
|
||||
}
|
||||
addProblemToCompilationResult = m;
|
||||
problem = problem_;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package lombok.eclipse;
|
||||
|
||||
|
||||
import org.eclipse.jdt.core.compiler.CategorizedProblem;
|
||||
import org.eclipse.jdt.internal.compiler.CompilationResult;
|
||||
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.problem.DefaultProblem;
|
||||
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
|
||||
import org.eclipse.jdt.internal.compiler.util.Util;
|
||||
|
||||
public class EclipseAstProblemView {
|
||||
/**
|
||||
* Adds a problem to the provided CompilationResult object so that it will show up
|
||||
* in the Problems/Warnings view.
|
||||
*/
|
||||
public static void addProblemToCompilationResult(CompilationUnitDeclaration ast,
|
||||
boolean isWarning, String message, int sourceStart, int sourceEnd) {
|
||||
if (ast.compilationResult == null) return;
|
||||
char[] fileNameArray = ast.getFileName();
|
||||
if (fileNameArray == null) fileNameArray = "(unknown).java".toCharArray();
|
||||
int lineNumber = 0;
|
||||
int columnNumber = 1;
|
||||
CompilationResult result = ast.compilationResult;
|
||||
int[] lineEnds = null;
|
||||
lineNumber = sourceStart >= 0
|
||||
? Util.getLineNumber(sourceStart, lineEnds = result.getLineSeparatorPositions(), 0, lineEnds.length-1)
|
||||
: 0;
|
||||
columnNumber = sourceStart >= 0
|
||||
? Util.searchColumnNumber(result.getLineSeparatorPositions(), lineNumber,sourceStart)
|
||||
: 0;
|
||||
|
||||
CategorizedProblem ecProblem = new LombokProblem(
|
||||
fileNameArray, message, 0, new String[0],
|
||||
isWarning ? ProblemSeverities.Warning : ProblemSeverities.Error,
|
||||
sourceStart, sourceEnd, lineNumber, columnNumber);
|
||||
ast.compilationResult.record(ecProblem, null);
|
||||
}
|
||||
|
||||
private static class LombokProblem extends DefaultProblem {
|
||||
private static final String MARKER_ID = "org.eclipse.jdt.apt.pluggable.core.compileProblem"; //$NON-NLS-1$
|
||||
|
||||
public LombokProblem(char[] originatingFileName, String message, int id,
|
||||
String[] stringArguments, int severity,
|
||||
int startPosition, int endPosition, int line, int column) {
|
||||
super(originatingFileName, message, id, stringArguments, severity, startPosition, endPosition, line, column);
|
||||
}
|
||||
|
||||
@Override public int getCategoryID() {
|
||||
return CAT_UNSPECIFIED;
|
||||
}
|
||||
|
||||
@Override public String getMarkerType() {
|
||||
return MARKER_ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,10 +39,15 @@ public class PatchDelegatePortal {
|
||||
} catch (IllegalAccessException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
throw Lombok.sneakyThrow(e.getCause());
|
||||
} catch (NullPointerException e) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
}
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,6 @@ package lombok.eclipse.agent;
|
||||
|
||||
import static lombok.eclipse.handlers.EclipseHandlerUtil.createAnnotation;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -41,39 +38,25 @@ import lombok.eclipse.TransformEclipseAST;
|
||||
import lombok.eclipse.handlers.EclipseHandlerUtil;
|
||||
import lombok.experimental.ExtensionMethod;
|
||||
|
||||
import org.eclipse.jdt.core.CompletionProposal;
|
||||
import org.eclipse.jdt.internal.codeassist.InternalCompletionContext;
|
||||
import org.eclipse.jdt.internal.codeassist.InternalCompletionProposal;
|
||||
import org.eclipse.jdt.internal.codeassist.InternalExtendedCompletionContext;
|
||||
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMemberAccess;
|
||||
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedNameReference;
|
||||
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleNameReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
|
||||
import org.eclipse.jdt.internal.compiler.ast.Annotation;
|
||||
import org.eclipse.jdt.internal.compiler.ast.ClassLiteralAccess;
|
||||
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.ast.Expression;
|
||||
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
|
||||
import org.eclipse.jdt.internal.compiler.ast.NameReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.QualifiedNameReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.ThisReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.Binding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.ProblemMethodBinding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.Scope;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
|
||||
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
|
||||
import org.eclipse.jdt.internal.ui.text.java.AbstractJavaCompletionProposal;
|
||||
import org.eclipse.jdt.ui.text.java.CompletionProposalCollector;
|
||||
|
||||
public class PatchExtensionMethod {
|
||||
static class Extension {
|
||||
@@ -138,7 +121,7 @@ public class PatchExtensionMethod {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static EclipseNode upToType(EclipseNode typeNode) {
|
||||
static EclipseNode upToType(EclipseNode typeNode) {
|
||||
EclipseNode node = typeNode;
|
||||
do {
|
||||
node = node.up();
|
||||
@@ -146,7 +129,7 @@ public class PatchExtensionMethod {
|
||||
return node;
|
||||
}
|
||||
|
||||
private static List<Extension> getApplicableExtensionMethods(EclipseNode typeNode, Annotation ann, TypeBinding receiverType) {
|
||||
static List<Extension> getApplicableExtensionMethods(EclipseNode typeNode, Annotation ann, TypeBinding receiverType) {
|
||||
List<Extension> extensions = new ArrayList<Extension>();
|
||||
if ((typeNode != null) && (ann != null) && (receiverType != null)) {
|
||||
BlockScope blockScope = ((TypeDeclaration) typeNode.get()).initializerScope;
|
||||
@@ -295,118 +278,7 @@ public class PatchExtensionMethod {
|
||||
return new QualifiedNameReference(sources, poss, source.sourceStart, source.sourceEnd);
|
||||
}
|
||||
}
|
||||
|
||||
static List<Extension> getExtensionMethods(CompletionProposalCollector completionProposalCollector) {
|
||||
List<Extension> extensions = new ArrayList<Extension>();
|
||||
ClassScope classScope = getClassScope(completionProposalCollector);
|
||||
if (classScope != null) {
|
||||
TypeDeclaration decl = classScope.referenceContext;
|
||||
TypeBinding firstParameterType = getFirstParameterType(decl, completionProposalCollector);
|
||||
for (EclipseNode typeNode = getTypeNode(decl); typeNode != null; typeNode = upToType(typeNode)) {
|
||||
Annotation ann = getAnnotation(ExtensionMethod.class, typeNode);
|
||||
extensions.addAll(0, getApplicableExtensionMethods(typeNode, ann, firstParameterType));
|
||||
}
|
||||
}
|
||||
return extensions;
|
||||
}
|
||||
|
||||
private static ClassScope getClassScope(CompletionProposalCollector completionProposalCollector) {
|
||||
ClassScope scope = null;
|
||||
try {
|
||||
InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector);
|
||||
InternalExtendedCompletionContext extendedContext = (InternalExtendedCompletionContext) Reflection.extendedContextField.get(context);
|
||||
if (extendedContext != null) {
|
||||
Scope assistScope = ((Scope) Reflection.assistScopeField.get(extendedContext));
|
||||
if (assistScope != null) {
|
||||
scope = assistScope.classScope();
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException ignore) {
|
||||
// ignore
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
private static TypeBinding getFirstParameterType(TypeDeclaration decl, CompletionProposalCollector completionProposalCollector) {
|
||||
TypeBinding firstParameterType = null;
|
||||
ASTNode node = getAssistNode(completionProposalCollector);
|
||||
if (node == null) return null;
|
||||
if (!(node instanceof CompletionOnQualifiedNameReference) && !(node instanceof CompletionOnSingleNameReference) && !(node instanceof CompletionOnMemberAccess)) return null;
|
||||
|
||||
if (node instanceof NameReference) {
|
||||
Binding binding = ((NameReference) node).binding;
|
||||
if ((node instanceof SingleNameReference) && (((SingleNameReference) node).token.length == 0)) {
|
||||
firstParameterType = decl.binding;
|
||||
} else if (binding instanceof VariableBinding) {
|
||||
firstParameterType = ((VariableBinding) binding).type;
|
||||
} else if (binding instanceof TypeBinding) {
|
||||
firstParameterType = (TypeBinding) binding;
|
||||
}
|
||||
} else if (node instanceof FieldReference) {
|
||||
firstParameterType = ((FieldReference) node).actualReceiverType;
|
||||
}
|
||||
return firstParameterType;
|
||||
}
|
||||
|
||||
static ASTNode getAssistNode(CompletionProposalCollector completionProposalCollector) {
|
||||
try {
|
||||
InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector);
|
||||
InternalExtendedCompletionContext extendedContext = (InternalExtendedCompletionContext) Reflection.extendedContextField.get(context);
|
||||
if (extendedContext == null) return null;
|
||||
return (ASTNode) Reflection.assistNodeField.get(extendedContext);
|
||||
} catch (Exception ignore) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class Reflection {
|
||||
public static final Field replacementOffsetField;
|
||||
public static final Field contextField;
|
||||
public static final Field extendedContextField;
|
||||
public static final Field assistNodeField;
|
||||
public static final Field assistScopeField;
|
||||
public static final Field lookupEnvironmentField;
|
||||
public static final Field completionEngineField;
|
||||
public static final Field nameLookupField;
|
||||
public static final Method createJavaCompletionProposalMethod;
|
||||
|
||||
static {
|
||||
replacementOffsetField = accessField(AbstractJavaCompletionProposal.class, "fReplacementOffset");
|
||||
contextField = accessField(CompletionProposalCollector.class, "fContext");
|
||||
extendedContextField = accessField(InternalCompletionContext.class, "extendedContext");
|
||||
assistNodeField = accessField(InternalExtendedCompletionContext.class, "assistNode");
|
||||
assistScopeField = accessField(InternalExtendedCompletionContext.class, "assistScope");
|
||||
lookupEnvironmentField = accessField(InternalExtendedCompletionContext.class, "lookupEnvironment");
|
||||
completionEngineField = accessField(InternalCompletionProposal.class, "completionEngine");
|
||||
nameLookupField = accessField(InternalCompletionProposal.class, "nameLookup");
|
||||
createJavaCompletionProposalMethod = accessMethod(CompletionProposalCollector.class, "createJavaCompletionProposal", CompletionProposal.class);
|
||||
}
|
||||
|
||||
static boolean isComplete() {
|
||||
Object[] requiredFieldsAndMethods = { replacementOffsetField, contextField, extendedContextField, assistNodeField, assistScopeField, lookupEnvironmentField, completionEngineField, nameLookupField, createJavaCompletionProposalMethod };
|
||||
for (Object o : requiredFieldsAndMethods) if (o == null) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Field accessField(Class<?> clazz, String fieldName) {
|
||||
try {
|
||||
return makeAccessible(clazz.getDeclaredField(fieldName));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static Method accessMethod(Class<?> clazz, String methodName, Class<?> parameter) {
|
||||
try {
|
||||
return makeAccessible(clazz.getDeclaredMethod(methodName, parameter));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends AccessibleObject> T makeAccessible(T object) {
|
||||
object.setAccessible(true);
|
||||
return object;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,21 +21,41 @@
|
||||
*/
|
||||
package lombok.eclipse.agent;
|
||||
|
||||
import static lombok.eclipse.agent.PatchExtensionMethod.*;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.eclipse.EclipseNode;
|
||||
import lombok.eclipse.agent.PatchExtensionMethod.Extension;
|
||||
import lombok.eclipse.agent.PatchExtensionMethod.Reflection;
|
||||
import lombok.experimental.ExtensionMethod;
|
||||
|
||||
import org.eclipse.jdt.core.CompletionProposal;
|
||||
import org.eclipse.jdt.internal.codeassist.InternalCompletionContext;
|
||||
import org.eclipse.jdt.internal.codeassist.InternalCompletionProposal;
|
||||
import org.eclipse.jdt.internal.codeassist.InternalExtendedCompletionContext;
|
||||
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMemberAccess;
|
||||
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedNameReference;
|
||||
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleNameReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
|
||||
import org.eclipse.jdt.internal.compiler.ast.Annotation;
|
||||
import org.eclipse.jdt.internal.compiler.ast.FieldReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.NameReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
|
||||
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.Binding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.Scope;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
|
||||
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
|
||||
import org.eclipse.jdt.internal.core.SearchableEnvironment;
|
||||
import org.eclipse.jdt.internal.ui.text.java.AbstractJavaCompletionProposal;
|
||||
import org.eclipse.jdt.ui.text.java.CompletionProposalCollector;
|
||||
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
|
||||
|
||||
@@ -49,11 +69,11 @@ public class PatchExtensionMethodCompletionProposal {
|
||||
if (canExtendCodeAssist(proposals)) {
|
||||
IJavaCompletionProposal firstProposal = proposals.get(0);
|
||||
int replacementOffset = getReplacementOffset(firstProposal);
|
||||
for (Extension extension : PatchExtensionMethod.getExtensionMethods(completionProposalCollector)) {
|
||||
for (Extension extension : getExtensionMethods(completionProposalCollector)) {
|
||||
for (MethodBinding method : extension.extensionMethods) {
|
||||
ExtensionMethodCompletionProposal newProposal = new ExtensionMethodCompletionProposal(replacementOffset);
|
||||
copyNameLookupAndCompletionEngine(completionProposalCollector, firstProposal, newProposal);
|
||||
ASTNode node = PatchExtensionMethod.getAssistNode(completionProposalCollector);
|
||||
ASTNode node = getAssistNode(completionProposalCollector);
|
||||
newProposal.setMethodBinding(method, node);
|
||||
createAndAddJavaCompletionProposal(completionProposalCollector, newProposal, proposals);
|
||||
}
|
||||
@@ -62,6 +82,70 @@ public class PatchExtensionMethodCompletionProposal {
|
||||
return proposals.toArray(new IJavaCompletionProposal[proposals.size()]);
|
||||
}
|
||||
|
||||
|
||||
private static List<Extension> getExtensionMethods(CompletionProposalCollector completionProposalCollector) {
|
||||
List<Extension> extensions = new ArrayList<Extension>();
|
||||
ClassScope classScope = getClassScope(completionProposalCollector);
|
||||
if (classScope != null) {
|
||||
TypeDeclaration decl = classScope.referenceContext;
|
||||
TypeBinding firstParameterType = getFirstParameterType(decl, completionProposalCollector);
|
||||
for (EclipseNode typeNode = getTypeNode(decl); typeNode != null; typeNode = upToType(typeNode)) {
|
||||
Annotation ann = getAnnotation(ExtensionMethod.class, typeNode);
|
||||
extensions.addAll(0, getApplicableExtensionMethods(typeNode, ann, firstParameterType));
|
||||
}
|
||||
}
|
||||
return extensions;
|
||||
}
|
||||
|
||||
static TypeBinding getFirstParameterType(TypeDeclaration decl, CompletionProposalCollector completionProposalCollector) {
|
||||
TypeBinding firstParameterType = null;
|
||||
ASTNode node = getAssistNode(completionProposalCollector);
|
||||
if (node == null) return null;
|
||||
if (!(node instanceof CompletionOnQualifiedNameReference) && !(node instanceof CompletionOnSingleNameReference) && !(node instanceof CompletionOnMemberAccess)) return null;
|
||||
|
||||
if (node instanceof NameReference) {
|
||||
Binding binding = ((NameReference) node).binding;
|
||||
if ((node instanceof SingleNameReference) && (((SingleNameReference) node).token.length == 0)) {
|
||||
firstParameterType = decl.binding;
|
||||
} else if (binding instanceof VariableBinding) {
|
||||
firstParameterType = ((VariableBinding) binding).type;
|
||||
} else if (binding instanceof TypeBinding) {
|
||||
firstParameterType = (TypeBinding) binding;
|
||||
}
|
||||
} else if (node instanceof FieldReference) {
|
||||
firstParameterType = ((FieldReference) node).actualReceiverType;
|
||||
}
|
||||
return firstParameterType;
|
||||
}
|
||||
|
||||
private static ASTNode getAssistNode(CompletionProposalCollector completionProposalCollector) {
|
||||
try {
|
||||
InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector);
|
||||
InternalExtendedCompletionContext extendedContext = (InternalExtendedCompletionContext) Reflection.extendedContextField.get(context);
|
||||
if (extendedContext == null) return null;
|
||||
return (ASTNode) Reflection.assistNodeField.get(extendedContext);
|
||||
} catch (Exception ignore) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static ClassScope getClassScope(CompletionProposalCollector completionProposalCollector) {
|
||||
ClassScope scope = null;
|
||||
try {
|
||||
InternalCompletionContext context = (InternalCompletionContext) Reflection.contextField.get(completionProposalCollector);
|
||||
InternalExtendedCompletionContext extendedContext = (InternalExtendedCompletionContext) Reflection.extendedContextField.get(context);
|
||||
if (extendedContext != null) {
|
||||
Scope assistScope = ((Scope) Reflection.assistScopeField.get(extendedContext));
|
||||
if (assistScope != null) {
|
||||
scope = assistScope.classScope();
|
||||
}
|
||||
}
|
||||
} catch (IllegalAccessException ignore) {
|
||||
// ignore
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
private static void copyNameLookupAndCompletionEngine(CompletionProposalCollector completionProposalCollector, IJavaCompletionProposal proposal,
|
||||
InternalCompletionProposal newProposal) {
|
||||
|
||||
@@ -97,4 +181,55 @@ public class PatchExtensionMethodCompletionProposal {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static class Reflection {
|
||||
public static final Field replacementOffsetField;
|
||||
public static final Field contextField;
|
||||
public static final Field extendedContextField;
|
||||
public static final Field assistNodeField;
|
||||
public static final Field assistScopeField;
|
||||
public static final Field lookupEnvironmentField;
|
||||
public static final Field completionEngineField;
|
||||
public static final Field nameLookupField;
|
||||
public static final Method createJavaCompletionProposalMethod;
|
||||
|
||||
static {
|
||||
replacementOffsetField = accessField(AbstractJavaCompletionProposal.class, "fReplacementOffset");
|
||||
contextField = accessField(CompletionProposalCollector.class, "fContext");
|
||||
extendedContextField = accessField(InternalCompletionContext.class, "extendedContext");
|
||||
assistNodeField = accessField(InternalExtendedCompletionContext.class, "assistNode");
|
||||
assistScopeField = accessField(InternalExtendedCompletionContext.class, "assistScope");
|
||||
lookupEnvironmentField = accessField(InternalExtendedCompletionContext.class, "lookupEnvironment");
|
||||
completionEngineField = accessField(InternalCompletionProposal.class, "completionEngine");
|
||||
nameLookupField = accessField(InternalCompletionProposal.class, "nameLookup");
|
||||
createJavaCompletionProposalMethod = accessMethod(CompletionProposalCollector.class, "createJavaCompletionProposal", CompletionProposal.class);
|
||||
}
|
||||
|
||||
static boolean isComplete() {
|
||||
Object[] requiredFieldsAndMethods = { replacementOffsetField, contextField, extendedContextField, assistNodeField, assistScopeField, lookupEnvironmentField, completionEngineField, nameLookupField, createJavaCompletionProposalMethod };
|
||||
for (Object o : requiredFieldsAndMethods) if (o == null) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static Field accessField(Class<?> clazz, String fieldName) {
|
||||
try {
|
||||
return makeAccessible(clazz.getDeclaredField(fieldName));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static Method accessMethod(Class<?> clazz, String methodName, Class<?> parameter) {
|
||||
try {
|
||||
return makeAccessible(clazz.getDeclaredMethod(methodName, parameter));
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static <T extends AccessibleObject> T makeAccessible(T object) {
|
||||
object.setAccessible(true);
|
||||
return object;
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-5
@@ -24,10 +24,10 @@ package lombok.eclipse.agent;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
|
||||
|
||||
import lombok.Lombok;
|
||||
|
||||
import org.eclipse.jdt.ui.text.java.IJavaCompletionProposal;
|
||||
|
||||
|
||||
public class PatchExtensionMethodCompletionProposalPortal {
|
||||
private static final String COMPLETION_PROPOSAL_COLLECTOR = "org.eclipse.jdt.ui.text.java.CompletionProposalCollector";
|
||||
@@ -44,10 +44,15 @@ public class PatchExtensionMethodCompletionProposalPortal {
|
||||
} catch (IllegalAccessException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
throw Lombok.sneakyThrow(e.getCause());
|
||||
} catch (NullPointerException e) {
|
||||
e.initCause(ReflectionForUi.problem);
|
||||
throw e;
|
||||
if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) {
|
||||
e.initCause(ReflectionForUi.problem);
|
||||
throw e;
|
||||
}
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
return (IJavaCompletionProposal[])javaCompletionProposals;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
package lombok.eclipse.agent;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import lombok.Lombok;
|
||||
|
||||
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
|
||||
|
||||
public class PatchExtensionMethodPortal {
|
||||
private static final String TYPE_BINDING = "org.eclipse.jdt.internal.compiler.lookup.TypeBinding";
|
||||
private static final String TYPE_BINDING_ARRAY = "[Lorg.eclipse.jdt.internal.compiler.lookup.TypeBinding;";
|
||||
private static final String MESSAGE_SEND = "org.eclipse.jdt.internal.compiler.ast.MessageSend";
|
||||
private static final String BLOCK_SCOPE = "org.eclipse.jdt.internal.compiler.lookup.BlockScope";
|
||||
private static final String METHOD_BINDING = "org.eclipse.jdt.internal.compiler.lookup.MethodBinding";
|
||||
private static final String PROBLEM_REPORTER = "org.eclipse.jdt.internal.compiler.problem.ProblemReporter";
|
||||
|
||||
public static TypeBinding resolveType(Object resolvedType, Object methodCall, Object scope) {
|
||||
try {
|
||||
return (TypeBinding) Reflection.resolveType.invoke(null, resolvedType, methodCall, scope);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
return (TypeBinding) resolvedType;
|
||||
} catch (IllegalAccessException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (NullPointerException e) {
|
||||
if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
}
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
return (TypeBinding)resolvedType;
|
||||
}
|
||||
}
|
||||
|
||||
public static void errorNoMethodFor(Object problemReporter, Object messageSend, Object recType, Object params) {
|
||||
try {
|
||||
Reflection.errorNoMethodFor.invoke(null, problemReporter, messageSend, recType, params);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
} catch (IllegalAccessException e) {
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw Lombok.sneakyThrow(e.getCause());
|
||||
} catch (NullPointerException e) {
|
||||
if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
}
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
}
|
||||
}
|
||||
|
||||
public static void invalidMethod(Object problemReporter, Object messageSend, Object method) {
|
||||
try {
|
||||
Reflection.invalidMethod.invoke(null, problemReporter, messageSend, method);
|
||||
} catch (NoClassDefFoundError e) {
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
} catch (IllegalAccessException e) {
|
||||
Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw Lombok.sneakyThrow(e.getCause());
|
||||
} catch (NullPointerException e) {
|
||||
if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
}
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Reflection {
|
||||
public static final Method resolveType, errorNoMethodFor, invalidMethod;
|
||||
public static final Throwable problem;
|
||||
|
||||
static {
|
||||
Method m = null, n = null, o = null;
|
||||
Throwable problem_ = null;
|
||||
try {
|
||||
m = PatchExtensionMethod.class.getMethod("resolveType", Class.forName(TYPE_BINDING), Class.forName(MESSAGE_SEND), Class.forName(BLOCK_SCOPE));
|
||||
n = PatchExtensionMethod.class.getMethod("errorNoMethodFor", Class.forName(PROBLEM_REPORTER),
|
||||
Class.forName(MESSAGE_SEND), Class.forName(TYPE_BINDING), Class.forName(TYPE_BINDING_ARRAY));
|
||||
o = PatchExtensionMethod.class.getMethod("invalidMethod", Class.forName(PROBLEM_REPORTER), Class.forName(MESSAGE_SEND), Class.forName(METHOD_BINDING));
|
||||
} catch (Throwable t) {
|
||||
// That's problematic, but as long as no local classes are used we don't actually need it.
|
||||
// Better fail on local classes than crash altogether.
|
||||
problem_ = t;
|
||||
}
|
||||
resolveType = m;
|
||||
errorNoMethodFor = n;
|
||||
invalidMethod = o;
|
||||
problem = problem_;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,12 +40,16 @@ public class PatchValEclipsePortal {
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
} catch (IllegalAccessException e) {
|
||||
Lombok.sneakyThrow(e);
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Lombok.sneakyThrow(e);
|
||||
throw Lombok.sneakyThrow(e.getCause());
|
||||
} catch (NullPointerException e) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
}
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,12 +60,16 @@ public class PatchValEclipsePortal {
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
} catch (IllegalAccessException e) {
|
||||
Lombok.sneakyThrow(e);
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Lombok.sneakyThrow(e);
|
||||
throw Lombok.sneakyThrow(e.getCause());
|
||||
} catch (NullPointerException e) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
}
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,12 +80,16 @@ public class PatchValEclipsePortal {
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
} catch (IllegalAccessException e) {
|
||||
Lombok.sneakyThrow(e);
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Lombok.sneakyThrow(e);
|
||||
throw Lombok.sneakyThrow(e.getCause());
|
||||
} catch (NullPointerException e) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
}
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,12 +100,16 @@ public class PatchValEclipsePortal {
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
} catch (IllegalAccessException e) {
|
||||
Lombok.sneakyThrow(e);
|
||||
throw Lombok.sneakyThrow(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
Lombok.sneakyThrow(e);
|
||||
throw Lombok.sneakyThrow(e.getCause());
|
||||
} catch (NullPointerException e) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
if (!"false".equals(System.getProperty("lombok.debug.reflection", "false"))) {
|
||||
e.initCause(Reflection.problem);
|
||||
throw e;
|
||||
}
|
||||
//ignore, we don't have access to the correct ECJ classes, so lombok can't possibly
|
||||
//do anything useful here.
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user