Scope completions done
This commit is contained in:
@@ -109,6 +109,11 @@
|
||||
<artifactId>rewrite-java</artifactId>
|
||||
<version>${rewrite-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openrewrite</groupId>
|
||||
<artifactId>rewrite-java-11</artifactId>
|
||||
<version>${rewrite-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
|
||||
@@ -25,7 +25,7 @@ import org.springframework.ide.vscode.boot.java.scope.ScopeCompletionProcessor;
|
||||
import org.springframework.ide.vscode.boot.java.snippets.JavaSnippet;
|
||||
import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetContext;
|
||||
import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetManager;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.java.value.ValueCompletionProcessor;
|
||||
import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.SpringPropertyIndexProvider;
|
||||
@@ -86,7 +86,7 @@ public class BootJavaCompletionEngineConfigurer {
|
||||
BootLanguageServerParams params,
|
||||
@Qualifier("adHocProperties") ProjectBasedPropertyIndexProvider adHocProperties,
|
||||
JavaSnippetManager snippetManager,
|
||||
CompilationUnitCache cuCache) {
|
||||
ORCompilationUnitCache cuCache) {
|
||||
SpringPropertyIndexProvider indexProvider = params.indexProvider;
|
||||
JavaProjectFinder javaProjectFinder = params.projectFinder;
|
||||
Map<String, CompletionProvider> providers = new HashMap<>();
|
||||
|
||||
@@ -155,10 +155,14 @@ public class BootLanguageServerBootApp {
|
||||
return SourceLinkFactory.createSourceLinks(server, cuCache, params.projectFinder);
|
||||
}
|
||||
|
||||
@Bean ORCompilationUnitCache cuCache(SimpleLanguageServer server, BootLanguageServerParams params) {
|
||||
@Bean ORCompilationUnitCache orcuCache(SimpleLanguageServer server, BootLanguageServerParams params) {
|
||||
return new ORCompilationUnitCache(params.projectFinder, server, params.projectObserver);
|
||||
}
|
||||
|
||||
@Bean CompilationUnitCache cuCache(SimpleLanguageServer server, BootLanguageServerParams params) {
|
||||
return new CompilationUnitCache(params.projectFinder, server, params.projectObserver);
|
||||
}
|
||||
|
||||
@Bean SpringXMLCompletionEngine xmlCompletionEngine(SimpleLanguageServer server, JavaProjectFinder projectFinder, SpringSymbolIndex symbolIndex, BootJavaConfig config) {
|
||||
return new SpringXMLCompletionEngine(server, projectFinder, symbolIndex, config);
|
||||
}
|
||||
@@ -188,7 +192,7 @@ public class BootLanguageServerBootApp {
|
||||
}
|
||||
}
|
||||
|
||||
@Bean JavaElementLocationProvider javaElementLocationProvider(SimpleLanguageServer server, CompilationUnitCache cuCache, JavaDocumentUriProvider javaDocUriProvider) {
|
||||
@Bean JavaElementLocationProvider javaElementLocationProvider(SimpleLanguageServer server, ORCompilationUnitCache cuCache, JavaDocumentUriProvider javaDocUriProvider) {
|
||||
switch (LspClient.currentClient()) {
|
||||
case ECLIPSE:
|
||||
case VSCODE:
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
package org.springframework.ide.vscode.boot.app;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.lsp4j.MessageType;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@@ -11,15 +11,18 @@
|
||||
package org.springframework.ide.vscode.boot.java.data;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.J.Annotation;
|
||||
import org.openrewrite.java.tree.J.ClassDeclaration;
|
||||
import org.openrewrite.java.tree.JavaType.FullyQualified;
|
||||
import org.openrewrite.java.tree.TypeUtils;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ASTUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ORAstUtils;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
@@ -33,14 +36,14 @@ import org.springframework.util.StringUtils;
|
||||
public class DataRepositoryCompletionProcessor implements CompletionProvider {
|
||||
|
||||
@Override
|
||||
public void provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type,
|
||||
public void provideCompletions(J node, Annotation annotation,
|
||||
int offset, IDocument doc, Collection<ICompletionProposal> completions) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provideCompletions(ASTNode node, int offset, IDocument doc, Collection<ICompletionProposal> completions) {
|
||||
TypeDeclaration type = ASTUtils.findDeclaringType(node);
|
||||
DataRepositoryDefinition repo = getDataRepositoryDefinition(type);
|
||||
public void provideCompletions(J node, int offset, IDocument doc, Collection<ICompletionProposal> completions) {
|
||||
ClassDeclaration declaration = ORAstUtils.findNode(node, ClassDeclaration.class);
|
||||
DataRepositoryDefinition repo = getDataRepositoryDefinition(declaration, declaration.getType());
|
||||
if (repo != null) {
|
||||
DomainType domainType = repo.getDomainType();
|
||||
if (domainType != null) {
|
||||
@@ -100,41 +103,27 @@ public class DataRepositoryCompletionProcessor implements CompletionProvider {
|
||||
return new FindByCompletionProposal(label.toString(), CompletionItemKind.Method, edits, null, null, Optional.of(additionalEdits), filter);
|
||||
}
|
||||
|
||||
private DataRepositoryDefinition getDataRepositoryDefinition(TypeDeclaration type) {
|
||||
private DataRepositoryDefinition getDataRepositoryDefinition(ClassDeclaration declaration, FullyQualified type) {
|
||||
if (type != null) {
|
||||
ITypeBinding resolvedType = type.resolveBinding();
|
||||
return getDataRepositoryDefinition(type, resolvedType);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private DataRepositoryDefinition getDataRepositoryDefinition(TypeDeclaration type, ITypeBinding resolvedType) {
|
||||
if (resolvedType != null) {
|
||||
|
||||
// interface analysis
|
||||
ITypeBinding[] interfaces = resolvedType.getInterfaces();
|
||||
for (ITypeBinding resolvedInterface : interfaces) {
|
||||
String simplifiedType = null;
|
||||
if (resolvedInterface.isParameterizedType()) {
|
||||
simplifiedType = resolvedInterface.getBinaryName();
|
||||
}
|
||||
else {
|
||||
simplifiedType = resolvedType.getQualifiedName();
|
||||
}
|
||||
List<FullyQualified> interfaces = type.getInterfaces();
|
||||
for (FullyQualified resolvedInterface : interfaces) {
|
||||
String simplifiedType = resolvedInterface.getFullyQualifiedName();
|
||||
|
||||
if (Constants.REPOSITORY_TYPE.equals(simplifiedType)) {
|
||||
DomainType domainType = null;
|
||||
if (resolvedInterface.isParameterizedType()) {
|
||||
ITypeBinding[] typeParameters = resolvedInterface.getTypeArguments();
|
||||
if (typeParameters != null && typeParameters.length > 0) {
|
||||
domainType = new DomainType(typeParameters[0]);
|
||||
}
|
||||
}
|
||||
// TODO Fix for OR AST
|
||||
// if (resolvedInterface.isParameterizedType()) {
|
||||
// ITypeBinding[] typeParameters = resolvedInterface.getTypeArguments();
|
||||
// if (typeParameters != null && typeParameters.length > 0) {
|
||||
// domainType = new DomainType(typeParameters[0]);
|
||||
// }
|
||||
// }
|
||||
return createDataRepositoryDefinitionFromType(domainType);
|
||||
}
|
||||
else {
|
||||
DataRepositoryDefinition repo = getDataRepositoryDefinition(type, resolvedInterface);
|
||||
DataRepositoryDefinition repo = getDataRepositoryDefinition(declaration, resolvedInterface);
|
||||
if (repo != null) {
|
||||
return repo;
|
||||
}
|
||||
@@ -142,9 +131,9 @@ public class DataRepositoryCompletionProcessor implements CompletionProvider {
|
||||
}
|
||||
|
||||
// super type analysis
|
||||
ITypeBinding superclass = resolvedType.getSuperclass();
|
||||
FullyQualified superclass = type.getSupertype();
|
||||
if (superclass != null) {
|
||||
return getDataRepositoryDefinition(type, superclass);
|
||||
return getDataRepositoryDefinition(declaration, superclass);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -15,12 +15,13 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.NodeFinder;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.J.Annotation;
|
||||
import org.openrewrite.java.tree.JavaType.FullyQualified;
|
||||
import org.openrewrite.java.tree.TypeUtils;
|
||||
import org.springframework.ide.vscode.boot.java.snippets.JavaSnippetManager;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ORAstUtils;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.LanguageSpecific;
|
||||
@@ -37,9 +38,9 @@ public class BootJavaCompletionEngine implements ICompletionEngine, LanguageSpec
|
||||
|
||||
private Map<String, CompletionProvider> completionProviders;
|
||||
private JavaSnippetManager snippets;
|
||||
private CompilationUnitCache cuCache;
|
||||
private ORCompilationUnitCache cuCache;
|
||||
|
||||
public BootJavaCompletionEngine(CompilationUnitCache cuCache, Map<String, CompletionProvider> specificProviders, JavaSnippetManager snippets) {
|
||||
public BootJavaCompletionEngine(ORCompilationUnitCache cuCache, Map<String, CompletionProvider> specificProviders, JavaSnippetManager snippets) {
|
||||
this.cuCache = cuCache;
|
||||
this.completionProviders = specificProviders;
|
||||
this.snippets = snippets;
|
||||
@@ -49,7 +50,7 @@ public class BootJavaCompletionEngine implements ICompletionEngine, LanguageSpec
|
||||
public Collection<ICompletionProposal> getCompletions(TextDocument document, int offset) throws Exception {
|
||||
return cuCache.withCompilationUnit(document, cu -> {
|
||||
if (cu != null) {
|
||||
ASTNode node = NodeFinder.perform(cu, offset, 0);
|
||||
J node = ORAstUtils.findAstNodeAt(cu, offset);
|
||||
|
||||
if (node != null) {
|
||||
Collection<ICompletionProposal> completions = new ArrayList<ICompletionProposal>();
|
||||
@@ -64,30 +65,25 @@ public class BootJavaCompletionEngine implements ICompletionEngine, LanguageSpec
|
||||
});
|
||||
}
|
||||
|
||||
private void collectCompletionsForAnnotations(ASTNode node, int offset, IDocument doc, Collection<ICompletionProposal> completions) {
|
||||
Annotation annotation = null;
|
||||
ASTNode exactNode = node;
|
||||
private void collectCompletionsForAnnotations(J node, int offset, IDocument doc, Collection<ICompletionProposal> completions) {
|
||||
Annotation annotation = ORAstUtils.findNode(node, Annotation.class);
|
||||
J exactNode = node;
|
||||
|
||||
while (node != null && !(node instanceof Annotation)) {
|
||||
node = node.getParent();
|
||||
}
|
||||
|
||||
if (node != null) {
|
||||
annotation = (Annotation) node;
|
||||
ITypeBinding type = annotation.resolveTypeBinding();
|
||||
if (annotation != null) {
|
||||
FullyQualified type = TypeUtils.asFullyQualified(annotation.getType());
|
||||
if (type != null) {
|
||||
String qualifiedName = type.getQualifiedName();
|
||||
String qualifiedName = type.getFullyQualifiedName();
|
||||
if (qualifiedName != null) {
|
||||
CompletionProvider provider = this.completionProviders.get(qualifiedName);
|
||||
if (provider != null) {
|
||||
provider.provideCompletions(exactNode, annotation, type, offset, doc, completions);
|
||||
provider.provideCompletions(exactNode, annotation, offset, doc, completions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void collectCompletions(ASTNode node, int offset, TextDocument document, Collection<ICompletionProposal> completions) {
|
||||
private void collectCompletions(J node, int offset, TextDocument document, Collection<ICompletionProposal> completions) {
|
||||
if (node != null) {
|
||||
for (CompletionProvider completionProvider : this.completionProviders.values()) {
|
||||
completionProvider.provideCompletions(node, offset, document, completions);
|
||||
|
||||
@@ -12,9 +12,8 @@ package org.springframework.ide.vscode.boot.java.handlers;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.J.Annotation;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
@@ -23,7 +22,7 @@ import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
*/
|
||||
public interface CompletionProvider {
|
||||
|
||||
void provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type, int offset, IDocument doc, Collection<ICompletionProposal> completions);
|
||||
void provideCompletions(ASTNode node, int offset, IDocument doc, Collection<ICompletionProposal> completions);
|
||||
void provideCompletions(J node, Annotation annotation, int offset, IDocument doc, Collection<ICompletionProposal> completions);
|
||||
void provideCompletions(J node, int offset, IDocument doc, Collection<ICompletionProposal> completions);
|
||||
|
||||
}
|
||||
|
||||
@@ -16,11 +16,12 @@ import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.openrewrite.java.JavaIsoVisitor;
|
||||
import org.openrewrite.java.tree.J.CompilationUnit;
|
||||
import org.openrewrite.java.tree.TypeUtils;
|
||||
import org.openrewrite.marker.Position;
|
||||
import org.openrewrite.marker.Range;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache;
|
||||
@@ -30,7 +31,9 @@ import org.springframework.ide.vscode.commons.java.IJavaModuleData;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.javadoc.TypeUrlProviderFromContainerUrl;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.util.text.Region;
|
||||
|
||||
import reactor.util.function.Tuple2;
|
||||
import reactor.util.function.Tuples;
|
||||
|
||||
/**
|
||||
* Base logic for {@link SourceLinks} independent of any client
|
||||
@@ -132,11 +135,12 @@ public abstract class AbstractSourceLinks implements SourceLinks {
|
||||
}).map(sourcePath -> findCU(project, sourcePath).orElse(null));
|
||||
}
|
||||
|
||||
protected Region findTypeRegion(CompilationUnit cu, String fqName) {
|
||||
protected Tuple2<Integer, Integer> findTypeRegion(CompilationUnit cu, String fqName) {
|
||||
if (cu == null) {
|
||||
return null;
|
||||
}
|
||||
int[] values = new int[] {0, -1};
|
||||
AtomicReference<Range> range = new AtomicReference<>();
|
||||
int lastDotIndex = fqName.lastIndexOf('.');
|
||||
String packageName = fqName.substring(0, lastDotIndex);
|
||||
String typeName = fqName.substring(lastDotIndex + 1);
|
||||
@@ -147,11 +151,9 @@ public abstract class AbstractSourceLinks implements SourceLinks {
|
||||
public org.openrewrite.java.tree.J.ClassDeclaration visitClassDeclaration(org.openrewrite.java.tree.J.ClassDeclaration classDecl, Object p) {
|
||||
String fqName = classDecl.getType().getFullyQualifiedName();
|
||||
visitedType.push(fqName);
|
||||
if (values[1] < 0) {
|
||||
if (range.get() == null) {
|
||||
if (String.join("$", visitedType.toArray(new String[visitedType.size()])).equals(typeName)) {
|
||||
Position pos = classDecl.getName().getMarkers().findFirst(Position.class).orElseThrow();
|
||||
values[0] = pos.getStartPosition();
|
||||
values[1] = pos.getLength();
|
||||
range.set(classDecl.getName().getMarkers().findFirst(Range.class).orElseThrow());
|
||||
}
|
||||
}
|
||||
if (values[1] < 0) {
|
||||
@@ -165,7 +167,7 @@ public abstract class AbstractSourceLinks implements SourceLinks {
|
||||
|
||||
}.visitNonNull(cu, visitedType);
|
||||
}
|
||||
return values[1] < 0 ? null : new Region(values[0], values[1]);
|
||||
return Optional.of(range.get()).map(r -> r.getStart()).map(start -> Tuples.of(start.getLine(), start.getColumn())).orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ import org.springframework.ide.vscode.commons.util.text.Region;
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
|
||||
import reactor.util.function.Tuple2;
|
||||
|
||||
/**
|
||||
* Source links for Atom client
|
||||
*
|
||||
@@ -54,10 +56,10 @@ public class AtomSourceLinks extends AbstractSourceLinks {
|
||||
@Override
|
||||
protected String positionLink(CompilationUnit cu, String fqName) {
|
||||
if (cu != null) {
|
||||
Region region = findTypeRegion(cu, fqName);
|
||||
Tuple2<Integer, Integer> region = findTypeRegion(cu, fqName);
|
||||
if (region != null) {
|
||||
int column = cu.getColumnNumber(region.getOffset());
|
||||
int line = cu.getLineNumber(region.getOffset());
|
||||
int column = region.getT2();
|
||||
int line = region.getT1();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("&line=");
|
||||
sb.append(line);
|
||||
|
||||
@@ -26,21 +26,22 @@ import org.eclipse.lsp4j.Position;
|
||||
import org.eclipse.lsp4j.Range;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache;
|
||||
import org.springframework.ide.vscode.commons.java.IField;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.java.IMember;
|
||||
import org.springframework.ide.vscode.commons.java.IMethod;
|
||||
import org.springframework.ide.vscode.commons.java.IType;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
public class DefaultJavaElementLocationProvider implements JavaElementLocationProvider {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(DefaultJavaElementLocationProvider.class);
|
||||
|
||||
private CompilationUnitCache cuCache;
|
||||
private ORCompilationUnitCache cuCache;
|
||||
private JavaDocumentUriProvider javaDocUriProvider;
|
||||
|
||||
public DefaultJavaElementLocationProvider(CompilationUnitCache cuCache, JavaDocumentUriProvider javaDocUriProvider) {
|
||||
public DefaultJavaElementLocationProvider(ORCompilationUnitCache cuCache, JavaDocumentUriProvider javaDocUriProvider) {
|
||||
this.cuCache = cuCache;
|
||||
this.javaDocUriProvider = javaDocUriProvider;
|
||||
}
|
||||
@@ -63,67 +64,68 @@ public class DefaultJavaElementLocationProvider implements JavaElementLocationPr
|
||||
if (cu == null) {
|
||||
return new Range(new Position(0, 0), new Position(0, 0));
|
||||
}
|
||||
cu.accept(new ASTVisitor() {
|
||||
|
||||
private Range nameRange(SimpleName nameNode) {
|
||||
int startOffset = nameNode.getStartPosition();
|
||||
int endOffset = nameNode.getLength() + startOffset;
|
||||
|
||||
// Line -1 because for CU lines are starting from 1
|
||||
return new Range(
|
||||
new Position(cu.getLineNumber(startOffset) - 1, cu.getColumnNumber(startOffset)),
|
||||
new Position(cu.getLineNumber(endOffset) - 1, cu.getColumnNumber(endOffset)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(MethodDeclaration node) {
|
||||
if (member instanceof IMethod) {
|
||||
String bindingKey = node.resolveBinding().getKey();
|
||||
if (matchMethodBindingKeys(memberBindingKey, bindingKey)) {
|
||||
range.set(nameRange(node.getName()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(EnumConstantDeclaration node) {
|
||||
if (member instanceof IField) {
|
||||
String bindingKey = node.resolveVariable().getKey();
|
||||
if (memberBindingKey.equals(bindingKey)) {
|
||||
range.set(nameRange(node.getName()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(EnumDeclaration node) {
|
||||
if (member instanceof IType) {
|
||||
String bindingKey = node.resolveBinding().getKey();
|
||||
if (memberBindingKey.equals(bindingKey)) {
|
||||
range.set(nameRange(node.getName()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(TypeDeclaration node) {
|
||||
if (member instanceof IType) {
|
||||
String bindingKey = node.resolveBinding().getKey();
|
||||
if (memberBindingKey.equals(bindingKey)) {
|
||||
range.set(nameRange(node.getName()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
// TODO: Fix for OR AST
|
||||
// cu.accept(new ASTVisitor() {
|
||||
//
|
||||
// private Range nameRange(SimpleName nameNode) {
|
||||
// int startOffset = nameNode.getStartPosition();
|
||||
// int endOffset = nameNode.getLength() + startOffset;
|
||||
//
|
||||
// // Line -1 because for CU lines are starting from 1
|
||||
// return new Range(
|
||||
// new Position(cu.getLineNumber(startOffset) - 1, cu.getColumnNumber(startOffset)),
|
||||
// new Position(cu.getLineNumber(endOffset) - 1, cu.getColumnNumber(endOffset)));
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean visit(MethodDeclaration node) {
|
||||
// if (member instanceof IMethod) {
|
||||
// String bindingKey = node.resolveBinding().getKey();
|
||||
// if (matchMethodBindingKeys(memberBindingKey, bindingKey)) {
|
||||
// range.set(nameRange(node.getName()));
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean visit(EnumConstantDeclaration node) {
|
||||
// if (member instanceof IField) {
|
||||
// String bindingKey = node.resolveVariable().getKey();
|
||||
// if (memberBindingKey.equals(bindingKey)) {
|
||||
// range.set(nameRange(node.getName()));
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean visit(EnumDeclaration node) {
|
||||
// if (member instanceof IType) {
|
||||
// String bindingKey = node.resolveBinding().getKey();
|
||||
// if (memberBindingKey.equals(bindingKey)) {
|
||||
// range.set(nameRange(node.getName()));
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public boolean visit(TypeDeclaration node) {
|
||||
// if (member instanceof IType) {
|
||||
// String bindingKey = node.resolveBinding().getKey();
|
||||
// if (memberBindingKey.equals(bindingKey)) {
|
||||
// range.set(nameRange(node.getName()));
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// });
|
||||
return range.get();
|
||||
});
|
||||
if (r == null) {
|
||||
|
||||
@@ -18,7 +18,8 @@ import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaModuleData;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
import org.springframework.ide.vscode.commons.util.text.Region;
|
||||
|
||||
import reactor.util.function.Tuple2;
|
||||
|
||||
/**
|
||||
* VSCode specific source links implementation
|
||||
@@ -40,10 +41,10 @@ public class VSCodeSourceLinks extends AbstractSourceLinks {
|
||||
@Override
|
||||
protected String positionLink(CompilationUnit cu, String fqName) {
|
||||
if (cu != null) {
|
||||
Region region = findTypeRegion(cu, fqName);
|
||||
Tuple2<Integer, Integer> region = findTypeRegion(cu, fqName);
|
||||
if (region != null) {
|
||||
int column = cu.getColumnNumber(region.getOffset());
|
||||
int line = cu.getLineNumber(region.getOffset());
|
||||
int column = region.getT2();
|
||||
int line = region.getT1();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('#');
|
||||
sb.append(line);
|
||||
|
||||
@@ -12,13 +12,14 @@ package org.springframework.ide.vscode.boot.java.scope;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.J.Annotation;
|
||||
import org.openrewrite.java.tree.J.Assignment;
|
||||
import org.openrewrite.java.tree.J.Empty;
|
||||
import org.openrewrite.java.tree.J.Literal;
|
||||
import org.openrewrite.marker.Range;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ORAstUtils;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
|
||||
@@ -28,15 +29,15 @@ import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
public class ScopeCompletionProcessor implements CompletionProvider {
|
||||
|
||||
@Override
|
||||
public void provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type,
|
||||
public void provideCompletions(J node, Annotation annotation,
|
||||
int offset, IDocument doc, Collection<ICompletionProposal> completions) {
|
||||
|
||||
try {
|
||||
if (node instanceof SimpleName && node.getParent() instanceof MemberValuePair) {
|
||||
MemberValuePair memberPair = (MemberValuePair) node.getParent();
|
||||
if (node instanceof Assignment) {
|
||||
Assignment assignment = (Assignment) node;
|
||||
|
||||
// case: @Scope(value=<*>)
|
||||
if ("value".equals(memberPair.getName().toString()) && memberPair.getValue().toString().equals("$missing$")) {
|
||||
if ("value".equals(assignment.getVariable().toString()) && assignment.getAssignment() == null) {
|
||||
for (ScopeNameCompletion completion : ScopeNameCompletionProposal.COMPLETIONS) {
|
||||
ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, offset, offset, "");
|
||||
completions.add(proposal);
|
||||
@@ -44,33 +45,38 @@ public class ScopeCompletionProcessor implements CompletionProvider {
|
||||
}
|
||||
}
|
||||
// case: @Scope(<*>)
|
||||
else if (node == annotation && doc.get(offset - 1, 2).endsWith("()")) {
|
||||
else if (node instanceof Empty && ORAstUtils.getParent(node) == annotation) {
|
||||
for (ScopeNameCompletion completion : ScopeNameCompletionProposal.COMPLETIONS) {
|
||||
ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, offset, offset, "");
|
||||
completions.add(proposal);
|
||||
}
|
||||
}
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof Annotation) {
|
||||
// case: @Scope("...")
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
String prefix = doc.get(node.getStartPosition(), offset - node.getStartPosition());
|
||||
else if (node instanceof Literal && ORAstUtils.getParent(node) == annotation) {
|
||||
String nodeStr = node.printTrimmed();
|
||||
if (nodeStr.startsWith("\"") && nodeStr.endsWith("\"")) {
|
||||
// case: @Scope("...")
|
||||
Range range = node.getMarkers().findFirst(Range.class).orElseThrow();
|
||||
String prefix = doc.get(range.getStart().getOffset(), offset - range.getStart().getOffset());
|
||||
for (ScopeNameCompletion completion : ScopeNameCompletionProposal.COMPLETIONS) {
|
||||
if (completion.getValue().startsWith(prefix)) {
|
||||
ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, node.getStartPosition(), node.getStartPosition() + node.getLength(), prefix);
|
||||
ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, range.getStart().getOffset(), range.getStart().getOffset() + range.length(), prefix);
|
||||
completions.add(proposal);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair) {
|
||||
MemberValuePair memberPair = (MemberValuePair) node.getParent();
|
||||
|
||||
// case: @Scope(value=<*>)
|
||||
if ("value".equals(memberPair.getName().toString()) && node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
String prefix = doc.get(node.getStartPosition(), offset - node.getStartPosition());
|
||||
// case: @Scope(value=<*>)
|
||||
else if (node instanceof Literal && ORAstUtils.getParent(node) instanceof Assignment) {
|
||||
Assignment assignment = (Assignment) ORAstUtils.getParent(node);
|
||||
String nodeStr = node.printTrimmed();
|
||||
|
||||
if ("value".equals(assignment.getVariable().printTrimmed()) && nodeStr.startsWith("\"") && nodeStr.endsWith("\"")) {
|
||||
Range range = node.getMarkers().findFirst(Range.class).orElseThrow();
|
||||
String prefix = doc.get(range.getStart().getOffset(), offset - range.getStart().getOffset());
|
||||
for (ScopeNameCompletion completion : ScopeNameCompletionProposal.COMPLETIONS) {
|
||||
if (completion.getValue().startsWith(prefix)) {
|
||||
ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, node.getStartPosition(), node.getStartPosition() + node.getLength(), prefix);
|
||||
ICompletionProposal proposal = new ScopeNameCompletionProposal(completion, doc, range.getStart().getOffset(), range.getStart().getOffset() + range.length(), prefix);
|
||||
completions.add(proposal);
|
||||
}
|
||||
}
|
||||
@@ -83,7 +89,7 @@ public class ScopeCompletionProcessor implements CompletionProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provideCompletions(ASTNode node, int offset, IDocument doc, Collection<ICompletionProposal> completions) {
|
||||
public void provideCompletions(J node, int offset, IDocument doc, Collection<ICompletionProposal> completions) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ package org.springframework.ide.vscode.boot.java.snippets;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.J.CompilationUnit;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
|
||||
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
|
||||
@@ -45,7 +45,7 @@ public class JavaSnippet {
|
||||
}
|
||||
|
||||
public Optional<ICompletionProposal> generateCompletion(Supplier<SnippetBuilder> snippetBuilderFactory,
|
||||
DocumentRegion query, ASTNode node, CompilationUnit cu) {
|
||||
DocumentRegion query, J node, CompilationUnit cu) {
|
||||
|
||||
if (context.appliesTo(node)) {
|
||||
return Optional.of(
|
||||
|
||||
@@ -10,21 +10,39 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.snippets;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.lsp4j.CompletionItemKind;
|
||||
import org.springframework.ide.vscode.boot.java.jdt.imports.ImportRewrite;
|
||||
import org.openrewrite.ExecutionContext;
|
||||
import org.openrewrite.Recipe;
|
||||
import org.openrewrite.Result;
|
||||
import org.openrewrite.TreeVisitor;
|
||||
import org.openrewrite.java.JavaIsoVisitor;
|
||||
import org.openrewrite.java.tree.J.CompilationUnit;
|
||||
import org.openrewrite.java.tree.JavaSourceFile;
|
||||
import org.openrewrite.shaded.jgit.diff.Edit;
|
||||
import org.openrewrite.shaded.jgit.diff.EditList;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.ide.vscode.boot.java.utils.JGitUtils;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
|
||||
import org.springframework.ide.vscode.commons.util.BadLocationException;
|
||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||
import org.springframework.ide.vscode.commons.util.Renderables;
|
||||
import org.springframework.ide.vscode.commons.util.text.DocumentRegion;
|
||||
import org.springframework.ide.vscode.commons.util.text.IDocument;
|
||||
import org.springframework.ide.vscode.commons.util.text.IRegion;
|
||||
import org.springframework.ide.vscode.commons.util.text.LanguageId;
|
||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
|
||||
public class JavaSnippetCompletion implements ICompletionProposal{
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(JavaSnippetCompletion.class);
|
||||
|
||||
private DocumentRegion query;
|
||||
private JavaSnippet javaSnippet;
|
||||
@@ -65,17 +83,69 @@ public class JavaSnippetCompletion implements ICompletionProposal{
|
||||
|
||||
@Override
|
||||
public Optional<DocumentEdits> getAdditionalEdit() {
|
||||
ImportRewrite rewrite = ImportRewrite.create(cu, true);
|
||||
|
||||
javaSnippet.getImports().ifPresent((imprts ->
|
||||
{
|
||||
for (String imprt : imprts) {
|
||||
rewrite.addImport(imprt);
|
||||
Recipe r = new Recipe() {
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Add Imports";
|
||||
}
|
||||
}));
|
||||
|
||||
@Override
|
||||
protected TreeVisitor<?, ExecutionContext> getVisitor() {
|
||||
return new JavaIsoVisitor<>() {
|
||||
public JavaSourceFile visitJavaSourceFile(JavaSourceFile cu, ExecutionContext p) {
|
||||
javaSnippet.getImports().ifPresent(imports -> imports.forEach(i -> maybeAddImport(i)));
|
||||
return cu;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
List<Result> results = r.run(List.of(cu));
|
||||
if (!results.isEmpty()) {
|
||||
Result result = results.get(0);
|
||||
TextDocument newDoc = new TextDocument(null, LanguageId.PLAINTEXT, 0, result.getAfter().printAll());
|
||||
|
||||
DocumentEdits edit = rewrite.createEdit(query.getDocument());
|
||||
|
||||
return edit != null ? Optional.of(edit) : Optional.empty();
|
||||
EditList diff = JGitUtils.getDiff(result.getBefore().printAll(), newDoc.get());
|
||||
if (!diff.isEmpty()) {
|
||||
IDocument doc = query.getDocument();
|
||||
DocumentEdits edits = new DocumentEdits(doc, false);
|
||||
for (Edit e : diff) {
|
||||
try {
|
||||
switch(e.getType()) {
|
||||
case DELETE:
|
||||
edits.delete(doc.getLineOffset(e.getBeginA()), getStartOfLine(doc, e.getEndA()));
|
||||
break;
|
||||
case INSERT:
|
||||
edits.insert(doc.getLineOffset(e.getBeginA()), newDoc.textBetween(newDoc.getLineOfOffset(e.getBeginB()), getStartOfLine(newDoc, e.getEndB())));
|
||||
break;
|
||||
case REPLACE:
|
||||
edits.replace(doc.getLineOfOffset(e.getBeginA()), getStartOfLine(doc, e.getEndA()), newDoc.textBetween(newDoc.getLineOfOffset(e.getBeginB()), getStartOfLine(newDoc, e.getEndB())));
|
||||
break;
|
||||
case EMPTY:
|
||||
break;
|
||||
}
|
||||
} catch (BadLocationException ex) {
|
||||
log.error("Diff conversion failed", ex);
|
||||
}
|
||||
}
|
||||
return Optional.of(edits);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private static int getStartOfLine(IDocument doc, int lineNumber) {
|
||||
IRegion lineInformation = doc.getLineInformation(lineNumber);
|
||||
if (lineInformation != null) {
|
||||
return lineInformation.getOffset();
|
||||
}
|
||||
if (lineNumber > 0) {
|
||||
IRegion currentLine = doc.getLineInformation(lineNumber - 1);
|
||||
return currentLine.getOffset() + currentLine.getLength();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@
|
||||
*******************************************************************************/
|
||||
package org.springframework.ide.vscode.boot.java.snippets;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.J.ClassDeclaration;
|
||||
|
||||
public interface JavaSnippetContext {
|
||||
JavaSnippetContext BOOT_MEMBERS = (node) -> node instanceof TypeDeclaration;
|
||||
JavaSnippetContext BOOT_MEMBERS = (node) -> node instanceof ClassDeclaration;
|
||||
|
||||
boolean appliesTo(ASTNode node);
|
||||
boolean appliesTo(J node);
|
||||
}
|
||||
|
||||
@@ -14,8 +14,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.J.CompilationUnit;
|
||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionProposal;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.PrefixFinder;
|
||||
import org.springframework.ide.vscode.commons.languageserver.util.SnippetBuilder;
|
||||
@@ -47,7 +47,7 @@ public class JavaSnippetManager {
|
||||
|
||||
}
|
||||
|
||||
public void getCompletions(IDocument doc, int offset, ASTNode node, CompilationUnit cu, Collection<ICompletionProposal> completions) {
|
||||
public void getCompletions(IDocument doc, int offset, J node, CompilationUnit cu, Collection<ICompletionProposal> completions) {
|
||||
DocumentRegion query = PREFIX_FINDER.getPrefixRegion(doc, offset);
|
||||
|
||||
for (JavaSnippet javaSnippet : snippets) {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package org.springframework.ide.vscode.boot.java.utils;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import org.openrewrite.shaded.jgit.diff.EditList;
|
||||
import org.openrewrite.shaded.jgit.diff.HistogramDiff;
|
||||
import org.openrewrite.shaded.jgit.diff.RawText;
|
||||
import org.openrewrite.shaded.jgit.diff.RawTextComparator;
|
||||
|
||||
public class JGitUtils {
|
||||
|
||||
public static EditList getDiff(String txt1, String txt2) {
|
||||
RawText rt1 = new RawText(txt1.getBytes(StandardCharsets.UTF_8));
|
||||
RawText rt2 = new RawText(txt2.getBytes(StandardCharsets.UTF_8));
|
||||
EditList diffList = new EditList();
|
||||
diffList.addAll(new HistogramDiff().diff(RawTextComparator.DEFAULT, rt1, rt2));
|
||||
return diffList;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package org.springframework.ide.vscode.boot.java.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.openrewrite.Cursor;
|
||||
import org.openrewrite.SourceFile;
|
||||
import org.openrewrite.Tree;
|
||||
import org.openrewrite.java.JavaIsoVisitor;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.J.CompilationUnit;
|
||||
import org.openrewrite.marker.Marker;
|
||||
import org.openrewrite.marker.Range;
|
||||
|
||||
public class ORAstUtils {
|
||||
|
||||
private static class AncestersMarker implements Marker {
|
||||
|
||||
private UUID uuid;
|
||||
private List<J> ancesters = List.of();
|
||||
|
||||
public AncestersMarker(List<J> ancesters) {
|
||||
this.uuid = Tree.randomId();
|
||||
this.ancesters = ancesters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getFirstAnsector(Class<T> clazz) {
|
||||
if (ancesters != null) {
|
||||
for (J node : ancesters) {
|
||||
if (clazz.isInstance(node)) {
|
||||
return (T) node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public J getParent() {
|
||||
if (ancesters != null && !ancesters.isEmpty()) {
|
||||
return ancesters.get(0);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static J findAstNodeAt(CompilationUnit cu, int offset) {
|
||||
AtomicReference<J> f = new AtomicReference<>();
|
||||
new JavaIsoVisitor<AtomicReference<J>>() {
|
||||
public J visit(Tree tree, AtomicReference<J> found) {
|
||||
if (tree == null) {
|
||||
return null;
|
||||
}
|
||||
if (found.get() == null && tree instanceof J) {
|
||||
J node = (J) tree;
|
||||
Range range = node.getMarkers().findFirst(Range.class).orElse(null);
|
||||
if (range != null
|
||||
&& range.getStart().getOffset() <= offset
|
||||
&& offset <= range.getEnd().getOffset()) {
|
||||
super.visit(tree, found);
|
||||
if (found.get() == null) {
|
||||
List<J> ancesters = new ArrayList<>();
|
||||
for (Cursor c = getCursor(); c != null && !(c.getValue() instanceof SourceFile); c = c.getParent()) {
|
||||
Object o = c.getValue();
|
||||
if (o instanceof J) {
|
||||
ancesters.add((J) o);
|
||||
}
|
||||
}
|
||||
J n = node.withMarkers(node.getMarkers().addIfAbsent(new AncestersMarker(ancesters)));
|
||||
found.set(n);
|
||||
return n;
|
||||
}
|
||||
} else {
|
||||
return (J) tree;
|
||||
}
|
||||
}
|
||||
return (J) tree;
|
||||
};
|
||||
}.visitNonNull(cu, f);
|
||||
return f.get();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T findNode(J node, Class<T> clazz) {
|
||||
if (clazz.isInstance(node)) {
|
||||
return (T) node;
|
||||
}
|
||||
AncestersMarker ancestry = node.getMarkers().findFirst(AncestersMarker.class).orElseThrow();
|
||||
return ancestry.getFirstAnsector(clazz);
|
||||
}
|
||||
|
||||
public static J getParent(J node) {
|
||||
AncestersMarker ancestry = node.getMarkers().findFirst(AncestersMarker.class).orElseThrow();
|
||||
return ancestry.getParent();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,7 +3,9 @@ package org.springframework.ide.vscode.boot.java.utils;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -11,6 +13,7 @@ import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
@@ -118,22 +121,27 @@ public class ORCompilationUnitCache implements DocumentContentProvider, Disposab
|
||||
|
||||
private JavaParser loadJavaParser(IJavaProject project) {
|
||||
try {
|
||||
return javaParsers.get(project, () -> JavaParser.fromJavaVersion().classpath(getClasspathEntries(project)).build());
|
||||
return javaParsers.get(project, () -> {
|
||||
List<Path> classpath = getClasspathEntries(project).stream().map(s -> new File(s).toPath()).collect(Collectors.toList());
|
||||
JavaParser jp = JavaParser.fromJavaVersion().build();
|
||||
jp.setClasspath(classpath);
|
||||
return jp;
|
||||
});
|
||||
} catch (ExecutionException e) {
|
||||
logger.error("{}", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String[] getClasspathEntries(IJavaProject project) throws Exception {
|
||||
private static Set<String> getClasspathEntries(IJavaProject project) throws Exception {
|
||||
if (project == null) {
|
||||
return new String[0];
|
||||
return Collections.emptySet();
|
||||
} else {
|
||||
IClasspath classpath = project.getClasspath();
|
||||
Stream<File> classpathEntries = IClasspathUtil.getAllBinaryRoots(classpath).stream();
|
||||
return classpathEntries
|
||||
.filter(file -> file.exists())
|
||||
.map(file -> file.getAbsolutePath()).toArray(String[]::new);
|
||||
.map(file -> file.getAbsolutePath()).collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +208,8 @@ public class ORCompilationUnitCache implements DocumentContentProvider, Disposab
|
||||
}
|
||||
});
|
||||
|
||||
Result result = new UpdateSourcePositions().run(javaParser.parseInputs(List.of(input), null, new InMemoryExecutionContext())).get(0);
|
||||
List<CompilationUnit> parseInputs = javaParser.parseInputs(List.of(input), null, new InMemoryExecutionContext());
|
||||
Result result = new UpdateSourcePositions().run(parseInputs).get(0);
|
||||
|
||||
logger.info("CU Cache: created new AST for {}", uri.toString());
|
||||
|
||||
@@ -234,5 +243,26 @@ public class ORCompilationUnitCache implements DocumentContentProvider, Disposab
|
||||
|
||||
return requestor.apply(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Never research shows at the AST is thread-safe when used in read-only mode:
|
||||
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=58314
|
||||
*
|
||||
* This means that the previous implemented synchronization around the requestor
|
||||
* working on the AST is not necessary as long as the requestor operates in read-only
|
||||
* mode on the AST nodes.
|
||||
*
|
||||
* Warning: Callers should take care to do all AST processing inside of the requestor callback and
|
||||
* not pass of AST nodes to helper functions that work aynchronously or store AST nodes or ITypeBindings
|
||||
* for later use. The JDT ASTs are not thread safe!
|
||||
*/
|
||||
@Deprecated
|
||||
public <T> T withCompilationUnit(TextDocument document, Function<CompilationUnit, T> requestor) {
|
||||
IJavaProject project = this.projectFinder != null ? projectFinder.find(document.getId()).orElse(null) : null;
|
||||
|
||||
URI uri = URI.create(document.getUri());
|
||||
return withCompilationUnit(project, uri, requestor);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -19,12 +19,13 @@ import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jdt.core.dom.ASTNode;
|
||||
import org.eclipse.jdt.core.dom.Annotation;
|
||||
import org.eclipse.jdt.core.dom.ITypeBinding;
|
||||
import org.eclipse.jdt.core.dom.MemberValuePair;
|
||||
import org.eclipse.jdt.core.dom.SimpleName;
|
||||
import org.eclipse.jdt.core.dom.StringLiteral;
|
||||
import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.openrewrite.java.tree.J;
|
||||
import org.openrewrite.java.tree.J.Annotation;
|
||||
import org.openrewrite.marker.Range;
|
||||
import org.springframework.ide.vscode.boot.java.handlers.CompletionProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.ProjectBasedPropertyIndexProvider;
|
||||
import org.springframework.ide.vscode.boot.metadata.PropertyInfo;
|
||||
@@ -54,7 +55,7 @@ public class ValueCompletionProcessor implements CompletionProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provideCompletions(ASTNode node, Annotation annotation, ITypeBinding type,
|
||||
public void provideCompletions(J node, Annotation annotation,
|
||||
int offset, IDocument doc, Collection<ICompletionProposal> completions) {
|
||||
|
||||
try {
|
||||
@@ -75,28 +76,29 @@ public class ValueCompletionProcessor implements CompletionProvider {
|
||||
completions.add(proposal);
|
||||
}
|
||||
}
|
||||
// TODO: Get these cases fixed for OR AST
|
||||
// case: @Value(prefix<*>)
|
||||
else if (node instanceof SimpleName && node.getParent() instanceof Annotation) {
|
||||
computeProposalsForSimpleName(node, completions, offset, doc);
|
||||
}
|
||||
// case: @Value(value=<*>)
|
||||
else if (node instanceof SimpleName && node.getParent() instanceof MemberValuePair
|
||||
&& "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
|
||||
computeProposalsForSimpleName(node, completions, offset, doc);
|
||||
}
|
||||
// case: @Value("prefix<*>")
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof Annotation) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
computeProposalsForStringLiteral(node, completions, offset, doc);
|
||||
}
|
||||
}
|
||||
// case: @Value(value="prefix<*>")
|
||||
else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair
|
||||
&& "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
|
||||
if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
computeProposalsForStringLiteral(node, completions, offset, doc);
|
||||
}
|
||||
}
|
||||
// else if (node instanceof SimpleName && node.getParent() instanceof Annotation) {
|
||||
// computeProposalsForSimpleName(node, completions, offset, doc);
|
||||
// }
|
||||
// // case: @Value(value=<*>)
|
||||
// else if (node instanceof SimpleName && node.getParent() instanceof MemberValuePair
|
||||
// && "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
|
||||
// computeProposalsForSimpleName(node, completions, offset, doc);
|
||||
// }
|
||||
// // case: @Value("prefix<*>")
|
||||
// else if (node instanceof StringLiteral && node.getParent() instanceof Annotation) {
|
||||
// if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
// computeProposalsForStringLiteral(node, completions, offset, doc);
|
||||
// }
|
||||
// }
|
||||
// // case: @Value(value="prefix<*>")
|
||||
// else if (node instanceof StringLiteral && node.getParent() instanceof MemberValuePair
|
||||
// && "value".equals(((MemberValuePair)node.getParent()).getName().toString())) {
|
||||
// if (node.toString().startsWith("\"") && node.toString().endsWith("\"")) {
|
||||
// computeProposalsForStringLiteral(node, completions, offset, doc);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@@ -104,7 +106,7 @@ public class ValueCompletionProcessor implements CompletionProvider {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void provideCompletions(ASTNode node, int offset, IDocument doc, Collection<ICompletionProposal> completions) {
|
||||
public void provideCompletions(J node, int offset, IDocument doc, Collection<ICompletionProposal> completions) {
|
||||
}
|
||||
|
||||
private void computeProposalsForSimpleName(ASTNode node, Collection<ICompletionProposal> completions, int offset,
|
||||
@@ -130,14 +132,17 @@ public class ValueCompletionProcessor implements CompletionProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private void computeProposalsForStringLiteral(ASTNode node, Collection<ICompletionProposal> completions, int offset,
|
||||
private void computeProposalsForStringLiteral(J node, Collection<ICompletionProposal> completions, int offset,
|
||||
IDocument doc) throws BadLocationException {
|
||||
String prefix = identifyPropertyPrefix(doc.get(node.getStartPosition() + 1, offset - (node.getStartPosition() + 1)), offset - (node.getStartPosition() + 1));
|
||||
Range r = node.getMarkers().findFirst(Range.class).orElseThrow();
|
||||
int nodeStartOffset = r.getStart().getOffset();
|
||||
int nodeLength = r.length();
|
||||
String prefix = identifyPropertyPrefix(doc.get(nodeStartOffset + 1, offset - (nodeStartOffset + 1)), offset - (nodeStartOffset + 1));
|
||||
|
||||
int startOffset = offset - prefix.length();
|
||||
int endOffset = offset;
|
||||
|
||||
String prePrefix = doc.get(node.getStartPosition() + 1, offset - prefix.length() - node.getStartPosition() - 1);
|
||||
String prePrefix = doc.get(nodeStartOffset + 1, offset - prefix.length() - nodeStartOffset - 1);
|
||||
|
||||
String preCompletion;
|
||||
if (prePrefix.endsWith("${")) {
|
||||
@@ -150,7 +155,7 @@ public class ValueCompletionProcessor implements CompletionProvider {
|
||||
preCompletion = "${";
|
||||
}
|
||||
|
||||
String fullNodeContent = doc.get(node.getStartPosition(), node.getLength());
|
||||
String fullNodeContent = doc.get(nodeStartOffset, nodeLength);
|
||||
String postCompletion = isClosingBracketMissing(fullNodeContent + preCompletion) ? "}" : "";
|
||||
|
||||
List<Match<PropertyInfo>> matches = findMatches(prefix, doc);
|
||||
|
||||
@@ -17,7 +17,7 @@ import org.springframework.ide.vscode.boot.app.BootLanguageServerParams;
|
||||
import org.springframework.ide.vscode.boot.editor.harness.PropertyIndexHarness;
|
||||
import org.springframework.ide.vscode.boot.java.links.SourceLinks;
|
||||
import org.springframework.ide.vscode.boot.java.links.VSCodeSourceLinks;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SymbolCache;
|
||||
import org.springframework.ide.vscode.boot.java.utils.SymbolCacheVoid;
|
||||
import org.springframework.ide.vscode.boot.java.utils.test.MockProjectObserver;
|
||||
@@ -65,7 +65,7 @@ public class SourceLinksTestConf {
|
||||
return (DefaultSpringPropertyIndexProvider) serverParams.indexProvider;
|
||||
}
|
||||
|
||||
@Bean SourceLinks sourceLinks(CompilationUnitCache cuCache, JavaProjectFinder projectFinder) {
|
||||
@Bean SourceLinks sourceLinks(ORCompilationUnitCache cuCache, JavaProjectFinder projectFinder) {
|
||||
return new VSCodeSourceLinks(cuCache, projectFinder);
|
||||
}
|
||||
|
||||
|
||||
@@ -67,6 +67,19 @@ public class ScopeCompletionTest {
|
||||
"@Scope(\"websocket\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBracketsWithSpacesInsideCompletion() throws Exception {
|
||||
prepareCase("@Scope(\"onClass\")", "@Scope( <*>)");
|
||||
assertAnnotationCompletions(
|
||||
"@Scope( \"application\"<*>)",
|
||||
"@Scope( \"globalSession\"<*>)",
|
||||
"@Scope( \"prototype\"<*>)",
|
||||
"@Scope( \"request\"<*>)",
|
||||
"@Scope( \"session\"<*>)",
|
||||
"@Scope( \"singleton\"<*>)",
|
||||
"@Scope( \"websocket\"<*>)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyStringLiteralCompletion() throws Exception {
|
||||
prepareCase("@Scope(\"onClass\")", "@Scope(\"<*>\")");
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.eclipse.lsp4j.TextDocumentIdentifier;
|
||||
import org.gradle.internal.impldep.com.google.common.collect.ImmutableList;
|
||||
import org.junit.Test;
|
||||
import org.springframework.ide.vscode.boot.java.links.VSCodeSourceLinks;
|
||||
import org.springframework.ide.vscode.boot.java.utils.CompilationUnitCache;
|
||||
import org.springframework.ide.vscode.boot.java.utils.ORCompilationUnitCache;
|
||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||
import org.springframework.ide.vscode.commons.javadoc.JavaDocProviders;
|
||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||
@@ -61,7 +61,7 @@ public class VSCodeSourceLinksTest {
|
||||
@Test
|
||||
public void testJavaSourceUrl() throws Exception {
|
||||
MavenJavaProject project = mavenProjectsCache.get("empty-boot-15-web-app");
|
||||
Optional<String> url = new VSCodeSourceLinks(new CompilationUnitCache(null, null, null), null).sourceLinkUrlForFQName(project, "com.example.EmptyBoot15WebAppApplication");
|
||||
Optional<String> url = new VSCodeSourceLinks(new ORCompilationUnitCache(null, null, null), null).sourceLinkUrlForFQName(project, "com.example.EmptyBoot15WebAppApplication");
|
||||
assertTrue(url.isPresent());
|
||||
Path projectPath = Paths.get(project.pom().getParent());
|
||||
URI uri = URI.create(url.get());
|
||||
@@ -78,7 +78,7 @@ public class VSCodeSourceLinksTest {
|
||||
@Test
|
||||
public void testClasspathResourceOnTomcatUrl() throws Exception {
|
||||
MavenJavaProject project = mavenProjectsCache.get("empty-boot-15-web-app");
|
||||
Optional<String> url = new VSCodeSourceLinks(new CompilationUnitCache(null, null, null), new JavaProjectFinder() {
|
||||
Optional<String> url = new VSCodeSourceLinks(new ORCompilationUnitCache(null, null, null), new JavaProjectFinder() {
|
||||
|
||||
@Override
|
||||
public Optional<IJavaProject> find(TextDocumentIdentifier doc) {
|
||||
@@ -107,7 +107,7 @@ public class VSCodeSourceLinksTest {
|
||||
@Test
|
||||
public void testJarUrl() throws Exception {
|
||||
MavenJavaProject project = mavenProjectsCache.get("empty-boot-15-web-app");
|
||||
Optional<String> url = new VSCodeSourceLinks(new CompilationUnitCache(null, null, null), null).sourceLinkUrlForFQName(project, "org.springframework.boot.autoconfigure.SpringBootApplication");
|
||||
Optional<String> url = new VSCodeSourceLinks(new ORCompilationUnitCache(null, null, null), null).sourceLinkUrlForFQName(project, "org.springframework.boot.autoconfigure.SpringBootApplication");
|
||||
assertTrue(url.isPresent());
|
||||
String headerPart = url.get().substring(0, url.get().indexOf('?'));
|
||||
assertEquals("jdt://contents/spring-boot-autoconfigure-1.5.8.RELEASE.jar/org.springframework.boot.autoconfigure/SpringBootApplication.class", headerPart);
|
||||
@@ -118,7 +118,7 @@ public class VSCodeSourceLinksTest {
|
||||
@Test
|
||||
public void testJarUrlInnerType() throws Exception {
|
||||
MavenJavaProject project = mavenProjectsCache.get("empty-boot-15-web-app");
|
||||
Optional<String> url = new VSCodeSourceLinks(new CompilationUnitCache(null, null, null), null).sourceLinkUrlForFQName(project, "org.springframework.web.client.RestTemplate$AcceptHeaderRequestCallback");
|
||||
Optional<String> url = new VSCodeSourceLinks(new ORCompilationUnitCache(null, null, null), null).sourceLinkUrlForFQName(project, "org.springframework.web.client.RestTemplate$AcceptHeaderRequestCallback");
|
||||
assertTrue(url.isPresent());
|
||||
String headerPart = url.get().substring(0, url.get().indexOf('?'));
|
||||
assertEquals("jdt://contents/spring-web-4.3.12.RELEASE.jar/org.springframework.web.client/RestTemplate$AcceptHeaderRequestCallback.class", headerPart);
|
||||
|
||||
Reference in New Issue
Block a user