From c3a3e04cb7d41246f11d7f6b11fbf53a1623f00a Mon Sep 17 00:00:00 2001 From: Anton Lobov Date: Tue, 24 Apr 2018 16:05:23 +0200 Subject: [PATCH] WEB-32238 JSON Schema: dropdown to choose JSON schemas --- .../JsonSchemaMappingsConfigurable.java | 8 +- ...sonSchemaMappingsProjectConfiguration.java | 49 +++- .../UserDefinedJsonSchemaConfiguration.java | 4 + .../extension/JsonSchemaFileProvider.java | 8 + .../jsonSchema/extension/JsonSchemaInfo.java | 84 +++++++ .../JsonSchemaProjectSelfProviderFactory.java | 28 +++ .../JsonSchemaUserDefinedProviderFactory.java | 10 +- .../jsonSchema/ide/JsonSchemaService.java | 13 +- .../jsonSchema/impl/JsonCachedValues.java | 13 +- ...sonSchemaConflictNotificationProvider.java | 13 +- .../impl/JsonSchemaServiceImpl.java | 172 ++++++++++--- .../remote/JsonSchemaCatalogManager.java | 49 ++++ .../widget/JsonSchemaInfoPopupStep.java | 155 ++++++++++++ .../widget/JsonSchemaStatusPopup.java | 70 ++++++ .../widget/JsonSchemaStatusWidget.java | 226 ++++++++++++++++++ .../jsonSchema/widget/JsonWidgetProvider.java | 23 ++ .../src/META-INF/JsonPlugin.xml | 4 +- 17 files changed, 874 insertions(+), 55 deletions(-) create mode 100644 json/src/com/jetbrains/jsonSchema/extension/JsonSchemaInfo.java create mode 100644 json/src/com/jetbrains/jsonSchema/widget/JsonSchemaInfoPopupStep.java create mode 100644 json/src/com/jetbrains/jsonSchema/widget/JsonSchemaStatusPopup.java create mode 100644 json/src/com/jetbrains/jsonSchema/widget/JsonSchemaStatusWidget.java create mode 100644 json/src/com/jetbrains/jsonSchema/widget/JsonWidgetProvider.java diff --git a/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsConfigurable.java b/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsConfigurable.java index 0ebb7a17ea7a..183698bb0cbe 100644 --- a/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsConfigurable.java +++ b/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsConfigurable.java @@ -84,9 +84,11 @@ public class JsonSchemaMappingsConfigurable extends MasterDetailsComponent imple return result; } - private void addProjectSchema() { - addCreatedMappings(new UserDefinedJsonSchemaConfiguration(createUniqueName(STUB_SCHEMA_NAME), - JsonSchemaVersion.SCHEMA_4, "", false, null)); + public UserDefinedJsonSchemaConfiguration addProjectSchema() { + UserDefinedJsonSchemaConfiguration configuration = new UserDefinedJsonSchemaConfiguration(createUniqueName(STUB_SCHEMA_NAME), + JsonSchemaVersion.SCHEMA_4, "", false, null); + addCreatedMappings(configuration); + return configuration; } @SuppressWarnings("SameParameterValue") diff --git a/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsProjectConfiguration.java b/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsProjectConfiguration.java index cf5ec903a304..f038ad83279c 100644 --- a/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsProjectConfiguration.java +++ b/json/src/com/jetbrains/jsonSchema/JsonSchemaMappingsProjectConfiguration.java @@ -9,22 +9,46 @@ import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.io.FileUtil; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.xmlb.annotations.Tag; import com.intellij.util.xmlb.annotations.XCollection; +import com.jetbrains.jsonSchema.extension.JsonSchemaInfo; import com.jetbrains.jsonSchema.ide.JsonSchemaService; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Collections; -import java.util.Map; -import java.util.Optional; -import java.util.TreeMap; +import java.util.*; @State(name = "JsonSchemaMappingsProjectConfiguration", storages = @Storage("jsonSchemas.xml")) public class JsonSchemaMappingsProjectConfiguration implements PersistentStateComponent { @NotNull private final Project myProject; public volatile MyState myState = new MyState(); + @Nullable + public UserDefinedJsonSchemaConfiguration findMappingBySchemaInfo(JsonSchemaInfo value) { + for (UserDefinedJsonSchemaConfiguration configuration : myState.myState.values()) { + if (Objects.equals(value.getUrl(), configuration.getRelativePathToSchema())) return configuration; + } + return null; + } + + @Nullable + public UserDefinedJsonSchemaConfiguration findMappingForFile(VirtualFile file) { + VirtualFile projectBaseDir = myProject.getBaseDir(); + for (UserDefinedJsonSchemaConfiguration configuration : myState.myState.values()) { + for (UserDefinedJsonSchemaConfiguration.Item pattern : configuration.patterns) { + if (pattern.pattern || pattern.directory) continue; + String path = pattern.path; + VirtualFile relativeFile = VfsUtil.findRelativeFile(projectBaseDir, path); + if (Objects.equals(relativeFile, file)) { + return configuration; + } + } + } + return null; + } + public static JsonSchemaMappingsProjectConfiguration getInstance(@NotNull final Project project) { return ServiceManager.getService(project, JsonSchemaMappingsProjectConfiguration.class); } @@ -51,6 +75,23 @@ public class JsonSchemaMappingsProjectConfiguration implements PersistentStateCo }); } + public void removeConfiguration(UserDefinedJsonSchemaConfiguration configuration) { + for (Map.Entry entry : myState.myState.entrySet()) { + if (entry.getValue() == configuration) { + myState.myState.remove(entry.getKey()); + return; + } + } + } + + public void addConfiguration(UserDefinedJsonSchemaConfiguration configuration) { + String name = configuration.getName(); + while (myState.myState.containsKey(name)) { + name += "1"; + } + myState.myState.put(name, configuration); + } + public Map getStateMap() { return Collections.unmodifiableMap(myState.myState); } diff --git a/json/src/com/jetbrains/jsonSchema/UserDefinedJsonSchemaConfiguration.java b/json/src/com/jetbrains/jsonSchema/UserDefinedJsonSchemaConfiguration.java index 30b64eeb4264..003ee15262f9 100644 --- a/json/src/com/jetbrains/jsonSchema/UserDefinedJsonSchemaConfiguration.java +++ b/json/src/com/jetbrains/jsonSchema/UserDefinedJsonSchemaConfiguration.java @@ -123,6 +123,10 @@ public class UserDefinedJsonSchemaConfiguration { myCalculatedPatterns.drop(); } + public void refreshPatterns() { + myCalculatedPatterns.drop(); + } + @NotNull public List> getCalculatedPatterns() { return myCalculatedPatterns.getValue(); diff --git a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaFileProvider.java b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaFileProvider.java index cabd89838e5a..9047afee82cc 100644 --- a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaFileProvider.java +++ b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaFileProvider.java @@ -26,4 +26,12 @@ public interface JsonSchemaFileProvider { default String getThirdPartyApiInformation() { return null; } + + default boolean isUserVisible() { return true; } + + @NotNull + default String getPresentableName() { return getName(); } + + @Nullable + default String getRemoteSource() { return null; } } diff --git a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaInfo.java b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaInfo.java new file mode 100644 index 000000000000..a667c09c9596 --- /dev/null +++ b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaInfo.java @@ -0,0 +1,84 @@ +// 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.extension; + +import com.intellij.openapi.util.text.StringUtil; +import com.jetbrains.jsonSchema.impl.JsonSchemaVersion; +import org.jetbrains.annotations.NotNull; + +public class JsonSchemaInfo { + private final JsonSchemaFileProvider myProvider; + private final String myUrl; + + public JsonSchemaInfo(@NotNull JsonSchemaFileProvider provider) { + myProvider = provider; + myUrl = null; + } + + public JsonSchemaInfo(@NotNull String url) { + myUrl = url; + myProvider = null; + } + + public JsonSchemaFileProvider getProvider() { + return myProvider; + } + + public String getUrl() { + return myProvider != null ? myProvider.getRemoteSource() : myUrl; + } + + public String getDescription() { + if (myProvider != null) { + String providerName = myProvider.getPresentableName(); + return sanitizeName(providerName); + } + + assert myUrl != null; + int index = StringUtil.lastIndexOfAny(myUrl, "/\\"); + if (index == -1) return sanitizeName(myUrl); + String possibleName = sanitizeName(myUrl.substring(index + 1)); + if (!isVeryDumbName(possibleName)) { + return possibleName; + } + + int index2 = myUrl.lastIndexOf('/', index - 1); + if (index2 != -1) { + possibleName = sanitizeName(myUrl.substring(index2 + 1, index)); + } + + // the only weird case + if ("http://json.schemastore.org/config".equals(myUrl)) { + return "asp.net config"; + } + + if (!isVeryDumbName(possibleName)) { + return possibleName; + } + + if (myUrl.startsWith("https://raw.githubusercontent.com/")) { + String substring = myUrl.substring("https://raw.githubusercontent.com/".length() + 1); + int slash = substring.indexOf('/'); + if (slash != -1) { + int slash2 = substring.indexOf('/', slash + 1); + if (slash2 != -1) { + return sanitizeName(substring.substring(slash + 1, slash2)); + } + } + } + + return possibleName; + } + + private static boolean isVeryDumbName(String possibleName) { + return "schema".equals(possibleName) || "config".equals(possibleName); + } + + @NotNull + private static String sanitizeName(String providerName) { + return StringUtil.trimEnd(StringUtil.trimEnd(StringUtil.trimEnd(providerName, ".json"), "-schema"), ".schema"); + } + + public JsonSchemaVersion getSchemaVersion() { + return myProvider != null ? myProvider.getSchemaVersion() : JsonSchemaVersion.SCHEMA_4; + } +} diff --git a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaProjectSelfProviderFactory.java b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaProjectSelfProviderFactory.java index f9b6cd4c0bd1..ec73d5fa2ac4 100644 --- a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaProjectSelfProviderFactory.java +++ b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaProjectSelfProviderFactory.java @@ -89,5 +89,33 @@ public class JsonSchemaProjectSelfProviderFactory implements JsonSchemaProviderF public SchemaType getSchemaType() { return SchemaType.schema; } + + @Nullable + @Override + public String getRemoteSource() { + switch (myFileName) { + case SCHEMA_JSON_FILE_NAME: + return "http://json-schema.org/draft-04/schema"; + case SCHEMA06_JSON_FILE_NAME: + return "http://json-schema.org/draft-06/schema"; + case SCHEMA07_JSON_FILE_NAME: + return "http://json-schema.org/draft-07/schema"; + } + return null; + } + + @NotNull + @Override + public String getPresentableName() { + switch (myFileName) { + case SCHEMA_JSON_FILE_NAME: + return "JSON Schema V4"; + case SCHEMA06_JSON_FILE_NAME: + return "JSON Schema V6"; + case SCHEMA07_JSON_FILE_NAME: + return "JSON Schema V7"; + } + return getName(); + } } } diff --git a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaUserDefinedProviderFactory.java b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaUserDefinedProviderFactory.java index 5862355d8289..1d5fb646eb9f 100644 --- a/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaUserDefinedProviderFactory.java +++ b/json/src/com/jetbrains/jsonSchema/extension/JsonSchemaUserDefinedProviderFactory.java @@ -8,7 +8,6 @@ import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.PairProcessor; import com.jetbrains.jsonSchema.JsonSchemaMappingsProjectConfiguration; import com.jetbrains.jsonSchema.UserDefinedJsonSchemaConfiguration; -import com.jetbrains.jsonSchema.ide.JsonSchemaService; import com.jetbrains.jsonSchema.impl.JsonSchemaVersion; import com.jetbrains.jsonSchema.remote.JsonFileResolver; import org.jetbrains.annotations.NotNull; @@ -99,8 +98,7 @@ public class JsonSchemaUserDefinedProviderFactory implements JsonSchemaProviderF @Override public boolean isAvailable(@NotNull VirtualFile file) { //noinspection SimplifiableIfStatement - if (myPatterns.isEmpty() || file.isDirectory() || !file.isValid() || getSchemaFile() == null || - JsonSchemaService.Impl.get(myProject).isSchemaFile(file)) return false; + if (myPatterns.isEmpty() || file.isDirectory() || !file.isValid() || getSchemaFile() == null) return false; return myPatterns.stream().anyMatch(processor -> processor.process(myProject, file)); } @@ -121,5 +119,11 @@ public class JsonSchemaUserDefinedProviderFactory implements JsonSchemaProviderF result = 31 * result + FileUtil.pathHashCode(myFile); return result; } + + @Nullable + @Override + public String getRemoteSource() { + return isHttpPath(myFile) ? myFile : null; + } } } diff --git a/json/src/com/jetbrains/jsonSchema/ide/JsonSchemaService.java b/json/src/com/jetbrains/jsonSchema/ide/JsonSchemaService.java index 857092601246..4920b2bb4030 100644 --- a/json/src/com/jetbrains/jsonSchema/ide/JsonSchemaService.java +++ b/json/src/com/jetbrains/jsonSchema/ide/JsonSchemaService.java @@ -8,12 +8,14 @@ import com.intellij.openapi.util.ModificationTracker; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider; +import com.jetbrains.jsonSchema.extension.JsonSchemaInfo; import com.jetbrains.jsonSchema.impl.JsonSchemaObject; import com.jetbrains.jsonSchema.impl.JsonSchemaVersion; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collection; +import java.util.List; public interface JsonSchemaService { class Impl { @@ -35,6 +37,13 @@ public interface JsonSchemaService { @NotNull Collection getSchemaFilesForFile(@NotNull VirtualFile file); + void registerRemoteUpdateCallback(Runnable callback); + void unregisterRemoteUpdateCallback(Runnable callback); + void registerResetAction(Runnable action); + void unregisterResetAction(Runnable action); + + void triggerUpdateRemote(); + @Nullable JsonSchemaObject getSchemaObject(@NotNull VirtualFile file); @@ -42,7 +51,7 @@ public interface JsonSchemaService { JsonSchemaObject getSchemaObjectForSchemaFile(@NotNull VirtualFile schemaFile); @Nullable - VirtualFile findSchemaFileByReference(@NotNull String reference, VirtualFile referent); + VirtualFile findSchemaFileByReference(@NotNull String reference, @Nullable VirtualFile referent); @Nullable JsonSchemaFileProvider getSchemaProvider(@NotNull final VirtualFile schemaFile); @@ -51,6 +60,8 @@ public interface JsonSchemaService { ModificationTracker getAnySchemaChangeTracker(); + List getAllUserVisibleSchemas(); + @NotNull static String normalizeId(@NotNull String id) { id = id.endsWith("#") ? id.substring(0, id.length() - 1) : id; diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonCachedValues.java b/json/src/com/jetbrains/jsonSchema/impl/JsonCachedValues.java index b722124bf392..372b599e6115 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonCachedValues.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonCachedValues.java @@ -41,17 +41,6 @@ public class JsonCachedValues { } private static final Key> SCHEMA_URL_KEY = Key.create("JsonSchemaUrlCache"); - @Nullable - public static VirtualFile getSchemaFileFromSchemaProperty(@NotNull VirtualFile file, @NotNull Project project) { - String schemaUrl = getSchemaUrlFromSchemaProperty(file, project); - - if (schemaUrl != null) { - VirtualFile virtualFile = JsonFileResolver.resolveSchemaByReference(file, schemaUrl); - if (virtualFile != null) return virtualFile; - } - return null; - } - @Nullable public static String getSchemaUrlFromSchemaProperty(@NotNull VirtualFile file, @NotNull Project project) { @@ -76,7 +65,7 @@ public class JsonCachedValues { if (schema == null) return null; JsonValue value = schema.getValue(); - return value instanceof JsonStringLiteral ? StringUtil.trimEnd(((JsonStringLiteral)value).getValue(), "#") : null; + return value instanceof JsonStringLiteral ? ((JsonStringLiteral)value).getValue() : null; } private static final Key> SCHEMA_ID_CACHE_KEY = Key.create("JsonSchemaIdCache"); diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaConflictNotificationProvider.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaConflictNotificationProvider.java index 7163eb8bec6f..0993e6309977 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaConflictNotificationProvider.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaConflictNotificationProvider.java @@ -64,7 +64,8 @@ public class JsonSchemaConflictNotificationProvider extends EditorNotifications. final Collection schemaFiles = myJsonSchemaService.getSchemaFilesForFile(file); if (schemaFiles.size() <= 1) return null; - final String message = createMessage(schemaFiles); + final String message = createMessage(schemaFiles, myJsonSchemaService, + "; ", "There are several JSON Schemas mapped to this file: ", ""); if (message == null) return null; final EditorNotificationPanel panel = new EditorNotificationPanel(LightColors.RED); @@ -76,9 +77,13 @@ public class JsonSchemaConflictNotificationProvider extends EditorNotifications. return panel; } - public String createMessage(@NotNull final Collection schemaFiles) { + public static String createMessage(@NotNull final Collection schemaFiles, + @NotNull JsonSchemaService jsonSchemaService, + @NotNull String separator, + @NotNull String prefix, + @NotNull String suffix) { final List> pairList = schemaFiles.stream() - .map(file -> myJsonSchemaService.getSchemaProvider(file)) + .map(file -> jsonSchemaService.getSchemaProvider(file)) .filter(Objects::nonNull) .map(provider -> Pair.create(SchemaType.userSchema.equals(provider.getSchemaType()), provider.getName())) .collect(Collectors.toList()); @@ -95,6 +100,6 @@ public class JsonSchemaConflictNotificationProvider extends EditorNotifications. else { return pair.getSecond(); } - }).collect(Collectors.joining("; ", "There are several JSON Schemas mapped to this file: ", "")); + }).collect(Collectors.joining(separator, prefix, suffix)); } } diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaServiceImpl.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaServiceImpl.java index 50504b8e72eb..e7bc3740a077 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaServiceImpl.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaServiceImpl.java @@ -13,9 +13,12 @@ import com.intellij.openapi.util.Factory; import com.intellij.openapi.util.ModificationTracker; import com.intellij.openapi.util.Ref; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.impl.http.HttpVirtualFile; +import com.intellij.util.containers.ConcurrentList; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.jsonSchema.JsonSchemaVfsListener; import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider; +import com.jetbrains.jsonSchema.extension.JsonSchemaInfo; import com.jetbrains.jsonSchema.extension.JsonSchemaProviderFactory; import com.jetbrains.jsonSchema.extension.SchemaType; import com.jetbrains.jsonSchema.ide.JsonSchemaService; @@ -80,53 +83,143 @@ public class JsonSchemaServiceImpl implements JsonSchemaService { public void reset() { myState.reset(); myAnyChangeCount.incrementAndGet(); + for (Runnable action: myResetActions) { + action.run(); + } DaemonCodeAnalyzer.getInstance(myProject).restart(); } @Override @Nullable public VirtualFile findSchemaFileByReference(@NotNull String reference, @Nullable VirtualFile referent) { - final Optional optional = myState.getFiles().stream() - .filter(file -> reference.equals(JsonCachedValues.getSchemaId(file, myProject))) - .findFirst(); + final Optional optional = findBuiltInSchemaByReference(reference); return optional.orElseGet(() -> JsonFileResolver.resolveSchemaByReference(referent, JsonSchemaService.normalizeId(reference))); } + private Optional findBuiltInSchemaByReference(@NotNull String reference) { + return myState.getFiles().stream() + .filter(file -> JsonSchemaService.normalizeId(reference).equals(JsonCachedValues.getSchemaId(file, myProject))) + .findFirst(); + } + @Override @NotNull public Collection getSchemaFilesForFile(@NotNull final VirtualFile file) { - List files = myState.getProviders().stream().filter(provider -> isProviderAvailable(file, provider)) - .map(processor -> processor.getSchemaFile()).collect(Collectors.toList()); - if (files.size() > 0) { - return files; + return getSchemasForFile(file, false); + } + + @NotNull + private Collection getSchemasForFile(@NotNull VirtualFile file, boolean single) { + List providers = getProvidersForFile(file); + + // proper priority: + // 1) user providers + // 2) $schema property + // 3) built-in providers + // 4) schema catalog + + boolean checkSchemaProperty = true; + if (providers.stream().noneMatch(p -> p.getSchemaType() == SchemaType.userSchema)) { + VirtualFile virtualFile = resolveFromSchemaProperty(file); + if (virtualFile != null) return ContainerUtil.createMaybeSingletonList(virtualFile); + checkSchemaProperty = false; + } + + if (!single) { + List files = ContainerUtil.newArrayList(); + for (JsonSchemaFileProvider provider : providers) { + VirtualFile schemaFile = provider.getSchemaFile(); + if (schemaFile != null) { + files.add(schemaFile); + } + } + if (!files.isEmpty()) { + return files; + } + } + else if (!providers.isEmpty()) { + final JsonSchemaFileProvider selected; + if (providers.size() > 2) return ContainerUtil.emptyList(); + if (providers.size() > 1) { + final Optional userSchema = + providers.stream().filter(provider -> SchemaType.userSchema.equals(provider.getSchemaType())).findFirst(); + if (!userSchema.isPresent()) return ContainerUtil.emptyList(); + selected = userSchema.get(); + } else selected = providers.get(0); + VirtualFile schemaFile = selected.getSchemaFile(); + return ContainerUtil.createMaybeSingletonList(schemaFile); + } + + if (checkSchemaProperty) { + VirtualFile virtualFile = resolveFromSchemaProperty(file); + if (virtualFile != null) return ContainerUtil.createMaybeSingletonList(virtualFile); } return ContainerUtil.createMaybeSingletonList(resolveSchemaFromOtherSources(file)); } + @NotNull + private List getProvidersForFile(@NotNull VirtualFile file) { + return myState.getProviders().stream().filter(provider -> isProviderAvailable(file, provider)).collect( + Collectors.toList()); + } + + @Nullable + private VirtualFile resolveFromSchemaProperty(@NotNull VirtualFile file) { + String schemaUrl = JsonCachedValues.getSchemaUrlFromSchemaProperty(file, myProject); + + if (schemaUrl != null) { + VirtualFile virtualFile = findSchemaFileByReference(schemaUrl, file); + if (virtualFile != null) return virtualFile; + } + return null; + } + + public List getAllUserVisibleSchemas() { + List schemas = myCatalogManager.getAllCatalogSchemas(); + Collection providers = myState.getProviders(); + List results = ContainerUtil.newArrayListWithCapacity(schemas.size() + providers.size()); + Set processedRemotes = ContainerUtil.newHashSet(); + for (JsonSchemaFileProvider provider: providers) { + if (provider.isUserVisible() + && provider.getRemoteSource() != null /*currently we're unable to handle providers without URLs properly*/ + && processedRemotes.add(provider.getRemoteSource())) { + results.add(new JsonSchemaInfo(provider)); + } + } + + for (String schema: schemas) { + if (processedRemotes.add(schema)) { + results.add(new JsonSchemaInfo(schema)); + } + } + return results; + } + @Nullable @Override public JsonSchemaObject getSchemaObject(@NotNull final VirtualFile file) { - List providers = - myState.getProviders().stream().filter(provider -> isProviderAvailable(file, provider)).collect(Collectors.toList()); + Collection schemas = getSchemasForFile(file, true); + if (schemas.size() == 0) return null; + assert schemas.size() == 1; + VirtualFile schemaFile = schemas.iterator().next(); + return JsonCachedValues.getSchemaObject(replaceHttpFileWithBuiltinIfNeeded(schemaFile), myProject); + } - if (providers.isEmpty()) { - VirtualFile virtualFile = resolveSchemaFromOtherSources(file); - return virtualFile == null ? null : JsonCachedValues.getSchemaObject(virtualFile, myProject); + private 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 + // this way we're preventing http files when a built-in schema exists + if (schemaFile instanceof HttpVirtualFile) { + String url = schemaFile.getUrl(); + Optional first = myState.getFiles().stream() + .filter(f -> { + JsonSchemaFileProvider prov = getSchemaProvider(f); + return prov != null && !(prov.getSchemaFile() instanceof HttpVirtualFile) && url.equals(prov.getRemoteSource()); + }).findFirst(); + return first.orElse(schemaFile); } - - if (providers.size() > 2) return null; - - final JsonSchemaFileProvider selected; - if (providers.size() > 1) { - final Optional userSchema = - providers.stream().filter(provider -> SchemaType.userSchema.equals(provider.getSchemaType())).findFirst(); - if (!userSchema.isPresent()) return null; - selected = userSchema.get(); - } else selected = providers.get(0); - if (selected.getSchemaFile() == null) return null; - - return JsonCachedValues.getSchemaObject(selected.getSchemaFile(), myProject); + return schemaFile; } @Nullable @@ -180,11 +273,36 @@ public class JsonSchemaServiceImpl implements JsonSchemaService { @Nullable private VirtualFile resolveSchemaFromOtherSources(@NotNull VirtualFile file) { - VirtualFile virtualFile = JsonCachedValues.getSchemaFileFromSchemaProperty(file, myProject); - if (virtualFile != null) return virtualFile; return myCatalogManager.getSchemaFileForFile(file); } + @Override + public void registerRemoteUpdateCallback(Runnable callback) { + myCatalogManager.registerCatalogUpdateCallback(callback); + } + + @Override + public void unregisterRemoteUpdateCallback(Runnable callback) { + myCatalogManager.unregisterCatalogUpdateCallback(callback); + } + + private final ConcurrentList myResetActions = ContainerUtil.createConcurrentList(); + + @Override + public void registerResetAction(Runnable action) { + myResetActions.add(action); + } + + @Override + public void unregisterResetAction(Runnable action) { + myResetActions.remove(action); + } + + @Override + public void triggerUpdateRemote() { + myCatalogManager.triggerUpdateCatalog(); + } + private static class MyState { @NotNull private final Factory> myFactory; @NotNull private final AtomicClearableLazyValue> myData; diff --git a/json/src/com/jetbrains/jsonSchema/remote/JsonSchemaCatalogManager.java b/json/src/com/jetbrains/jsonSchema/remote/JsonSchemaCatalogManager.java index 1a4d897e8be7..76e93ed4b879 100644 --- a/json/src/com/jetbrains/jsonSchema/remote/JsonSchemaCatalogManager.java +++ b/json/src/com/jetbrains/jsonSchema/remote/JsonSchemaCatalogManager.java @@ -5,7 +5,9 @@ import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Pair; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.impl.http.FileDownloadingAdapter; import com.intellij.openapi.vfs.impl.http.HttpVirtualFile; +import com.intellij.openapi.vfs.impl.http.RemoteFileInfo; import com.intellij.openapi.vfs.impl.http.RemoteFileManager; import com.intellij.util.containers.ContainerUtil; import com.jetbrains.jsonSchema.impl.JsonCachedValues; @@ -14,6 +16,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; @@ -65,6 +68,52 @@ public class JsonSchemaCatalogManager { return null; } + public List getAllCatalogSchemas() { + if (myCatalog != null) { + List, String>> catalog = JsonCachedValues.getSchemaCatalog(myCatalog, myProject); + if (catalog == null) return ContainerUtil.emptyList(); + List results = ContainerUtil.newArrayListWithCapacity(catalog.size()); + for (Pair, String> item: catalog) { + results.add(item.second); + } + return results; + } + + return ContainerUtil.emptyList(); + } + + private final Map myDownloadingAdapters = ContainerUtil.createConcurrentWeakMap(); + public void registerCatalogUpdateCallback(Runnable callback) { + if (myCatalog instanceof HttpVirtualFile) { + RemoteFileInfo info = ((HttpVirtualFile)myCatalog).getFileInfo(); + if (info != null) { + FileDownloadingAdapter adapter = new FileDownloadingAdapter() { + @Override + public void fileDownloaded(VirtualFile localFile) { + callback.run(); + } + }; + myDownloadingAdapters.put(callback, adapter); + info.addDownloadingListener(adapter); + } + } + } + + public void unregisterCatalogUpdateCallback(Runnable callback) { + if (!myDownloadingAdapters.containsKey(callback)) return; + + if (myCatalog instanceof HttpVirtualFile) { + RemoteFileInfo info = ((HttpVirtualFile)myCatalog).getFileInfo(); + if (info != null) { + info.removeDownloadingListener(myDownloadingAdapters.get(callback)); + } + } + } + + public void triggerUpdateCatalog() { + JsonFileResolver.startFetchingHttpFileIfNeeded(myCatalog); + } + @Nullable private static String resolveSchemaFile(@NotNull VirtualFile file, @NotNull VirtualFile catalogFile, @NotNull Project project) { JsonFileResolver.startFetchingHttpFileIfNeeded(catalogFile); diff --git a/json/src/com/jetbrains/jsonSchema/widget/JsonSchemaInfoPopupStep.java b/json/src/com/jetbrains/jsonSchema/widget/JsonSchemaInfoPopupStep.java new file mode 100644 index 000000000000..838afb5a5922 --- /dev/null +++ b/json/src/com/jetbrains/jsonSchema/widget/JsonSchemaInfoPopupStep.java @@ -0,0 +1,155 @@ +// 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.widget; + +import com.intellij.icons.AllIcons; +import com.intellij.openapi.options.ShowSettingsUtil; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.ListSeparator; +import com.intellij.openapi.ui.popup.PopupStep; +import com.intellij.openapi.ui.popup.util.BaseListPopupStep; +import com.intellij.openapi.vfs.VfsUtil; +import com.intellij.openapi.vfs.VfsUtilCore; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.ui.EmptyIcon; +import com.intellij.util.ui.JBUI; +import com.jetbrains.jsonSchema.JsonSchemaMappingsConfigurable; +import com.jetbrains.jsonSchema.JsonSchemaMappingsProjectConfiguration; +import com.jetbrains.jsonSchema.UserDefinedJsonSchemaConfiguration; +import com.jetbrains.jsonSchema.extension.JsonSchemaInfo; +import com.jetbrains.jsonSchema.ide.JsonSchemaService; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static com.jetbrains.jsonSchema.widget.JsonSchemaStatusPopup.*; + +class JsonSchemaInfoPopupStep extends BaseListPopupStep { + private final Project myProject; + private final VirtualFile myVirtualFile; + @NotNull private final JsonSchemaService myService; + private static final Icon EMPTY_ICON = JBUI.scale(EmptyIcon.create(AllIcons.General.Add.getIconWidth())); + + public JsonSchemaInfoPopupStep(@NotNull List allSchemas, @NotNull Project project, @NotNull VirtualFile virtualFile, + @NotNull JsonSchemaService service) { + super(null, allSchemas); + myProject = project; + myVirtualFile = virtualFile; + myService = service; + } + + @NotNull + @Override + public String getTextFor(JsonSchemaInfo value) { + return value.getDescription(); + } + + @Override + public Icon getIconFor(JsonSchemaInfo value) { + if (value == ADD_SCHEMA) { + return AllIcons.General.Add; + } + + if (value == EDIT_MAPPINGS) { + return AllIcons.General.EditItemInSection; + } + + if (value == LOAD_REMOTE) { + return AllIcons.Actions.Refresh; + } + + return EMPTY_ICON; + } + + @Nullable + @Override + public ListSeparator getSeparatorAbove(JsonSchemaInfo value) { + List values = getValues(); + int index = values.indexOf(value); + if (index - 1 > 0) { + JsonSchemaInfo info = values.get(index - 1); + if (info == EDIT_MAPPINGS) { + return new ListSeparator("Registered schemas"); + } + if (value.getProvider() == null && info.getProvider() != null) { + return new ListSeparator("SchemaStore.org schemas"); + } + } + return null; + } + + @Override + public PopupStep onChosen(JsonSchemaInfo selectedValue, boolean finalChoice) { + if (finalChoice) { + if (selectedValue == ADD_SCHEMA) { + return doFinalStep(() -> { + JsonSchemaMappingsConfigurable configurable = new JsonSchemaMappingsConfigurable(myProject); + ShowSettingsUtil.getInstance().editConfigurable(myProject, configurable, () -> { + UserDefinedJsonSchemaConfiguration configuration = configurable.addProjectSchema(); + configuration.patterns.add(new UserDefinedJsonSchemaConfiguration.Item( + VfsUtilCore.getRelativePath(myVirtualFile, myProject.getBaseDir()), false, false)); + }); + }); + } + else if (selectedValue == EDIT_MAPPINGS) { + return doFinalStep(() -> ShowSettingsUtil.getInstance().editConfigurable(myProject, new JsonSchemaMappingsConfigurable(myProject))); + } + else if (selectedValue == LOAD_REMOTE) { + return doFinalStep(() -> myService.triggerUpdateRemote()); + } + else { + setMapping(selectedValue, myVirtualFile, myProject); + return doFinalStep(() -> myService.reset()); + } + } + return PopupStep.FINAL_CHOICE; + } + + @Override + public boolean isSpeedSearchEnabled() { + return true; + } + + private static void setMapping(@Nullable JsonSchemaInfo selectedValue, @NotNull VirtualFile virtualFile, @NotNull Project project) { + JsonSchemaMappingsProjectConfiguration configuration = JsonSchemaMappingsProjectConfiguration.getInstance(project); + + VirtualFile projectBaseDir = project.getBaseDir(); + + UserDefinedJsonSchemaConfiguration mappingForFile = configuration.findMappingForFile(virtualFile); + if (mappingForFile != null) { + for (UserDefinedJsonSchemaConfiguration.Item pattern : mappingForFile.patterns) { + if (Objects.equals(VfsUtil.findRelativeFile(projectBaseDir, pattern.path), virtualFile)) { + mappingForFile.patterns.remove(pattern); + if (mappingForFile.patterns.size() == 0) { + configuration.removeConfiguration(mappingForFile); + } + else { + mappingForFile.refreshPatterns(); + } + break; + } + } + } + + if (selectedValue == null) return; + + String path = VfsUtilCore.getRelativePath(virtualFile, projectBaseDir); + + UserDefinedJsonSchemaConfiguration existing = configuration.findMappingBySchemaInfo(selectedValue); + UserDefinedJsonSchemaConfiguration.Item item = new UserDefinedJsonSchemaConfiguration.Item(path, false, false); + if (existing != null) { + existing.patterns.add(item); + existing.refreshPatterns(); + } + else { + configuration.addConfiguration(new UserDefinedJsonSchemaConfiguration(selectedValue.getDescription(), + selectedValue.getSchemaVersion(), + selectedValue.getUrl(), + false, + Collections.singletonList(item))); + } + } +} diff --git a/json/src/com/jetbrains/jsonSchema/widget/JsonSchemaStatusPopup.java b/json/src/com/jetbrains/jsonSchema/widget/JsonSchemaStatusPopup.java new file mode 100644 index 000000000000..751597187d0a --- /dev/null +++ b/json/src/com/jetbrains/jsonSchema/widget/JsonSchemaStatusPopup.java @@ -0,0 +1,70 @@ +// 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.widget; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.JBPopupFactory; +import com.intellij.openapi.ui.popup.ListPopup; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.ContainerUtil; +import com.jetbrains.jsonSchema.extension.JsonSchemaInfo; +import com.jetbrains.jsonSchema.ide.JsonSchemaService; +import org.jetbrains.annotations.NotNull; + +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class JsonSchemaStatusPopup { + static final JsonSchemaInfo ADD_SCHEMA = new JsonSchemaInfo("") { + @Override + public String getDescription() { + return "Add new schema"; + } + }; + + static final JsonSchemaInfo EDIT_MAPPINGS = new JsonSchemaInfo("") { + @Override + public String getDescription() { + return "Edit schema mappings"; + } + }; + + static final JsonSchemaInfo LOAD_REMOTE = new JsonSchemaInfo("") { + @Override + public String getDescription() { + return "Load SchemaStore mappings"; + } + }; + + static ListPopup createPopup(@NotNull JsonSchemaService service, + @NotNull Project project, + @NotNull VirtualFile virtualFile, + boolean showOnlyEdit) { + JsonSchemaInfoPopupStep step = createPopupStep(service, project, virtualFile, showOnlyEdit); + return JBPopupFactory.getInstance().createListPopup(step); + } + + @NotNull + static JsonSchemaInfoPopupStep createPopupStep(@NotNull JsonSchemaService service, + @NotNull Project project, + @NotNull VirtualFile virtualFile, boolean showOnlyEdit) { + List allSchemas; + if (!showOnlyEdit) { + List infos = service.getAllUserVisibleSchemas(); + Comparator comparator = Comparator.comparing(JsonSchemaInfo::getDescription, String::compareTo); + Stream registered = infos.stream().filter(i -> i.getProvider() != null).sorted(comparator); + List otherList = infos.stream().filter(i -> i.getProvider() == null).sorted(comparator).collect(Collectors.toList()); + if (otherList.size() == 0) { + otherList = ContainerUtil.createMaybeSingletonList(LOAD_REMOTE); + } + allSchemas = Stream.concat(registered, otherList.stream()).collect(Collectors.toList()); + allSchemas.add(0, ADD_SCHEMA); + allSchemas.add(1, EDIT_MAPPINGS); + } + else { + allSchemas = ContainerUtil.createMaybeSingletonList(EDIT_MAPPINGS); + } + return new JsonSchemaInfoPopupStep(allSchemas, project, virtualFile, service); + } +} diff --git a/json/src/com/jetbrains/jsonSchema/widget/JsonSchemaStatusWidget.java b/json/src/com/jetbrains/jsonSchema/widget/JsonSchemaStatusWidget.java new file mode 100644 index 000000000000..b1836aedeb98 --- /dev/null +++ b/json/src/com/jetbrains/jsonSchema/widget/JsonSchemaStatusWidget.java @@ -0,0 +1,226 @@ +// 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.widget; + +import com.intellij.icons.AllIcons; +import com.intellij.json.JsonLanguage; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.actionSystem.DataContext; +import com.intellij.openapi.fileTypes.FileType; +import com.intellij.openapi.fileTypes.LanguageFileType; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.popup.ListPopup; +import com.intellij.openapi.util.SystemInfo; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.impl.http.FileDownloadingAdapter; +import com.intellij.openapi.vfs.impl.http.HttpVirtualFile; +import com.intellij.openapi.vfs.impl.http.RemoteFileInfo; +import com.intellij.openapi.wm.StatusBar; +import com.intellij.openapi.wm.StatusBarWidget; +import com.intellij.openapi.wm.WindowManager; +import com.intellij.openapi.wm.impl.status.EditorBasedStatusBarPopup; +import com.jetbrains.jsonSchema.extension.JsonSchemaFileProvider; +import com.jetbrains.jsonSchema.extension.SchemaType; +import com.jetbrains.jsonSchema.ide.JsonSchemaService; +import com.jetbrains.jsonSchema.impl.JsonSchemaConflictNotificationProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +public class JsonSchemaStatusWidget { + private final Project myProject; + private final JsonSchemaService myService; + + @Nullable + private MyWidget myWidget; + private final Runnable myUpdateCallback = () -> { + if (myWidget != null) { + myWidget.update(); + } + }; + + public JsonSchemaStatusWidget(final Project project) { + myProject = project; + myService = JsonSchemaService.Impl.get(project); + myService.registerRemoteUpdateCallback(myUpdateCallback); + myService.registerResetAction(myUpdateCallback); + showOrHideWidget(false); + } + + public void destroy() { + myService.unregisterRemoteUpdateCallback(myUpdateCallback); + myService.unregisterResetAction(myUpdateCallback); + showOrHideWidget(true); + } + + private void showOrHideWidget(boolean forceRemove) { + StatusBar statusBar = WindowManager.getInstance().getStatusBar(myProject); + if (statusBar == null) { + return; + } + + boolean showWidget = !forceRemove; + if (showWidget) { + if (myWidget == null) { + myWidget = new MyWidget(myProject); + statusBar.addWidget(myWidget, myWidget.getAnchor()); + } + } + else { + if (myWidget != null) { + statusBar.removeWidget(myWidget.ID()); + myWidget = null; + } + } + } + + private class MyWidget extends EditorBasedStatusBarPopup { + public MyWidget(Project project) { + super(project); + } + + private class MyWidgetState extends WidgetState { + boolean warning = false; + public MyWidgetState(String toolTip, String text, boolean actionEnabled) { + super(toolTip, text, actionEnabled); + } + + public boolean isWarning() { + return warning; + } + + public void setWarning(boolean warning) { + this.warning = warning; + this.setIcon(warning ? AllIcons.General.Warning : null); + } + } + + @NotNull + @Override + protected WidgetState getWidgetState(@Nullable VirtualFile file) { + if (file == null) { + return new MyWidgetState("", "", false); + } + FileType fileType = file.getFileType(); + if (!(fileType instanceof LanguageFileType) || !(((LanguageFileType)fileType).getLanguage() instanceof JsonLanguage)) { + WidgetState state = new MyWidgetState("", "", true); + state.setHidden(true); + return state; + } + + Collection schemaFiles = myService.getSchemaFilesForFile(file); + if (schemaFiles.size() == 0) { + return new MyWidgetState("No JSON Schema defined", "No JSON schema", true); + } + + if (schemaFiles.size() != 1) { + List onlyUserSchemas = schemaFiles.stream().filter(s -> { + JsonSchemaFileProvider provider = myService.getSchemaProvider(s); + return provider != null && provider.getSchemaType() == SchemaType.userSchema; + }).collect(Collectors.toList()); + if (onlyUserSchemas.size() > 1) { + MyWidgetState state = new MyWidgetState(JsonSchemaConflictNotificationProvider.createMessage(schemaFiles, myService, + "
", "Conflicting schemas:
", ""), + schemaFiles.size() + " schemas (!)", true); + state.setWarning(true); + return state; + } + schemaFiles = onlyUserSchemas; + if (schemaFiles.size() == 0) { + return new MyWidgetState("No JSON Schema defined", "No JSON schema", true); + } + } + + VirtualFile schemaFile = schemaFiles.iterator().next(); + JsonSchemaFileProvider provider = myService.getSchemaProvider(schemaFile); + if (provider != null) { + String providerName = provider.getPresentableName(); + String shortName = StringUtil.trimEnd(StringUtil.trimEnd(providerName, ".json"), "-schema"); + String name = shortName.startsWith("JSON Schema") ? shortName : ("JSON Schema: " + shortName); + String kind = provider.getSchemaType() == SchemaType.embeddedSchema || provider.getSchemaType() == SchemaType.schema ? " (bundled)" : ""; + return new MyWidgetState("JSON Schema: " + providerName + kind, name, true); + } + if (schemaFile instanceof HttpVirtualFile) { + RemoteFileInfo info = ((HttpVirtualFile)schemaFile).getFileInfo(); + if (info == null) { + MyWidgetState state = new MyWidgetState("Error downloading schema", "JSON schema error", true); + state.setWarning(true); + return state; + } + + //noinspection EnumSwitchStatementWhichMissesCases + switch (info.getState()) { + case DOWNLOADING_NOT_STARTED: + case DOWNLOADING_IN_PROGRESS: + info.addDownloadingListener(new FileDownloadingAdapter() { + @Override + public void fileDownloaded(VirtualFile localFile) { + if (myWidget != null) { + myWidget.update(); + } + } + }); + return new MyWidgetState("Download is scheduled or in progress", "Downloading JSON schema", false); + case ERROR_OCCURRED: + MyWidgetState state = new MyWidgetState("Error downloading schema", "JSON schema error", true); + state.setWarning(true); + return state; + } + } + + if (!myService.isSchemaFile(schemaFile)) { + MyWidgetState state = new MyWidgetState("File is not a schema", "JSON schema error", true); + state.setWarning(true); + return state; + } + + return new MyWidgetState("JSON Schema: " + getSchemaFileDesc(schemaFile), "JSON Schema: " + schemaFile.getNameWithoutExtension(), true); + } + + @NotNull + private String getSchemaFileDesc(VirtualFile schemaFile) { + if (schemaFile instanceof HttpVirtualFile) { + return schemaFile.getPresentableUrl(); + } + return schemaFile.getName(); + } + + @Nullable + @Override + protected ListPopup createPopup(DataContext context) { + final VirtualFile virtualFile = CommonDataKeys.VIRTUAL_FILE.getData(context); + if (virtualFile == null) return null; + + Project project = getProject(); + return doCreatePopup(virtualFile, project, ((MyWidgetState)getWidgetState(virtualFile)).isWarning()); + } + + @NotNull + private ListPopup doCreatePopup(VirtualFile virtualFile, Project project, boolean showOnlyEdit) { + return JsonSchemaStatusPopup.createPopup(myService, project, virtualFile, showOnlyEdit); + } + + @Override + protected void registerCustomListeners() { + } + + @NotNull + @Override + protected StatusBarWidget createInstance(Project project) { + return new MyWidget(project); + } + + @NotNull + @Override + public String ID() { + return "JSONSchemaSelector"; + } + + public String getAnchor() { + return "after " + (SystemInfo.isMac ? "Encoding" : "InsertOverwrite"); + } + } +} diff --git a/json/src/com/jetbrains/jsonSchema/widget/JsonWidgetProvider.java b/json/src/com/jetbrains/jsonSchema/widget/JsonWidgetProvider.java new file mode 100644 index 000000000000..f7bb98020ce3 --- /dev/null +++ b/json/src/com/jetbrains/jsonSchema/widget/JsonWidgetProvider.java @@ -0,0 +1,23 @@ +// 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.widget; + +import com.intellij.openapi.components.AbstractProjectComponent; +import com.intellij.openapi.project.Project; + +public class JsonWidgetProvider extends AbstractProjectComponent { + private JsonSchemaStatusWidget myWidget; + + protected JsonWidgetProvider(Project project) { + super(project); + } + + @Override + public void projectOpened() { + myWidget = new JsonSchemaStatusWidget(myProject); + } + + @Override + public void projectClosed() { + myWidget.destroy(); + } +} diff --git a/platform/platform-resources/src/META-INF/JsonPlugin.xml b/platform/platform-resources/src/META-INF/JsonPlugin.xml index ad0f0df67659..55668007229d 100644 --- a/platform/platform-resources/src/META-INF/JsonPlugin.xml +++ b/platform/platform-resources/src/META-INF/JsonPlugin.xml @@ -7,7 +7,9 @@ - + + com.jetbrains.jsonSchema.widget.JsonWidgetProvider +