diff --git a/json/src/com/jetbrains/jsonSchema/JsonSchemaFileType.java b/json/src/com/jetbrains/jsonSchema/JsonSchemaFileType.java index c337929499fe..ce35bd313bde 100644 --- a/json/src/com/jetbrains/jsonSchema/JsonSchemaFileType.java +++ b/json/src/com/jetbrains/jsonSchema/JsonSchemaFileType.java @@ -22,6 +22,7 @@ import com.intellij.openapi.fileTypes.ex.FileTypeIdentifiableByVirtualFile; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectUtil; import com.intellij.openapi.vfs.VirtualFile; +import com.jetbrains.jsonSchema.ide.JsonSchemaService; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -64,6 +65,6 @@ public class JsonSchemaFileType extends LanguageFileType implements FileTypeIden @Override public boolean isMyFileType(@NotNull VirtualFile file) { final Project project = ProjectUtil.guessProjectForFile(file); - return project != null && JsonSchemaMappingsProjectConfiguration.getInstance(project).isRegisteredSchemaFile(file); + return project != null && JsonSchemaService.Impl.get(project).isRegisteredSchemaFile(project, file); } } diff --git a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaFileProvider.java b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaFileProvider.java index fd9475873c24..2f94b81ed6dd 100644 --- a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaFileProvider.java +++ b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaFileProvider.java @@ -1,11 +1,12 @@ package com.jetbrains.jsonSchema.extension; +import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.NotNull; public interface JsonSchemaFileProvider { - boolean isAvailable(@NotNull VirtualFile file); + boolean isAvailable(@NotNull Project project, @NotNull VirtualFile file); @NotNull String getName(); diff --git a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaImportedProviderFactory.java b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaImportedProviderFactory.java index 1bb4ea9cca59..79728a6ab629 100644 --- a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaImportedProviderFactory.java +++ b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaImportedProviderFactory.java @@ -135,7 +135,7 @@ public class JsonSchemaImportedProviderFactory implements JsonSchemaProviderFact } @Override - public boolean isAvailable(@NotNull VirtualFile file) { + public boolean isAvailable(@NotNull Project project, @NotNull VirtualFile file) { if (myPatterns.isEmpty() || file.isDirectory() || !file.isValid() || myProject != null && JsonSchemaMappingsProjectConfiguration.getInstance(myProject).isRegisteredSchemaFile(file)) return false; for (Processor pattern : myPatterns) { diff --git a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaProjectSelfProviderFactory.java b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaProjectSelfProviderFactory.java index b9a3f3152453..e079613e4db5 100644 --- a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaProjectSelfProviderFactory.java +++ b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaProjectSelfProviderFactory.java @@ -15,7 +15,6 @@ */ package com.jetbrains.jsonSchema.extension; -import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; @@ -23,6 +22,7 @@ import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.jsonSchema.JsonSchemaFileType; import com.jetbrains.jsonSchema.JsonSchemaMappingsProjectConfiguration; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Collections; import java.util.List; @@ -30,35 +30,27 @@ import java.util.List; /** * @author Irina.Chernushina on 2/24/2016. */ -public class JsonSchemaProjectSelfProviderFactory { +public class JsonSchemaProjectSelfProviderFactory implements JsonSchemaProviderFactory { private static final Logger LOG = Logger.getInstance("#com.jetbrains.jsonSchema.extension.JsonSchemaProjectSelfProviderFactory"); public static final String SCHEMA_JSON_FILE_NAME = "schema.json"; private final List myProviders; - public static JsonSchemaProjectSelfProviderFactory getInstance(final Project project) { - return ServiceManager.getService(project, JsonSchemaProjectSelfProviderFactory.class); + public JsonSchemaProjectSelfProviderFactory() { + myProviders = Collections.singletonList(new MyJsonSchemaFileProvider()); } - public JsonSchemaProjectSelfProviderFactory(final Project project) { - myProviders = Collections.singletonList(new MyJsonSchemaFileProvider(project)); - } - - public List getProviders() { + @Override + public List getProviders(@Nullable Project project) { return myProviders; } private static class MyJsonSchemaFileProvider implements JsonSchemaFileProvider { public static final Pair KEY = Pair.create(SchemaType.schema, SchemaType.schema); - private final Project myProject; - - public MyJsonSchemaFileProvider(Project project) { - myProject = project; - } @Override - public boolean isAvailable(@NotNull VirtualFile file) { - if (myProject == null || !JsonSchemaFileType.INSTANCE.equals(file.getFileType())) return false; - return JsonSchemaMappingsProjectConfiguration.getInstance(myProject).isRegisteredSchemaFile(file); + public boolean isAvailable(@NotNull Project project, @NotNull VirtualFile file) { + if (!JsonSchemaFileType.INSTANCE.equals(file.getFileType())) return false; + return JsonSchemaMappingsProjectConfiguration.getInstance(project).isRegisteredSchemaFile(file); } @NotNull @@ -69,29 +61,12 @@ public class JsonSchemaProjectSelfProviderFactory { @Override public VirtualFile getSchemaFile() { - return JsonSchemaProviderFactory.getResourceFile(JsonSchemaSelfProviderFactory.class, "jsonSchema/schema.json"); + return JsonSchemaProviderFactory.getResourceFile(JsonSchemaProjectSelfProviderFactory.class, "jsonSchema/schema.json"); } @Override public SchemaType getSchemaType() { return SchemaType.schema; } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - MyJsonSchemaFileProvider provider = (MyJsonSchemaFileProvider)o; - - if (myProject != null ? !myProject.equals(provider.myProject) : provider.myProject != null) return false; - - return true; - } - - @Override - public int hashCode() { - return myProject != null ? myProject.hashCode() : 0; - } } } diff --git a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaSelfProviderFactory.java b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaSelfProviderFactory.java deleted file mode 100644 index bf5c19cac9cc..000000000000 --- a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaSelfProviderFactory.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2000-2016 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.jetbrains.jsonSchema.extension; - -import com.intellij.openapi.project.Project; -import org.jetbrains.annotations.Nullable; - -import java.util.List; - -/** - * @author Irina.Chernushina on 2/16/2016. - */ -public class JsonSchemaSelfProviderFactory implements JsonSchemaProviderFactory { - @Override - public List getProviders(@Nullable Project project) { - return JsonSchemaProjectSelfProviderFactory.getInstance(project).getProviders(); - } -} diff --git a/json/src/com/jetbrains/jsonSchema/extension/schema/JsonSchemaRefReferenceProvider.java b/json/src/com/jetbrains/jsonSchema/extension/schema/JsonSchemaRefReferenceProvider.java index d0b10c898dc3..5d8170758bc3 100644 --- a/json/src/com/jetbrains/jsonSchema/extension/schema/JsonSchemaRefReferenceProvider.java +++ b/json/src/com/jetbrains/jsonSchema/extension/schema/JsonSchemaRefReferenceProvider.java @@ -16,6 +16,7 @@ package com.jetbrains.jsonSchema.extension.schema; import com.intellij.json.psi.JsonValue; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.text.StringUtil; @@ -25,6 +26,7 @@ import com.intellij.psi.search.GlobalSearchScope; import com.intellij.util.ProcessingContext; import com.intellij.util.indexing.FileBasedIndex; import com.jetbrains.jsonSchema.ide.JsonSchemaService; +import com.jetbrains.jsonSchema.impl.JsonSchemaResourcesRootsProvider; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -63,15 +65,16 @@ public class JsonSchemaRefReferenceProvider extends PsiReferenceProvider { ref = text.substring(idx + 1); } + final Project project = getElement().getProject(); final Ref> reference = new Ref<>(); final GlobalSearchScope filter = id == null ? GlobalSearchScope.fileScope(getElement().getContainingFile()) : - GlobalSearchScope.allScope(getElement().getProject()); + JsonSchemaResourcesRootsProvider.enlarge(project, GlobalSearchScope.allScope(project)); String finalId = id; index.processValues(JsonSchemaFileIndex.PROPERTIES_INDEX, ref, null, new FileBasedIndex.ValueProcessor() { @Override public boolean process(VirtualFile file, Integer value) { if (finalId != null) { - if (!JsonSchemaService.Impl.getEx(getElement().getProject()).checkFileForId(finalId, file)) { + if (!JsonSchemaService.Impl.getEx(project).checkFileForId(finalId, file)) { return true; } } diff --git a/json/src/com/jetbrains/jsonSchema/ide/JsonSchemaService.java b/json/src/com/jetbrains/jsonSchema/ide/JsonSchemaService.java index 66df74ad20f5..d9aa9f86d494 100644 --- a/json/src/com/jetbrains/jsonSchema/ide/JsonSchemaService.java +++ b/json/src/com/jetbrains/jsonSchema/ide/JsonSchemaService.java @@ -34,6 +34,8 @@ public interface JsonSchemaService { boolean isSchemaFile(@NotNull VirtualFile file, @NotNull Consumer errorConsumer); + boolean isRegisteredSchemaFile(Project project, @NotNull VirtualFile file); + @Nullable DocumentationProvider getDocumentationProvider(@Nullable VirtualFile file); diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaResourcesRootsProvider.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaResourcesRootsProvider.java new file mode 100644 index 000000000000..df3fc11d9d14 --- /dev/null +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaResourcesRootsProvider.java @@ -0,0 +1,93 @@ +/* + * Copyright 2000-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.jetbrains.jsonSchema.impl; + +import com.intellij.openapi.extensions.Extensions; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.AtomicNotNullLazyValue; +import com.intellij.openapi.util.NotNullLazyValue; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.psi.search.GlobalSearchScope; +import com.intellij.util.indexing.IndexableSetContributor; +import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider; +import com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory; +import com.jetbrains.jsonSchema.extension.SchemaType; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author Irina.Chernushina on 4/14/2016. + */ +public class JsonSchemaResourcesRootsProvider extends IndexableSetContributor { + private static final NotNullLazyValue> ourFiles = new AtomicNotNullLazyValue>() { + @NotNull + @Override + protected Set compute() { + final Set set = new HashSet<>(); + final JsonSchemaProviderFactory[] extensions = Extensions.getExtensions(JsonSchemaProviderFactory.EP_NAME); + for (JsonSchemaProviderFactory extension : extensions) { + final List providers = extension.getProviders(null); + for (JsonSchemaFileProvider provider : providers) { + if (!SchemaType.userSchema.equals(provider.getSchemaType())) { + set.add(provider.getSchemaFile()); + } + } + } + return set; + } + }; + + @NotNull + @Override + public Set getAdditionalRootsToIndex() { + return ourFiles.getValue(); + } + + @NotNull + public static GlobalSearchScope enlarge(@NotNull final Project project, @NotNull final GlobalSearchScope scope) { + // we need to create the scope manually since files are outside project root (embedded) + return scope.union(new GlobalSearchScope() { + @Override + public boolean contains(@NotNull final VirtualFile file) { + return ourFiles.getValue().contains(file); + } + + @Override + public int compare(@NotNull final VirtualFile file1, @NotNull final VirtualFile file2) { + return scope.compare(file1, file2); + } + + @Override + public boolean isSearchInModuleContent(@NotNull final Module aModule) { + return scope.isSearchInModuleContent(aModule); + } + + @Override + public boolean isSearchInLibraries() { + return scope.isSearchInLibraries(); + } + + @Override + public boolean isSearchOutsideRootModel() { + return true; + } + }); + } +} diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaServiceImpl.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaServiceImpl.java index 2d5fb9ad7ee6..3eae82cff603 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaServiceImpl.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaServiceImpl.java @@ -43,6 +43,7 @@ public class JsonSchemaServiceImpl implements JsonSchemaServiceEx { private final Project myProject; private final Object myLock; private final Map myWrappers = new HashMap<>(); + private final Set mySchemaFiles = new HashSet<>(); private final JsonSchemaExportedDefinitions myDefinitions; public JsonSchemaServiceImpl(@Nullable Project project) { @@ -84,6 +85,27 @@ public class JsonSchemaServiceImpl implements JsonSchemaServiceEx { return wrapper != null; } + @Override + public boolean isRegisteredSchemaFile(@NotNull Project project, @NotNull VirtualFile file) { + synchronized (myLock) { + ensureSchemaFiles(project); + return mySchemaFiles.contains(file); + } + } + + private void ensureSchemaFiles(@NotNull final Project project) { + synchronized (myLock) { + if (!mySchemaFiles.isEmpty()) return; + final JsonSchemaProviderFactory[] factories = getProviderFactories(); + for (JsonSchemaProviderFactory factory : factories) { + final List providers = factory.getProviders(project); + for (JsonSchemaFileProvider provider : providers) { + mySchemaFiles.add(provider.getSchemaFile()); + } + } + } + } + @Override public boolean isSchemaFile(@NotNull VirtualFile file, @NotNull final Consumer errorConsumer) { final String text; @@ -179,6 +201,7 @@ public class JsonSchemaServiceImpl implements JsonSchemaServiceEx { synchronized (myLock) { myWrappers.clear(); myDefinitions.reset(); + mySchemaFiles.clear(); } } @@ -225,14 +248,16 @@ public class JsonSchemaServiceImpl implements JsonSchemaServiceEx { @Nullable private List getWrappers(@Nullable VirtualFile file) { - if (file == null) return null; + if (file == null || myProject == null) return null; final List wrappers = new ArrayList<>(); JsonSchemaProviderFactory[] factories = getProviderFactories(); - for (JsonSchemaProviderFactory factory : factories) { - for (JsonSchemaFileProvider provider : factory.getProviders(myProject)) { - if (provider.isAvailable(file)) { + synchronized (myLock) { + final Set files = mySchemaFiles.isEmpty() ? new HashSet<>() : null; + for (JsonSchemaProviderFactory factory : factories) { + for (JsonSchemaFileProvider provider : factory.getProviders(myProject)) { final VirtualFile key = provider.getSchemaFile(); - synchronized (myLock) { + if (files != null) files.add(key); + if (provider.isAvailable(myProject, file)) { JsonSchemaObjectCodeInsightWrapper wrapper = myWrappers.get(key); if (wrapper == null) { wrapper = createWrapper(provider); @@ -243,6 +268,7 @@ public class JsonSchemaServiceImpl implements JsonSchemaServiceEx { } } } + if (files != null) mySchemaFiles.addAll(files); } return wrappers; } diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaCrossReferencesTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaCrossReferencesTest.java index 4eaa2d92e9db..a9c5490d507c 100644 --- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaCrossReferencesTest.java +++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaCrossReferencesTest.java @@ -247,4 +247,58 @@ public class JsonSchemaCrossReferencesTest extends CompletionTestCase { instance.removeSchema(inherited); instance.removeSchema(base); } + + public void testJsonSchemaGlobalRefsCrossResolve() throws Exception { + configureByFiles(null, BASE_SCHEMA_RESOLVE_PATH + "/referencingGlobalSchema.json"); + + String moduleDir = null; + VirtualFile moduleFile = null; + VirtualFile[] children = getProject().getBaseDir().getChildren(); + for (VirtualFile child : children) { + if (child.isDirectory()) { + moduleDir = child.getName(); + moduleFile = child; + break; + } + } + Assert.assertNotNull(moduleDir); + + AreaPicoContainer container = Extensions.getArea(getProject()).getPicoContainer(); + final String key = JsonSchemaMappingsProjectConfiguration.class.getName(); + container.unregisterComponent(key); + container.registerComponentImplementation(key, TestJsonSchemaMappingsProjectConfiguration.class); + + final JsonSchemaMappingsProjectConfiguration instance = JsonSchemaMappingsProjectConfiguration.getInstance(getProject()); + final JsonSchemaMappingsConfigurationBase.SchemaInfo inherited + = new JsonSchemaMappingsConfigurationBase.SchemaInfo("inherited", "/" + moduleDir + "/referencingGlobalSchema.json", false, Collections.emptyList()); + + instance.addSchema(inherited); + + try { + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + myFileTypeManager.associatePattern(JsonSchemaFileType.INSTANCE, "*Schema.json"); + } + }); + int offset = myEditor.getCaretModel().getPrimaryCaret().getOffset(); + final PsiReference referenceAt = myFile.findReferenceAt(offset); + Assert.assertNotNull(referenceAt); + final PsiElement resolve = referenceAt.resolve(); + Assert.assertNotNull(resolve); + Assert.assertEquals("\"enum\"", resolve.getText()); + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + myFileTypeManager.removeAssociatedExtension(JsonSchemaFileType.INSTANCE, "*Schema.json"); + } + }); + } finally { + container.unregisterComponent(key); + container.registerComponentImplementation(key, JsonSchemaMappingsProjectConfiguration.class); + } + + instance.removeSchema(inherited); + } } diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaTestProvider.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaTestProvider.java index 0d6a2678a8de..9c9b27ad3ddf 100644 --- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaTestProvider.java +++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaTestProvider.java @@ -3,6 +3,7 @@ package com.jetbrains.jsonSchema; import com.intellij.json.JsonLanguage; import com.intellij.openapi.fileTypes.LanguageFileType; +import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider; import com.jetbrains.jsonSchema.extension.SchemaType; @@ -16,7 +17,7 @@ public class JsonSchemaTestProvider implements JsonSchemaFileProvider { } @Override - public boolean isAvailable(@NotNull VirtualFile file) { + public boolean isAvailable(@NotNull Project project, @NotNull VirtualFile file) { return file.getFileType() instanceof LanguageFileType && ((LanguageFileType)file.getFileType()).getLanguage().isKindOf(JsonLanguage.INSTANCE); } diff --git a/json/tests/testData/jsonSchema/schemaFile/resolve/referencingGlobalSchema.json b/json/tests/testData/jsonSchema/schemaFile/resolve/referencingGlobalSchema.json new file mode 100644 index 000000000000..612d7e8a0109 --- /dev/null +++ b/json/tests/testData/jsonSchema/schemaFile/resolve/referencingGlobalSchema.json @@ -0,0 +1,7 @@ +{ + "properties": { + "smth": { + "$ref" : "http://json-schema.org/draft-04/schema#/properties/enum" + } + } +} \ No newline at end of file diff --git a/platform/indexing-api/src/com/intellij/util/indexing/IndexableSetContributor.java b/platform/indexing-api/src/com/intellij/util/indexing/IndexableSetContributor.java index ec93cbe14b2e..97ec2863064a 100644 --- a/platform/indexing-api/src/com/intellij/util/indexing/IndexableSetContributor.java +++ b/platform/indexing-api/src/com/intellij/util/indexing/IndexableSetContributor.java @@ -60,7 +60,7 @@ public abstract class IndexableSetContributor { * the returned set should not contain nulls or invalid files */ @NotNull - public abstract Set getAdditionalRootsToIndex(); + public abstract Set getAdditionalRootsToIndex(); @NotNull private static Set filterOutNulls(@NotNull IndexableSetContributor contributor, diff --git a/platform/platform-resources/src/META-INF/JsonPlugin.xml b/platform/platform-resources/src/META-INF/JsonPlugin.xml index 9f50e0b13dbb..518f8fbc79ae 100644 --- a/platform/platform-resources/src/META-INF/JsonPlugin.xml +++ b/platform/platform-resources/src/META-INF/JsonPlugin.xml @@ -77,10 +77,11 @@ + - +