[http-client + yaml + json] IJ-CR-112452 Review fixes

- Made intention preview available
- Merged subsequent read actions
- Made isAvailable work very fast
- Tried to set the same id to all commands to achieve single redo for the entire request, but that didn't work out yet

GitOrigin-RevId: 54f4a3458bb05d1a03729483a95b269fbf2875e0
This commit is contained in:
Nikita Katkov
2023-08-09 17:24:32 +02:00
committed by intellij-monorepo-bot
parent 17e3852cca
commit 9c49b47de2
6 changed files with 79 additions and 58 deletions

View File

@@ -42,9 +42,7 @@ public interface JsonSchemaService {
Collection<VirtualFile> getSchemaFilesForFile(@NotNull VirtualFile file);
@Nullable
default VirtualFile getDynamicSchemaForFile(@NotNull PsiFile psiFile) {
return null;
}
VirtualFile getDynamicSchemaForFile(@NotNull PsiFile psiFile);
void registerRemoteUpdateCallback(@NotNull Runnable callback);
void unregisterRemoteUpdateCallback(@NotNull Runnable callback);
void registerResetAction(Runnable action);

View File

@@ -260,6 +260,11 @@ public final class JsonCachedValues {
return value == JsonSchemaObject.NULL_OBJ ? null : value;
}
public static boolean hasComputedSchemaObjectForFile(@NotNull PsiFile file) {
CachedValue<JsonSchemaObject> data = CompletionUtil.getOriginalOrSelf(file).getUserData(OBJECT_FOR_FILE_KEY);
return data != null && data.hasUpToDateValue();
}
private static @NotNull Pair<PsiFile, JsonSchemaObject> getSchemaFile(@NotNull PsiFile originalFile,
@NotNull JsonSchemaService service) {
VirtualFile virtualFile = originalFile.getVirtualFile();

View File

@@ -116,44 +116,44 @@ public class AddMissingPropertyFix implements LocalQuickFix, BatchQuickFix {
if (node == null) return null;
PsiElement element = node instanceof PsiFile ? node.getFirstChild() : node;
Ref<PsiElement> newElementRef = Ref.create(null);
WriteAction.run(() -> { performFixInner(hadComma, element, newElementRef); });
return newElementRef.get();
}
WriteAction.run(() -> {
boolean isSingle = myData.myMissingPropertyIssues.size() == 1;
PsiElement processedElement = element;
List<JsonValidationError.MissingPropertyIssueData> reverseOrder
= ContainerUtil.reverse(new ArrayList<>(myData.myMissingPropertyIssues));
for (JsonValidationError.MissingPropertyIssueData issue: reverseOrder) {
Object defaultValueObject = issue.defaultValue;
String defaultValue = formatDefaultValue(defaultValueObject, element.getLanguage());
PsiElement property = myQuickFixAdapter.createProperty(issue.propertyName, defaultValue == null
? myQuickFixAdapter
.getDefaultValueFromType(issue.propertyType)
: defaultValue, element);
PsiElement newElement;
if (processedElement instanceof LeafPsiElement) {
newElement = myQuickFixAdapter.adjustPropertyAnchor((LeafPsiElement)processedElement).addBefore(property, null);
public void performFixInner(@NotNull Ref<Boolean> hadComma, PsiElement element, Ref<PsiElement> newElementRef) {
boolean isSingle = myData.myMissingPropertyIssues.size() == 1;
PsiElement processedElement = element;
List<JsonValidationError.MissingPropertyIssueData> reverseOrder
= ContainerUtil.reverse(new ArrayList<>(myData.myMissingPropertyIssues));
for (JsonValidationError.MissingPropertyIssueData issue: reverseOrder) {
Object defaultValueObject = issue.defaultValue;
String defaultValue = formatDefaultValue(defaultValueObject, element.getLanguage());
PsiElement property = myQuickFixAdapter.createProperty(issue.propertyName, defaultValue == null
? myQuickFixAdapter
.getDefaultValueFromType(issue.propertyType)
: defaultValue, element);
PsiElement newElement;
if (processedElement instanceof LeafPsiElement) {
newElement = myQuickFixAdapter.adjustPropertyAnchor((LeafPsiElement)processedElement).addBefore(property, null);
}
else {
if (processedElement == element) {
newElement = processedElement.addBefore(property, processedElement.getLastChild());
}
else {
if (processedElement == element) {
newElement = processedElement.addBefore(property, processedElement.getLastChild());
}
else {
newElement = processedElement.getParent().addBefore(property, processedElement);
}
}
PsiElement adjusted = myQuickFixAdapter.adjustNewProperty(newElement);
hadComma.set(myQuickFixAdapter.ensureComma(adjusted, PsiTreeUtil.skipWhitespacesAndCommentsForward(newElement)));
if (!hadComma.get()) {
hadComma.set(processedElement == element && myQuickFixAdapter.ensureComma(PsiTreeUtil.skipWhitespacesAndCommentsBackward(newElement), adjusted));
}
processedElement = adjusted;
if (isSingle) {
newElementRef.set(adjusted);
newElement = processedElement.getParent().addBefore(property, processedElement);
}
}
});
return newElementRef.get();
PsiElement adjusted = myQuickFixAdapter.adjustNewProperty(newElement);
hadComma.set(myQuickFixAdapter.ensureComma(adjusted, PsiTreeUtil.skipWhitespacesAndCommentsForward(newElement)));
if (!hadComma.get()) {
hadComma.set(processedElement == element && myQuickFixAdapter.ensureComma(PsiTreeUtil.skipWhitespacesAndCommentsBackward(newElement), adjusted));
}
processedElement = adjusted;
if (isSingle) {
newElementRef.set(adjusted);
}
}
}
@Nullable

View File

@@ -3,6 +3,7 @@ package com.jetbrains.jsonSchema.impl.fixes
import com.intellij.codeInsight.actions.ReformatCodeProcessor
import com.intellij.codeInsight.intention.IntentionAction
import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
import com.intellij.json.JsonBundle
import com.intellij.json.psi.JsonObject
import com.intellij.openapi.command.WriteCommandAction
@@ -14,6 +15,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.util.parentOfType
import com.jetbrains.jsonSchema.extension.JsonLikeSyntaxAdapter
import com.jetbrains.jsonSchema.impl.JsonCachedValues
import com.jetbrains.jsonSchema.impl.JsonOriginalPsiWalker
open class AddOptionalPropertiesIntention : IntentionAction {
@@ -31,8 +33,7 @@ open class AddOptionalPropertiesIntention : IntentionAction {
override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean {
val containingObject = findContainingObjectNode(editor, file) ?: return false
val missingProperties = collectMissingPropertiesFromSchema(containingObject, containingObject.project)
return missingProperties != null && !missingProperties.hasOnlyRequiredPropertiesMissing
return JsonCachedValues.hasComputedSchemaObjectForFile(containingObject.containingFile)
}
override fun invoke(project: Project, editor: Editor, file: PsiFile) {
@@ -55,4 +56,14 @@ open class AddOptionalPropertiesIntention : IntentionAction {
protected open fun getSyntaxAdapter(project: Project): JsonLikeSyntaxAdapter {
return JsonOriginalPsiWalker.INSTANCE.getSyntaxAdapter(project)
}
override fun generatePreview(project: Project, editor: Editor, file: PsiFile): IntentionPreviewInfo {
val containingObject = findContainingObjectNode(editor, file) ?: return IntentionPreviewInfo.EMPTY
val missingProperties = collectMissingPropertiesFromSchema(containingObject, containingObject.project)
?.missingKnownProperties ?: return IntentionPreviewInfo.EMPTY
AddMissingPropertyFix(missingProperties, getSyntaxAdapter(project))
.performFixInner(Ref.create(), containingObject, Ref.create())
ReformatCodeProcessor(containingObject.containingFile, false).run()
return IntentionPreviewInfo.DIFF
}
}

View File

@@ -3,7 +3,9 @@ package com.jetbrains.jsonSchema.intentions
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler
import com.intellij.json.JsonTestCase
import com.intellij.psi.PsiFile
import com.jetbrains.jsonSchema.JsonSchemaHighlightingTestBase.registerJsonSchema
import com.jetbrains.jsonSchema.ide.JsonSchemaService
import com.jetbrains.jsonSchema.impl.fixes.AddOptionalPropertiesIntention
import org.intellij.lang.annotations.Language
import org.junit.Assert
@@ -55,12 +57,23 @@ class AddOptionalPropertiesIntentionTest : JsonTestCase() {
""".trimIndent(),
"json"
) { true }
myFixture.configureByText("test.json", before)
val json = myFixture.configureByText("test.json", before)
ensureSchemaIsCached(json)
val intention = myFixture.getAvailableIntention(AddOptionalPropertiesIntention().text)!!
ShowIntentionActionsHandler.chooseActionAndInvoke(myFixture.file, myFixture.editor, intention, intention.text)
ensureSchemaIsCached(json)
val previewText = myFixture.getIntentionPreviewText(intention)
Assert.assertEquals(after, previewText)
myFixture.checkResult(after)
}
// Since intention now relies on CachedValue API, we have to make sure that no dependencies were updated before intention#isAvailable call
private fun ensureSchemaIsCached(json: PsiFile) {
Assert.assertNotNull(JsonSchemaService.Impl.get(myFixture.project).getSchemaObject(json))
}
fun `test insert properties in empty object`() {
doTest(
"""
@@ -116,24 +129,6 @@ class AddOptionalPropertiesIntentionTest : JsonTestCase() {
)
}
fun `test intention unavailable if all properties are present`() {
registerJsonSchema(myFixture, """
{
"properties": {
"a" : {}
}
}
""".trimIndent(), "json") { true }
myFixture.configureByText("test.json", """
{
"a": 123
<caret>
}
""".trimIndent())
Assert.assertNull(myFixture.getAvailableIntention (AddOptionalPropertiesIntention().text))
}
fun `test add example from schema as default value`() {
doTest(
"""

View File

@@ -2,8 +2,10 @@
package org.jetbrains.yaml.intention
import com.intellij.codeInsight.intention.impl.ShowIntentionActionsHandler
import com.intellij.psi.PsiFile
import com.intellij.testFramework.fixtures.BasePlatformTestCase
import com.jetbrains.jsonSchema.JsonSchemaHighlightingTestBase
import com.jetbrains.jsonSchema.ide.JsonSchemaService
import com.jetbrains.jsonSchema.impl.fixes.AddOptionalPropertiesIntention
import org.intellij.lang.annotations.Language
import org.jetbrains.yaml.intentions.YAMLAddOptionalPropertiesIntention
@@ -58,12 +60,22 @@ class YAMLAddOptionalPropertiesIntentionTest : BasePlatformTestCase() {
) { true }
}
private fun ensureSchemaIsCached(json: PsiFile) {
Assert.assertNotNull(JsonSchemaService.Impl.get(myFixture.project).getSchemaObject(json))
}
private fun doTest(@Language("yaml") before: String, @Language("yaml") after: String) {
addSchema()
myFixture.configureByText("test.yaml", before)
val yaml = myFixture.configureByText("test.yaml", before)
Assert.assertNotNull(JsonSchemaService.Impl.get(myFixture.project).getSchemaObject(yaml))
ensureSchemaIsCached(yaml)
val intention = myFixture.findSingleIntention(YAMLAddOptionalPropertiesIntention().text)
ShowIntentionActionsHandler.chooseActionAndInvoke(myFixture.file, myFixture.editor, intention, intention.text)
ensureSchemaIsCached(yaml)
val previewText = myFixture.getIntentionPreviewText(intention)
Assert.assertEquals(after, previewText)
myFixture.checkResult(after)
}