json schema performance: cache computed json schema for a psi file, don't recompute it every time

this fixes performance tests after adding the injection feature
injection checks are invoked for every matching-by-class psi element, and that is really expensive if to recompute
This commit is contained in:
Anton Lobov
2019-03-14 18:51:00 +01:00
parent d70ce4fae1
commit cc18337aad
16 changed files with 61 additions and 28 deletions
@@ -57,7 +57,7 @@ public class JsonSpellcheckerStrategy extends SpellcheckingStrategy {
Project project = element.getProject();
final JsonSchemaService service = JsonSchemaService.Impl.get(project);
if (!service.isApplicableToFile(file)) return false;
final JsonSchemaObject rootSchema = service.getSchemaObject(file);
final JsonSchemaObject rootSchema = service.getSchemaObject(element.getContainingFile());
if (rootSchema == null) return false;
if (service.isSchemaFile(rootSchema)) {
JsonProperty property = ObjectUtils.tryCast(element.getParent(), JsonProperty.class);
@@ -54,6 +54,9 @@ public interface JsonSchemaService {
@Nullable
JsonSchemaObject getSchemaObject(@NotNull VirtualFile file);
@Nullable
JsonSchemaObject getSchemaObject(@NotNull PsiFile file);
@Nullable
JsonSchemaObject getSchemaObjectForSchemaFile(@NotNull VirtualFile schemaFile);
@@ -1,6 +1,7 @@
// 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.jetbrains.jsonSchema.impl;
import com.intellij.codeInsight.completion.CompletionUtil;
import com.intellij.json.navigation.JsonQualifiedNameKind;
import com.intellij.json.navigation.JsonQualifiedNameProvider;
import com.intellij.json.psi.*;
@@ -20,6 +21,7 @@ import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.jsonSchema.JsonPointerUtil;
import com.jetbrains.jsonSchema.JsonSchemaCatalogEntry;
import com.jetbrains.jsonSchema.ide.JsonSchemaService;
import com.jetbrains.jsonSchema.remote.JsonFileResolver;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -207,4 +209,21 @@ public class JsonCachedValues {
@NotNull Key<CachedValue<T>> key) {
return CachedValuesManager.getCachedValue(psiFile, key, () -> CachedValueProvider.Result.create(eval.fun(psiFile), psiFile));
}
public static final Key<CachedValue<JsonSchemaObject>> OBJECT_FOR_FILE_KEY = new Key<>("JsonCachedValues.OBJ_KEY");
@Nullable
static JsonSchemaObject computeSchemaForFile(@NotNull PsiFile file, @NotNull JsonSchemaService service) {
final PsiFile originalFile = CompletionUtil.getOriginalOrSelf(file);
JsonSchemaObject value = CachedValuesManager.getCachedValue(originalFile, OBJECT_FOR_FILE_KEY, () -> {
VirtualFile virtualFile = originalFile.getVirtualFile();
JsonSchemaObject schemaObject = virtualFile == null ? null : service.getSchemaObject(virtualFile);
VirtualFile schemaFile = schemaObject == null ? null : service.resolveSchemaFile(schemaObject);
PsiFile psiFile = schemaFile == null ? null : originalFile.getManager().findFile(schemaFile);
JsonSchemaObject object = schemaObject == null ? JsonSchemaObject.NULL_OBJ : schemaObject;
return psiFile == null
? CachedValueProvider.Result.create(object, originalFile)
: CachedValueProvider.Result.create(object, originalFile, psiFile);
});
return value == JsonSchemaObject.NULL_OBJ ? null : value;
}
}
@@ -6,8 +6,8 @@ import com.intellij.json.psi.JsonStringLiteral;
import com.intellij.lang.Language;
import com.intellij.lang.injection.MultiHostRegistrar;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.util.ThreeState;
import com.jetbrains.jsonSchema.extension.JsonLikePsiWalker;
import com.jetbrains.jsonSchema.ide.JsonSchemaService;
@@ -28,9 +28,8 @@ public class JsonSchemaBasedLanguageInjector extends JsonSchemaInjectorBase {
@Nullable
public static Language getLanguageToInject(@NotNull PsiElement context) {
Project project = context.getProject();
VirtualFile file = context.getContainingFile().getVirtualFile();
if (file == null) return null;
JsonSchemaObject schemaObject = JsonSchemaService.Impl.get(project).getSchemaObject(file);
PsiFile containingFile = context.getContainingFile();
JsonSchemaObject schemaObject = JsonSchemaService.Impl.get(project).getSchemaObject(containingFile);
if (schemaObject == null) return null;
JsonLikePsiWalker walker = JsonLikePsiWalker.getWalker(context, schemaObject);
if (walker == null || walker.isName(context) != ThreeState.NO) return null;
@@ -68,7 +68,7 @@ public class JsonSchemaCompletionContributor extends CompletionContributor {
final JsonSchemaService service = JsonSchemaService.Impl.get(position.getProject());
if (!service.isApplicableToFile(file)) return;
final JsonSchemaObject rootSchema = service.getSchemaObject(file);
final JsonSchemaObject rootSchema = service.getSchemaObject(position.getContainingFile());
if (rootSchema == null) return;
PsiElement positionParent = position.getParent();
if (positionParent != null) {
@@ -59,7 +59,7 @@ public class JsonSchemaDocumentationProvider implements DocumentationProvider {
final JsonSchemaService service = JsonSchemaService.Impl.get(element.getProject());
VirtualFile virtualFile = containingFile.getViewProvider().getVirtualFile();
if (!service.isApplicableToFile(virtualFile)) return null;
final JsonSchemaObject rootSchema = service.getSchemaObject(virtualFile);
final JsonSchemaObject rootSchema = service.getSchemaObject(containingFile);
if (rootSchema == null) return null;
return generateDoc(element, rootSchema, preferShort, forcedPropName);
@@ -32,7 +32,7 @@ public class JsonSchemaGotoDeclarationHandler implements GotoDeclarationHandler
if (file == null || !service.isApplicableToFile(file)) return null;
final JsonPointerPosition steps = JsonOriginalPsiWalker.INSTANCE.findPosition(literal, true);
if (steps == null) return null;
final JsonSchemaObject schemaObject = service.getSchemaObject(file);
final JsonSchemaObject schemaObject = service.getSchemaObject(containingFile);
if (schemaObject != null) {
final PsiElement target = new JsonSchemaResolver(sourceElement.getProject(), schemaObject, steps).findNavigationTarget(((JsonProperty)parent).getValue());
if (target != null) {
@@ -14,6 +14,7 @@ import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vfs.VirtualFileManager;
import com.intellij.openapi.vfs.impl.http.HttpVirtualFile;
import com.intellij.psi.PsiFile;
import com.intellij.util.containers.ConcurrentList;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.MultiMap;
@@ -285,6 +286,13 @@ public class JsonSchemaServiceImpl implements JsonSchemaService {
return JsonCachedValues.getSchemaObject(replaceHttpFileWithBuiltinIfNeeded(schemaFile), myProject);
}
@Nullable
@Override
public JsonSchemaObject getSchemaObject(@NotNull PsiFile file) {
return JsonCachedValues.computeSchemaForFile(file, this);
}
public VirtualFile replaceHttpFileWithBuiltinIfNeeded(VirtualFile schemaFile) {
// this hack is needed to handle user-defined mappings via urls
// we cannot perform that inside corresponding provider, because it leads to recursive component dependency
@@ -460,8 +468,9 @@ public class JsonSchemaServiceImpl implements JsonSchemaService {
@NotNull
@Override
public MultiMap<VirtualFile, JsonSchemaFileProvider> compute() {
MultiMap<VirtualFile, JsonSchemaFileProvider> map = createFileProviderMap(myFactory.create(), myProject);
myIsComputed.set(true);
return createFileProviderMap(myFactory.create(), myProject);
return map;
}
@NotNull
@@ -34,7 +34,7 @@ public abstract class JsonSchemaBasedInspectionBase extends LocalInspectionTool
VirtualFile virtualFile = file.getViewProvider().getVirtualFile();
if (!service.isApplicableToFile(virtualFile)) return PsiElementVisitor.EMPTY_VISITOR;
return doBuildVisitor(root, service.getSchemaObject(virtualFile), service, holder, session);
return doBuildVisitor(root, service.getSchemaObject(file), service, holder, session);
}
protected abstract PsiElementVisitor doBuildVisitor(@NotNull JsonValue root,
@@ -819,11 +819,11 @@ public class JsonSchemaCrossReferencesTest extends JsonSchemaHeavyAbstractTest {
@Override
public void configureFiles() {
configureByFiles(null, "completionWithRootRef.json", "cycledWithRootRefSchema.json");
complete();
}
@Override
public void doCheck() {
complete();
checkCompletion("\"id\"", "\"testProp\"");
}
});
@@ -28,11 +28,11 @@ public class JsonSchemaPerformanceTest extends JsonSchemaHeavyAbstractTest {
}
public void testSwaggerHighlighting() {
doPerformanceTest(10000, "swagger");
doPerformanceTest(8000, "swagger");
}
public void testTsLintSchema() {
doPerformanceTest(9000, "tslint-schema");
doPerformanceTest(7500, "tslint-schema");
}
private void doPerformanceTest(int expectedMs, String jsonFileNameWithoutExtension) {
@@ -13,6 +13,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
public final class JsonSchemaTestServiceImpl extends JsonSchemaServiceImpl {
private static JsonSchemaFileProvider provider;
@@ -36,4 +37,15 @@ public final class JsonSchemaTestServiceImpl extends JsonSchemaServiceImpl {
}
});
}
@Override
public int hashCode() {
return Objects.hash(JsonSchemaTestServiceImpl.class, "test");
}
@Override
public boolean equals(Object obj) {
if (obj instanceof JsonSchemaTestServiceImpl) return true;
return super.equals(obj);
}
}
@@ -15,7 +15,7 @@ public class YamlJsonSchemaCompletionContributor extends CompletionContributor {
public void fillCompletionVariants(@NotNull CompletionParameters parameters, @NotNull CompletionResultSet result) {
final PsiElement position = parameters.getPosition();
final JsonSchemaService jsonSchemaService = JsonSchemaService.Impl.get(position.getProject());
JsonSchemaObject object = jsonSchemaService.getSchemaObject(parameters.getOriginalFile().getVirtualFile());
JsonSchemaObject object = jsonSchemaService.getSchemaObject(parameters.getOriginalFile());
if (object != null) {
JsonSchemaCompletionContributor.doCompletion(parameters, result, object, false);
}
@@ -3,7 +3,6 @@ package org.jetbrains.yaml.schema;
import com.intellij.lang.documentation.DocumentationProviderEx;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
@@ -39,9 +38,7 @@ public class YamlJsonSchemaDocumentationProvider extends DocumentationProviderEx
final JsonSchemaService jsonSchemaService = JsonSchemaService.Impl.get(element.getProject());
PsiFile containingFile = element.getContainingFile();
if (containingFile == null) return null;
VirtualFile virtualFile = containingFile.getVirtualFile();
if (virtualFile == null) return null;
JsonSchemaObject schemaObject = jsonSchemaService.getSchemaObject(virtualFile);
JsonSchemaObject schemaObject = jsonSchemaService.getSchemaObject(containingFile);
if (schemaObject == null) return null;
return JsonSchemaDocumentationProvider.generateDoc(element, schemaObject, preferShort, null);
}
@@ -64,8 +61,7 @@ public class YamlJsonSchemaDocumentationProvider extends DocumentationProviderEx
@NotNull PsiFile file,
@Nullable PsiElement contextElement) {
JsonSchemaService service = JsonSchemaService.Impl.get(file.getProject());
VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile == null || service == null || service.getSchemaObject(virtualFile) == null) return null;
if (service == null || service.getSchemaObject(file) == null) return null;
return contextElement;
}
}
@@ -31,7 +31,7 @@ public class YamlJsonSchemaGotoDeclarationHandler implements GotoDeclarationHand
if (file == null || !service.isApplicableToFile(file)) return null;
final JsonPointerPosition steps = YamlJsonPsiWalker.INSTANCE.findPosition(literal, true);
if (steps == null) return null;
final JsonSchemaObject schemaObject = service.getSchemaObject(file);
final JsonSchemaObject schemaObject = service.getSchemaObject(containingFile);
if (schemaObject != null) {
final PsiElement target = new JsonSchemaResolver(sourceElement.getProject(), schemaObject, steps).findNavigationTarget(literal.getValue());
if (target != null) {
@@ -33,16 +33,11 @@ public abstract class YamlJsonSchemaInspectionBase extends LocalInspectionTool {
if (!service.isApplicableToFile(virtualFile)) {
return PsiElementVisitor.EMPTY_VISITOR;
}
final JsonSchemaObject rootSchema = service.getSchemaObject(virtualFile);
final JsonSchemaObject rootSchema = service.getSchemaObject(file);
if (rootSchema == null) {
return PsiElementVisitor.EMPTY_VISITOR;
}
JsonSchemaObject object = service.getSchemaObject(virtualFile);
if (object == null) {
return PsiElementVisitor.EMPTY_VISITOR;
}
return doBuildVisitor(holder, session, root, object);
return doBuildVisitor(holder, session, root, rootSchema);
}
protected abstract PsiElementVisitor doBuildVisitor(@NotNull ProblemsHolder holder,