initial implementation towards a compilation-unit-based content-assist for types and packages
This commit is contained in:
@@ -68,6 +68,8 @@ import org.springframework.ide.vscode.commons.protocol.HighlightParams;
|
|||||||
import org.springframework.ide.vscode.commons.protocol.ProgressParams;
|
import org.springframework.ide.vscode.commons.protocol.ProgressParams;
|
||||||
import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient;
|
import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient;
|
||||||
import org.springframework.ide.vscode.commons.protocol.java.ClasspathListenerParams;
|
import org.springframework.ide.vscode.commons.protocol.java.ClasspathListenerParams;
|
||||||
|
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteData;
|
||||||
|
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteParams;
|
||||||
import org.springframework.ide.vscode.commons.protocol.java.JavaDataParams;
|
import org.springframework.ide.vscode.commons.protocol.java.JavaDataParams;
|
||||||
import org.springframework.ide.vscode.commons.protocol.java.JavaSearchParams;
|
import org.springframework.ide.vscode.commons.protocol.java.JavaSearchParams;
|
||||||
import org.springframework.ide.vscode.commons.protocol.java.JavaTypeHierarchyParams;
|
import org.springframework.ide.vscode.commons.protocol.java.JavaTypeHierarchyParams;
|
||||||
@@ -75,6 +77,7 @@ import org.springframework.ide.vscode.commons.protocol.java.TypeData;
|
|||||||
import org.springframework.ide.vscode.commons.protocol.java.TypeDescriptorData;
|
import org.springframework.ide.vscode.commons.protocol.java.TypeDescriptorData;
|
||||||
import org.springframework.tooling.jdt.ls.commons.Logger;
|
import org.springframework.tooling.jdt.ls.commons.Logger;
|
||||||
import org.springframework.tooling.jdt.ls.commons.classpath.ReusableClasspathListenerHandler;
|
import org.springframework.tooling.jdt.ls.commons.classpath.ReusableClasspathListenerHandler;
|
||||||
|
import org.springframework.tooling.jdt.ls.commons.java.JavaCodeCompletion;
|
||||||
import org.springframework.tooling.jdt.ls.commons.java.JavaData;
|
import org.springframework.tooling.jdt.ls.commons.java.JavaData;
|
||||||
import org.springframework.tooling.jdt.ls.commons.java.JavaFluxSearch;
|
import org.springframework.tooling.jdt.ls.commons.java.JavaFluxSearch;
|
||||||
import org.springframework.tooling.jdt.ls.commons.java.TypeHierarchy;
|
import org.springframework.tooling.jdt.ls.commons.java.TypeHierarchy;
|
||||||
@@ -125,11 +128,10 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final private JavaData javaData = new JavaData(STS4LanguageClientImpl::label , Logger.forEclipsePlugin(LanguageServerCommonsActivator::getInstance));
|
private final JavaData javaData = new JavaData(STS4LanguageClientImpl::label , Logger.forEclipsePlugin(LanguageServerCommonsActivator::getInstance));
|
||||||
|
private final JavaFluxSearch javaFluxSearch = new JavaFluxSearch(Logger.forEclipsePlugin(LanguageServerCommonsActivator::getInstance), javaData);
|
||||||
final private JavaFluxSearch javaFluxSearch = new JavaFluxSearch(Logger.forEclipsePlugin(LanguageServerCommonsActivator::getInstance), javaData);
|
private final TypeHierarchy typeHierarchy = new TypeHierarchy(Logger.forEclipsePlugin(LanguageServerCommonsActivator::getInstance), javaData);
|
||||||
|
private final JavaCodeCompletion codeComplete = new JavaCodeCompletion();
|
||||||
final private TypeHierarchy typeHierarchy = new TypeHierarchy(Logger.forEclipsePlugin(LanguageServerCommonsActivator::getInstance), javaData);
|
|
||||||
|
|
||||||
private static final String ANNOTION_TYPE_ID = "org.springframework.tooling.bootinfo";
|
private static final String ANNOTION_TYPE_ID = "org.springframework.tooling.bootinfo";
|
||||||
|
|
||||||
@@ -482,4 +484,17 @@ public class STS4LanguageClientImpl extends LanguageClientImpl implements STS4La
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<List<JavaCodeCompleteData>> javaCodeComplete(JavaCodeCompleteParams params) {
|
||||||
|
return CompletableFuture.supplyAsync(() -> {
|
||||||
|
try {
|
||||||
|
return codeComplete.codeComplete(params.getProjectUri(), params.getPrefix(), params.isIncludeTypes(), params.isIncludePackages());
|
||||||
|
} catch (Exception e) {
|
||||||
|
LanguageServerCommonsActivator.logError(e, "Failed to do code complete with prefix '" + params.getPrefix()
|
||||||
|
+ "' in project " + params.getProjectUri());
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import org.eclipse.lsp4j.jsonrpc.services.JsonNotification;
|
|||||||
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
|
import org.eclipse.lsp4j.jsonrpc.services.JsonRequest;
|
||||||
import org.eclipse.lsp4j.services.LanguageClient;
|
import org.eclipse.lsp4j.services.LanguageClient;
|
||||||
import org.springframework.ide.vscode.commons.protocol.java.ClasspathListenerParams;
|
import org.springframework.ide.vscode.commons.protocol.java.ClasspathListenerParams;
|
||||||
|
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteData;
|
||||||
|
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteParams;
|
||||||
import org.springframework.ide.vscode.commons.protocol.java.JavaDataParams;
|
import org.springframework.ide.vscode.commons.protocol.java.JavaDataParams;
|
||||||
import org.springframework.ide.vscode.commons.protocol.java.JavaSearchParams;
|
import org.springframework.ide.vscode.commons.protocol.java.JavaSearchParams;
|
||||||
import org.springframework.ide.vscode.commons.protocol.java.JavaTypeHierarchyParams;
|
import org.springframework.ide.vscode.commons.protocol.java.JavaTypeHierarchyParams;
|
||||||
@@ -71,4 +73,7 @@ public interface STS4LanguageClient extends LanguageClient {
|
|||||||
@JsonRequest("sts/javaSuperTypes")
|
@JsonRequest("sts/javaSuperTypes")
|
||||||
CompletableFuture<List<TypeDescriptorData>> javaSuperTypes(JavaTypeHierarchyParams params);
|
CompletableFuture<List<TypeDescriptorData>> javaSuperTypes(JavaTypeHierarchyParams params);
|
||||||
|
|
||||||
|
@JsonRequest("sts/javaCodeComplete")
|
||||||
|
CompletableFuture<List<JavaCodeCompleteData>> javaCodeComplete(JavaCodeCompleteParams params);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2019 Pivotal, Inc.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* https://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Pivotal, Inc. - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.springframework.ide.vscode.commons.protocol.java;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Martin Lippert
|
||||||
|
*/
|
||||||
|
public class JavaCodeCompleteData {
|
||||||
|
|
||||||
|
public static final String PACKAGE_PROPOSAL = "package";
|
||||||
|
public static final String CLASS_PROPOSAL = "class";
|
||||||
|
public static final String INTERFACE_PROPOSAL = "interface";
|
||||||
|
public static final String ENUM_PROPOSAL = "enum";
|
||||||
|
|
||||||
|
private String kind;
|
||||||
|
private String fullyQualifiedName;
|
||||||
|
private int relevance;
|
||||||
|
|
||||||
|
public String getFullyQualifiedName() {
|
||||||
|
return this.fullyQualifiedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFullyQualifiedName(String fullyQualifiedName) {
|
||||||
|
this.fullyQualifiedName = fullyQualifiedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKind() {
|
||||||
|
return this.kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKind(String kind) {
|
||||||
|
this.kind = kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRelevance() {
|
||||||
|
return this.relevance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRelevance(int relevance) {
|
||||||
|
this.relevance = relevance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isInterfaceProposal() {
|
||||||
|
return INTERFACE_PROPOSAL.equals(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEnumProposal() {
|
||||||
|
return ENUM_PROPOSAL.equals(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPackageProposal() {
|
||||||
|
return PACKAGE_PROPOSAL.equals(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isClassProposal() {
|
||||||
|
return CLASS_PROPOSAL.equals(kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2019 Pivotal, Inc.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* https://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Pivotal, Inc. - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.springframework.ide.vscode.commons.protocol.java;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Martin Lippert
|
||||||
|
*/
|
||||||
|
public class JavaCodeCompleteParams {
|
||||||
|
|
||||||
|
private String projectUri;
|
||||||
|
private String prefix;
|
||||||
|
private boolean includeTypes;
|
||||||
|
private boolean includePackages;
|
||||||
|
|
||||||
|
public JavaCodeCompleteParams(String projectUri, String prefix, boolean includeTypes, boolean includePackages) {
|
||||||
|
super();
|
||||||
|
this.projectUri = projectUri;
|
||||||
|
this.prefix = prefix;
|
||||||
|
this.includeTypes = includeTypes;
|
||||||
|
this.includePackages = includePackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProjectUri() {
|
||||||
|
return projectUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setProjectUri(String projectUri) {
|
||||||
|
this.projectUri = projectUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPrefix() {
|
||||||
|
return prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrefix(String prefix) {
|
||||||
|
this.prefix = prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isIncludeTypes() {
|
||||||
|
return includeTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIncludeTypes(boolean includeTypes) {
|
||||||
|
this.includeTypes = includeTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isIncludePackages() {
|
||||||
|
return includePackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIncludePackages(boolean includePackages) {
|
||||||
|
this.includePackages = includePackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + (includePackages ? 1231 : 1237);
|
||||||
|
result = prime * result + (includeTypes ? 1231 : 1237);
|
||||||
|
result = prime * result + ((prefix == null) ? 0 : prefix.hashCode());
|
||||||
|
result = prime * result + ((projectUri == null) ? 0 : projectUri.hashCode());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
JavaCodeCompleteParams other = (JavaCodeCompleteParams) obj;
|
||||||
|
if (includePackages != other.includePackages)
|
||||||
|
return false;
|
||||||
|
if (includeTypes != other.includeTypes)
|
||||||
|
return false;
|
||||||
|
if (prefix == null) {
|
||||||
|
if (other.prefix != null)
|
||||||
|
return false;
|
||||||
|
} else if (!prefix.equals(other.prefix))
|
||||||
|
return false;
|
||||||
|
if (projectUri == null) {
|
||||||
|
if (other.projectUri != null)
|
||||||
|
return false;
|
||||||
|
} else if (!projectUri.equals(other.projectUri))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -113,6 +113,8 @@ import org.springframework.ide.vscode.commons.protocol.HighlightParams;
|
|||||||
import org.springframework.ide.vscode.commons.protocol.ProgressParams;
|
import org.springframework.ide.vscode.commons.protocol.ProgressParams;
|
||||||
import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient;
|
import org.springframework.ide.vscode.commons.protocol.STS4LanguageClient;
|
||||||
import org.springframework.ide.vscode.commons.protocol.java.ClasspathListenerParams;
|
import org.springframework.ide.vscode.commons.protocol.java.ClasspathListenerParams;
|
||||||
|
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteData;
|
||||||
|
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteParams;
|
||||||
import org.springframework.ide.vscode.commons.protocol.java.JavaDataParams;
|
import org.springframework.ide.vscode.commons.protocol.java.JavaDataParams;
|
||||||
import org.springframework.ide.vscode.commons.protocol.java.JavaSearchParams;
|
import org.springframework.ide.vscode.commons.protocol.java.JavaSearchParams;
|
||||||
import org.springframework.ide.vscode.commons.protocol.java.JavaTypeHierarchyParams;
|
import org.springframework.ide.vscode.commons.protocol.java.JavaTypeHierarchyParams;
|
||||||
@@ -387,6 +389,11 @@ public class LanguageServerHarness {
|
|||||||
return CompletableFuture.completedFuture(Collections.emptyList());
|
return CompletableFuture.completedFuture(Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<List<JavaCodeCompleteData>> javaCodeComplete(JavaCodeCompleteParams params) {
|
||||||
|
return CompletableFuture.completedFuture(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2019 Pivotal, Inc.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* https://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Pivotal, Inc. - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.springframework.tooling.jdt.ls.commons.java;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.core.IJavaProject;
|
||||||
|
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteData;
|
||||||
|
import org.springframework.tooling.jdt.ls.commons.resources.ResourceUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Martin Lippert
|
||||||
|
*/
|
||||||
|
public class JavaCodeCompletion {
|
||||||
|
|
||||||
|
public List<JavaCodeCompleteData> codeComplete(String project, String prefix, boolean includeTypes, boolean includePackages) throws Exception {
|
||||||
|
URI projectUri = project == null ? null : URI.create(project);
|
||||||
|
IJavaProject javaProject = projectUri == null ? null : ResourceUtils.getJavaProject(projectUri);
|
||||||
|
|
||||||
|
JavaCodeCompletionProposalCollector collector = new JavaCodeCompletionProposalCollector(includeTypes, includePackages);
|
||||||
|
JavaCodeCompletionUtils.codeComplete(javaProject, prefix, collector);
|
||||||
|
|
||||||
|
return collector.getProposals();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2019 Pivotal, Inc.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* https://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Pivotal, Inc. - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.springframework.tooling.jdt.ls.commons.java;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.core.CompletionProposal;
|
||||||
|
import org.eclipse.jdt.core.CompletionRequestor;
|
||||||
|
import org.eclipse.jdt.core.Flags;
|
||||||
|
import org.eclipse.jdt.core.Signature;
|
||||||
|
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Martin Lippert
|
||||||
|
*/
|
||||||
|
public class JavaCodeCompletionProposalCollector extends CompletionRequestor {
|
||||||
|
|
||||||
|
private final boolean includeTypes;
|
||||||
|
private final boolean includePackages;
|
||||||
|
private final List<JavaCodeCompleteData> proposals;
|
||||||
|
|
||||||
|
public JavaCodeCompletionProposalCollector(boolean includeTypes, boolean includePackages) {
|
||||||
|
this.includeTypes = includeTypes;
|
||||||
|
this.includePackages = includePackages;
|
||||||
|
this.proposals = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(CompletionProposal proposal) {
|
||||||
|
if (includeTypes && proposal.getKind() == CompletionProposal.TYPE_REF) {
|
||||||
|
if (!JavaCodeCompletionUtils.CLASS_NAME.equals(String.valueOf(proposal.getCompletion()))) {
|
||||||
|
addTypeProposal(proposal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (includePackages && proposal.getKind() == CompletionProposal.PACKAGE_REF) {
|
||||||
|
addPackageProposal(proposal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<JavaCodeCompleteData> getProposals() {
|
||||||
|
return proposals;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addPackageProposal(CompletionProposal proposal) {
|
||||||
|
JavaCodeCompleteData proposalData = new JavaCodeCompleteData();
|
||||||
|
|
||||||
|
proposalData.setKind(JavaCodeCompleteData.PACKAGE_PROPOSAL);
|
||||||
|
proposalData.setRelevance(proposal.getRelevance());
|
||||||
|
proposalData.setFullyQualifiedName(String.valueOf(proposal.getDeclarationSignature()));
|
||||||
|
|
||||||
|
this.proposals.add(proposalData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addTypeProposal(CompletionProposal proposal) {
|
||||||
|
JavaCodeCompleteData proposalData = new JavaCodeCompleteData();
|
||||||
|
|
||||||
|
if (Flags.isInterface(proposal.getFlags())) {
|
||||||
|
proposalData.setKind(JavaCodeCompleteData.INTERFACE_PROPOSAL);
|
||||||
|
}
|
||||||
|
else if (Flags.isEnum(proposal.getFlags())) {
|
||||||
|
proposalData.setKind(JavaCodeCompleteData.ENUM_PROPOSAL);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
proposalData.setKind(JavaCodeCompleteData.CLASS_PROPOSAL);
|
||||||
|
}
|
||||||
|
proposalData.setRelevance(proposal.getRelevance());
|
||||||
|
|
||||||
|
String fullyQualifiedName = String.valueOf(Signature.toCharArray(proposal.getSignature()));
|
||||||
|
proposalData.setFullyQualifiedName(fullyQualifiedName);
|
||||||
|
|
||||||
|
this.proposals.add(proposalData);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
/*******************************************************************************
|
||||||
|
* Copyright (c) 2019 Pivotal, Inc.
|
||||||
|
* All rights reserved. This program and the accompanying materials
|
||||||
|
* are made available under the terms of the Eclipse Public License v1.0
|
||||||
|
* which accompanies this distribution, and is available at
|
||||||
|
* https://www.eclipse.org/legal/epl-v10.html
|
||||||
|
*
|
||||||
|
* Contributors:
|
||||||
|
* Pivotal, Inc. - initial API and implementation
|
||||||
|
*******************************************************************************/
|
||||||
|
package org.springframework.tooling.jdt.ls.commons.java;
|
||||||
|
|
||||||
|
import org.eclipse.jdt.core.IBuffer;
|
||||||
|
import org.eclipse.jdt.core.ICompilationUnit;
|
||||||
|
import org.eclipse.jdt.core.IJavaProject;
|
||||||
|
import org.eclipse.jdt.core.IPackageFragment;
|
||||||
|
import org.eclipse.jdt.core.IPackageFragmentRoot;
|
||||||
|
import org.eclipse.jdt.core.JavaModelException;
|
||||||
|
import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Martin Lippert
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("restriction")
|
||||||
|
public class JavaCodeCompletionUtils {
|
||||||
|
|
||||||
|
public static final String CLASS_NAME = "_xxx";
|
||||||
|
|
||||||
|
private static final String CLASS_SOURCE_START = "public class " + CLASS_NAME + " {\n"
|
||||||
|
+ " public void main(String[] args) {\n" + " ";
|
||||||
|
|
||||||
|
private static final String CLASS_SOURCE_END = "\n" + " }\n" + "}";
|
||||||
|
|
||||||
|
|
||||||
|
public static void codeComplete(IJavaProject project, String prefix, JavaCodeCompletionProposalCollector proposalCollector) {
|
||||||
|
if (prefix == null || prefix.length() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ICompilationUnit unit = createCompilationUnit(project, prefix);
|
||||||
|
|
||||||
|
prefix = prefix.replace('$', '.');
|
||||||
|
|
||||||
|
String sourceStart = CLASS_SOURCE_START + prefix;
|
||||||
|
String packageName = null;
|
||||||
|
|
||||||
|
int dot = prefix.lastIndexOf('.');
|
||||||
|
if (dot > -1) {
|
||||||
|
packageName = prefix.substring(0, dot);
|
||||||
|
sourceStart = "package " + packageName + ";\n" + sourceStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
String source = sourceStart + CLASS_SOURCE_END;
|
||||||
|
setContents(unit, source);
|
||||||
|
|
||||||
|
unit.codeComplete(sourceStart.length(), proposalCollector, DefaultWorkingCopyOwner.PRIMARY);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ICompilationUnit createCompilationUnit(IJavaProject project, String prefix) throws JavaModelException {
|
||||||
|
IPackageFragment root = getPackageFragment(project, prefix);
|
||||||
|
ICompilationUnit unit = root.getCompilationUnit("_xxx.java").getWorkingCopy(null);
|
||||||
|
return unit;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IPackageFragment getPackageFragment(IJavaProject project, String prefix) throws JavaModelException {
|
||||||
|
int dot = prefix.lastIndexOf('.');
|
||||||
|
if (dot > -1) {
|
||||||
|
String packageName = prefix.substring(0, dot);
|
||||||
|
for (IPackageFragmentRoot root : project.getPackageFragmentRoots()) {
|
||||||
|
IPackageFragment p = root.getPackageFragment(packageName);
|
||||||
|
if (p != null && p.exists()) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IPackageFragment[] packages = project.getPackageFragments();
|
||||||
|
for (IPackageFragment p : packages) {
|
||||||
|
if (p.getElementName().equals(packageName))
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (IPackageFragmentRoot p : project.getAllPackageFragmentRoots()) {
|
||||||
|
if (p.getKind() == IPackageFragmentRoot.K_SOURCE) {
|
||||||
|
return p.getPackageFragment("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return project.getPackageFragments()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void setContents(ICompilationUnit cu, String source) {
|
||||||
|
if (cu == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
synchronized (cu) {
|
||||||
|
IBuffer buffer;
|
||||||
|
try {
|
||||||
|
|
||||||
|
buffer = cu.getBuffer();
|
||||||
|
}
|
||||||
|
catch (JavaModelException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
buffer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer != null)
|
||||||
|
buffer.setContents(source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -41,6 +41,7 @@ public class JavaFluxSearch {
|
|||||||
public List<String> fuzzySearchPackages(JavaSearchParams params) throws Exception {
|
public List<String> fuzzySearchPackages(JavaSearchParams params) throws Exception {
|
||||||
URI projectUri = params.getProjectUri() == null ? null : URI.create(params.getProjectUri());
|
URI projectUri = params.getProjectUri() == null ? null : URI.create(params.getProjectUri());
|
||||||
IJavaProject javaProject = projectUri == null ? null : ResourceUtils.getJavaProject(projectUri);
|
IJavaProject javaProject = projectUri == null ? null : ResourceUtils.getJavaProject(projectUri);
|
||||||
|
|
||||||
PackageFluxSearch fluxPackageSearch = packageSearchCache.get(
|
PackageFluxSearch fluxPackageSearch = packageSearchCache.get(
|
||||||
Tuples.of(params.isIncludeBinaries(), params.isIncludeSystemLibs()), () -> new PackageFluxSearch(logger, params.isIncludeBinaries(), params.isIncludeSystemLibs())
|
Tuples.of(params.isIncludeBinaries(), params.isIncludeSystemLibs()), () -> new PackageFluxSearch(logger, params.isIncludeBinaries(), params.isIncludeSystemLibs())
|
||||||
);
|
);
|
||||||
@@ -50,6 +51,7 @@ public class JavaFluxSearch {
|
|||||||
public List<TypeDescriptorData> fuzzySearchTypes(JavaSearchParams params) throws Exception {
|
public List<TypeDescriptorData> fuzzySearchTypes(JavaSearchParams params) throws Exception {
|
||||||
URI projectUri = params.getProjectUri() == null ? null : URI.create(params.getProjectUri());
|
URI projectUri = params.getProjectUri() == null ? null : URI.create(params.getProjectUri());
|
||||||
IJavaProject javaProject = projectUri == null ? null : ResourceUtils.getJavaProject(projectUri);
|
IJavaProject javaProject = projectUri == null ? null : ResourceUtils.getJavaProject(projectUri);
|
||||||
|
|
||||||
TypeFluxSearch fluxTypeSearch = typeSearchCache.get(
|
TypeFluxSearch fluxTypeSearch = typeSearchCache.get(
|
||||||
Tuples.of(params.isIncludeBinaries(), params.isIncludeSystemLibs()), () -> new TypeFluxSearch(logger, javaData, params.isIncludeBinaries(), params.isIncludeSystemLibs())
|
Tuples.of(params.isIncludeBinaries(), params.isIncludeSystemLibs()), () -> new TypeFluxSearch(logger, javaData, params.isIncludeBinaries(), params.isIncludeSystemLibs())
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import org.springframework.ide.vscode.boot.xml.completions.TypeCompletionProposa
|
|||||||
import org.springframework.ide.vscode.commons.languageserver.completion.ICompletionEngine;
|
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.completion.ICompletionProposal;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,12 +51,12 @@ public class SpringXMLCompletionEngine implements ICompletionEngine {
|
|||||||
private final BootJavaConfig config;
|
private final BootJavaConfig config;
|
||||||
|
|
||||||
public SpringXMLCompletionEngine(SpringXMLLanguageServerComponents springXMLLanguageServerComponents,
|
public SpringXMLCompletionEngine(SpringXMLLanguageServerComponents springXMLLanguageServerComponents,
|
||||||
JavaProjectFinder projectFinder, SpringSymbolIndex symbolIndex, SimpleTextDocumentService simpleTextDocumentService, BootJavaConfig config) {
|
SimpleLanguageServer server, JavaProjectFinder projectFinder, SpringSymbolIndex symbolIndex, BootJavaConfig config) {
|
||||||
|
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
|
||||||
this.completionProviders = new HashMap<>();
|
this.completionProviders = new HashMap<>();
|
||||||
this.completionProviders.put(new XMLCompletionProviderKey(BEANS_NAMESPACE, null, BEAN_ELEMENT, CLASS_ATTRIBUTE), new TypeCompletionProposalProvider(projectFinder, simpleTextDocumentService, true));
|
this.completionProviders.put(new XMLCompletionProviderKey(BEANS_NAMESPACE, null, BEAN_ELEMENT, CLASS_ATTRIBUTE), new TypeCompletionProposalProvider(server, projectFinder, true, true, false, false));
|
||||||
this.completionProviders.put(new XMLCompletionProviderKey(BEANS_NAMESPACE, BEAN_ELEMENT, PROPERTY_ELEMENT, NAME_ATTRIBUTE), new PropertyNameCompletionProposalProvider(projectFinder));
|
this.completionProviders.put(new XMLCompletionProviderKey(BEANS_NAMESPACE, BEAN_ELEMENT, PROPERTY_ELEMENT, NAME_ATTRIBUTE), new PropertyNameCompletionProposalProvider(projectFinder));
|
||||||
this.completionProviders.put(new XMLCompletionProviderKey(BEANS_NAMESPACE, BEAN_ELEMENT, PROPERTY_ELEMENT, REF_ATTRIBUTE), new BeanRefCompletionProposalProvider(projectFinder, symbolIndex));
|
this.completionProviders.put(new XMLCompletionProviderKey(BEANS_NAMESPACE, BEAN_ELEMENT, PROPERTY_ELEMENT, REF_ATTRIBUTE), new BeanRefCompletionProposalProvider(projectFinder, symbolIndex));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public class SpringXMLLanguageServerComponents implements LanguageServerComponen
|
|||||||
server.doOnInitialized(this::initialized);
|
server.doOnInitialized(this::initialized);
|
||||||
server.onShutdown(this::shutdown);
|
server.onShutdown(this::shutdown);
|
||||||
|
|
||||||
this.completionEngine = new SpringXMLCompletionEngine(this, projectFinder, symbolIndex, server.getTextDocumentService(), config);
|
this.completionEngine = new SpringXMLCompletionEngine(this, server, projectFinder, symbolIndex, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ package org.springframework.ide.vscode.boot.xml.completions;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.eclipse.lsp4j.CompletionItemKind;
|
import org.eclipse.lsp4j.CompletionItemKind;
|
||||||
import org.eclipse.lsp4xml.dom.DOMAttr;
|
import org.eclipse.lsp4xml.dom.DOMAttr;
|
||||||
@@ -22,33 +23,35 @@ import org.eclipse.lsp4xml.dom.DOMNode;
|
|||||||
import org.eclipse.lsp4xml.dom.parser.Scanner;
|
import org.eclipse.lsp4xml.dom.parser.Scanner;
|
||||||
import org.springframework.ide.vscode.boot.xml.XMLCompletionProvider;
|
import org.springframework.ide.vscode.boot.xml.XMLCompletionProvider;
|
||||||
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
import org.springframework.ide.vscode.commons.java.IJavaProject;
|
||||||
import org.springframework.ide.vscode.commons.java.IType;
|
|
||||||
import org.springframework.ide.vscode.commons.languageserver.completion.DocumentEdits;
|
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.completion.ICompletionProposal;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
import org.springframework.ide.vscode.commons.languageserver.java.JavaProjectFinder;
|
||||||
import org.springframework.ide.vscode.commons.languageserver.util.SimpleTextDocumentService;
|
import org.springframework.ide.vscode.commons.languageserver.util.SimpleLanguageServer;
|
||||||
|
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteData;
|
||||||
|
import org.springframework.ide.vscode.commons.protocol.java.JavaCodeCompleteParams;
|
||||||
import org.springframework.ide.vscode.commons.util.Renderable;
|
import org.springframework.ide.vscode.commons.util.Renderable;
|
||||||
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
import org.springframework.ide.vscode.commons.util.text.TextDocument;
|
||||||
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
import reactor.util.function.Tuple2;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Martin Lippert
|
* @author Martin Lippert
|
||||||
*/
|
*/
|
||||||
public class TypeCompletionProposalProvider implements XMLCompletionProvider {
|
public class TypeCompletionProposalProvider implements XMLCompletionProvider {
|
||||||
|
|
||||||
private final JavaProjectFinder projectFinder;
|
private final JavaProjectFinder projectFinder;
|
||||||
private final boolean classesOnly;
|
private final SimpleLanguageServer server;
|
||||||
private final Set<String> typeSearchAlreadyInitializedProjects = Collections.synchronizedSet(new HashSet<>());
|
private final boolean packagesAllowed;
|
||||||
|
private final boolean classesAllowed;
|
||||||
|
private final boolean interfacesAllowed;
|
||||||
|
private final boolean enumsAllowed;
|
||||||
|
|
||||||
public TypeCompletionProposalProvider(JavaProjectFinder projectFinder, SimpleTextDocumentService simpleTextDocumentService, boolean classesOnly) {
|
public TypeCompletionProposalProvider(SimpleLanguageServer server, JavaProjectFinder projectFinder,
|
||||||
|
boolean packagesAllowed, boolean classesAllowed, boolean interfacesAllowed, boolean enumsAllowed) {
|
||||||
|
this.server = server;
|
||||||
this.projectFinder = projectFinder;
|
this.projectFinder = projectFinder;
|
||||||
this.classesOnly = classesOnly;
|
this.packagesAllowed = packagesAllowed;
|
||||||
|
this.classesAllowed = classesAllowed;
|
||||||
simpleTextDocumentService.onDidOpen(doc -> {
|
this.interfacesAllowed = interfacesAllowed;
|
||||||
initializeTypeSearch(doc);
|
this.enumsAllowed = enumsAllowed;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -69,68 +72,81 @@ public class TypeCompletionProposalProvider implements XMLCompletionProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Flux<Tuple2<IType, Double>> types = project.getIndex().fuzzySearchTypes(prefix, true, true);
|
// Flux<Tuple2<IType, Double>> types = project.getIndex().fuzzySearchTypes(prefix, true, true);
|
||||||
Flux<Tuple2<IType, Double>> types = project.getIndex().camelcaseSearchTypes(prefix, true, true);
|
// Flux<Tuple2<IType, Double>> types = project.getIndex().camelcaseSearchTypes(prefix, true, true);
|
||||||
|
|
||||||
|
JavaCodeCompleteParams params = new JavaCodeCompleteParams(project.getLocationUri().toString(), prefix, true, true);
|
||||||
|
CompletableFuture<List<JavaCodeCompleteData>> completions = server.getClient().javaCodeComplete(params);
|
||||||
|
|
||||||
final String prefixStr = prefix;
|
final String finalPrefix = prefix;
|
||||||
|
|
||||||
return types
|
try {
|
||||||
.filter(result -> result.getT1() != null && result.getT1().getElementName() != null && result.getT1().getElementName().length() > 0)
|
return completions.get().stream()
|
||||||
.filter(result -> classesOnly ? result.getT1().isClass() : true)
|
.filter(proposal -> {
|
||||||
.map(t -> createProposal(t, doc, offset, prefixStr))
|
if (proposal.isClassProposal()) return classesAllowed;
|
||||||
.collectList().block();
|
if (proposal.isInterfaceProposal()) return interfacesAllowed;
|
||||||
|
if (proposal.isEnumProposal()) return enumsAllowed;
|
||||||
|
if (proposal.isPackageProposal()) return packagesAllowed;
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.map(proposal -> createProposal(proposal, doc, finalPrefix, offset))
|
||||||
|
.filter(proposal -> proposal != null)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
// TODO: logging
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ICompletionProposal createProposal(Tuple2<IType, Double> t, TextDocument doc, int offset, String prefix) {
|
private ICompletionProposal createProposal(JavaCodeCompleteData proposal, TextDocument doc, String prefix, int offset) {
|
||||||
IType type = t.getT1();
|
if (proposal.isPackageProposal()) {
|
||||||
|
return createPackageProposal(proposal, doc, prefix, offset);
|
||||||
|
}
|
||||||
|
else if (proposal.isClassProposal() || proposal.isInterfaceProposal() || proposal.isEnumProposal()) {
|
||||||
|
return createTypeProposal(proposal, doc, prefix, offset);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeCompletionProposal createPackageProposal(JavaCodeCompleteData proposal, TextDocument doc, String prefix, int offset) {
|
||||||
|
String label = proposal.getFullyQualifiedName();
|
||||||
|
CompletionItemKind kind = CompletionItemKind.Module;
|
||||||
|
|
||||||
|
DocumentEdits edits = new DocumentEdits(doc, false);
|
||||||
|
edits.replace(offset - prefix.length(), offset, proposal.getFullyQualifiedName());
|
||||||
|
|
||||||
|
Renderable renderable = null;
|
||||||
|
|
||||||
|
return new TypeCompletionProposal(label, kind, edits, proposal.getFullyQualifiedName(), renderable, proposal.getRelevance());
|
||||||
|
}
|
||||||
|
|
||||||
|
private TypeCompletionProposal createTypeProposal(JavaCodeCompleteData proposal, TextDocument doc, String prefix, int offset) {
|
||||||
|
String label = proposal.getFullyQualifiedName();
|
||||||
|
|
||||||
String label = type.getFullyQualifiedName();
|
|
||||||
int splitIndex = Math.max(label.lastIndexOf("."), label.lastIndexOf("$"));
|
int splitIndex = Math.max(label.lastIndexOf("."), label.lastIndexOf("$"));
|
||||||
|
|
||||||
if (splitIndex > 0) {
|
if (splitIndex > 0) {
|
||||||
label = label.substring(splitIndex + 1) + " - " + label.substring(0, splitIndex);
|
label = label.substring(splitIndex + 1) + " - " + label.substring(0, splitIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
CompletionItemKind kind;
|
CompletionItemKind kind = CompletionItemKind.Class;
|
||||||
if (type.isClass()) {
|
if (proposal.isInterfaceProposal()) {
|
||||||
kind = CompletionItemKind.Class;
|
|
||||||
}
|
|
||||||
else if (type.isInterface()) {
|
|
||||||
kind = CompletionItemKind.Interface;
|
kind = CompletionItemKind.Interface;
|
||||||
}
|
}
|
||||||
else if (type.isEnum()) {
|
else if (proposal.isEnumProposal()) {
|
||||||
kind = CompletionItemKind.Enum;
|
kind = CompletionItemKind.Enum;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
kind = CompletionItemKind.Property;
|
|
||||||
}
|
|
||||||
|
|
||||||
DocumentEdits edits = new DocumentEdits(doc, false);
|
DocumentEdits edits = new DocumentEdits(doc, false);
|
||||||
edits.replace(offset - prefix.length(), offset, type.getFullyQualifiedName());
|
edits.replace(offset - prefix.length(), offset, proposal.getFullyQualifiedName());
|
||||||
|
|
||||||
Renderable renderable = null;
|
Renderable renderable = null;
|
||||||
|
|
||||||
return new TypeCompletionProposal(label, kind, edits, type.getFullyQualifiedName(), renderable, t.getT2());
|
return new TypeCompletionProposal(label, kind, edits, proposal.getFullyQualifiedName(), renderable, proposal.getRelevance());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* trigger initial type search as soon as the doc opens to avoid search timeouts when doing type proposals for the first time
|
|
||||||
*/
|
|
||||||
private void initializeTypeSearch(TextDocument doc) {
|
|
||||||
Optional<IJavaProject> foundProject = this.projectFinder.find(doc.getId());
|
|
||||||
if (foundProject.isPresent()) {
|
|
||||||
IJavaProject project = foundProject.get();
|
|
||||||
String projectName = project.getElementName();
|
|
||||||
|
|
||||||
if (!this.typeSearchAlreadyInitializedProjects.contains(projectName)) {
|
|
||||||
|
|
||||||
this.typeSearchAlreadyInitializedProjects.add(projectName);
|
|
||||||
Flux<Tuple2<IType, Double>> types = project.getIndex().camelcaseSearchTypes("", true, true);
|
|
||||||
types.count();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user