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+ }
0 commit comments