1.11 Add json key jump to field | 增加 json 跳转到字段

This commit is contained in:
林万程
2022-03-15 23:31:44 +08:00
parent 3045abd416
commit ac4ed47c9f
8 changed files with 220 additions and 112 deletions

View File

@@ -0,0 +1,72 @@
package io.github.linwancen.plugin.show;
import com.intellij.json.psi.JsonProperty;
import com.intellij.json.psi.JsonStringLiteral;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ProcessingContext;
import io.github.linwancen.plugin.show.doc.PsiClassUtils;
import io.github.linwancen.plugin.show.json.JsonRef;
import io.github.linwancen.plugin.show.json.JsonUtils;
import org.jetbrains.annotations.NotNull;
import java.util.*;
public class JsonJump extends PsiReferenceContributor {
@Override
public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) {
registrar.registerReferenceProvider(PlatformPatterns.psiElement(JsonStringLiteral.class),
new PsiReferenceProvider() {
@Override
public PsiReference @NotNull [] getReferencesByElement(@NotNull PsiElement element,
@NotNull ProcessingContext context) {
JsonProperty jsonProp = PsiTreeUtil.getParentOfType(element, JsonProperty.class, true);
if (jsonProp == null) {
return PsiReference.EMPTY_ARRAY;
}
VirtualFile virtualFile = element.getContainingFile().getVirtualFile();
if (virtualFile == null) {
return PsiReference.EMPTY_ARRAY;
}
Project project = element.getProject();
List<PsiField> psiFields = new ArrayList<>();
List<PsiField> tips = new ArrayList<>();
PsiClass[] psiClasses = PsiClassUtils.encClass(virtualFile, project);
List<String> jsonPath = JsonUtils.jsonPath(jsonProp);
put(project, psiFields, tips, psiClasses, jsonPath, jsonPath.size() - 1);
List<PsiReference> list = new ArrayList<>();
for (PsiField psiField : psiFields) {
list.add(new JsonRef(element, psiField, tips));
}
return list.toArray(new PsiReference[0]);
}
});
}
private static void put(Project project, List<PsiField> psiFields, List<PsiField> tips,
PsiClass[] psiClasses, List<String> jsonPath, int level) {
String name = jsonPath.get(level);
for (PsiClass psiClass : psiClasses) {
if (level == 1) {
tips.addAll(Arrays.asList(psiClass.getAllFields()));
}
PsiField psiField = psiClass.findFieldByName(name, true);
if (psiField == null) {
continue;
}
if (level == 0) {
psiFields.add(psiField);
} else {
String classFullName = PsiClassUtils.toClassFullName(psiField);
PsiClass[] classes = PsiClassUtils.fullNameToClass(classFullName, project);
put(project, psiFields, tips, classes, jsonPath, level - 1);
}
}
}
}

View File

