Skip to content

Commit 05e40f9

Browse files
authored
Add completion support for classfile. (#261)
Signed-off-by: Yaohai Zheng <[email protected]>
1 parent 5d5b39e commit 05e40f9

File tree

3 files changed

+264
-5
lines changed

3 files changed

+264
-5
lines changed

com.microsoft.java.debug.plugin/META-INF/MANIFEST.MF

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ Require-Bundle: org.eclipse.core.runtime,
1818
org.eclipse.jdt.launching,
1919
com.google.gson,
2020
org.apache.commons.lang3,
21-
org.eclipse.lsp4j
21+
org.eclipse.lsp4j,
22+
com.google.guava
2223
Bundle-ClassPath: lib/commons-io-2.5.jar,
2324
.,
2425
lib/rxjava-2.1.1.jar,
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2016-2018 Red Hat Inc. and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License v1.0
5+
* which accompanies this distribution, and is available at
6+
* http://www.eclipse.org/legal/epl-v10.html
7+
*
8+
* Copied form org.eclipse.jdt.ls.core.internal.contentassist.CompletionsProvider and modified it for class file completion.
9+
*
10+
* Contributors:
11+
* Red Hat Inc. - initial API and implementation
12+
*******************************************************************************/
13+
14+
package com.microsoft.java.debug.plugin.internal;
15+
16+
import java.util.ArrayList;
17+
import java.util.HashMap;
18+
import java.util.List;
19+
import java.util.Map;
20+
import java.util.Set;
21+
import java.util.logging.Level;
22+
import java.util.logging.Logger;
23+
24+
import org.eclipse.core.runtime.CoreException;
25+
import org.eclipse.core.runtime.IPath;
26+
import org.eclipse.jdt.core.CompletionContext;
27+
import org.eclipse.jdt.core.CompletionProposal;
28+
import org.eclipse.jdt.core.CompletionRequestor;
29+
import org.eclipse.jdt.core.IClassFile;
30+
import org.eclipse.jdt.core.IClasspathEntry;
31+
import org.eclipse.jdt.core.IJavaElement;
32+
import org.eclipse.jdt.core.IJavaProject;
33+
import org.eclipse.jdt.core.IType;
34+
import org.eclipse.jdt.core.ITypeRoot;
35+
import org.eclipse.jdt.core.JavaModelException;
36+
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
37+
import org.eclipse.jdt.ls.core.internal.contentassist.CompletionProposalDescriptionProvider;
38+
import org.eclipse.jdt.ls.core.internal.contentassist.GetterSetterCompletionProposal;
39+
import org.eclipse.jdt.ls.core.internal.contentassist.SortTextHelper;
40+
import org.eclipse.jdt.ls.core.internal.handlers.CompletionResolveHandler;
41+
import org.eclipse.jdt.ls.core.internal.handlers.CompletionResponse;
42+
import org.eclipse.jdt.ls.core.internal.handlers.CompletionResponses;
43+
import org.eclipse.lsp4j.CompletionItem;
44+
import org.eclipse.lsp4j.CompletionItemKind;
45+
46+
import com.google.common.collect.ImmutableSet;
47+
import com.microsoft.java.debug.core.Configuration;
48+
49+
50+
@SuppressWarnings("restriction")
51+
public final class CompletionProposalRequestor extends CompletionRequestor {
52+
53+
private static final Logger logger = Logger.getLogger(Configuration.LOGGER_NAME);
54+
55+
private List<CompletionProposal> proposals = new ArrayList<>();
56+
private final ITypeRoot typeRoot;
57+
private CompletionProposalDescriptionProvider descriptionProvider;
58+
private CompletionResponse response;
59+
private boolean isTestCodeExcluded;
60+
private CompletionContext context;
61+
62+
// Update SUPPORTED_KINDS when mapKind changes
63+
// @formatter:off
64+
public static final Set<CompletionItemKind> SUPPORTED_KINDS = ImmutableSet.of(CompletionItemKind.Constructor,
65+
CompletionItemKind.Class, CompletionItemKind.Module, CompletionItemKind.Field, CompletionItemKind.Keyword,
66+
CompletionItemKind.Reference, CompletionItemKind.Variable, CompletionItemKind.Function,
67+
CompletionItemKind.Text);
68+
// @formatter:on
69+
70+
/**
71+
* Constructor.
72+
* @param typeRoot ITypeRoot
73+
* @param offset within the source file
74+
*/
75+
public CompletionProposalRequestor(ITypeRoot typeRoot, int offset) {
76+
this.typeRoot = typeRoot;
77+
response = new CompletionResponse();
78+
response.setOffset(offset);
79+
if (typeRoot instanceof IClassFile) {
80+
isTestCodeExcluded = true;
81+
} else {
82+
isTestCodeExcluded = !isTestSource(typeRoot.getJavaProject(), typeRoot);
83+
}
84+
setRequireExtendedContext(true);
85+
}
86+
87+
private boolean isTestSource(IJavaProject project, ITypeRoot cu) {
88+
if (project == null) {
89+
return true;
90+
}
91+
try {
92+
IClasspathEntry[] resolvedClasspath = project.getResolvedClasspath(true);
93+
final IPath resourcePath = cu.getResource().getFullPath();
94+
for (IClasspathEntry e : resolvedClasspath) {
95+
if (e.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
96+
if (e.isTest()) {
97+
if (e.getPath().isPrefixOf(resourcePath)) {
98+
return true;
99+
}
100+
}
101+
}
102+
}
103+
} catch (JavaModelException e) {
104+
logger.log(Level.WARNING, String.format("Failed to judge the cu scope: %s", e.toString()), e);
105+
}
106+
return false;
107+
}
108+
109+
@Override
110+
public void accept(CompletionProposal proposal) {
111+
if (!isIgnored(proposal.getKind())) {
112+
if (proposal.getKind() == CompletionProposal.POTENTIAL_METHOD_DECLARATION) {
113+
acceptPotentialMethodDeclaration(proposal);
114+
} else {
115+
if (proposal.getKind() == CompletionProposal.PACKAGE_REF && typeRoot.getParent() != null
116+
&& String.valueOf(proposal.getCompletion()).equals(typeRoot.getParent().getElementName())) {
117+
// Hacky way to boost relevance of current package, for package completions,
118+
// until
119+
// https://bugs.eclipse.org/518140 is fixed
120+
proposal.setRelevance(proposal.getRelevance() + 1);
121+
}
122+
proposals.add(proposal);
123+
}
124+
}
125+
}
126+
127+
/**
128+
* Return the completion list requested.
129+
* @return list of the completion item.
130+
*/
131+
public List<CompletionItem> getCompletionItems() {
132+
response.setProposals(proposals);
133+
CompletionResponses.store(response);
134+
List<CompletionItem> completionItems = new ArrayList<>(proposals.size());
135+
for (int i = 0; i < proposals.size(); i++) {
136+
completionItems.add(toCompletionItem(proposals.get(i), i));
137+
}
138+
return completionItems;
139+
}
140+
141+
/**
142+
* To completion item.
143+
* @param proposal proposal
144+
* @param index index
145+
* @return CompletionItem
146+
*/
147+
public CompletionItem toCompletionItem(CompletionProposal proposal, int index) {
148+
final CompletionItem $ = new CompletionItem();
149+
$.setKind(mapKind(proposal.getKind()));
150+
Map<String, String> data = new HashMap<>();
151+
data.put(CompletionResolveHandler.DATA_FIELD_REQUEST_ID, String.valueOf(response.getId()));
152+
data.put(CompletionResolveHandler.DATA_FIELD_PROPOSAL_ID, String.valueOf(index));
153+
$.setData(data);
154+
this.descriptionProvider.updateDescription(proposal, $);
155+
$.setSortText(SortTextHelper.computeSortText(proposal));
156+
return $;
157+
}
158+
159+
@Override
160+
public void acceptContext(CompletionContext context) {
161+
super.acceptContext(context);
162+
this.context = context;
163+
response.setContext(context);
164+
this.descriptionProvider = new CompletionProposalDescriptionProvider(context);
165+
}
166+
167+
private CompletionItemKind mapKind(final int kind) {
168+
// When a new CompletionItemKind is added, don't forget to update
169+
// SUPPORTED_KINDS
170+
switch (kind) {
171+
case CompletionProposal.ANONYMOUS_CLASS_CONSTRUCTOR_INVOCATION:
172+
case CompletionProposal.CONSTRUCTOR_INVOCATION:
173+
return CompletionItemKind.Constructor;
174+
case CompletionProposal.ANONYMOUS_CLASS_DECLARATION:
175+
case CompletionProposal.TYPE_REF:
176+
return CompletionItemKind.Class;
177+
case CompletionProposal.FIELD_IMPORT:
178+
case CompletionProposal.METHOD_IMPORT:
179+
case CompletionProposal.METHOD_NAME_REFERENCE:
180+
case CompletionProposal.PACKAGE_REF:
181+
case CompletionProposal.TYPE_IMPORT:
182+
return CompletionItemKind.Module;
183+
case CompletionProposal.FIELD_REF:
184+
case CompletionProposal.FIELD_REF_WITH_CASTED_RECEIVER:
185+
return CompletionItemKind.Field;
186+
case CompletionProposal.KEYWORD:
187+
return CompletionItemKind.Keyword;
188+
case CompletionProposal.LABEL_REF:
189+
return CompletionItemKind.Reference;
190+
case CompletionProposal.LOCAL_VARIABLE_REF:
191+
case CompletionProposal.VARIABLE_DECLARATION:
192+
return CompletionItemKind.Variable;
193+
case CompletionProposal.METHOD_DECLARATION:
194+
case CompletionProposal.METHOD_REF:
195+
case CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER:
196+
case CompletionProposal.POTENTIAL_METHOD_DECLARATION:
197+
return CompletionItemKind.Function;
198+
// text
199+
case CompletionProposal.ANNOTATION_ATTRIBUTE_REF:
200+
case CompletionProposal.JAVADOC_BLOCK_TAG:
201+
case CompletionProposal.JAVADOC_FIELD_REF:
202+
case CompletionProposal.JAVADOC_INLINE_TAG:
203+
case CompletionProposal.JAVADOC_METHOD_REF:
204+
case CompletionProposal.JAVADOC_PARAM_REF:
205+
case CompletionProposal.JAVADOC_TYPE_REF:
206+
case CompletionProposal.JAVADOC_VALUE_REF:
207+
default:
208+
return CompletionItemKind.Text;
209+
}
210+
}
211+
212+
@Override
213+
public void setIgnored(int completionProposalKind, boolean ignore) {
214+
super.setIgnored(completionProposalKind, ignore);
215+
if (completionProposalKind == CompletionProposal.METHOD_DECLARATION && !ignore) {
216+
setRequireExtendedContext(true);
217+
}
218+
}
219+
220+
private void acceptPotentialMethodDeclaration(CompletionProposal proposal) {
221+
try {
222+
IJavaElement enclosingElement = null;
223+
if (response.getContext().isExtended()) {
224+
enclosingElement = response.getContext().getEnclosingElement();
225+
} else if (typeRoot != null) {
226+
// kept for backward compatibility: CU is not reconciled at this moment,
227+
// information is missing (bug 70005)
228+
enclosingElement = typeRoot.getElementAt(proposal.getCompletionLocation() + 1);
229+
}
230+
if (enclosingElement == null) {
231+
return;
232+
}
233+
IType type = (IType) enclosingElement.getAncestor(IJavaElement.TYPE);
234+
if (type != null) {
235+
String prefix = String.valueOf(proposal.getName());
236+
int completionStart = proposal.getReplaceStart();
237+
int completionEnd = proposal.getReplaceEnd();
238+
int relevance = proposal.getRelevance() + 6;
239+
240+
GetterSetterCompletionProposal.evaluateProposals(type, prefix, completionStart,
241+
completionEnd - completionStart, relevance, proposals);
242+
}
243+
} catch (CoreException e) {
244+
JavaLanguageServerPlugin.logException("Accept potential method declaration failed for completion ", e);
245+
}
246+
}
247+
248+
@Override
249+
public boolean isTestCodeExcluded() {
250+
return isTestCodeExcluded;
251+
}
252+
253+
public CompletionContext getContext() {
254+
return context;
255+
}
256+
257+
}

com.microsoft.java.debug.plugin/src/main/java/com/microsoft/java/debug/plugin/internal/CompletionsProvider.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import org.eclipse.jdt.core.CompletionProposal;
2222
import org.eclipse.jdt.core.IJavaProject;
2323
import org.eclipse.jdt.core.IType;
24-
import org.eclipse.jdt.ls.core.internal.contentassist.CompletionProposalRequestor;
24+
import org.eclipse.jdt.core.ITypeRoot;
2525
import org.eclipse.jdt.ls.core.internal.handlers.JsonRpcHelpers;
2626

2727
import com.microsoft.java.debug.core.Configuration;
@@ -49,9 +49,10 @@ public List<CompletionItem> codeComplete(StackFrame frame, String snippet, int l
4949

5050
try {
5151
IType type = resolveType(frame);
52-
if (type != null && type.getCompilationUnit() != null) {
53-
final int offset = JsonRpcHelpers.toOffset(type.getCompilationUnit().getBuffer(), frame.location().lineNumber(), 0);
54-
CompletionProposalRequestor collector = new CompletionProposalRequestor(type.getCompilationUnit(), offset);
52+
if (type != null) {
53+
ITypeRoot r = type.getCompilationUnit() != null ? type.getCompilationUnit() : type.getClassFile();
54+
final int offset = JsonRpcHelpers.toOffset(r.getBuffer(), frame.location().lineNumber(), 0);
55+
CompletionProposalRequestor collector = new CompletionProposalRequestor(r, offset);
5556

5657
collector.setAllowsRequiredProposals(CompletionProposal.FIELD_REF, CompletionProposal.TYPE_REF, true);
5758
collector.setAllowsRequiredProposals(CompletionProposal.FIELD_REF, CompletionProposal.TYPE_IMPORT, true);

0 commit comments

Comments
 (0)