Skip to content

Commit e843c2c

Browse files
CsCherrYYrgrunber
authored andcommitted
Add support for LSP-standard TypeHierarchy
But keep legacy command which is still used by vscode-java. Signed-off-by: Shi Chen <[email protected]> Also-by: Mickael Istria <[email protected]>
1 parent eba4458 commit e843c2c

File tree

4 files changed

+471
-0
lines changed

4 files changed

+471
-0
lines changed

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/InitHandler.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ public void registerCapabilities(InitializeResult initializeResult) {
221221
semanticTokensOptions.setDocumentSelector(List.of(new DocumentFilter("java", "file", null), new DocumentFilter("java", "jdt", null)));
222222
semanticTokensOptions.setLegend(SemanticTokensHandler.legend());
223223
capabilities.setSemanticTokensProvider(semanticTokensOptions);
224+
capabilities.setTypeHierarchyProvider(Boolean.TRUE);
224225

225226
initializeResult.setCapabilities(capabilities);
226227
}

org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/JDTLanguageServer.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@
147147
import org.eclipse.lsp4j.TextDocumentIdentifier;
148148
import org.eclipse.lsp4j.TextEdit;
149149
import org.eclipse.lsp4j.TypeDefinitionParams;
150+
import org.eclipse.lsp4j.TypeHierarchyItem;
151+
import org.eclipse.lsp4j.TypeHierarchyPrepareParams;
152+
import org.eclipse.lsp4j.TypeHierarchySubtypesParams;
153+
import org.eclipse.lsp4j.TypeHierarchySupertypesParams;
150154
import org.eclipse.lsp4j.WillSaveTextDocumentParams;
151155
import org.eclipse.lsp4j.WorkspaceEdit;
152156
import org.eclipse.lsp4j.WorkspaceSymbol;
@@ -180,6 +184,7 @@ public class JDTLanguageServer extends BaseJDTLanguageServer implements Language
180184
private ClasspathUpdateHandler classpathUpdateHandler;
181185
private JVMConfigurator jvmConfigurator;
182186
private WorkspaceExecuteCommandHandler commandHandler;
187+
private TypeHierarchyHandler typeHierarchyHandler = new TypeHierarchyHandler();
183188

