diff --git a/headless-services/spring-boot-language-server/pom.xml b/headless-services/spring-boot-language-server/pom.xml
index b13b7399b..298c81400 100644
--- a/headless-services/spring-boot-language-server/pom.xml
+++ b/headless-services/spring-boot-language-server/pom.xml
@@ -109,6 +109,11 @@
rewrite-java
${rewrite-version}
+
+ org.openrewrite
+ rewrite-java-11
+ ${rewrite-version}
+
commons-io
commons-io
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaCompletionEngineConfigurer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaCompletionEngineConfigurer.java
index b57e497c8..e9d5aa42b 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaCompletionEngineConfigurer.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootJavaCompletionEngineConfigurer.java
@@ -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 providers = new HashMap<>();
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java
index c91867dcb..7f82a279b 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerBootApp.java
@@ -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:
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java
index ffe42d682..9a78e82f2 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/app/BootLanguageServerInitializer.java
@@ -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;
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryCompletionProcessor.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryCompletionProcessor.java
index 7f65893e0..e854d9500 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryCompletionProcessor.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/data/DataRepositoryCompletionProcessor.java
@@ -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 completions) {
}
@Override
- public void provideCompletions(ASTNode node, int offset, IDocument doc, Collection completions) {
- TypeDeclaration type = ASTUtils.findDeclaringType(node);
- DataRepositoryDefinition repo = getDataRepositoryDefinition(type);
+ public void provideCompletions(J node, int offset, IDocument doc, Collection 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 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;
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java
index ce68095c5..384896da0 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/BootJavaCompletionEngine.java
@@ -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 completionProviders;
private JavaSnippetManager snippets;
- private CompilationUnitCache cuCache;
+ private ORCompilationUnitCache cuCache;
- public BootJavaCompletionEngine(CompilationUnitCache cuCache, Map specificProviders, JavaSnippetManager snippets) {
+ public BootJavaCompletionEngine(ORCompilationUnitCache cuCache, Map specificProviders, JavaSnippetManager snippets) {
this.cuCache = cuCache;
this.completionProviders = specificProviders;
this.snippets = snippets;
@@ -49,7 +50,7 @@ public class BootJavaCompletionEngine implements ICompletionEngine, LanguageSpec
public Collection 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 completions = new ArrayList();
@@ -64,30 +65,25 @@ public class BootJavaCompletionEngine implements ICompletionEngine, LanguageSpec
});
}
- private void collectCompletionsForAnnotations(ASTNode node, int offset, IDocument doc, Collection completions) {
- Annotation annotation = null;
- ASTNode exactNode = node;
+ private void collectCompletionsForAnnotations(J node, int offset, IDocument doc, Collection 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 completions) {
+ private void collectCompletions(J node, int offset, TextDocument document, Collection completions) {
if (node != null) {
for (CompletionProvider completionProvider : this.completionProviders.values()) {
completionProvider.provideCompletions(node, offset, document, completions);
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/CompletionProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/CompletionProvider.java
index 405cb7661..47c4d585a 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/CompletionProvider.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/handlers/CompletionProvider.java
@@ -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 completions);
- void provideCompletions(ASTNode node, int offset, IDocument doc, Collection completions);
+ void provideCompletions(J node, Annotation annotation, int offset, IDocument doc, Collection completions);
+ void provideCompletions(J node, int offset, IDocument doc, Collection completions);
}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AbstractSourceLinks.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AbstractSourceLinks.java
index 3b80f100a..5194727d6 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AbstractSourceLinks.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AbstractSourceLinks.java
@@ -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 findTypeRegion(CompilationUnit cu, String fqName) {
if (cu == null) {
return null;
}
int[] values = new int[] {0, -1};
+ AtomicReference 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);
}
}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AtomSourceLinks.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AtomSourceLinks.java
index bdb8b2df7..759246904 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AtomSourceLinks.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/AtomSourceLinks.java
@@ -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 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);
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/DefaultJavaElementLocationProvider.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/DefaultJavaElementLocationProvider.java
index 0cb5c7d1f..bb7927fea 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/DefaultJavaElementLocationProvider.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/DefaultJavaElementLocationProvider.java
@@ -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) {
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/VSCodeSourceLinks.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/VSCodeSourceLinks.java
index 2ee3e156f..9451b38bb 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/VSCodeSourceLinks.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/links/VSCodeSourceLinks.java
@@ -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 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);
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/ScopeCompletionProcessor.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/ScopeCompletionProcessor.java
index 80d5ffcc9..1d87131fb 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/ScopeCompletionProcessor.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/scope/ScopeCompletionProcessor.java
@@ -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 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 completions) {
+ public void provideCompletions(J node, int offset, IDocument doc, Collection completions) {
}
}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippet.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippet.java
index 4e5326c50..fb90cce3e 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippet.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippet.java
@@ -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 generateCompletion(Supplier snippetBuilderFactory,
- DocumentRegion query, ASTNode node, CompilationUnit cu) {
+ DocumentRegion query, J node, CompilationUnit cu) {
if (context.appliesTo(node)) {
return Optional.of(
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetCompletion.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetCompletion.java
index 9b018a425..2d4fbccf3 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetCompletion.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetCompletion.java
@@ -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 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 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;
+ }
+
}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetContext.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetContext.java
index ee3a471da..4e683c7bb 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetContext.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetContext.java
@@ -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);
}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetManager.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetManager.java
index 0f0a22d75..57e744cdf 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetManager.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/snippets/JavaSnippetManager.java
@@ -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 completions) {
+ public void getCompletions(IDocument doc, int offset, J node, CompilationUnit cu, Collection completions) {
DocumentRegion query = PREFIX_FINDER.getPrefixRegion(doc, offset);
for (JavaSnippet javaSnippet : snippets) {
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/JGitUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/JGitUtils.java
new file mode 100644
index 000000000..189d918ae
--- /dev/null
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/JGitUtils.java
@@ -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;
+ }
+
+}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ORAstUtils.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ORAstUtils.java
new file mode 100644
index 000000000..c5b777978
--- /dev/null
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ORAstUtils.java
@@ -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 ancesters = List.of();
+
+ public AncestersMarker(List ancesters) {
+ this.uuid = Tree.randomId();
+ this.ancesters = ancesters;
+ }
+
+ @Override
+ public UUID getId() {
+ return uuid;
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getFirstAnsector(Class 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 f = new AtomicReference<>();
+ new JavaIsoVisitor>() {
+ public J visit(Tree tree, AtomicReference 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 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 findNode(J node, Class 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();
+ }
+
+}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ORCompilationUnitCache.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ORCompilationUnitCache.java
index 28e7fbfad..a9e28cb9f 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ORCompilationUnitCache.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/utils/ORCompilationUnitCache.java
@@ -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 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 getClasspathEntries(IJavaProject project) throws Exception {
if (project == null) {
- return new String[0];
+ return Collections.emptySet();
} else {
IClasspath classpath = project.getClasspath();
Stream 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 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 withCompilationUnit(TextDocument document, Function requestor) {
+ IJavaProject project = this.projectFinder != null ? projectFinder.find(document.getId()).orElse(null) : null;
+
+ URI uri = URI.create(document.getUri());
+ return withCompilationUnit(project, uri, requestor);
+ }
+
}
diff --git a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java
index e882cc476..7837f35de 100644
--- a/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java
+++ b/headless-services/spring-boot-language-server/src/main/java/org/springframework/ide/vscode/boot/java/value/ValueCompletionProcessor.java
@@ -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 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 completions) {
+ public void provideCompletions(J node, int offset, IDocument doc, Collection completions) {
}
private void computeProposalsForSimpleName(ASTNode node, Collection completions, int offset,
@@ -130,14 +132,17 @@ public class ValueCompletionProcessor implements CompletionProvider {
}
}
- private void computeProposalsForStringLiteral(ASTNode node, Collection completions, int offset,
+ private void computeProposalsForStringLiteral(J node, Collection 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> matches = findMatches(prefix, doc);
diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/SourceLinksTestConf.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/SourceLinksTestConf.java
index 2ce2ec818..7c41ac168 100644
--- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/SourceLinksTestConf.java
+++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/bootiful/SourceLinksTestConf.java
@@ -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);
}
diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/scope/test/ScopeCompletionTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/scope/test/ScopeCompletionTest.java
index a2fa69576..eaaf24f36 100644
--- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/scope/test/ScopeCompletionTest.java
+++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/scope/test/ScopeCompletionTest.java
@@ -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(\"<*>\")");
diff --git a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java
index a5bc15346..bf22d4f17 100644
--- a/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java
+++ b/headless-services/spring-boot-language-server/src/test/java/org/springframework/ide/vscode/boot/java/utils/test/VSCodeSourceLinksTest.java
@@ -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 url = new VSCodeSourceLinks(new CompilationUnitCache(null, null, null), null).sourceLinkUrlForFQName(project, "com.example.EmptyBoot15WebAppApplication");
+ Optional 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 url = new VSCodeSourceLinks(new CompilationUnitCache(null, null, null), new JavaProjectFinder() {
+ Optional url = new VSCodeSourceLinks(new ORCompilationUnitCache(null, null, null), new JavaProjectFinder() {
@Override
public Optional 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 url = new VSCodeSourceLinks(new CompilationUnitCache(null, null, null), null).sourceLinkUrlForFQName(project, "org.springframework.boot.autoconfigure.SpringBootApplication");
+ Optional 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 url = new VSCodeSourceLinks(new CompilationUnitCache(null, null, null), null).sourceLinkUrlForFQName(project, "org.springframework.web.client.RestTemplate$AcceptHeaderRequestCallback");
+ Optional 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);