Skip to content

Commit 100e75f

Browse files
wip: KotlinClassRenderer
1 parent d21ce08 commit 100e75f

File tree

3 files changed

+513
-0
lines changed

3 files changed

+513
-0
lines changed

idea/src/META-INF/plugin.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,7 @@
506506
<debuggerEditorTextProvider language="kotlin" implementationClass="org.jetbrains.kotlin.idea.debugger.KotlinEditorTextProvider"/>
507507
<debuggerClassFilterProvider implementation="org.jetbrains.kotlin.idea.debugger.filter.KotlinDebuggerInternalClassesFilterProvider"/>
508508
<debugger.nodeRenderer implementation="org.jetbrains.kotlin.idea.debugger.render.KotlinClassWithDelegatedPropertyRenderer"/>
509+
<debugger.nodeRenderer implementation="org.jetbrains.kotlin.idea.debugger.render.KotlinClassRenderer"/>
509510
<debugger.sourcePositionProvider implementation="org.jetbrains.kotlin.idea.debugger.KotlinSourcePositionProvider"/>
510511
<debugger.sourcePositionHighlighter implementation="org.jetbrains.kotlin.idea.debugger.KotlinSourcePositionHighlighter"/>
511512
<debugger.frameExtraVarsProvider implementation="org.jetbrains.kotlin.idea.debugger.KotlinFrameExtraVariablesProvider"/>
Lines changed: 354 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,354 @@
1+
/*
2+
* Copyright 2010-2016 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.jetbrains.kotlin.idea.debugger.render;
17+
18+
import com.intellij.openapi.diagnostic.Logger;
19+
import com.intellij.openapi.util.Key;
20+
import com.intellij.psi.*;
21+
import com.intellij.psi.util.InheritanceUtil;
22+
import com.intellij.psi.util.PsiTreeUtil;
23+
import com.intellij.refactoring.util.RefactoringChangeUtil;
24+
import com.intellij.util.ArrayUtil;
25+
import com.intellij.util.IncorrectOperationException;
26+
import org.jetbrains.annotations.NotNull;
27+
import org.jetbrains.annotations.Nullable;
28+
import org.jetbrains.kotlin.psi.KtExpression;
29+
import org.jetbrains.kotlin.psi.KtThisExpression;
30+
31+
public class ChangeContextUtil {
32+
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.ChangeContextUtil");
33+
34+
public static final Key<String> ENCODED_KEY = Key.create("ENCODED_KEY");
35+
public static final Key<PsiClass> THIS_QUALIFIER_CLASS_KEY = Key.create("THIS_QUALIFIER_CLASS_KEY");
36+
public static final Key<PsiMember> REF_MEMBER_KEY = Key.create("REF_MEMBER_KEY");
37+
public static final Key<Boolean> CAN_REMOVE_QUALIFIER_KEY = Key.create("CAN_REMOVE_QUALIFIER_KEY");
38+
public static final Key<PsiClass> REF_CLASS_KEY = Key.create("REF_CLASS_KEY");
39+
public static final Key<PsiClass> REF_MEMBER_THIS_CLASS_KEY = Key.create("REF_MEMBER_THIS_CLASS_KEY");
40+
41+
private ChangeContextUtil() {}
42+
43+
public static void encodeContextInfo(PsiElement scope, boolean includeRefClasses) {
44+
encodeContextInfo(scope, scope, includeRefClasses, true);
45+
}
46+
47+
public static void encodeContextInfo(PsiElement scope, boolean includeRefClasses, boolean canChangeQualifier) {
48+
encodeContextInfo(scope, scope, includeRefClasses, canChangeQualifier);
49+
}
50+
51+
private static void encodeContextInfo(PsiElement scope,
52+
PsiElement topLevelScope,
53+
boolean includeRefClasses,
54+
boolean canChangeQualifier) {
55+
if (scope instanceof PsiThisExpression){
56+
scope.putCopyableUserData(ENCODED_KEY, "");
57+
58+
PsiThisExpression thisExpr = (PsiThisExpression)scope;
59+
final PsiJavaCodeReferenceElement qualifier = thisExpr.getQualifier();
60+
if (qualifier == null){
61+
PsiClass thisClass = RefactoringChangeUtil.getThisClass(thisExpr);
62+
if (thisClass != null && !(thisClass instanceof PsiAnonymousClass)){
63+
thisExpr.putCopyableUserData(THIS_QUALIFIER_CLASS_KEY, thisClass);
64+
}
65+
}
66+
else {
67+
final PsiElement resolved = qualifier.resolve();
68+
if (resolved instanceof PsiClass && resolved == topLevelScope) {
69+
thisExpr.putCopyableUserData(THIS_QUALIFIER_CLASS_KEY, (PsiClass)topLevelScope);
70+
}
71+
}
72+
}
73+
else if (scope instanceof PsiReferenceExpression){
74+
scope.putCopyableUserData(ENCODED_KEY, "");
75+
76+
PsiReferenceExpression refExpr = (PsiReferenceExpression)scope;
77+
PsiExpression qualifier = refExpr.getQualifierExpression();
78+
if (qualifier == null){
79+
final JavaResolveResult resolveResult = refExpr.advancedResolve(false);
80+
final PsiElement refElement = resolveResult.getElement();
81+
if (refElement != null && !PsiTreeUtil.isAncestor(topLevelScope, refElement, false)){
82+
if (refElement instanceof PsiClass){
83+
if (includeRefClasses){
84+
refExpr.putCopyableUserData(REF_CLASS_KEY, (PsiClass)refElement);
85+
}
86+
}
87+
else if (refElement instanceof PsiMember){
88+
refExpr.putCopyableUserData(REF_MEMBER_KEY, ( (PsiMember)refElement));
89+
final PsiElement resolveScope = resolveResult.getCurrentFileResolveScope();
90+
if (resolveScope instanceof PsiClass && !PsiTreeUtil.isAncestor(topLevelScope, resolveScope, false)) {
91+
refExpr.putCopyableUserData(REF_MEMBER_THIS_CLASS_KEY, (PsiClass)resolveScope);
92+
}
93+
}
94+
}
95+
}
96+
else if (canChangeQualifier) {
97+
refExpr.putCopyableUserData(CAN_REMOVE_QUALIFIER_KEY, canRemoveQualifier(refExpr));
98+
}
99+
}
100+
else if (includeRefClasses) {
101+
PsiReference ref = scope.getReference();
102+
if (ref != null){
103+
scope.putCopyableUserData(ENCODED_KEY, "");
104+
105+
PsiElement refElement = ref.resolve();
106+
if (refElement instanceof PsiClass && !PsiTreeUtil.isAncestor(topLevelScope, refElement, false)){
107+
scope.putCopyableUserData(REF_CLASS_KEY, (PsiClass)refElement);
108+
}
109+
}
110+
}
111+
112+
for(PsiElement child = scope.getFirstChild(); child != null; child = child.getNextSibling()){
113+
encodeContextInfo(child, topLevelScope, includeRefClasses, canChangeQualifier);
114+
}
115+
}
116+
117+
public static PsiElement decodeContextInfo(@NotNull PsiElement scope,
118+
@Nullable PsiClass thisClass,
119+
@Nullable KtExpression thisAccessExpr) throws IncorrectOperationException {
120+
if (scope.getCopyableUserData(ENCODED_KEY) != null) {
121+
scope.putCopyableUserData(ENCODED_KEY, null);
122+
123+
if (scope instanceof PsiThisExpression) {
124+
PsiThisExpression thisExpr = (PsiThisExpression)scope;
125+
scope = decodeThisExpression(thisExpr, thisClass, thisAccessExpr);
126+
}
127+
else if (scope instanceof PsiReferenceExpression) {
128+
scope = decodeReferenceExpression((PsiReferenceExpression)scope, thisAccessExpr, thisClass);
129+
}
130+
else {
131+
PsiClass refClass = scope.getCopyableUserData(REF_CLASS_KEY);
132+
scope.putCopyableUserData(REF_CLASS_KEY, null);
133+
134+
if (refClass != null && refClass.isValid()) {
135+
PsiReference ref = scope.getReference();
136+
if (ref != null) {
137+
final String qualifiedName = refClass.getQualifiedName();
138+
if (qualifiedName != null) {
139+
if (JavaPsiFacade.getInstance(refClass.getProject()).findClass(qualifiedName, scope.getResolveScope()) != null) {
140+
scope = ref.bindToElement(refClass);
141+
}
142+
}
143+
}
144+
}
145+
}
146+
}
147+
148+
if (scope instanceof PsiClass) {
149+
if (thisAccessExpr != null) {
150+
thisAccessExpr = (KtExpression) qualifyThis(thisAccessExpr, thisClass);
151+
}
152+
}
153+
154+
PsiElement child = scope.getFirstChild();
155+
while (child != null) {
156+
child = decodeContextInfo(child, thisClass, thisAccessExpr).getNextSibling();
157+
}
158+
159+
return scope;
160+
}
161+
162+
private static PsiElement decodeThisExpression(PsiThisExpression thisExpr,
163+
PsiClass thisClass,
164+
KtExpression thisAccessExpr) throws IncorrectOperationException {
165+
final PsiJavaCodeReferenceElement qualifier = thisExpr.getQualifier();
166+
PsiClass encodedQualifierClass = thisExpr.getCopyableUserData(THIS_QUALIFIER_CLASS_KEY);
167+
thisExpr.putCopyableUserData(THIS_QUALIFIER_CLASS_KEY, null);
168+
if (qualifier == null){
169+
if (encodedQualifierClass != null && encodedQualifierClass.isValid()){
170+
if (encodedQualifierClass.equals(thisClass) && thisAccessExpr != null && thisAccessExpr.isValid()){
171+
return thisExpr.replace(thisAccessExpr);
172+
}
173+
}
174+
}
175+
else {
176+
PsiClass qualifierClass = (PsiClass)qualifier.resolve();
177+
if (encodedQualifierClass == qualifierClass && thisClass != null) {
178+
qualifier.bindToElement(thisClass);
179+
}
180+
else {
181+
if (qualifierClass != null) {
182+
if (qualifierClass.equals(thisClass) && thisAccessExpr != null && thisAccessExpr.isValid()) {
183+
return thisExpr.replace(thisAccessExpr);
184+
}
185+
}
186+
}
187+
}
188+
return thisExpr;
189+
}
190+
191+
private static PsiReferenceExpression decodeReferenceExpression(@NotNull PsiReferenceExpression refExpr,
192+
KtExpression thisAccessExpr,
193+
PsiClass thisClass) throws IncorrectOperationException {
194+
PsiManager manager = refExpr.getManager();
195+
PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
196+
197+
PsiExpression qualifier = refExpr.getQualifierExpression();
198+
if (qualifier == null){
199+
PsiMember refMember = refExpr.getCopyableUserData(REF_MEMBER_KEY);
200+
refExpr.putCopyableUserData(REF_MEMBER_KEY, null);
201+
202+
if (refMember != null && refMember.isValid()){
203+
PsiClass containingClass = refMember.getContainingClass();
204+
if (refMember.hasModifierProperty(PsiModifier.STATIC)){
205+
PsiElement refElement = refExpr.resolve();
206+
if (!manager.areElementsEquivalent(refMember, refElement)){
207+
final PsiClass currentClass = PsiTreeUtil.getParentOfType(refExpr, PsiClass.class);
208+
if (currentClass == null || !InheritanceUtil.isInheritorOrSelf(currentClass, containingClass, true)) {
209+
refExpr.setQualifierExpression(factory.createReferenceExpression(containingClass));
210+
}
211+
}
212+
}
213+
else {
214+
final PsiClass realParentClass = refExpr.getCopyableUserData(REF_MEMBER_THIS_CLASS_KEY);
215+
refExpr.putCopyableUserData(REF_MEMBER_THIS_CLASS_KEY, null);
216+
if (thisAccessExpr != null && thisClass != null && realParentClass != null &&
217+
InheritanceUtil.isInheritorOrSelf(thisClass, realParentClass, true)) {
218+
boolean needQualifier = true;
219+
PsiElement refElement = refExpr.resolve();
220+
if (refMember.equals(refElement) ||
221+
(refElement instanceof PsiMethod && refMember instanceof PsiMethod && ArrayUtil.find(((PsiMethod)refElement).findSuperMethods(), refMember) > -1)){
222+
if (thisAccessExpr instanceof PsiThisExpression && ((PsiThisExpression)thisAccessExpr).getQualifier() == null) {
223+
//Trivial qualifier
224+
needQualifier = false;
225+
}
226+
else {
227+
final PsiClass currentClass = findThisClass(refExpr, refMember);
228+
if (thisAccessExpr instanceof PsiThisExpression){
229+
PsiJavaCodeReferenceElement thisQualifier = ((PsiThisExpression)thisAccessExpr).getQualifier();
230+
PsiClass thisExprClass = thisQualifier != null
231+
? (PsiClass)thisQualifier.resolve()
232+
: RefactoringChangeUtil.getThisClass(refExpr);
233+
if (currentClass.equals(thisExprClass) || thisExprClass.isInheritor(realParentClass, true)){ // qualifier is not necessary
234+
needQualifier = false;
235+
}
236+
}
237+
}
238+
}
239+
240+
if (needQualifier){
241+
//refExpr.setQualifierExpression(thisAccessExpr);
242+
}
243+
}
244+
else if (thisClass != null && realParentClass != null && PsiTreeUtil.isAncestor(realParentClass, thisClass, true)) {
245+
PsiElement refElement = refExpr.resolve();
246+
if (refElement != null && !manager.areElementsEquivalent(refMember, refElement)) {
247+
refExpr = RefactoringChangeUtil.qualifyReference(refExpr, refMember, null);
248+
}
249+
}
250+
}
251+
}
252+
else {
253+
PsiClass refClass = refExpr.getCopyableUserData(REF_CLASS_KEY);
254+
refExpr.putCopyableUserData(REF_CLASS_KEY, null);
255+
if (refClass != null && refClass.isValid()){
256+
refExpr = (PsiReferenceExpression)refExpr.bindToElement(refClass);
257+
}
258+
}
259+
}
260+
else{
261+
Boolean couldRemove = refExpr.getCopyableUserData(CAN_REMOVE_QUALIFIER_KEY);
262+
refExpr.putCopyableUserData(CAN_REMOVE_QUALIFIER_KEY, null);
263+
264+
if (couldRemove == Boolean.FALSE && canRemoveQualifier(refExpr)){
265+
PsiReferenceExpression newRefExpr = (PsiReferenceExpression)factory.createExpressionFromText(
266+
refExpr.getReferenceName(), null);
267+
refExpr = (PsiReferenceExpression)refExpr.replace(newRefExpr);
268+
}
269+
}
270+
return refExpr;
271+
}
272+
273+
private static PsiClass findThisClass(PsiReferenceExpression refExpr, PsiMember refMember) {
274+
LOG.assertTrue(refExpr.getQualifierExpression() == null);
275+
final PsiClass refMemberClass = refMember.getContainingClass();
276+
if (refMemberClass == null) return null;
277+
PsiElement parent = refExpr.getContext();
278+
while(parent != null){
279+
if (parent instanceof PsiClass){
280+
if (parent.equals(refMemberClass) || ((PsiClass)parent).isInheritor(refMemberClass, true)){
281+
return (PsiClass)parent;
282+
}
283+
}
284+
parent = parent.getContext();
285+
}
286+
287+
return refMemberClass;
288+
}
289+
290+
public static boolean canRemoveQualifier(PsiReferenceExpression refExpr) {
291+
try{
292+
PsiExpression qualifier = refExpr.getQualifierExpression();
293+
if (!(qualifier instanceof PsiReferenceExpression)) return false;
294+
if (refExpr.getTypeParameters().length > 0) return false;
295+
PsiElement qualifierRefElement = ((PsiReferenceExpression)qualifier).resolve();
296+
if (!(qualifierRefElement instanceof PsiClass)) return false;
297+
PsiElement refElement = refExpr.resolve();
298+
if (refElement == null) return false;
299+
PsiElementFactory factory = JavaPsiFacade.getInstance(refExpr.getProject()).getElementFactory();
300+
if (refExpr.getParent() instanceof PsiMethodCallExpression){
301+
PsiMethodCallExpression methodCall = (PsiMethodCallExpression)refExpr.getParent();
302+
PsiMethodCallExpression newMethodCall = (PsiMethodCallExpression)factory.createExpressionFromText(
303+
refExpr.getReferenceName() + "()", refExpr);
304+
newMethodCall.getArgumentList().replace(methodCall.getArgumentList());
305+
PsiElement newRefElement = newMethodCall.getMethodExpression().resolve();
306+
return refElement.equals(newRefElement);
307+
}
308+
else if (refExpr instanceof PsiMethodReferenceExpression) {
309+
return false;
310+
}
311+
else {
312+
PsiReferenceExpression newRefExpr = (PsiReferenceExpression)factory.createExpressionFromText(
313+
refExpr.getReferenceName(), refExpr);
314+
PsiElement newRefElement = newRefExpr.resolve();
315+
return refElement.equals(newRefElement);
316+
}
317+
}
318+
catch(IncorrectOperationException e){
319+
LOG.error(e);
320+
return false;
321+
}
322+
}
323+
324+
private static PsiElement qualifyThis(PsiElement scope, PsiClass thisClass) throws IncorrectOperationException {
325+
if (scope instanceof KtThisExpression){
326+
KtThisExpression thisExpr = (KtThisExpression)scope;
327+
//todo
328+
if (thisExpr.getLabelQualifier() == null){
329+
if (thisClass instanceof PsiAnonymousClass) return null;
330+
PsiThisExpression qualifiedThis = RefactoringChangeUtil.createThisExpression(thisClass.getManager(), thisClass);
331+
if (thisExpr.getParent() != null) {
332+
return thisExpr.replace(qualifiedThis);
333+
} else {
334+
return qualifiedThis;
335+
}
336+
}
337+
}
338+
else if (!(scope instanceof PsiClass)){
339+
for(PsiElement child = scope.getFirstChild(); child != null; child = child.getNextSibling()){
340+
if (qualifyThis(child, thisClass) == null) return null;
341+
}
342+
}
343+
return scope;
344+
}
345+
346+
public static void clearContextInfo(PsiElement scope) {
347+
scope.putCopyableUserData(THIS_QUALIFIER_CLASS_KEY, null);
348+
scope.putCopyableUserData(REF_MEMBER_KEY, null);
349+
scope.putCopyableUserData(CAN_REMOVE_QUALIFIER_KEY, null);
350+
for(PsiElement child = scope.getFirstChild(); child != null; child = child.getNextSibling()){
351+
clearContextInfo(child);
352+
}
353+
}
354+
}

0 commit comments

Comments
 (0)