@@ -1,59 +1,31 @@
package io.github.linwancen.plugin.show.doc;
import com.intellij.json.psi.JsonProperty;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.PsiDocCommentOwner;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiReference;
import com.intellij.psi.javadoc.PsiDocComment;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class JsonDocUtils {
private JsonDocUtils() {}
/**
* depend on JsonJump
*/
@Nullable
public static PsiDocComment jsonDoc(PsiElement element, int startOffset, int endOffset) {
JsonProperty jsonProp = PsiTreeUtil.getParentOfType(element, JsonProperty.class, true, startOffset);
if (jsonProp == null || jsonProp.getNameElement().getTextRange().getEndOffset() > endOffset) {
return null;
}
VirtualFile virtualFile = element.getContainingFile().getVirtualFile();
PsiClass[] psiClasses = PsiClassUtils.encClass(virtualFile, element.getProject());
List<String> jsonPath = jsonPath(jsonProp);
return doc(psiClasses, element.getProject(), jsonPath, jsonPath.size() - 1);
}
@NotNull
public static List<String> jsonPath(JsonProperty jsonProp) {
ArrayList<String> jsonPath = new ArrayList<>();
do {
jsonPath.add(jsonProp.getName());
} while ((jsonProp = PsiTreeUtil.getParentOfType(jsonProp, JsonProperty.class)) != null);
return jsonPath;
}
@Nullable
private static PsiDocComment doc(PsiClass[] psiClasses, Project project, List<String> jsonPath, int level) {
String name = jsonPath.get(level);
for (PsiClass psiClass : psiClasses) {
PsiField psiField = psiClass.findFieldByName(name, true);
if (psiField == null) {
continue;
}
if (level == 0) {
PsiDocComment docComment = DocUtils.srcOrByteCodeDoc(psiField);
if (docComment != null) {
return docComment;
}
} else {
String classFullName = toClassFullName(psiField);
PsiClass[] classes = PsiClassUtils.fullNameToClass(classFullName, project);
PsiDocComment docComment = doc(classes, project, jsonPath, level - 1);
for (PsiReference reference : jsonProp.getNameElement().getReferences()) {
PsiElement resolve = reference.resolve();
if (resolve instanceof PsiDocCommentOwner) {
PsiDocCommentOwner owner = (PsiDocCommentOwner) resolve;
PsiDocComment docComment = DocUtils.srcOrByteCodeDoc(owner);
if (docComment != null) {
return docComment;
}
@@ -61,26 +33,4 @@ public class JsonDocUtils {
}
return null;
}
@NotNull
private static String toClassFullName(PsiField psiField) {
// <> only in .java
PsiElement navElement = psiField.getNavigationElement();
if (navElement instanceof PsiField) {
psiField = (PsiField) navElement;
}
PsiTypeElement typeElement = psiField.getTypeElement();
if (typeElement != null) {
PsiJavaCodeReferenceElement code = typeElement.getInnermostComponentReferenceElement();
if (code != null) {
PsiType[] types = code.getTypeParameters();
if (types.length > 0) {
// List
return types[types.length - 1].getCanonicalText();
}
}
}
// Array
return psiField.getType().getDeepComponentType().getCanonicalText();
}
}

View File

@@ -2,8 +2,7 @@ package io.github.linwancen.plugin.show.doc;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiClass;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiShortNamesCache;
import org.jetbrains.annotations.NotNull;
@@ -51,4 +50,18 @@ public class PsiClassUtils {
JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance(project);
return javaPsiFacade.findClasses(className, GlobalSearchScope.allScope(project));
}
@NotNull
public static String toClassFullName(PsiField psiField) {
// Array
// use replace simpler than getDeepComponentType()
String typeName = psiField.getType().getCanonicalText().replace("[]", "");
// List
// use substring() because clsFieldImpl.getInnermostComponentReferenceElement() == null
int index = typeName.indexOf("<");
if (index >= 0) {
return typeName.substring(index + 1, typeName.length() - 1);
}
return typeName;
}
}

View File

@@ -0,0 +1,41 @@
package io.github.linwancen.plugin.show.json;
import com.intellij.psi.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class JsonRef extends PsiReferenceBase<PsiElement> implements PsiPolyVariantReference {
final PsiField psiField;
final List<PsiField> tips;
public JsonRef(@NotNull PsiElement element, @NotNull PsiField psiField, @NotNull List<PsiField> tips) {
super(element);
this.psiField = psiField;
this.tips = tips;
}
/**
* do not use it because PsiReference.resolveReference() is @Experimental
*/
@Override
public ResolveResult @NotNull [] multiResolve(boolean incompleteCode) {
return new ResolveResult[]{new PsiElementResolveResult(psiField)};
}
@Nullable
@Override
public PsiElement resolve() {
return psiField;
}
/**
* I don't know how to use it
*/
@Override
public Object @NotNull [] getVariants() {
return tips.toArray();
}
}

View File

@@ -0,0 +1,22 @@
package io.github.linwancen.plugin.show.json;
import com.intellij.json.psi.JsonProperty;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
public class JsonUtils {
private JsonUtils() {}
@NotNull
public static List<String> jsonPath(JsonProperty jsonProp) {
ArrayList<String> jsonPath = new ArrayList<>();
do {
jsonPath.add(jsonProp.getName());
} while ((jsonProp = PsiTreeUtil.getParentOfType(jsonProp, JsonProperty.class)) != null);
return jsonPath;
}
}