From 802c4c6676c4ec51bbb9330a29e90ecb2e90828c Mon Sep 17 00:00:00 2001 From: Anton Lobov Date: Tue, 27 Mar 2018 11:55:31 +0200 Subject: [PATCH] WEB-31883, WEB-31989: array indexes in JSON breadcrumbs/qualified names --- json/intellij.json.iml | 1 - json/src/com/intellij/json/JsonUtil.java | 17 ++++++++++ .../breadcrumbs/JsonBreadcrumbsProvider.java | 26 +++++++--------- .../navigation/JsonQualifiedNameProvider.java | 31 ++++++++++++++----- .../impl/JsonSchemaDocumentationProvider.java | 2 +- .../intellij/json/JsonBreadcrumbsTest.java | 19 ++++++++++++ .../com/intellij/json/JsonNavigationTest.java | 2 +- .../testData/breadcrumbs/ComplexItems.json | 11 +++++++ 8 files changed, 85 insertions(+), 24 deletions(-) create mode 100644 json/tests/test/com/intellij/json/JsonBreadcrumbsTest.java create mode 100644 json/tests/testData/breadcrumbs/ComplexItems.json diff --git a/json/intellij.json.iml b/json/intellij.json.iml index 5d8690af9e46..240d9ea3b74f 100644 --- a/json/intellij.json.iml +++ b/json/intellij.json.iml @@ -12,7 +12,6 @@ - diff --git a/json/src/com/intellij/json/JsonUtil.java b/json/src/com/intellij/json/JsonUtil.java index eaa9685b8565..1245d73eaf4c 100644 --- a/json/src/com/intellij/json/JsonUtil.java +++ b/json/src/com/intellij/json/JsonUtil.java @@ -2,6 +2,7 @@ package com.intellij.json; import com.intellij.json.psi.*; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.PsiElement; import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -65,4 +66,20 @@ public class JsonUtil { final JsonStringLiteral literal = getPropertyValueOfType(object, name, JsonStringLiteral.class); return literal == null ? null : Collections.singletonList(StringUtil.unquoteString(literal.getText())); } + + public static boolean isArrayElement(@NotNull PsiElement element) { + return element instanceof JsonValue && element.getParent() instanceof JsonArray; + } + + public static int getArrayIndexOfItem(@NotNull PsiElement e) { + PsiElement parent = e.getParent(); + if (!(parent instanceof JsonArray)) return -1; + List elements = ((JsonArray)parent).getValueList(); + for (int i = 0; i < elements.size(); i++) { + if (e == elements.get(i)) { + return i; + } + } + return -1; + } } diff --git a/json/src/com/intellij/json/breadcrumbs/JsonBreadcrumbsProvider.java b/json/src/com/intellij/json/breadcrumbs/JsonBreadcrumbsProvider.java index 2684bcbe1fb6..9d376a7a3aef 100644 --- a/json/src/com/intellij/json/breadcrumbs/JsonBreadcrumbsProvider.java +++ b/json/src/com/intellij/json/breadcrumbs/JsonBreadcrumbsProvider.java @@ -1,17 +1,19 @@ package com.intellij.json.breadcrumbs; import com.intellij.json.JsonLanguage; +import com.intellij.json.JsonUtil; +import com.intellij.json.psi.JsonProperty; import com.intellij.lang.Language; import com.intellij.psi.PsiElement; -import com.intellij.xml.breadcrumbs.BreadcrumbsInfoProvider; -import com.intellij.json.psi.JsonProperty; +import com.intellij.ui.breadcrumbs.BreadcrumbsProvider; +import com.jetbrains.jsonSchema.impl.JsonSchemaDocumentationProvider; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * @author Mikhail Golubev */ -public class JsonBreadcrumbsProvider extends BreadcrumbsInfoProvider { +public class JsonBreadcrumbsProvider implements BreadcrumbsProvider { private static final Language[] LANGUAGES = new Language[]{JsonLanguage.INSTANCE}; @Override @@ -21,7 +23,7 @@ public class JsonBreadcrumbsProvider extends BreadcrumbsInfoProvider { @Override public boolean acceptElement(@NotNull PsiElement e) { - return e instanceof JsonProperty; + return e instanceof JsonProperty || JsonUtil.isArrayElement(e); } @NotNull @@ -30,20 +32,16 @@ public class JsonBreadcrumbsProvider extends BreadcrumbsInfoProvider { if (e instanceof JsonProperty) { return ((JsonProperty)e).getName(); } - //else if (isArrayElement(e)) { - // List elements = ((JsonArray)e.getParent()).getValueList(); - // for (int i = 0; i < elements.size(); i++) { - // if (e == elements.get(i)) { - // return String.valueOf(i); - // } - // } - //} - throw new AssertionError("Breadcrumbs can be extracted only from JsonProperty elements"); + else if (JsonUtil.isArrayElement(e)) { + int i = JsonUtil.getArrayIndexOfItem(e); + if (i != -1) return String.valueOf(i); + } + throw new AssertionError("Breadcrumbs can be extracted only from JsonProperty elements or JsonArray child items"); } @Nullable @Override public String getElementTooltip(@NotNull PsiElement e) { - return null; + return JsonSchemaDocumentationProvider.findSchemaAndGenerateDoc(e, null, true); } } diff --git a/json/src/com/intellij/json/navigation/JsonQualifiedNameProvider.java b/json/src/com/intellij/json/navigation/JsonQualifiedNameProvider.java index 45ca4daf10e3..e6773e95ed81 100644 --- a/json/src/com/intellij/json/navigation/JsonQualifiedNameProvider.java +++ b/json/src/com/intellij/json/navigation/JsonQualifiedNameProvider.java @@ -1,6 +1,8 @@ package com.intellij.json.navigation; import com.intellij.ide.actions.QualifiedNameProvider; +import com.intellij.json.JsonUtil; +import com.intellij.json.psi.JsonArray; import com.intellij.json.psi.JsonElement; import com.intellij.json.psi.JsonProperty; import com.intellij.openapi.project.Project; @@ -9,8 +11,6 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.Nullable; -import java.util.LinkedList; - /** * @author Mikhail Golubev */ @@ -27,13 +27,30 @@ public class JsonQualifiedNameProvider implements QualifiedNameProvider { if (!(element instanceof JsonElement)) { return null; } - final LinkedList qualifiers = new LinkedList<>(); - JsonProperty parentProperty = PsiTreeUtil.getNonStrictParentOfType(element, JsonProperty.class); + JsonElement parentProperty = PsiTreeUtil.getNonStrictParentOfType(element, JsonProperty.class, JsonArray.class); + StringBuilder builder = new StringBuilder(); while (parentProperty != null) { - qualifiers.addFirst(parentProperty.getName()); - parentProperty = PsiTreeUtil.getParentOfType(parentProperty, JsonProperty.class); + if (parentProperty instanceof JsonProperty) { + builder.insert(0, parentProperty.getName()); + builder.insert(0, "."); + } + else { + int index = JsonUtil.getArrayIndexOfItem(element instanceof JsonProperty ? element.getParent() : element); + if (index == -1) return null; + builder.insert(0, "[" + index + "]"); + } + element = parentProperty; + parentProperty = PsiTreeUtil.getParentOfType(parentProperty, JsonProperty.class, JsonArray.class); } - return qualifiers.isEmpty() ? null : StringUtil.join(qualifiers, "."); + + if (builder.length() == 0) return null; + + // if the first operation is array indexing, we insert the 'root' element $ + if (builder.charAt(0) == '[') { + builder.insert(0, "$"); + } + + return StringUtil.trimStart(builder.toString(), "."); } @Override diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaDocumentationProvider.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaDocumentationProvider.java index 85234b05ecc1..deb24a1d69ff 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaDocumentationProvider.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaDocumentationProvider.java @@ -36,7 +36,7 @@ public class JsonSchemaDocumentationProvider implements DocumentationProvider { } @Nullable - private static String findSchemaAndGenerateDoc(PsiElement element, @Nullable PsiElement originalElement, final boolean preferShort) { + public static String findSchemaAndGenerateDoc(PsiElement element, @Nullable PsiElement originalElement, final boolean preferShort) { element = ObjectUtils.coalesce(originalElement, element); final PsiFile containingFile = element.getContainingFile(); if (containingFile == null) return null; diff --git a/json/tests/test/com/intellij/json/JsonBreadcrumbsTest.java b/json/tests/test/com/intellij/json/JsonBreadcrumbsTest.java new file mode 100644 index 000000000000..9416a649def0 --- /dev/null +++ b/json/tests/test/com/intellij/json/JsonBreadcrumbsTest.java @@ -0,0 +1,19 @@ +// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package com.intellij.json; + +import com.intellij.ui.components.breadcrumbs.Crumb; + +import java.util.List; + +public class JsonBreadcrumbsTest extends JsonTestCase { + + private void doTest(String... components) { + myFixture.configureByFile("breadcrumbs/" + getTestName(false) + ".json"); + List caret = myFixture.getBreadcrumbsAtCaret(); + assertOrderedEquals(caret.stream().map(Crumb::getText).toArray(String[]::new), components); + } + + public void testComplexItems() { + doTest("foo", "bar", "0", "0", "baz"); + } +} diff --git a/json/tests/test/com/intellij/json/JsonNavigationTest.java b/json/tests/test/com/intellij/json/JsonNavigationTest.java index 1ca4acf83f8d..da610a6bd854 100644 --- a/json/tests/test/com/intellij/json/JsonNavigationTest.java +++ b/json/tests/test/com/intellij/json/JsonNavigationTest.java @@ -15,6 +15,6 @@ public class JsonNavigationTest extends JsonTestCase { final PsiElement element = myFixture.getElementAtCaret(); assertInstanceOf(element, JsonProperty.class); final String qualifiedName = CopyReferenceAction.elementToFqn(element); - assertEquals("foo.bar.baz", qualifiedName); + assertEquals("foo.bar[0][0].baz", qualifiedName); } } diff --git a/json/tests/testData/breadcrumbs/ComplexItems.json b/json/tests/testData/breadcrumbs/ComplexItems.json new file mode 100644 index 000000000000..c5f889219193 --- /dev/null +++ b/json/tests/testData/breadcrumbs/ComplexItems.json @@ -0,0 +1,11 @@ +{ + "foo": { + "bar": [ + [ + { + "baz": null + } + ] + ] + } +} \ No newline at end of file