184189
private ProgressReporterManager progressReporterManager;
185190
/**
@@ -1173,6 +1178,24 @@ public CompletableFuture<CheckExtractInterfaceResponse> checkExtractInterfaceSta
11731178
return computeAsync((monitor) -> ExtractInterfaceHandler.checkExtractInterfaceStatus(params));
11741179
}
11751180

1181+
@Override
1182+
public CompletableFuture<List<TypeHierarchyItem>> prepareTypeHierarchy(TypeHierarchyPrepareParams params) {
1183+
logInfo(">> textDocument/prepareTypeHierarchy");
1184+
return computeAsync(monitor -> typeHierarchyHandler.prepareTypeHierarchy(params, monitor));
1185+
}
1186+
1187+
@Override
1188+
public CompletableFuture<List<TypeHierarchyItem>> typeHierarchySupertypes(TypeHierarchySupertypesParams params) {
1189+
logInfo(">> typeHierarchy/supertypes");
1190+
return computeAsync(monitor -> typeHierarchyHandler.getSupertypeItems(params, monitor));
1191+
}
1192+
1193+
@Override
1194+
public CompletableFuture<List<TypeHierarchyItem>> typeHierarchySubtypes(TypeHierarchySubtypesParams params) {
1195+
logInfo(">> typeHierarchy/subtypes");
1196+
return computeAsync(monitor -> typeHierarchyHandler.getSubtypeItems(params, monitor));
1197+
}
1198+
11761199
private <R> CompletableFuture<R> computeAsyncWithClientProgress(Function<IProgressMonitor, R> code) {
11771200
return CompletableFutures.computeAsync((cc) -> {
11781201
IProgressMonitor monitor = progressReporterManager.getProgressReporter(cc);
Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,282 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2021 Microsoft Corporation and others.
3+
* All rights reserved. This program and the accompanying materials
4+
* are made available under the terms of the Eclipse Public License 2.0
5+
* which accompanies this distribution, and is available at
6+
* https://www.eclipse.org/legal/epl-2.0/
7+
*
8+
* SPDX-License-Identifier: EPL-2.0
9+
*
10+
* Contributors:
11+
* Microsoft Corporation - initial API and implementation
12+
*******************************************************************************/
13+
package org.eclipse.jdt.ls.core.internal.handlers;
14+
15+
import java.util.ArrayList;
16+
import java.util.Arrays;
17+
import java.util.Collections;
18+
import java.util.HashMap;
19+
import java.util.List;
20+
import java.util.Map;
21+
22+
import org.eclipse.core.runtime.IProgressMonitor;
23+
import org.eclipse.jdt.core.ICompilationUnit;
24+
import org.eclipse.jdt.core.IJavaElement;
25+
import org.eclipse.jdt.core.IMember;
26+
import org.eclipse.jdt.core.IMethod;
27+
import org.eclipse.jdt.core.IOrdinaryClassFile;
28+
import org.eclipse.jdt.core.IPackageFragment;
29+
import org.eclipse.jdt.core.IType;
30+
import org.eclipse.jdt.core.ITypeHierarchy;
31+
import org.eclipse.jdt.core.ITypeRoot;
32+
import org.eclipse.jdt.core.JavaCore;
33+
import org.eclipse.jdt.core.JavaModelException;
34+
import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner;
35+
import org.eclipse.jdt.internal.core.JavaModelManager;
36+
import org.eclipse.jdt.ls.core.internal.JDTUtils;
37+
import org.eclipse.jdt.ls.core.internal.JDTUtils.LocationType;
38+
import org.eclipse.jdt.ls.core.internal.JSONUtility;
39+
import org.eclipse.jdt.ls.core.internal.JavaLanguageServerPlugin;
40+
import org.eclipse.lsp4j.Location;
41+
import org.eclipse.lsp4j.Position;
42+
import org.eclipse.lsp4j.Range;
43+
import org.eclipse.lsp4j.SymbolKind;
44+
import org.eclipse.lsp4j.SymbolTag;
45+
import org.eclipse.lsp4j.TextDocumentIdentifier;
46+
import org.eclipse.lsp4j.TypeHierarchyItem;
47+
import org.eclipse.lsp4j.TypeHierarchyPrepareParams;
48+
import org.eclipse.lsp4j.TypeHierarchySubtypesParams;
49+
import org.eclipse.lsp4j.TypeHierarchySupertypesParams;
50+
51+
public class TypeHierarchyHandler {
52+
53+
public enum TypeHierarchyDirection {
54+
Subtype, Supertype;
55+
}
56+
57+
private static class TypeHierarchyItemData {
58+
private String handleIdentifier;
59+
private String methodIdentifier;
60+
private String methodName;
61+
62+
public TypeHierarchyItemData(String handleIdentifier, String methodIdentifier, String methodName) {
63+
this.handleIdentifier = handleIdentifier;
64+
this.methodIdentifier = methodIdentifier;
65+
this.methodName = methodName;
66+
}
67+
68+
private static TypeHierarchyItemData getTypeHierarchyItemData(Object data) {
69+
if (data == null) {
70+
return null;
71+
}
72+
Map<String, String> map = JSONUtility.toModel(data, Map.class);
73+
String handleIdentifier = map.get("element");
74+
String methodIdentifier = map.get("method");
75+
String methodName = map.get("method_name");
76+
return new TypeHierarchyItemData(handleIdentifier, methodIdentifier, methodName);
77+
}
78+
}
79+
80+
public List<TypeHierarchyItem> prepareTypeHierarchy(TypeHierarchyPrepareParams params, IProgressMonitor monitor) {
81+
if (params == null) {
82+
return Collections.emptyList();
83+
}
84+
TextDocumentIdentifier textDocument = params.getTextDocument();
85+
if (textDocument == null) {
86+
return Collections.emptyList();
87+
}
88+
Position position = params.getPosition();
89+
String uri = textDocument.getUri();
90+
return getTypeHierarchyItems(uri, position, monitor);
91+
}
92+
93+
private List<TypeHierarchyItem> getTypeHierarchyItems(String uri, Position position, IProgressMonitor monitor) {
94+
if (uri == null || position == null) {
95+
return Collections.emptyList();
96+
}
97+
try {
98+
IMember member = getMember(uri, position, monitor);
99+
IMethod targetMethod = null;
100+
if (member instanceof IMethod) {
101+
targetMethod = (IMethod) member;
102+
}
103+
TypeHierarchyItem item = targetMethod == null ? TypeHierarchyHandler.toTypeHierarchyItem(member) : TypeHierarchyHandler.toTypeHierarchyItem(member, false, targetMethod);
104+
if (item == null) {
105+
return Collections.emptyList();
106+
}
107+
return Arrays.asList(item);
108+
} catch (JavaModelException e) {
109+
return Collections.emptyList();
110+
}
111+
}
112+
113+
public List<TypeHierarchyItem> getSupertypeItems(TypeHierarchySupertypesParams params, IProgressMonitor monitor) {
114+
return getTypeHierarchyItems(params.getItem(), TypeHierarchyDirection.Supertype, monitor);
115+
}
116+
117+
public List<TypeHierarchyItem> getSubtypeItems(TypeHierarchySubtypesParams params, IProgressMonitor monitor) {
118+
return getTypeHierarchyItems(params.getItem(), TypeHierarchyDirection.Subtype, monitor);
119+
}
120+
121+
private List<TypeHierarchyItem> getTypeHierarchyItems(TypeHierarchyItem item, TypeHierarchyDirection direction, IProgressMonitor monitor) {
122+
TypeHierarchyItemData data = TypeHierarchyItemData.getTypeHierarchyItemData(item.getData());
123+
if (data == null) {
124+
return Collections.emptyList();
125+
}
126+
IJavaElement element = JavaCore.create(data.handleIdentifier);
127+
IMember member = null;
128+
IMethod targetMethod = null;
129+
if (data.methodIdentifier != null) {
130+
targetMethod = (IMethod) JavaCore.create(data.methodIdentifier);
131+
}
132+
if (element instanceof IType || element instanceof IMethod) {
133+
member = (IMember) element;
134+
} else if (element instanceof IOrdinaryClassFile classFile) {
135+
member = classFile.getType();
136+
} else {
137+
return Collections.emptyList();
138+
}
139+
return resolveTypeHierarchyItems(member, targetMethod, direction, monitor);
140+
}
141+
142+
private List<TypeHierarchyItem> resolveTypeHierarchyItems(IMember member, IMethod targetMethod, TypeHierarchyDirection direction, IProgressMonitor monitor) {
143+
if (monitor.isCanceled()) {
144+
return Collections.emptyList();
145+
}
146+
IType type = null;
147+
if (member instanceof IType) {
148+
type = (IType) member;
149+
} else {
150+
type = member.getDeclaringType();
151+
}
152+
try {
153+
ITypeHierarchy typeHierarchy = null;
154+
List<TypeHierarchyItem> items = new ArrayList<>();
155+
IType[] hierarchyTypes = null;
156+
if (direction == TypeHierarchyDirection.Supertype) {
157+
typeHierarchy = type.newSupertypeHierarchy(DefaultWorkingCopyOwner.PRIMARY, monitor);
158+
hierarchyTypes = typeHierarchy.getSupertypes(type);
159+
} else {
160+
ICompilationUnit[] workingCopies = JavaModelManager.getJavaModelManager().getWorkingCopies(DefaultWorkingCopyOwner.PRIMARY, true);
161+
typeHierarchy = type.newTypeHierarchy(workingCopies, monitor);
162+
hierarchyTypes = typeHierarchy.getSubtypes(type);
163+
}
164+
for (IType hierarchyType : hierarchyTypes) {
165+
if (monitor.isCanceled()) {
166+
return Collections.emptyList();
167+
}
168+
TypeHierarchyItem item = null;
169+
if (targetMethod != null) {
170+
IMethod[] matches = hierarchyType.findMethods(targetMethod);
171+
boolean excludeMember = matches == null || matches.length == 0;
172+
// Do not show java.lang.Object unless target method is based there
173+
if (!excludeMember || !"java.lang.Object".equals(hierarchyType.getFullyQualifiedName())) {
174+
item = TypeHierarchyHandler.toTypeHierarchyItem(excludeMember ? hierarchyType : matches[0], excludeMember, targetMethod);
175+
}
176+
} else {
177+
item = TypeHierarchyHandler.toTypeHierarchyItem(hierarchyType);
178+
}
179+
if (item == null) {
180+
continue;
181+
}
182+
items.add(item);
183+
}
184+
return items;
185+
} catch (JavaModelException e) {
186+
return Collections.emptyList();
187+
}
188+
}
189+
190+
private IMember getMember(String uri, Position position, IProgressMonitor monitor) throws JavaModelException {
191+
IJavaElement typeElement = findTypeElement(JDTUtils.resolveTypeRoot(uri), position, monitor);
192+
if (typeElement instanceof IType type) {
193+
return type;
194+
} else if (typeElement instanceof IMethod method) {
195+
return method;
196+
} else {
197+
return null;
198+
}
199+
}
200+
201+
private static IJavaElement findTypeElement(ITypeRoot unit, Position position, IProgressMonitor monitor) throws JavaModelException {
202+
if (unit == null) {
203+
return null;
204+
}
205+
IJavaElement element = JDTUtils.findElementAtSelection(unit, position.getLine(), position.getCharacter(), JavaLanguageServerPlugin.getPreferencesManager(), monitor);
206+
if (element == null) {
207+
if (unit instanceof IOrdinaryClassFile classFile) {
208+
element = classFile.getType();
209+
} else if (unit instanceof ICompilationUnit) {
210+
element = unit.findPrimaryType();
211+
}
212+
}
213+
return element;
214+
}
215+
216+
private static TypeHierarchyItem toTypeHierarchyItem(IMember member) throws JavaModelException {
217+
return toTypeHierarchyItem(member, false, null);
218+
}
219+
220+
private static TypeHierarchyItem toTypeHierarchyItem(IMember member, boolean excludeMember, IMethod targetMethod) throws JavaModelException {
221+
if (member == null) {
222+
return null;
223+
}
224+
Location location = getLocation(member, LocationType.FULL_RANGE);
225+
Location selectLocation = getLocation(member, LocationType.NAME_RANGE);
226+
if (location == null || selectLocation == null) {
227+
return null;
228+
}
229+
230+
Range range = location.getRange();
231+
String uri = location.getUri();
232+
Range selectionRange = selectLocation.getRange();
233+
234+
IType type = null;
235+
if (member instanceof IType) {
236+
type = (IType) member;
237+
} else {
238+
type = member.getDeclaringType();
239+
}
240+
241+
String name = null;
242+
String detail = null;
243+
String fullyQualifiedName = type.getFullyQualifiedName();
244+
int index = fullyQualifiedName.lastIndexOf('.');
245+
if (index >= 1 && index < fullyQualifiedName.length() - 1 && !type.isAnonymous()) {
246+
name = fullyQualifiedName.substring(index + 1);
247+
detail = fullyQualifiedName.substring(0, index);
248+
} else {
249+
name = JDTUtils.getName(type);
250+
IPackageFragment packageFragment = type.getPackageFragment();
251+
if (packageFragment != null) {
252+
detail = packageFragment.getElementName();
253+
}
254+
}
255+
SymbolKind kind = excludeMember ? SymbolKind.Null : DocumentSymbolHandler.mapKind(type);
256+
List<SymbolTag> tags = new ArrayList<>();
257+
if (JDTUtils.isDeprecated(member)) {
258+
tags.add(SymbolTag.Deprecated);
259+
}
260+
Map<String, String> data = new HashMap<>();
261+
data.put("element", member.getHandleIdentifier());
262+
if (targetMethod != null) {
263+
data.put("method", targetMethod.getHandleIdentifier());
264+
data.put("method_name", targetMethod.getElementName());
265+
} else if (member instanceof IMethod) {
266+
data.put("method", member.getHandleIdentifier());
267+
data.put("method_name", member.getElementName());
268+
}
269+
TypeHierarchyItem item = new TypeHierarchyItem(name, kind, uri, range, selectionRange, detail);
270+
item.setTags(tags);
271+
item.setData(data);
272+
return item;
273+
}
274+
275+
private static Location getLocation(IMember member, LocationType locationType) throws JavaModelException {
276+
Location location = locationType.toLocation(member);
277+
if (location == null && member.getClassFile() != null) {
278+
location = JDTUtils.toLocation(member.getClassFile());
279+
}
280+
return location;
281+
}
282+
}

0 commit comments

Comments
 (0)