diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaCompletionContributor.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaCompletionContributor.java index 749064ab01a6..8b0a752eef7e 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaCompletionContributor.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaCompletionContributor.java @@ -22,6 +22,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.util.PsiUtilCore; import com.intellij.util.Consumer; +import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.jsonSchema.extension.JsonLikePsiWalker; import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider; @@ -210,13 +211,13 @@ public class JsonSchemaCompletionContributor extends CompletionContributor { } private void addPropertyVariant(@NotNull String key, @NotNull JsonSchemaObject jsonSchemaObject, boolean hasValue, boolean insertComma) { - final String description = jsonSchemaObject.getDescription(); - final String title = jsonSchemaObject.getTitle(); + jsonSchemaObject = ObjectUtils.coalesce(ContainerUtil.getFirstItem(new JsonSchemaResolver(jsonSchemaObject).resolve()), + jsonSchemaObject); key = !myWrapInQuotes ? key : StringUtil.wrapWithDoubleQuote(key); LookupElementBuilder builder = LookupElementBuilder.create(key); - String typeText = StringUtil.isEmpty(title) ? description : title; - if (!StringUtil.isEmpty(typeText)) { + final String typeText = StringUtil.escapeXml(jsonSchemaObject.getDocumentation(true)); + if (!StringUtil.isEmptyOrSpaces(typeText)) { builder = builder.withTypeText(typeText, true); } diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaDocumentationProvider.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaDocumentationProvider.java index c866e1e02346..b399cd843109 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaDocumentationProvider.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaDocumentationProvider.java @@ -1,15 +1,11 @@ package com.jetbrains.jsonSchema.impl; -import com.intellij.json.psi.JsonObject; -import com.intellij.json.psi.JsonProperty; -import com.intellij.json.psi.JsonStringLiteral; -import com.intellij.json.psi.JsonValue; import com.intellij.lang.documentation.DocumentationProvider; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; -import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.ObjectUtils; import com.jetbrains.jsonSchema.extension.JsonLikePsiWalker; import com.jetbrains.jsonSchema.ide.JsonSchemaService; import org.jetbrains.annotations.NotNull; @@ -23,7 +19,7 @@ public class JsonSchemaDocumentationProvider implements DocumentationProvider { @Nullable @Override public String getQuickNavigateInfo(PsiElement element, PsiElement originalElement) { - return null; + return findSchemaAndGenerateDoc(element, originalElement, true); } @Nullable @@ -35,21 +31,24 @@ public class JsonSchemaDocumentationProvider implements DocumentationProvider { @Nullable @Override public String generateDoc(PsiElement element, @Nullable PsiElement originalElement) { + return findSchemaAndGenerateDoc(element, originalElement, false); + } + + @Nullable + private 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; final JsonSchemaService service = JsonSchemaService.Impl.get(element.getProject()); final JsonSchemaObject rootSchema = service.getSchemaObject(containingFile.getViewProvider().getVirtualFile()); if (rootSchema == null) return null; - if (JsonSchemaService.isSchemaFile(containingFile)) { - return generateForJsonSchemaFileType(element); - } - return generateDoc(element, rootSchema); + return generateDoc(element, rootSchema, preferShort); } @Nullable public static String generateDoc(@NotNull final PsiElement element, - @NotNull final JsonSchemaObject rootSchema) { + @NotNull final JsonSchemaObject rootSchema, final boolean preferShort) { final JsonLikePsiWalker walker = JsonLikePsiWalker.getWalker(element, rootSchema); if (walker == null) return null; @@ -59,24 +58,8 @@ public class JsonSchemaDocumentationProvider implements DocumentationProvider { final Collection schemas = new JsonSchemaResolver(rootSchema, true, position).resolve(); - return schemas.stream().filter(schema -> !StringUtil.isEmptyOrSpaces(schema.getDescription())) - .findFirst().map(JsonSchemaObject::getDescription).orElse(null); - } - - @Nullable - private static String generateForJsonSchemaFileType(@NotNull PsiElement element) { - final JsonProperty jsonProperty = - element instanceof JsonProperty ? (JsonProperty)element : PsiTreeUtil.getParentOfType(element, JsonProperty.class); - if (jsonProperty != null) { - final JsonValue value = jsonProperty.getValue(); - if (value instanceof JsonObject) { - final JsonProperty description = ((JsonObject)value).findProperty("description"); - if (description != null && description.getValue() instanceof JsonStringLiteral) { - return StringUtil.escapeXml(StringUtil.unquoteString(description.getValue().getText())); - } - } - } - return null; + return StringUtil.escapeXml(schemas.stream().filter(schema -> !StringUtil.isEmptyOrSpaces(schema.getDocumentation(preferShort))) + .findFirst().map(schema -> schema.getDocumentation(preferShort)).orElse(null)); } @Nullable diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java index 6a413de23d00..b531bfe4a3e0 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaObject.java @@ -87,7 +87,7 @@ public class JsonSchemaObject { // peer pointer is not merged! public void mergeValues(JsonSchemaObject other) { - // we do not copy id, schema, title and description + // we do not copy id, schema myProperties.putAll(other.myProperties); myDefinitionsMap = copyMap(myDefinitionsMap, other.myDefinitionsMap); @@ -95,6 +95,9 @@ public class JsonSchemaObject { other.myPatternProperties == null ? null : other.myPatternProperties.mySchemasMap); myPatternProperties = map == null ? null : new PatternProperties(map); + if (!StringUtil.isEmptyOrSpaces(other.myTitle)) { + myTitle = other.myTitle; + } if (!StringUtil.isEmptyOrSpaces(other.myDescription)) { myDescription = other.myDescription; } @@ -548,6 +551,12 @@ public class JsonSchemaObject { return current; } + @Nullable + public String getDocumentation(final boolean preferShort) { + if (preferShort) return StringUtil.isEmptyOrSpaces(myTitle) ? myDescription : myTitle; + return StringUtil.isEmptyOrSpaces(myDescription) ? myTitle : myDescription; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaResolver.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaResolver.java index 169821a805cb..2f635c73dc09 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaResolver.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaResolver.java @@ -54,6 +54,7 @@ public class JsonSchemaResolver { return MatchResult.create(node); } + @NotNull public Collection resolve() { return resolve(false, false, false); } diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonBySchemaDocumentationBaseTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonBySchemaDocumentationBaseTest.java index 191fa9ba56e2..1cd2dea2fa74 100644 --- a/json/tests/test/com/jetbrains/jsonSchema/JsonBySchemaDocumentationBaseTest.java +++ b/json/tests/test/com/jetbrains/jsonSchema/JsonBySchemaDocumentationBaseTest.java @@ -20,8 +20,10 @@ import com.intellij.json.JsonLanguage; import com.intellij.lang.LanguageDocumentation; import com.intellij.lang.documentation.DocumentationProvider; import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiUtilBase; import com.jetbrains.jsonSchema.impl.JsonSchemaDocumentationProvider; import org.jetbrains.annotations.NotNull; +import org.junit.Assert; import java.util.ArrayList; @@ -52,7 +54,8 @@ public abstract class JsonBySchemaDocumentationBaseTest extends JsonSchemaHeavyA @Override public void doCheck() { - PsiElement psiElement = DocumentationManager.getInstance(getProject()).findTargetElement(myEditor, myFile); + final PsiElement psiElement = PsiUtilBase.getElementAtCaret(myEditor); + Assert.assertNotNull(psiElement); assertDocumentation(psiElement, psiElement, hasDoc); } }); @@ -65,6 +68,12 @@ public abstract class JsonBySchemaDocumentationBaseTest extends JsonSchemaHeavyA protected void assertDocumentation(@NotNull PsiElement docElement, @NotNull PsiElement context, boolean shouldHaveDoc) { DocumentationProvider documentationProvider = DocumentationManager.getProviderFromElement(context); String inlineDoc = documentationProvider.generateDoc(docElement, context); + String quickNavigate = documentationProvider.getQuickNavigateInfo(docElement, context); + checkExpectedDoc(shouldHaveDoc, inlineDoc, false); + checkExpectedDoc(shouldHaveDoc, quickNavigate, true); + } + + private void checkExpectedDoc(boolean shouldHaveDoc, String inlineDoc, boolean preferShort) { if (shouldHaveDoc) { assertNotNull("inline help is null", inlineDoc); } @@ -72,7 +81,7 @@ public abstract class JsonBySchemaDocumentationBaseTest extends JsonSchemaHeavyA assertNull("inline help is not null", inlineDoc); } if (shouldHaveDoc) { - assertSameLinesWithFile(getTestDataPath() + "/" + getTestName(true) + ".html", inlineDoc); + assertSameLinesWithFile(getTestDataPath() + "/" + getTestName(true) + (preferShort ? "_short.html" : ".html"), inlineDoc); } } } diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaDocumentationTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaDocumentationTest.java index b7f47cb68571..88e69f8823ba 100644 --- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaDocumentationTest.java +++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaDocumentationTest.java @@ -32,4 +32,12 @@ public class JsonSchemaDocumentationTest extends JsonBySchemaDocumentationBaseTe public void testCheckEscaping() throws Exception { doTest(true, "json"); } + + public void testWithDefinition() throws Exception { + doTest(true, "json"); + } + + public void testWithTitleInDefinition() throws Exception { + doTest(true, "json"); + } } diff --git a/json/tests/test/com/jetbrains/jsonSchema/impl/JsonBySchemaCompletionTest.java b/json/tests/test/com/jetbrains/jsonSchema/impl/JsonBySchemaCompletionTest.java index fe2072a64959..631165f20686 100644 --- a/json/tests/test/com/jetbrains/jsonSchema/impl/JsonBySchemaCompletionTest.java +++ b/json/tests/test/com/jetbrains/jsonSchema/impl/JsonBySchemaCompletionTest.java @@ -1,7 +1,9 @@ package com.jetbrains.jsonSchema.impl; +import com.intellij.codeInsight.lookup.LookupElementPresentation; import com.jetbrains.jsonSchema.JsonSchemaHighlightingTest; import org.jetbrains.annotations.NotNull; +import org.junit.Assert; /** * @author Irina.Chernushina on 10/1/2015. @@ -157,6 +159,47 @@ public class JsonBySchemaCompletionTest extends JsonBySchemaCompletionBaseTest { testImpl(schema, "{\"null\": }", "false", "null", "true"); } + public void testDescriptionFromDefinitionInCompletion() throws Exception { + final String schema = "{\n" + + " \"definitions\": {\n" + + " \"target\": {\n" + + " \"description\": \"Target description\"\n" + + " }\n" + + " },\n" + + " \"properties\": {\n" + + " \"source\": {\n" + + " \"$ref\": \"#/definitions/target\"\n" + + " }\n" + + " }\n" + + "}"; + testImpl(schema, "{}", "\"source\""); + Assert.assertEquals(1, myItems.length); + final LookupElementPresentation presentation = new LookupElementPresentation(); + myItems[0].renderElement(presentation); + Assert.assertEquals("Target description", presentation.getTypeText()); + } + + public void testDescriptionFromTitleInCompletion() throws Exception { + final String schema = "{\n" + + " \"definitions\": {\n" + + " \"target\": {\n" + + " \"title\": \"Target title\",\n" + + " \"description\": \"Target description\"\n" + + " }\n" + + " },\n" + + " \"properties\": {\n" + + " \"source\": {\n" + + " \"$ref\": \"#/definitions/target\"\n" + + " }\n" + + " }\n" + + "}"; + testImpl(schema, "{}", "\"source\""); + Assert.assertEquals(1, myItems.length); + final LookupElementPresentation presentation = new LookupElementPresentation(); + myItems[0].renderElement(presentation); + Assert.assertEquals("Target title", presentation.getTypeText()); + } + @NotNull private static String parcelShopSchema() { return "{\n" + diff --git a/json/tests/testData/jsonSchema/documentation/checkEscaping_short.html b/json/tests/testData/jsonSchema/documentation/checkEscaping_short.html new file mode 100644 index 000000000000..c73fc4ac3bd3 --- /dev/null +++ b/json/tests/testData/jsonSchema/documentation/checkEscaping_short.html @@ -0,0 +1 @@ +Disallows /// <reference path=> imports (use ES6-style imports instead) \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/documentation/secondLevel_short.html b/json/tests/testData/jsonSchema/documentation/secondLevel_short.html new file mode 100644 index 000000000000..888aa3fb0ae8 --- /dev/null +++ b/json/tests/testData/jsonSchema/documentation/secondLevel_short.html @@ -0,0 +1 @@ +my second level property description 1 \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/documentation/simple_short.html b/json/tests/testData/jsonSchema/documentation/simple_short.html new file mode 100644 index 000000000000..c4941ce9e4db --- /dev/null +++ b/json/tests/testData/jsonSchema/documentation/simple_short.html @@ -0,0 +1 @@ +my property description 1 \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/documentation/withDefinition.html b/json/tests/testData/jsonSchema/documentation/withDefinition.html new file mode 100644 index 000000000000..d4bcb21909aa --- /dev/null +++ b/json/tests/testData/jsonSchema/documentation/withDefinition.html @@ -0,0 +1 @@ +Description found through definition \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/documentation/withDefinition.json b/json/tests/testData/jsonSchema/documentation/withDefinition.json new file mode 100644 index 000000000000..67cd5bfb5990 --- /dev/null +++ b/json/tests/testData/jsonSchema/documentation/withDefinition.json @@ -0,0 +1,3 @@ +{ + "source": {} +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/documentation/withDefinitionSchema.json b/json/tests/testData/jsonSchema/documentation/withDefinitionSchema.json new file mode 100644 index 000000000000..12ca74daad47 --- /dev/null +++ b/json/tests/testData/jsonSchema/documentation/withDefinitionSchema.json @@ -0,0 +1,12 @@ +{ + "definitions": { + "target": { + "description": "Description found through definition" + } + }, + "properties": { + "source": { + "$ref": "#/definitions/target" + } + } +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/documentation/withDefinition_short.html b/json/tests/testData/jsonSchema/documentation/withDefinition_short.html new file mode 100644 index 000000000000..d4bcb21909aa --- /dev/null +++ b/json/tests/testData/jsonSchema/documentation/withDefinition_short.html @@ -0,0 +1 @@ +Description found through definition \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/documentation/withTitleInDefinition.html b/json/tests/testData/jsonSchema/documentation/withTitleInDefinition.html new file mode 100644 index 000000000000..d4bcb21909aa --- /dev/null +++ b/json/tests/testData/jsonSchema/documentation/withTitleInDefinition.html @@ -0,0 +1 @@ +Description found through definition \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/documentation/withTitleInDefinition.json b/json/tests/testData/jsonSchema/documentation/withTitleInDefinition.json new file mode 100644 index 000000000000..67cd5bfb5990 --- /dev/null +++ b/json/tests/testData/jsonSchema/documentation/withTitleInDefinition.json @@ -0,0 +1,3 @@ +{ + "source": {} +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/documentation/withTitleInDefinitionSchema.json b/json/tests/testData/jsonSchema/documentation/withTitleInDefinitionSchema.json new file mode 100644 index 000000000000..7995bfeb9710 --- /dev/null +++ b/json/tests/testData/jsonSchema/documentation/withTitleInDefinitionSchema.json @@ -0,0 +1,13 @@ +{ + "definitions": { + "target": { + "title": "Title found through definition", + "description": "Description found through definition" + } + }, + "properties": { + "source": { + "$ref": "#/definitions/target" + } + } +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/documentation/withTitleInDefinition_short.html b/json/tests/testData/jsonSchema/documentation/withTitleInDefinition_short.html new file mode 100644 index 000000000000..3fc51fb08d52 --- /dev/null +++ b/json/tests/testData/jsonSchema/documentation/withTitleInDefinition_short.html @@ -0,0 +1 @@ +Title found through definition \ No newline at end of file