diff --git a/microprofile.jdt/com.redhat.microprofile.jdt.core/plugin.xml b/microprofile.jdt/com.redhat.microprofile.jdt.core/plugin.xml index 8fd6abf9c..11258945e 100644 --- a/microprofile.jdt/com.redhat.microprofile.jdt.core/plugin.xml +++ b/microprofile.jdt/com.redhat.microprofile.jdt.core/plugin.xml @@ -25,6 +25,7 @@ + diff --git a/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/commons/JavaKind.java b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/commons/JavaKind.java new file mode 100644 index 000000000..713cc32d5 --- /dev/null +++ b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/commons/JavaKind.java @@ -0,0 +1,52 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.microprofile.commons; + +/** + * The java element kind. + * + * @author Angelo ZERR + * + */ +public enum JavaKind { + + TYPE(1), METHOD(2), ANNOTATION(3); + + private final int value; + + JavaKind(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static JavaKind forValue(int value) { + JavaKind[] allValues = JavaKind.values(); + if (value < 1 || value > allValues.length) + throw new IllegalArgumentException("Illegal enum value: " + value); + return allValues[value - 1]; + } + + public static JavaKind getScope(String scope) { + if (scope != null) { + scope = scope.toUpperCase(); + try { + return JavaKind.valueOf(scope); + } catch (Exception e) { + // Do nothing + } + } + return TYPE; + } +} diff --git a/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/commons/JavaSnippetContext.java b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/commons/JavaSnippetContext.java new file mode 100644 index 000000000..7b419e774 --- /dev/null +++ b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/commons/JavaSnippetContext.java @@ -0,0 +1,28 @@ +package com.redhat.microprofile.commons; + +import java.util.List; + +public class JavaSnippetContext { + + private JavaKind kind; + + private List type; + + public JavaKind getKind() { + return kind; + } + + public void setKind(JavaKind kind) { + this.kind = kind; + } + + public List getType() { + return type; + } + + public void setType(List type) { + this.type = type; + } + + +} diff --git a/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/commons/MicroProfileJavaCompletionParams.java b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/commons/MicroProfileJavaCompletionParams.java new file mode 100644 index 000000000..f3aca718c --- /dev/null +++ b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/commons/MicroProfileJavaCompletionParams.java @@ -0,0 +1,83 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.microprofile.commons; + +import java.util.List; + +import org.eclipse.lsp4j.Position; + +/** + * MicroProfile Java completion parameters. + * + * @author Angelo ZERR + * + */ +public class MicroProfileJavaCompletionParams { + + private String uri; + private Position position; + private List contexts; + + public MicroProfileJavaCompletionParams() { + + } + + public MicroProfileJavaCompletionParams(String uri, Position position) { + this(); + setUri(uri); + setPosition(position); + } + + /** + * Returns the java file uri. + * + * @return the java file uri. + */ + public String getUri() { + return uri; + } + + /** + * Set the java file uri. + * + * @param uri the java file uri. + */ + public void setUri(String uri) { + this.uri = uri; + } + + /** + * Returns the completion position + * + * @return the completion position + */ + public Position getPosition() { + return position; + } + + /** + * Sets the completion position + * + * @param position the completion position + */ + public void setPosition(Position position) { + this.position = position; + } + + public List getContexts() { + return contexts; + } + + public void setContexts(List contexts) { + this.contexts = contexts; + } +} diff --git a/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/commons/MicroProfileJavaCompletionResult.java b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/commons/MicroProfileJavaCompletionResult.java new file mode 100644 index 000000000..eef7ed377 --- /dev/null +++ b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/commons/MicroProfileJavaCompletionResult.java @@ -0,0 +1,33 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.microprofile.commons; + +import java.util.List; + +/** + * MicroProfile Java completion result. + * + * @author Angelo ZERR + * + */ +public class MicroProfileJavaCompletionResult { + + private List resolvedContexts; + + public List getResolvedContexts() { + return resolvedContexts; + } + + public void setResolvedContexts(List resolvedContexts) { + this.resolvedContexts = resolvedContexts; + } +} diff --git a/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/core/PropertiesManagerForJava.java b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/core/PropertiesManagerForJava.java index a0bfa1d37..1c5d906d9 100644 --- a/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/core/PropertiesManagerForJava.java +++ b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/core/PropertiesManagerForJava.java @@ -22,16 +22,16 @@ import org.eclipse.jdt.core.JavaModelException; import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeLens; -import org.eclipse.lsp4j.Command; import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.PublishDiagnosticsParams; -import org.eclipse.lsp4j.jsonrpc.messages.Either; import com.redhat.microprofile.commons.DocumentFormat; import com.redhat.microprofile.commons.MicroProfileJavaCodeActionParams; import com.redhat.microprofile.commons.MicroProfileJavaCodeLensParams; +import com.redhat.microprofile.commons.MicroProfileJavaCompletionParams; +import com.redhat.microprofile.commons.MicroProfileJavaCompletionResult; import com.redhat.microprofile.commons.MicroProfileJavaDiagnosticsParams; import com.redhat.microprofile.commons.MicroProfileJavaHoverParams; import com.redhat.microprofile.jdt.core.java.codelens.JavaCodeLensContext; @@ -41,6 +41,7 @@ import com.redhat.microprofile.jdt.internal.core.java.JavaFeaturesRegistry; import com.redhat.microprofile.jdt.internal.core.java.codeaction.CodeActionHandler; import com.redhat.microprofile.jdt.internal.core.java.codelens.JavaCodeLensDefinition; +import com.redhat.microprofile.jdt.internal.core.java.completion.CompletionHandler; import com.redhat.microprofile.jdt.internal.core.java.diagnostics.JavaDiagnosticsDefinition; import com.redhat.microprofile.jdt.internal.core.java.hover.JavaHoverDefinition; @@ -60,8 +61,11 @@ public static PropertiesManagerForJava getInstance() { private final CodeActionHandler codeActionHandler; + private final CompletionHandler completionHandler; + private PropertiesManagerForJava() { this.codeActionHandler = new CodeActionHandler(); + this.completionHandler = new CompletionHandler(); } /** @@ -124,6 +128,20 @@ private void collectCodeLens(String uri, ITypeRoot typeRoot, IJDTUtils utils, Mi definitions.forEach(definition -> definition.endCodeLens(context, monitor)); } + /** + * Returns completion result for the given uri and position. + * + * @param params the completion parameters + * @param utils the utilities class + * @param monitor the monitor + * @return completion result for the given uri and position. + * @throws JavaModelException + */ + public MicroProfileJavaCompletionResult completion(MicroProfileJavaCompletionParams params, IJDTUtils utils, + IProgressMonitor monitor) throws JavaModelException { + return completionHandler.completion(params, utils, monitor); + } + /** * Returns diagnostics for the given uris list. * diff --git a/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/internal/core/java/completion/CompletionHandler.java b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/internal/core/java/completion/CompletionHandler.java new file mode 100644 index 000000000..a42135ed1 --- /dev/null +++ b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/internal/core/java/completion/CompletionHandler.java @@ -0,0 +1,236 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.microprofile.jdt.internal.core.java.completion; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IAnnotation; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IMemberValuePair; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.NodeFinder; +import org.eclipse.jdt.core.manipulation.CoreASTProvider; +import org.eclipse.jdt.internal.codeassist.complete.CompletionOnMarkerAnnotationName; +import org.eclipse.jdt.internal.compiler.ast.ASTNode; + +import com.redhat.microprofile.commons.JavaKind; +import com.redhat.microprofile.commons.JavaSnippetContext; +import com.redhat.microprofile.commons.MicroProfileJavaCompletionParams; +import com.redhat.microprofile.commons.MicroProfileJavaCompletionResult; +import com.redhat.microprofile.jdt.core.utils.AnnotationUtils; +import com.redhat.microprofile.jdt.core.utils.IJDTUtils; +import com.redhat.microprofile.jdt.core.utils.JDTTypeUtils; +import com.redhat.microprofile.jdt.internal.core.java.completion.JavaScopeFinder.JavaScope; + +/** + * Java completion handler. + * + * @author Angelo ZERR + * + */ +public class CompletionHandler { + + /** + * Returns completion result for the given uri and position. + * + * @param params the completion parameters + * @param utils the utilities class + * @param monitor the monitor + * @return completion result for the given uri and position. + * @throws JavaModelException + */ + public MicroProfileJavaCompletionResult completion(MicroProfileJavaCompletionParams params, IJDTUtils utils, + IProgressMonitor monitor) throws JavaModelException { + MicroProfileJavaCompletionResult result = new MicroProfileJavaCompletionResult(); + + String uri = params.getUri(); + ICompilationUnit unit = utils.resolveCompilationUnit(uri); + if (unit == null) { + return result; + } + + IJavaProject javaProject = unit.getJavaProject(); + int offset = utils.toOffset(unit.getBuffer(), params.getPosition().getLine(), + params.getPosition().getCharacter()); + JavaScope scope = getJavaScope(unit, offset, monitor); + + List resolvedContexts = new ArrayList<>(); + result.setResolvedContexts(resolvedContexts); + for (JavaSnippetContext context : params.getContexts()) { + resolvedContexts.add(resolve(context, scope, javaProject)); + } + + /* + * JavaScope scope = getJavaScope(unit, offset, monitor); + * result.setScope(scope); + * + * if (params.isCollectProjectDependencies()) { List projectDependencies + * = Stream.of(((JavaProject) javaProject).getResolvedClasspath()) .map(entry -> + * getExtensionName(entry.getPath().toString())).filter(Objects::nonNull). + * collect(Collectors.toList()); + * result.setProjectDependencies(projectDependencies); } + */ + /* + * CompletionProposalRequestor collector = new CompletionProposalRequestor(unit, + * offset); unit.codeComplete(offset, collector); + * + * CompletionContext completionContext = collector.getContext(); if + * (completionContext instanceof InternalCompletionContext) { ASTNode node = + * ((InternalCompletionContext) completionContext).getCompletionNode(); + * result.setScope(getJavaScope(node, unit, completionContext.getTokenStart(), + * offset, monitor)); } + */ + return result; + } + + private boolean resolve(JavaSnippetContext context, JavaScope scope, IJavaProject javaProject) { + if (context == null) { + return true; + } + if (context.getKind() == JavaKind.ANNOTATION) { + String type = context.getType().get(0); + IType annotationType = JDTTypeUtils.findType(javaProject, type); + if (annotationType != null) { + try { + IAnnotation target = AnnotationUtils.getAnnotation(annotationType, "java.lang.annotation.Target"); + if (target != null) { + List all = null; + IMemberValuePair pair = target.getMemberValuePairs()[0]; + if (pair.getValue() instanceof String) { + all = Arrays.asList(getName(((String) pair.getValue()))); + } else if (pair.getValue() instanceof String[]) { + all = Stream.of((String[]) pair.getValue()).map(v -> getName(v)) + .collect(Collectors.toList()); + } + if (all.isEmpty()) { + return true; + } + switch (scope) { + case TYPE: + return all.contains("TYPE"); + case FIELD: + return all.contains("FIELD"); + case METHOD: + return all.contains("METHOD"); + case PARAMETER: + return all.contains("PARAMETER"); + default: + return true; + } + + } + } catch (JavaModelException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + return false; + + } + if (context.getType() != null) { + for (String type : context.getType()) { + if (JDTTypeUtils.findType(javaProject, type) == null) { + return false; + } + } + } + return true; + } + + private static String getName(String v) { + return v.substring(v.lastIndexOf('.') + 1); + } + + private static String getExtensionName(String location) { + if (location == null) { + return null; + } + if (!location.endsWith(".jar")) { + return null; + } + int start = location.lastIndexOf('/'); + if (start == -1) { + return null; + } + start++; + int end = location.lastIndexOf('-'); + if (end == -1) { + end = location.lastIndexOf('.'); + } + if (end < start) { + return null; + } + String extensionName = location.substring(start, end); + if (extensionName.endsWith("-deployment")) { + extensionName = extensionName.substring(0, extensionName.length() - "-deployment".length()); + } + return extensionName; + } + + private static JavaScope getJavaScope(ICompilationUnit unit, int start, IProgressMonitor monitor) { + CompilationUnit ast = CoreASTProvider.getInstance().getAST(unit, CoreASTProvider.WAIT_YES, monitor); + JavaScopeFinder finder = new JavaScopeFinder(ast, start); + return finder.getScope(); + } + + private static JavaScope getOLDJavaScope(ASTNode node, ICompilationUnit unit, int start, int end, + IProgressMonitor monitor) { + CompilationUnit ast = CoreASTProvider.getInstance().getAST(unit, CoreASTProvider.WAIT_YES, monitor); + JavaScopeFinder finder = new JavaScopeFinder(ast, start); + JavaScope scope = finder.getScope(); + if (true) { + return scope; + } + if (node instanceof CompletionOnMarkerAnnotationName) { + + NodeFinder nodeFinder = new NodeFinder(ast, start, 100); + org.eclipse.jdt.core.dom.ASTNode coveredNode = nodeFinder.getCoveredNode(); + org.eclipse.jdt.core.dom.ASTNode coveringNode = nodeFinder.getCoveringNode(); + // nodeFinder.getCoveredNode() + if (coveredNode != null) { + + } +// if (coveringNode != null) { +// switch (coveringNode.getNodeType()) { +// case org.eclipse.jdt.core.dom.ASTNode.COMPILATION_UNIT: +// return JavaKind.TYPE; +// case org.eclipse.jdt.core.dom.ASTNode.TYPE_DECLARATION: +// return JavaKind.METHOD; +// case org.eclipse.jdt.core.dom.ASTNode.METHOD_DECLARATION: +// return JavaKind.PARAMETER; +// } +// } + /* + * CompletionOnMarkerAnnotationName marker = (CompletionOnMarkerAnnotationName) + * node; if ((node.bits & ASTNode.T_JavaLangAnnotationTarget) != 0) { // marker. + * } + * + * TypeReference typeReference = marker.type; Binding recipent = + * marker.resolvedType; if (recipent != null) { int kind = recipent.kind(); + * switch (kind) { case Binding.FIELD: return JavaScope.FIELD; case + * Binding.METHOD: return JavaScope.METHOD; case Binding.TYPE_PARAMETER: return + * JavaScope.PARAMETER; case Binding.TYPE: return JavaScope.TYPE; default: + * break; } } + */ + } + return JavaScope.UNKNOWN; + } + +} diff --git a/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/internal/core/java/completion/CompletionProposalRequestor.java b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/internal/core/java/completion/CompletionProposalRequestor.java new file mode 100644 index 000000000..2f92274a8 --- /dev/null +++ b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/internal/core/java/completion/CompletionProposalRequestor.java @@ -0,0 +1,46 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.microprofile.jdt.internal.core.java.completion; + +import org.eclipse.jdt.core.CompletionContext; +import org.eclipse.jdt.core.CompletionProposal; +import org.eclipse.jdt.core.CompletionRequestor; +import org.eclipse.jdt.core.ICompilationUnit; + +/** + * JDT completion request to get the JDT completion context. + * + * @author Angelo ZERR + * + */ +public class CompletionProposalRequestor extends CompletionRequestor { + + private CompletionContext context; + + public void acceptContext(CompletionContext context) { + super.acceptContext(context); + this.context = context; + } + + public CompletionProposalRequestor(ICompilationUnit unit, int offset) { + super.setRequireExtendedContext(true); + } + + @Override + public void accept(CompletionProposal proposal) { + + } + + public CompletionContext getContext() { + return context; + } +} diff --git a/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/internal/core/java/completion/JavaScopeFinder.java b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/internal/core/java/completion/JavaScopeFinder.java new file mode 100644 index 000000000..4af3a0d26 --- /dev/null +++ b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/internal/core/java/completion/JavaScopeFinder.java @@ -0,0 +1,109 @@ +package com.redhat.microprofile.jdt.internal.core.java.completion; + +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.ASTVisitor; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.Block; +import org.eclipse.jdt.core.dom.FieldDeclaration; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.Modifier; +import org.eclipse.jdt.core.dom.SimpleName; +import org.eclipse.jdt.core.dom.TypeDeclaration; + +public class JavaScopeFinder { + + static enum JavaScope { + UNKNOWN, TYPE, METHOD, FIELD, PARAMETER + + } + + private static class JavaScopeFinderVisitor extends ASTVisitor { + + private JavaScope scope; + private int fStart; + private ASTNode fCoveringNode; + private ASTNode fCoveredNode; + + JavaScopeFinderVisitor(int offset) { + super(true); // include Javadoc tags + this.fStart = offset; + } + + @Override + public boolean preVisit2(ASTNode node) { + int nodeStart = node.getStartPosition(); + int nodeEnd = nodeStart + node.getLength(); + if (nodeEnd < this.fStart /* || this.fEnd < nodeStart */) { + return false; + } + if (nodeStart <= this.fStart /* && this.fEnd <= nodeEnd */) { + this.fCoveringNode = node; + } + if (this.fStart <= nodeStart /* && nodeEnd <= this.fEnd */) { + if (this.fCoveringNode == node) { // nodeStart == fStart && nodeEnd == fEnd + this.fCoveredNode = node; + return true; // look further for node with same length as parent + } else if (this.fCoveredNode == null) { // no better found + this.fCoveredNode = node; + } + return false; + } + return true; + } + + public JavaScope getScope() { + return scope; + } + + } + + private JavaScope scope; + + /** + * Instantiate a new node finder using the given root node, the given start and + * the given length. + * + * @param root the given root node + * @param start the given start + * @param length the given length + */ + public JavaScopeFinder(ASTNode root, int start) { + JavaScopeFinderVisitor nodeFinderVisitor = new JavaScopeFinderVisitor(start); + root.accept(nodeFinderVisitor); + this.scope = computeScope(nodeFinderVisitor.fCoveredNode); + } + + private static JavaScope computeScope(ASTNode coveredNode) { + if (coveredNode == null) { + return JavaScope.UNKNOWN; + } + if (coveredNode instanceof SimpleName) { + ASTNode parent = coveredNode.getParent(); + if (parent instanceof Annotation) { + parent = parent.getParent(); + return computeScope(parent); + } + return JavaScope.UNKNOWN; + } else if (coveredNode instanceof MethodDeclaration) { + return JavaScope.METHOD; + } else if (coveredNode instanceof TypeDeclaration) { + return JavaScope.TYPE; + } else if (coveredNode instanceof FieldDeclaration) { + return JavaScope.FIELD; + } else if (coveredNode instanceof Block) { + ASTNode parent = coveredNode.getParent(); + if (parent instanceof MethodDeclaration) { + return JavaScope.PARAMETER; + } + } else if (coveredNode instanceof Modifier) { + ASTNode parent = coveredNode.getParent(); + return computeScope(parent); + } + + return JavaScope.UNKNOWN; + } + + public JavaScope getScope() { + return scope; + } +} diff --git a/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/internal/core/ls/MicroProfileDelegateCommandHandlerForJava.java b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/internal/core/ls/MicroProfileDelegateCommandHandlerForJava.java index 4fef3d0ad..dd80942bf 100644 --- a/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/internal/core/ls/MicroProfileDelegateCommandHandlerForJava.java +++ b/microprofile.jdt/com.redhat.microprofile.jdt.core/src/main/java/com/redhat/microprofile/jdt/internal/core/ls/MicroProfileDelegateCommandHandlerForJava.java @@ -12,7 +12,6 @@ package com.redhat.microprofile.jdt.internal.core.ls; import static com.redhat.microprofile.jdt.internal.core.ls.ArgumentUtils.getBoolean; -import static com.redhat.microprofile.jdt.internal.core.ls.ArgumentUtils.getCodeActionContext; import static com.redhat.microprofile.jdt.internal.core.ls.ArgumentUtils.getFirst; import static com.redhat.microprofile.jdt.internal.core.ls.ArgumentUtils.getInt; import static com.redhat.microprofile.jdt.internal.core.ls.ArgumentUtils.getPosition; @@ -21,8 +20,10 @@ import static com.redhat.microprofile.jdt.internal.core.ls.ArgumentUtils.getStringList; import static com.redhat.microprofile.jdt.internal.core.ls.ArgumentUtils.getTextDocumentIdentifier; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; @@ -31,17 +32,20 @@ import org.eclipse.lsp4j.CodeAction; import org.eclipse.lsp4j.CodeActionContext; import org.eclipse.lsp4j.CodeLens; -import org.eclipse.lsp4j.Command; +import org.eclipse.lsp4j.Diagnostic; import org.eclipse.lsp4j.Hover; import org.eclipse.lsp4j.Position; import org.eclipse.lsp4j.PublishDiagnosticsParams; import org.eclipse.lsp4j.Range; import org.eclipse.lsp4j.TextDocumentIdentifier; -import org.eclipse.lsp4j.jsonrpc.messages.Either; import com.redhat.microprofile.commons.DocumentFormat; +import com.redhat.microprofile.commons.JavaKind; +import com.redhat.microprofile.commons.JavaSnippetContext; import com.redhat.microprofile.commons.MicroProfileJavaCodeActionParams; import com.redhat.microprofile.commons.MicroProfileJavaCodeLensParams; +import com.redhat.microprofile.commons.MicroProfileJavaCompletionParams; +import com.redhat.microprofile.commons.MicroProfileJavaCompletionResult; import com.redhat.microprofile.commons.MicroProfileJavaDiagnosticsParams; import com.redhat.microprofile.commons.MicroProfileJavaHoverParams; import com.redhat.microprofile.jdt.core.PropertiesManagerForJava; @@ -56,6 +60,8 @@ public class MicroProfileDelegateCommandHandlerForJava implements IDelegateComma private static final String JAVA_CODEACTION_COMMAND_ID = "microprofile/java/codeAction"; private static final String JAVA_CODELENS_COMMAND_ID = "microprofile/java/codeLens"; + private static final String JAVA_COMPLETION_COMMAND_ID = "microprofile/java/completion"; + private static final String JAVA_DIAGNOSTICS_COMMAND_ID = "microprofile/java/diagnostics"; private static final String JAVA_HOVER_COMMAND_ID = "microprofile/java/hover"; @@ -69,6 +75,8 @@ public Object executeCommand(String commandId, List arguments, IProgress return getCodeActionForJava(arguments, commandId, progress); case JAVA_CODELENS_COMMAND_ID: return getCodeLensForJava(arguments, commandId, progress); + case JAVA_COMPLETION_COMMAND_ID: + return getCompletionForJava(arguments, commandId, progress); case JAVA_DIAGNOSTICS_COMMAND_ID: return getDiagnosticsForJava(arguments, commandId, progress); case JAVA_HOVER_COMMAND_ID: @@ -173,6 +181,89 @@ private static MicroProfileJavaCodeLensParams createMicroProfileJavaCodeLensPara return params; } + /** + * Returns the Java completion result for the given Java file. + * + * @param arguments + * @param commandId + * @param monitor + * @return the Java completion result for the given Java file. + * @throws CoreException + * @throws JavaModelException + */ + private static MicroProfileJavaCompletionResult getCompletionForJava(List arguments, String commandId, + IProgressMonitor monitor) throws JavaModelException, CoreException { + // Create java code lens parameter + MicroProfileJavaCompletionParams params = createMicroProfileJavaCompletionParams(arguments, commandId); + // Return code lenses from the lens parameter + return PropertiesManagerForJava.getInstance().completion(params, JDTUtilsLSImpl.getInstance(), monitor); + } + + /** + * Create java completion parameter from the given arguments map. + * + * @param arguments + * @param commandId + * + * @return java code lens parameter + */ + private static MicroProfileJavaCompletionParams createMicroProfileJavaCompletionParams(List arguments, + String commandId) { + Map obj = getFirst(arguments); + if (obj == null) { + throw new UnsupportedOperationException(String.format( + "Command '%s' must be call with one MicroProfileJavaCompletionParams argument!", commandId)); + } + String javaFileUri = getString(obj, "uri"); + if (javaFileUri == null) { + throw new UnsupportedOperationException(String.format( + "Command '%s' must be call with required MicroProfileJavaCompletionParams.uri (java URI)!", + commandId)); + } + Position completionPosition = getPosition(obj, "position"); + MicroProfileJavaCompletionParams params = new MicroProfileJavaCompletionParams(javaFileUri, completionPosition); + params.setContexts(getJavaCompletionContexts(obj)); + return params; + } + + private static List getJavaCompletionContexts(Map obj) { + List> contextsObj = (List>) obj.get("contexts"); + if (contextsObj == null) { + return Collections.emptyList(); + } + return contextsObj.stream().map(contextObj -> { + JavaSnippetContext context = new JavaSnippetContext(); + JavaKind kind = JavaKind.TYPE; + if (contextObj != null) { + Number kindIndex = (Number) contextObj.get("kind"); + if (kindIndex != null) { + kind = JavaKind.forValue(kindIndex.intValue()); + } + context.setType((List) contextObj.get("type")); + } + context.setKind(kind); + return context; + }).collect(Collectors.toList()); + } + + public static CodeActionContext getCodeActionContext(Map obj, String key) { + Map contextObj = (Map) obj.get(key); + if (contextObj == null) { + return null; + } + List> diagnosticsObj = (List>) contextObj.get("diagnostics"); + List diagnostics = diagnosticsObj.stream().map(diagnosticObj -> { + Diagnostic diagnostic = new Diagnostic(); + diagnostic.setRange(getRange(diagnosticObj, "range")); + diagnostic.setCode(getString(diagnosticObj, "code")); + diagnostic.setMessage(getString(diagnosticObj, "message")); + diagnostic.setSource(getString(diagnosticObj, "source")); + return diagnostic; + }).collect(Collectors.toList()); + List only = null; + return new CodeActionContext(diagnostics, only); + } + /** * * @param arguments diff --git a/microprofile.ls/README.md b/microprofile.ls/README.md index 2c8bcb5a0..c798cb33a 100644 --- a/microprofile.ls/README.md +++ b/microprofile.ls/README.md @@ -62,7 +62,7 @@ Java and properties completion snippets are managed by the MicroProfile LS (snip The `"context"` key can be provided to specify the condition in which the snippet should appear: - * for `properties files`: the context/extension can be declared in the snippet to show the snippet only if the extension belongs to the project. + * for `properties files`: the context/dependency can be declared in the snippet to show the snippet only if the extension belongs to the project. ```json "Add datasource properties": { @@ -71,7 +71,7 @@ The `"context"` key can be provided to specify the condition in which the snippe ... ], "context": { - "extension": "quarkus-agroal" + "dependency": "quarkus-agroal" } } ``` diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/commons/JavaKind.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/commons/JavaKind.java new file mode 100644 index 000000000..713cc32d5 --- /dev/null +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/commons/JavaKind.java @@ -0,0 +1,52 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.microprofile.commons; + +/** + * The java element kind. + * + * @author Angelo ZERR + * + */ +public enum JavaKind { + + TYPE(1), METHOD(2), ANNOTATION(3); + + private final int value; + + JavaKind(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public static JavaKind forValue(int value) { + JavaKind[] allValues = JavaKind.values(); + if (value < 1 || value > allValues.length) + throw new IllegalArgumentException("Illegal enum value: " + value); + return allValues[value - 1]; + } + + public static JavaKind getScope(String scope) { + if (scope != null) { + scope = scope.toUpperCase(); + try { + return JavaKind.valueOf(scope); + } catch (Exception e) { + // Do nothing + } + } + return TYPE; + } +} diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/commons/JavaSnippetContext.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/commons/JavaSnippetContext.java new file mode 100644 index 000000000..7b419e774 --- /dev/null +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/commons/JavaSnippetContext.java @@ -0,0 +1,28 @@ +package com.redhat.microprofile.commons; + +import java.util.List; + +public class JavaSnippetContext { + + private JavaKind kind; + + private List type; + + public JavaKind getKind() { + return kind; + } + + public void setKind(JavaKind kind) { + this.kind = kind; + } + + public List getType() { + return type; + } + + public void setType(List type) { + this.type = type; + } + + +} diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/commons/MicroProfileJavaCompletionParams.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/commons/MicroProfileJavaCompletionParams.java new file mode 100644 index 000000000..f3aca718c --- /dev/null +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/commons/MicroProfileJavaCompletionParams.java @@ -0,0 +1,83 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.microprofile.commons; + +import java.util.List; + +import org.eclipse.lsp4j.Position; + +/** + * MicroProfile Java completion parameters. + * + * @author Angelo ZERR + * + */ +public class MicroProfileJavaCompletionParams { + + private String uri; + private Position position; + private List contexts; + + public MicroProfileJavaCompletionParams() { + + } + + public MicroProfileJavaCompletionParams(String uri, Position position) { + this(); + setUri(uri); + setPosition(position); + } + + /** + * Returns the java file uri. + * + * @return the java file uri. + */ + public String getUri() { + return uri; + } + + /** + * Set the java file uri. + * + * @param uri the java file uri. + */ + public void setUri(String uri) { + this.uri = uri; + } + + /** + * Returns the completion position + * + * @return the completion position + */ + public Position getPosition() { + return position; + } + + /** + * Sets the completion position + * + * @param position the completion position + */ + public void setPosition(Position position) { + this.position = position; + } + + public List getContexts() { + return contexts; + } + + public void setContexts(List contexts) { + this.contexts = contexts; + } +} diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/commons/MicroProfileJavaCompletionResult.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/commons/MicroProfileJavaCompletionResult.java new file mode 100644 index 000000000..eef7ed377 --- /dev/null +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/commons/MicroProfileJavaCompletionResult.java @@ -0,0 +1,33 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.microprofile.commons; + +import java.util.List; + +/** + * MicroProfile Java completion result. + * + * @author Angelo ZERR + * + */ +public class MicroProfileJavaCompletionResult { + + private List resolvedContexts; + + public List getResolvedContexts() { + return resolvedContexts; + } + + public void setResolvedContexts(List resolvedContexts) { + this.resolvedContexts = resolvedContexts; + } +} diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/JavaTextDocumentService.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/JavaTextDocumentService.java index ff20be5c5..96244043e 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/JavaTextDocumentService.java +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/JavaTextDocumentService.java @@ -16,6 +16,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -37,22 +38,25 @@ import org.eclipse.lsp4j.HoverParams; import org.eclipse.lsp4j.MarkupKind; import org.eclipse.lsp4j.PublishDiagnosticsParams; -import org.eclipse.lsp4j.jsonrpc.CompletableFutures; import org.eclipse.lsp4j.jsonrpc.messages.Either; import com.redhat.microprofile.commons.DocumentFormat; +import com.redhat.microprofile.commons.JavaSnippetContext; import com.redhat.microprofile.commons.MicroProfileJavaCodeActionParams; import com.redhat.microprofile.commons.MicroProfileJavaCodeLensParams; +import com.redhat.microprofile.commons.MicroProfileJavaCompletionParams; import com.redhat.microprofile.commons.MicroProfileJavaDiagnosticsParams; import com.redhat.microprofile.commons.MicroProfileJavaHoverParams; import com.redhat.microprofile.ls.commons.BadLocationException; import com.redhat.microprofile.ls.commons.TextDocument; import com.redhat.microprofile.ls.commons.TextDocuments; import com.redhat.microprofile.ls.commons.client.CommandKind; +import com.redhat.microprofile.ls.commons.snippets.Snippet; import com.redhat.microprofile.ls.commons.snippets.TextDocumentSnippetRegistry; import com.redhat.microprofile.settings.MicroProfileCodeLensSettings; import com.redhat.microprofile.settings.SharedSettings; import com.redhat.microprofile.snippets.LanguageId; +import com.redhat.microprofile.snippets.SnippetContextForJava; /** * LSP text document service for Java file. @@ -107,25 +111,46 @@ public void didSave(DidSaveTextDocumentParams params) { @Override public CompletableFuture, CompletionList>> completion(CompletionParams params) { - return CompletableFutures.computeAsync(cancel -> { - try { - // Returns java snippets - TextDocument document = documents.get(params.getTextDocument().getUri()); - int completionOffset = document.offsetAt(params.getPosition()); - boolean canSupportMarkdown = true; - CompletionList list = new CompletionList(); - list.setItems(new ArrayList<>()); - getSnippetRegistry().getCompletionItems(document, completionOffset, canSupportMarkdown, context -> { - return true; - }).forEach(item -> { - list.getItems().add(item); + MicroProfileJavaCompletionParams javaParams = new MicroProfileJavaCompletionParams( + params.getTextDocument().getUri(), params.getPosition()); + List snippets = getSnippetRegistry().getSnippets(); + List contexts = snippets.stream().map(snippet -> (JavaSnippetContext) snippet.getContext()) + .collect(Collectors.toList()); + javaParams.setContexts(contexts); + + return microprofileLanguageServer.getLanguageClient().getJavaCompletion(javaParams). // + thenApply(result -> { + try { + List resolved = result.getResolvedContexts(); + + // Returns java snippets + TextDocument document = documents.get(params.getTextDocument().getUri()); + int completionOffset = document.offsetAt(params.getPosition()); + boolean canSupportMarkdown = true; + CompletionList list = new CompletionList(); + list.setItems(new ArrayList<>()); + + AtomicInteger i = new AtomicInteger(); + getSnippetRegistry() + .getCompletionItems(document, completionOffset, canSupportMarkdown, context -> { + int j = i.getAndIncrement(); + if (context == null || resolved == null) { + return true; + } + if (context instanceof SnippetContextForJava) { + return ((SnippetContextForJava) context) + .isMatch(resolved.get(j)); + } + return false; + }).forEach(item -> { + list.getItems().add(item); + }); + return Either.forRight(list); + } catch (BadLocationException e) { + LOGGER.log(Level.SEVERE, "Error while getting java completions", e); + return Either.forRight(null); + } }); - return Either.forRight(list); - } catch (BadLocationException e) { - LOGGER.log(Level.SEVERE, "Error while getting java completions", e); - return Either.forRight(null); - } - }); } @Override diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/api/MicroProfileJavaCodeActionProvider.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/api/MicroProfileJavaCodeActionProvider.java index ffa274c2f..01340afa7 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/api/MicroProfileJavaCodeActionProvider.java +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/api/MicroProfileJavaCodeActionProvider.java @@ -1,14 +1,17 @@ /******************************************************************************* -* Copyright (c) 2019 Red Hat Inc. and others. +* Copyright (c) 2020 Red Hat Inc. and others. * All rights reserved. This program and the accompanying materials * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v20.html * +* SPDX-License-Identifier: EPL-2.0 +* * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ package com.redhat.microprofile.ls.api; +import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -27,7 +30,7 @@ public interface MicroProfileJavaCodeActionProvider { @JsonRequest("microprofile/java/codeAction") default CompletableFuture> getJavaCodeAction(MicroProfileJavaCodeActionParams javaParams) { - return CompletableFuture.completedFuture(null); + return CompletableFuture.completedFuture(Collections.emptyList()); } } diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/api/MicroProfileJavaCompletionProvider.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/api/MicroProfileJavaCompletionProvider.java new file mode 100644 index 000000000..2aac0de29 --- /dev/null +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/api/MicroProfileJavaCompletionProvider.java @@ -0,0 +1,35 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.microprofile.ls.api; + +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; + +import com.redhat.microprofile.commons.MicroProfileJavaCompletionParams; +import com.redhat.microprofile.commons.MicroProfileJavaCompletionResult; + +/** + * MicroProfile Java completion provider. + * + * @author Angelo ZERR + * + */ +public interface MicroProfileJavaCompletionProvider { + + @JsonRequest("microprofile/java/completion") + default CompletableFuture getJavaCompletion( + MicroProfileJavaCompletionParams javaParams) { + return CompletableFuture.completedFuture(null); + } + +} diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/api/MicroProfileLanguageClientAPI.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/api/MicroProfileLanguageClientAPI.java index 1ab130194..9560cc148 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/api/MicroProfileLanguageClientAPI.java +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/api/MicroProfileLanguageClientAPI.java @@ -4,6 +4,8 @@ * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v20.html * +* SPDX-License-Identifier: EPL-2.0 +* * Contributors: * Red Hat Inc. - initial API and implementation *******************************************************************************/ @@ -19,6 +21,6 @@ */ public interface MicroProfileLanguageClientAPI extends LanguageClient, MicroProfileProjectInfoProvider, MicroProfilePropertyDefinitionProvider, MicroProfileJavaCodeActionProvider, MicroProfileJavaCodeLensProvider, - MicroProfileJavaDiagnosticsProvider, MicroProfileJavaHoverProvider { + MicroProfileJavaCompletionProvider, MicroProfileJavaDiagnosticsProvider, MicroProfileJavaHoverProvider { } diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/commons/snippets/SnippetDeserializer.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/commons/snippets/SnippetDeserializer.java index 59a5afb13..280fd2fa0 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/commons/snippets/SnippetDeserializer.java +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/commons/snippets/SnippetDeserializer.java @@ -21,6 +21,7 @@ import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapter; /** * GSON deserializer to build Snippet from vscode JSON snippet. @@ -36,9 +37,9 @@ class SnippetDeserializer implements JsonDeserializer { private static final String BODY_ELT = "body"; private static final String CONTEXT_ELT = "context"; - private final JsonDeserializer> contextDeserializer; + private final TypeAdapter> contextDeserializer; - public SnippetDeserializer(JsonDeserializer> contextDeserializer) { + public SnippetDeserializer(TypeAdapter> contextDeserializer) { this.contextDeserializer = contextDeserializer; } @@ -96,7 +97,7 @@ public Snippet deserialize(JsonElement json, Type typeOfT, JsonDeserializationCo if (contextDeserializer != null) { JsonElement contextElt = snippetObj.get(CONTEXT_ELT); if (contextElt != null) { - ISnippetContext snippetContext = contextDeserializer.deserialize(contextElt, typeOfT, context); + ISnippetContext snippetContext = contextDeserializer.fromJsonTree(contextElt); snippet.setContext(snippetContext); } } diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/commons/snippets/SnippetRegistry.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/commons/snippets/SnippetRegistry.java index 4fee72d18..371b74428 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/commons/snippets/SnippetRegistry.java +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/ls/commons/snippets/SnippetRegistry.java @@ -36,9 +36,9 @@ import org.eclipse.lsp4j.jsonrpc.messages.Either; import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializer; import com.google.gson.JsonIOException; import com.google.gson.JsonSyntaxException; +import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; /** @@ -106,7 +106,7 @@ public void registerSnippets(InputStream in) throws IOException { * context. * @throws IOException */ - public void registerSnippets(InputStream in, JsonDeserializer> contextDeserializer) + public void registerSnippets(InputStream in, TypeAdapter> contextDeserializer) throws IOException { registerSnippets(new InputStreamReader(in, StandardCharsets.UTF_8.name()), contextDeserializer); } @@ -130,7 +130,7 @@ public void registerSnippets(Reader in) throws IOException { * context. * @throws IOException */ - public void registerSnippets(Reader in, JsonDeserializer> contextDeserializer) + public void registerSnippets(Reader in, TypeAdapter> contextDeserializer) throws IOException { JsonReader reader = new JsonReader(in); reader.beginObject(); @@ -146,8 +146,7 @@ public void registerSnippets(Reader in, JsonDeserializer> contextDeserializer) - throws JsonIOException, JsonSyntaxException { + TypeAdapter> contextDeserializer) throws JsonIOException, JsonSyntaxException { GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(Snippet.class, new SnippetDeserializer(contextDeserializer)); return builder.create().fromJson(reader, Snippet.class); diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/services/MicroProfileCompletions.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/services/MicroProfileCompletions.java index 4010938c7..6db35dd5b 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/services/MicroProfileCompletions.java +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/services/MicroProfileCompletions.java @@ -48,7 +48,7 @@ import com.redhat.microprofile.settings.MicroProfileCompletionSettings; import com.redhat.microprofile.settings.MicroProfileFormattingSettings; import com.redhat.microprofile.snippets.LanguageId; -import com.redhat.microprofile.snippets.SnippetContextProperties; +import com.redhat.microprofile.snippets.SnippetContextForProperties; import com.redhat.microprofile.utils.DocumentationUtils; import com.redhat.microprofile.utils.MicroProfilePropertiesUtils; import com.redhat.microprofile.utils.MicroProfilePropertiesUtils.FormattedPropertyResult; @@ -420,8 +420,8 @@ private static void collectSnippetSuggestions(int completionOffset, Node node, P boolean markdownSupported = completionSettings.isDocumentationFormatSupported(MarkupKind.MARKDOWN); snippetRegistry.getCompletionItems(document.getDocument(), completionOffset, markdownSupported, context -> { - if (context instanceof SnippetContextProperties) { - SnippetContextProperties contextProperties = (SnippetContextProperties) context; + if (context instanceof SnippetContextForProperties) { + SnippetContextForProperties contextProperties = (SnippetContextForProperties) context; return contextProperties.isMatch(extensions); } return false; diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/MicroProfileJavaSnippetRegistryLoader.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/MicroProfileJavaSnippetRegistryLoader.java index ca257a785..4f3a2b6da 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/MicroProfileJavaSnippetRegistryLoader.java +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/MicroProfileJavaSnippetRegistryLoader.java @@ -26,8 +26,10 @@ public class MicroProfileJavaSnippetRegistryLoader implements ISnippetRegistryLo @Override public void load(SnippetRegistry registry) throws IOException { - registry.registerSnippets(MicroProfileJavaSnippetRegistryLoader.class.getResourceAsStream("mp-metrics.json")); - registry.registerSnippets(MicroProfileJavaSnippetRegistryLoader.class.getResourceAsStream("mp-openapi.json")); + registry.registerSnippets(MicroProfileJavaSnippetRegistryLoader.class.getResourceAsStream("mp-metrics.json"), + SnippetContextForJava.TYPE_ADAPTER); + registry.registerSnippets(MicroProfileJavaSnippetRegistryLoader.class.getResourceAsStream("mp-openapi.json"), + SnippetContextForJava.TYPE_ADAPTER); } @Override diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/SnippetContextForJava.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/SnippetContextForJava.java new file mode 100644 index 000000000..02d006588 --- /dev/null +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/SnippetContextForJava.java @@ -0,0 +1,92 @@ +/******************************************************************************* +* Copyright (c) 2020 Red Hat Inc. and others. +* All rights reserved. This program and the accompanying materials +* which accompanies this distribution, and is available at +* http://www.eclipse.org/legal/epl-v20.html +* +* SPDX-License-Identifier: EPL-2.0 +* +* Contributors: +* Red Hat Inc. - initial API and implementation +*******************************************************************************/ +package com.redhat.microprofile.snippets; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import com.redhat.microprofile.commons.JavaKind; +import com.redhat.microprofile.commons.JavaSnippetContext; +import com.redhat.microprofile.ls.commons.snippets.ISnippetContext; + +/** + * A snippet context for Java files which matches java scope and dependency. + * + * @author Angelo ZERR + * + */ +public class SnippetContextForJava extends JavaSnippetContext implements ISnippetContext { + + public static final TypeAdapter TYPE_ADAPTER = new SnippetContextForJavaAdapter(); + + public SnippetContextForJava(JavaKind kind, List types) { + super.setKind(kind); + super.setType(types); + } + + @Override + public boolean isMatch(Boolean context) { + if (context == null) { + return true; + } + return context; + } + + private static class SnippetContextForJavaAdapter extends TypeAdapter { + + @Override + public SnippetContextForJava read(final JsonReader in) throws IOException { + JsonToken nextToken = in.peek(); + if (nextToken == JsonToken.NULL) { + return null; + } + + JavaKind kind = JavaKind.TYPE; + List types = new ArrayList<>(); + in.beginObject(); + while (in.hasNext()) { + String name = in.nextName(); + switch (name) { + case "type": + if (in.peek() == JsonToken.BEGIN_ARRAY) { + in.beginArray(); + while (in.peek() != JsonToken.END_ARRAY) { + types.add(in.nextString()); + } + in.endArray(); + } else { + types.add(in.nextString()); + } + break; + case "kind": + kind = JavaKind.getScope(in.nextString()); + break; + default: + in.skipValue(); + } + } + in.endObject(); + return new SnippetContextForJava(kind, types); + } + + @Override + public void write(JsonWriter out, SnippetContextForJava value) throws IOException { + // Do nothing + } + } + +} diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/SnippetContextProperties.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/SnippetContextForProperties.java similarity index 50% rename from microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/SnippetContextProperties.java rename to microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/SnippetContextForProperties.java index d1d3cda19..8c5984c47 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/SnippetContextProperties.java +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/SnippetContextForProperties.java @@ -11,12 +11,17 @@ *******************************************************************************/ package com.redhat.microprofile.snippets; +import java.io.IOException; import java.util.Set; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; import com.redhat.microprofile.ls.commons.snippets.ISnippetContext; /** - * A snippet context properties which matches extension. You can write a vscode + * A snippet context properties which matches dependency. You can write a vscode * snippet that declares a context like this: * * @@ -32,7 +37,7 @@ ], "description": "Configure Quarkus datasource", "context": { - "extension": "quarkus-agroal" + "dependency": "quarkus-agroal" } } @@ -44,17 +49,46 @@ * @author Angelo ZERR * */ -public class SnippetContextProperties implements ISnippetContext> { +public class SnippetContextForProperties implements ISnippetContext> { - private String extension; + public static final TypeAdapter TYPE_ADAPTER = new SnippetContextForPropertiesAdapter(); - public SnippetContextProperties(String extension) { - this.extension = extension; + private String dependency; + + public SnippetContextForProperties(String dependency) { + this.dependency = dependency; } @Override - public boolean isMatch(Set extensions) { - return extensions.contains(extension); + public boolean isMatch(Set dependencys) { + return dependencys.contains(dependency); + } + + private static class SnippetContextForPropertiesAdapter extends TypeAdapter { + public SnippetContextForProperties read(JsonReader in) throws IOException { + if (in.peek() == JsonToken.NULL) { + in.nextNull(); + return null; + } + String dependency = null; + in.beginObject(); + while (in.hasNext()) { + String name = in.nextName(); + switch (name) { + case "dependency": + dependency = in.nextString(); + break; + default: + in.skipValue(); + } + } + in.endObject(); + return new SnippetContextForProperties(dependency); + } + + public void write(JsonWriter writer, SnippetContextForProperties value) throws IOException { + // Do nothing + } } } diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/SnippetContextPropertiesDeserializer.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/SnippetContextPropertiesDeserializer.java deleted file mode 100644 index fe923c324..000000000 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/microprofile/snippets/SnippetContextPropertiesDeserializer.java +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************* -* Copyright (c) 2020 Red Hat Inc. and others. -* All rights reserved. This program and the accompanying materials -* which accompanies this distribution, and is available at -* http://www.eclipse.org/legal/epl-v20.html -* -* SPDX-License-Identifier: EPL-2.0 -* -* Contributors: -* Red Hat Inc. - initial API and implementation -*******************************************************************************/ -package com.redhat.microprofile.snippets; - -import java.lang.reflect.Type; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; - -/** - * GSON snippet context deserializer to create instance of - * {@link SnippetContextProperties} from the JSON declaration: - * - * - * "context": { - "extension": "quarkus-agroal" - } - * - * - * @author Angeko ZERR - * - */ -public class SnippetContextPropertiesDeserializer implements JsonDeserializer { - - private static final String EXTENSION_ELT = "extension"; - - @Override - public SnippetContextProperties deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - json = json.getAsJsonObject().get(EXTENSION_ELT); - if (json != null && json.isJsonPrimitive()) { - String extension = json.getAsString(); - return new SnippetContextProperties(extension); - } - return null; - } - -} diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/quarkus/snippets/QuarkusJavaSnippetRegistryLoader.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/quarkus/snippets/QuarkusJavaSnippetRegistryLoader.java index 28bf07019..dabdfe220 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/quarkus/snippets/QuarkusJavaSnippetRegistryLoader.java +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/quarkus/snippets/QuarkusJavaSnippetRegistryLoader.java @@ -16,10 +16,10 @@ import com.redhat.microprofile.ls.commons.snippets.ISnippetRegistryLoader; import com.redhat.microprofile.ls.commons.snippets.SnippetRegistry; import com.redhat.microprofile.snippets.LanguageId; - +import com.redhat.microprofile.snippets.SnippetContextForJava; /** - * Snippet loader for Quarkus in java files. + * Snippet loader for Quarkus in Java files. * * @author Angelo ZERR * @@ -28,7 +28,8 @@ public class QuarkusJavaSnippetRegistryLoader implements ISnippetRegistryLoader @Override public void load(SnippetRegistry registry) throws IOException { - registry.registerSnippets(QuarkusJavaSnippetRegistryLoader.class.getResourceAsStream("quarkus-java.json")); + registry.registerSnippets(QuarkusJavaSnippetRegistryLoader.class.getResourceAsStream("quarkus-java.json"), + SnippetContextForJava.TYPE_ADAPTER); } @Override diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/quarkus/snippets/QuarkusPropertiesSnippetRegistryLoader.java b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/quarkus/snippets/QuarkusPropertiesSnippetRegistryLoader.java index be415e065..688490e23 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/quarkus/snippets/QuarkusPropertiesSnippetRegistryLoader.java +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/java/com/redhat/quarkus/snippets/QuarkusPropertiesSnippetRegistryLoader.java @@ -14,7 +14,7 @@ import com.redhat.microprofile.ls.commons.snippets.ISnippetRegistryLoader; import com.redhat.microprofile.ls.commons.snippets.SnippetRegistry; import com.redhat.microprofile.snippets.LanguageId; -import com.redhat.microprofile.snippets.SnippetContextPropertiesDeserializer; +import com.redhat.microprofile.snippets.SnippetContextForProperties; /** * Snippet loader for Quarkus in properties files. @@ -28,7 +28,7 @@ public class QuarkusPropertiesSnippetRegistryLoader implements ISnippetRegistryL public void load(SnippetRegistry registry) throws Exception { registry.registerSnippets( QuarkusPropertiesSnippetRegistryLoader.class.getResourceAsStream("quarkus-properties.json"), - new SnippetContextPropertiesDeserializer()); + SnippetContextForProperties.TYPE_ADAPTER); } @Override diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/microprofile/snippets/mp-metrics.json b/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/microprofile/snippets/mp-metrics.json index d72e8bb07..1a7003b96 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/microprofile/snippets/mp-metrics.json +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/microprofile/snippets/mp-metrics.json @@ -10,7 +10,11 @@ "\tdescription = \"${2:description}\"", ")" ], - "description": "An annotation that contains the metadata information when requesting a metric to be injected or produced. This annotation can be used on fields of type Meter, Timer, Counter, and Histogram. For Gauge, the @Metric annotation can only be used on producer methods/fields." + "description": "An annotation that contains the metadata information when requesting a metric to be injected or produced. This annotation can be used on fields of type Meter, Timer, Counter, and Histogram. For Gauge, the @Metric annotation can only be used on producer methods/fields.", + "context": { + "kind": "annotation", + "type": "org.eclipse.microprofile.metrics.annotation.Metric" + } }, "@Counted": { "prefix": [ @@ -46,7 +50,11 @@ "\tdescription = \"${2:description}\"", ")" ], - "description": "Denotes a gauge which counts the parallel invocations of the annotated object." + "description": "Denotes a gauge which counts the parallel invocations of the annotated object.", + "context": { + "target": ["method", "field", "parameter", "annotation_type"], + "dependency": "microprofile-metrics" + } }, "@Metered": { "prefix": [ diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/microprofile/snippets/mp-openapi.json b/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/microprofile/snippets/mp-openapi.json index 1174651d5..c80f46646 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/microprofile/snippets/mp-openapi.json +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/microprofile/snippets/mp-openapi.json @@ -10,7 +10,11 @@ "\tdescription = \"${2:description}\"", ")" ], - "description": "Describes an operation or typically a HTTP method against a specific path." + "description": "Describes an operation or typically a HTTP method against a specific path.", + "context": { + "kind": "annotation", + "type": "org.eclipse.microprofile.openapi.annotations.Operation" + } }, "@Content": { "prefix": [ diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/quarkus/snippets/quarkus-java.json b/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/quarkus/snippets/quarkus-java.json index 8f73466a4..403b09a32 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/quarkus/snippets/quarkus-java.json +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/quarkus/snippets/quarkus-java.json @@ -19,7 +19,11 @@ "\t}", "}" ], - "description": "Quarkus REST resource class" + "description": "Quarkus REST resource class", + "context": { + "kind": "class", + "type": "javax.ws.rs.GET" + } }, "Quarkus - new resource method": { "prefix": "qrm", @@ -30,7 +34,11 @@ "\treturn \"hello\";", "}" ], - "description": "Quarkus REST resource method" + "description": "Quarkus REST resource method", + "context": { + "kind": "method", + "type": "javax.ws.rs.GET" + } }, "Quarkus - new test resource class": { "prefix": "qtrc", @@ -57,7 +65,11 @@ "", "}" ], - "description": "Quarkus test resource class" + "description": "Quarkus test resource class", + "context": { + "kind": "class", + "type": "org.junit.jupiter.api.Test" + } }, "Quarkus - new native test resource class": { "prefix": "qntrc", diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/quarkus/snippets/quarkus-properties.json b/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/quarkus/snippets/quarkus-properties.json index c2a142970..c26b18f5d 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/quarkus/snippets/quarkus-properties.json +++ b/microprofile.ls/com.redhat.microprofile.ls/src/main/resources/com/redhat/quarkus/snippets/quarkus-properties.json @@ -10,7 +10,7 @@ ], "description": "Configure Quarkus datasource", "context": { - "extension": "quarkus-agroal" + "dependency": "quarkus-agroal" } }, "Add Jaeger properties": { @@ -23,7 +23,7 @@ ], "description": "Configure Jaeger tracer", "context": { - "extension": "quarkus-jaeger" + "dependency": "quarkus-jaeger" } } } diff --git a/microprofile.ls/com.redhat.microprofile.ls/src/test/java/com/redhat/microprofile/ls/commons/snippets/SnippetRegistryTest.java b/microprofile.ls/com.redhat.microprofile.ls/src/test/java/com/redhat/microprofile/ls/commons/snippets/SnippetRegistryTest.java index 550588360..80736aaf0 100644 --- a/microprofile.ls/com.redhat.microprofile.ls/src/test/java/com/redhat/microprofile/ls/commons/snippets/SnippetRegistryTest.java +++ b/microprofile.ls/com.redhat.microprofile.ls/src/test/java/com/redhat/microprofile/ls/commons/snippets/SnippetRegistryTest.java @@ -22,6 +22,8 @@ import org.junit.Assert; import org.junit.Test; +import com.redhat.microprofile.snippets.LanguageId; + /** * Test for snippet registry. * @@ -145,4 +147,10 @@ public void prefixCompletion() { assertCompletion("m |", registry, c("mp", "", r(0, 2, 2)), c("quarkus", "", r(0, 2, 2))); assertCompletion("m|", registry, c("mp", "", r(0, 0, 1)), c("quarkus", "", r(0, 0, 1))); } + + @Test + public void javaSnippets() { + TextDocumentSnippetRegistry registry = new TextDocumentSnippetRegistry(LanguageId.java.name()); + + } }