From 92996cecaabb27ca4ca96a2a1dfde34a38e87293 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 30 Sep 2024 17:05:19 +0200 Subject: [PATCH] several optimizations for speeding up getFileType() (part of IJPL-162944 speedup find usages in kotlin files): - Do not query FileTypeOverrider extension point in hot path; store in fileTypeOverriderCache instead - Store FileTypeManager instance in FileTypeRegistry field to avoid indirection in hot path - Use fields to store some services queried in hot path - perform modifications under myPendingInitializationLock.writeLock() to avoid corruption GitOrigin-RevId: 9a6ec245f31a779542e2e830089573e8ce25333b --- .../application/ApplicationManager.java | 3 +- .../openapi/fileTypes/FileTypeRegistry.java | 39 ++- .../fileTypes/StubFileTypeRegistry.java | 76 ++++ platform/ide-core/api-dump-unreviewed.txt | 22 +- .../openapi/fileTypes/FileTypeManager.java | 26 +- ...eManager.java => StubFileTypeManager.java} | 5 +- .../file/exclude/UserFileTypeOverrider.java | 4 +- .../platform-impl/api-dump-unreviewed.txt | 3 - .../ide/plugins/BundledPluginsLister.kt | 6 +- .../impl/FileChooserDialogHelper.kt | 11 +- .../fileTypes/impl/FileTypeManagerImpl.java | 329 +++++++++++------- .../openapi/fileTypes/impl/FileTypesTest.java | 7 - .../kotlin/org/toml/lang/psi/ElementTypes.kt | 3 +- 13 files changed, 335 insertions(+), 199 deletions(-) create mode 100644 platform/core-api/src/com/intellij/openapi/fileTypes/StubFileTypeRegistry.java rename platform/ide-core/src/com/intellij/openapi/fileTypes/{MockFileTypeManager.java => StubFileTypeManager.java} (90%) diff --git a/platform/core-api/src/com/intellij/openapi/application/ApplicationManager.java b/platform/core-api/src/com/intellij/openapi/application/ApplicationManager.java index 41f546d4859f..5c0fe6d65f20 100644 --- a/platform/core-api/src/com/intellij/openapi/application/ApplicationManager.java +++ b/platform/core-api/src/com/intellij/openapi/application/ApplicationManager.java @@ -48,12 +48,11 @@ public class ApplicationManager { ) { Application old = ourApplication; setApplication(instance); - Supplier oldFileTypeRegistry = FileTypeRegistry.setInstanceSupplier(fileTypeRegistryGetter); + FileTypeRegistry.setInstanceSupplier(fileTypeRegistryGetter, parent); Disposer.register(parent, () -> { if (old != null) { // to prevent NPEs in threads still running setApplication(old); - FileTypeRegistry.setInstanceSupplier(oldFileTypeRegistry); } }); } diff --git a/platform/core-api/src/com/intellij/openapi/fileTypes/FileTypeRegistry.java b/platform/core-api/src/com/intellij/openapi/fileTypes/FileTypeRegistry.java index e6c5ba871132..0a39f7524ae3 100644 --- a/platform/core-api/src/com/intellij/openapi/fileTypes/FileTypeRegistry.java +++ b/platform/core-api/src/com/intellij/openapi/fileTypes/FileTypeRegistry.java @@ -2,9 +2,12 @@ package com.intellij.openapi.fileTypes; import com.intellij.lang.Language; +import com.intellij.openapi.Disposable; +import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.fileTypes.ex.FileTypeIdentifiableByVirtualFile; +import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.ByteSequence; import com.intellij.openapi.vfs.VirtualFile; import org.jetbrains.annotations.ApiStatus; @@ -31,18 +34,23 @@ import java.util.function.Supplier; * {@link com.intellij.openapi.application.ReadAction#nonBlocking}. */ public abstract class FileTypeRegistry { - private static Supplier instanceGetter; + private static volatile FileTypeRegistry instance; + static { + ApplicationManager.registerCleaner(() -> instance = null); + } @ApiStatus.Internal - public static Supplier setInstanceSupplier(@NotNull Supplier supplier) { - Supplier oldValue = instanceGetter; - instanceGetter = supplier; - return oldValue; + public static void setInstanceSupplier(@NotNull Supplier supplier, @NotNull Disposable parentDisposable) { + FileTypeRegistry oldInstance = instance; + instance = supplier.get(); + Disposer.register(parentDisposable, () -> { + instance = oldInstance; + }); } @ApiStatus.Internal public static boolean isInstanceSupplierSet() { - return instanceGetter != null; + return instance != null; } public abstract boolean isFileIgnored(@NotNull VirtualFile file); @@ -59,13 +67,20 @@ public abstract class FileTypeRegistry { } public static FileTypeRegistry getInstance() { - Supplier instanceGetter = FileTypeRegistry.instanceGetter; - if (instanceGetter == null) { - // in tests FileTypeManager service maybe not preloaded, so, ourInstanceGetter is not set - //noinspection deprecation - return ApplicationManager.getApplication().getServiceByClassName("com.intellij.openapi.fileTypes.FileTypeManager"); + FileTypeRegistry cached = instance; + if (cached == null) { + // in tests FileTypeManager service maybe not preloaded, so, instance is not set + Application application = ApplicationManager.getApplication(); + Class aClass = null; + try { + aClass = (Class)Class.forName("com.intellij.openapi.fileTypes.FileTypeManager"); + } + catch (ClassNotFoundException ignored) { + } + instance = cached = application == null || aClass == null || !application.hasComponent(aClass) + ? new StubFileTypeRegistry() : application.getService(aClass); } - return instanceGetter.get(); + return cached; } /** diff --git a/platform/core-api/src/com/intellij/openapi/fileTypes/StubFileTypeRegistry.java b/platform/core-api/src/com/intellij/openapi/fileTypes/StubFileTypeRegistry.java new file mode 100644 index 000000000000..36b041825ac8 --- /dev/null +++ b/platform/core-api/src/com/intellij/openapi/fileTypes/StubFileTypeRegistry.java @@ -0,0 +1,76 @@ +// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.openapi.fileTypes; + +import com.intellij.lang.Language; +import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; + +/** + * Supplies empty {@link FileTypeRegistry} implementation when {@link com.intellij.openapi.fileTypes.FileTypeManager} is not available + */ +final class StubFileTypeRegistry extends FileTypeRegistry { + @Override + public @NotNull FileType getFileTypeByFileName(@NotNull String fileName) { + return StubLanguageFileType.INSTANCE; + } + + @Override + public @NotNull FileType getFileTypeByFile(@NotNull VirtualFile file) { + return StubLanguageFileType.INSTANCE; + } + + @Override + public @NotNull FileType getFileTypeByExtension(@NotNull String extension) { + return StubLanguageFileType.INSTANCE; + } + + @Override + public FileType @NotNull [] getRegisteredFileTypes() { + return new FileType[] {StubLanguageFileType.INSTANCE}; + } + + @Override + public boolean isFileIgnored(@NotNull VirtualFile file) { + return false; + } + + @Override + public @Nullable FileType findFileTypeByName(@NotNull String fileTypeName) { + return null; + } + + private static final class StubLanguageFileType extends LanguageFileType { + static final LanguageFileType INSTANCE = new StubLanguageFileType(); + + private StubLanguageFileType() { + super(Language.ANY); + } + + @Override + @NotNull + public String getName() { + return "Mock"; + } + + @Override + @NotNull + public String getDescription() { + //noinspection HardCodedStringLiteral + return "Mock"; + } + + @Override + @NotNull + public String getDefaultExtension() { + return ".mockExtensionThatProbablyWon'tEverExist"; + } + + @Override + public Icon getIcon() { + return null; + } + } +} diff --git a/platform/ide-core/api-dump-unreviewed.txt b/platform/ide-core/api-dump-unreviewed.txt index 2ca6b53b7f64..05e0b4e71fe4 100644 --- a/platform/ide-core/api-dump-unreviewed.txt +++ b/platform/ide-core/api-dump-unreviewed.txt @@ -1570,11 +1570,13 @@ com.intellij.openapi.fileTypes.FileTypeListener - fileTypesChanged(com.intellij.openapi.fileTypes.FileTypeEvent):V a:com.intellij.openapi.fileTypes.FileTypeManager - com.intellij.openapi.fileTypes.FileTypeRegistry +- com.intellij.openapi.Disposable - sf:TOPIC:com.intellij.util.messages.Topic -- ():V +- p:():V - a:associate(com.intellij.openapi.fileTypes.FileType,com.intellij.openapi.fileTypes.FileNameMatcher):V - f:associateExtension(com.intellij.openapi.fileTypes.FileType,java.lang.String):V - f:associatePattern(com.intellij.openapi.fileTypes.FileType,java.lang.String):V +- dispose():V - a:getAssociations(com.intellij.openapi.fileTypes.FileType):java.util.List - a:getIgnoredFilesList():java.lang.String - s:getInstance():com.intellij.openapi.fileTypes.FileTypeManager @@ -1591,24 +1593,6 @@ c:com.intellij.openapi.fileTypes.FileTypes - sf:PLAIN_TEXT:com.intellij.openapi.fileTypes.LanguageFileType - sf:UNKNOWN:com.intellij.openapi.fileTypes.FileType - p:():V -f:com.intellij.openapi.fileTypes.MockFileTypeManager -- com.intellij.openapi.fileTypes.FileTypeManager -- ():V -- associate(com.intellij.openapi.fileTypes.FileType,com.intellij.openapi.fileTypes.FileNameMatcher):V -- findFileTypeByName(java.lang.String):com.intellij.openapi.fileTypes.FileType -- getAssociations(com.intellij.openapi.fileTypes.FileType):java.util.List -- getFileTypeByExtension(java.lang.String):com.intellij.openapi.fileTypes.FileType -- getFileTypeByFile(com.intellij.openapi.vfs.VirtualFile):com.intellij.openapi.fileTypes.FileType -- getFileTypeByFileName(java.lang.String):com.intellij.openapi.fileTypes.FileType -- getIgnoredFilesList():java.lang.String -- getKnownFileTypeOrAssociate(com.intellij.openapi.vfs.VirtualFile,com.intellij.openapi.project.Project):com.intellij.openapi.fileTypes.FileType -- getRegisteredFileTypes():com.intellij.openapi.fileTypes.FileType[] -- getStdFileType(java.lang.String):com.intellij.openapi.fileTypes.FileType -- isFileIgnored(com.intellij.openapi.vfs.VirtualFile):Z -- isFileIgnored(java.lang.String):Z -- registerFileType(com.intellij.openapi.fileTypes.FileType,java.lang.String[]):V -- removeAssociation(com.intellij.openapi.fileTypes.FileType,com.intellij.openapi.fileTypes.FileNameMatcher):V -- setIgnoredFilesList(java.lang.String):V f:com.intellij.openapi.fileTypes.MockLanguageFileType - com.intellij.openapi.fileTypes.LanguageFileType - sf:INSTANCE:com.intellij.openapi.fileTypes.LanguageFileType diff --git a/platform/ide-core/src/com/intellij/openapi/fileTypes/FileTypeManager.java b/platform/ide-core/src/com/intellij/openapi/fileTypes/FileTypeManager.java index c86359e30053..74b397a91d9a 100644 --- a/platform/ide-core/src/com/intellij/openapi/fileTypes/FileTypeManager.java +++ b/platform/ide-core/src/com/intellij/openapi/fileTypes/FileTypeManager.java @@ -1,9 +1,7 @@ // Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.openapi.fileTypes; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.CachedSingletonsRegistry; +import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.util.messages.Topic; @@ -12,21 +10,11 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.jps.model.fileTypes.FileNameMatcherFactory; import java.util.List; -import java.util.function.Supplier; /** * Manages the relationship between filenames and {@link FileType} instances. */ -public abstract class FileTypeManager extends FileTypeRegistry { - static { - FileTypeRegistry.setInstanceSupplier(FileTypeManager::getInstance); - } - - private static final Supplier ourInstance = CachedSingletonsRegistry.lazy(() -> { - Application app = ApplicationManager.getApplication(); - return app == null ? new MockFileTypeManager() : app.getService(FileTypeManager.class); - }); - +public abstract class FileTypeManager extends FileTypeRegistry implements Disposable { @Topic.AppLevel public static final @NotNull Topic TOPIC = new Topic<>(FileTypeListener.class, Topic.BroadcastDirection.TO_DIRECT_CHILDREN); @@ -35,7 +23,15 @@ public abstract class FileTypeManager extends FileTypeRegistry { */ @SuppressWarnings("MethodOverridesStaticMethodOfSuperclass") public static FileTypeManager getInstance() { - return ourInstance.get(); + FileTypeRegistry instance = FileTypeRegistry.getInstance(); + return instance instanceof FileTypeManager ftm ? ftm : new StubFileTypeManager(); + } + + protected FileTypeManager() { + } + + @Override + public void dispose() { } /** @deprecated use {@code com.intellij.fileType} extension point instead */ diff --git a/platform/ide-core/src/com/intellij/openapi/fileTypes/MockFileTypeManager.java b/platform/ide-core/src/com/intellij/openapi/fileTypes/StubFileTypeManager.java similarity index 90% rename from platform/ide-core/src/com/intellij/openapi/fileTypes/MockFileTypeManager.java rename to platform/ide-core/src/com/intellij/openapi/fileTypes/StubFileTypeManager.java index c9d2fe58aa91..60ec3adbb009 100644 --- a/platform/ide-core/src/com/intellij/openapi/fileTypes/MockFileTypeManager.java +++ b/platform/ide-core/src/com/intellij/openapi/fileTypes/StubFileTypeManager.java @@ -8,7 +8,10 @@ import org.jetbrains.annotations.Nullable; import java.util.List; -public final class MockFileTypeManager extends FileTypeManager { +/** + * empty implementation of {@link FileTypeManager} in case the {@link com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl} is not available + */ +final class StubFileTypeManager extends FileTypeManager { @Override public @NotNull FileType getFileTypeByFileName(@NotNull String fileName) { return MockLanguageFileType.INSTANCE; diff --git a/platform/lang-impl/src/com/intellij/openapi/file/exclude/UserFileTypeOverrider.java b/platform/lang-impl/src/com/intellij/openapi/file/exclude/UserFileTypeOverrider.java index 191ec246a08d..3a0730033b2c 100644 --- a/platform/lang-impl/src/com/intellij/openapi/file/exclude/UserFileTypeOverrider.java +++ b/platform/lang-impl/src/com/intellij/openapi/file/exclude/UserFileTypeOverrider.java @@ -12,9 +12,11 @@ import org.jetbrains.annotations.Nullable; * Substitutes type for files which users explicitly marked with "Override File Type" action */ final class UserFileTypeOverrider implements FileTypeOverrider { + private final OverrideFileTypeManager myOverrideFileTypeManager = OverrideFileTypeManager.getInstance(); + @Override public @Nullable FileType getOverriddenFileType(@NotNull VirtualFile file) { - String overriddenType = OverrideFileTypeManager.getInstance().getFileValue(file); + String overriddenType = myOverrideFileTypeManager.getFileValue(file); if (overriddenType != null) { return FileTypeManager.getInstance().findFileTypeByName(overriddenType); } diff --git a/platform/platform-impl/api-dump-unreviewed.txt b/platform/platform-impl/api-dump-unreviewed.txt index 111d1e307dd9..a496ac781bd1 100644 --- a/platform/platform-impl/api-dump-unreviewed.txt +++ b/platform/platform-impl/api-dump-unreviewed.txt @@ -13423,12 +13423,10 @@ f:com.intellij.openapi.fileTypes.impl.FileTypeBean - setPluginDescriptor(com.intellij.openapi.extensions.PluginDescriptor):V c:com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl - com.intellij.openapi.fileTypes.ex.FileTypeManagerEx -- com.intellij.openapi.Disposable - com.intellij.openapi.components.PersistentStateComponent - com.intellij.openapi.extensions.ExtensionPointListener - p:(kotlinx.coroutines.CoroutineScope):V - associate(com.intellij.openapi.fileTypes.FileType,com.intellij.openapi.fileTypes.FileNameMatcher):V -- dispose():V - drainReDetectQueue():V - extensionAdded(com.intellij.openapi.fileTypes.impl.FileTypeBean,com.intellij.openapi.extensions.PluginDescriptor):V - extensionRemoved(com.intellij.openapi.fileTypes.impl.FileTypeBean,com.intellij.openapi.extensions.PluginDescriptor):V @@ -13461,7 +13459,6 @@ c:com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl - registerFileType(com.intellij.openapi.fileTypes.FileType,java.lang.String[]):V - removeAssociation(com.intellij.openapi.fileTypes.FileType,com.intellij.openapi.fileTypes.FileNameMatcher):V - removePlainTextAssociationsForFile(java.lang.CharSequence):V -- setConflictResultConsumer(java.util.function.Consumer):V - setIgnoredFilesList(java.lang.String):V - unregisterFileType(com.intellij.openapi.fileTypes.FileType,com.intellij.openapi.extensions.PluginDescriptor):V *:com.intellij.openapi.fileTypes.impl.FileTypeOverrider diff --git a/platform/platform-impl/src/com/intellij/ide/plugins/BundledPluginsLister.kt b/platform/platform-impl/src/com/intellij/ide/plugins/BundledPluginsLister.kt index cb18324a4b71..4f230520b468 100644 --- a/platform/platform-impl/src/com/intellij/ide/plugins/BundledPluginsLister.kt +++ b/platform/platform-impl/src/com/intellij/ide/plugins/BundledPluginsLister.kt @@ -3,11 +3,13 @@ package com.intellij.ide.plugins import com.fasterxml.jackson.core.JsonFactory import com.fasterxml.jackson.core.JsonGenerator -import com.intellij.openapi.application.* +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.application.EDT +import com.intellij.openapi.application.ModernApplicationStarter +import com.intellij.openapi.application.PathManager import com.intellij.openapi.diagnostic.logger import com.intellij.openapi.fileTypes.FileTypeManager import com.intellij.openapi.fileTypes.PlainTextLikeFileType -import com.intellij.openapi.progress.blockingContext import com.intellij.util.io.jackson.array import com.intellij.util.io.jackson.obj import com.intellij.util.lang.UrlClassLoader diff --git a/platform/platform-impl/src/com/intellij/openapi/fileChooser/impl/FileChooserDialogHelper.kt b/platform/platform-impl/src/com/intellij/openapi/fileChooser/impl/FileChooserDialogHelper.kt index 4f9a382a3ca6..9a7e170afc3b 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileChooser/impl/FileChooserDialogHelper.kt +++ b/platform/platform-impl/src/com/intellij/openapi/fileChooser/impl/FileChooserDialogHelper.kt @@ -3,6 +3,7 @@ package com.intellij.openapi.fileChooser.impl import com.intellij.core.CoreFileTypeRegistry import com.intellij.ide.highlighter.ArchiveFileType +import com.intellij.openapi.Disposable import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.application.impl.LaterInvocator import com.intellij.openapi.command.CommandProcessorEx @@ -10,6 +11,7 @@ import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.fileChooser.FileChooserDescriptor import com.intellij.openapi.fileTypes.FileTypeRegistry import com.intellij.openapi.ui.Messages +import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.NlsContexts import com.intellij.openapi.util.NlsSafe import com.intellij.openapi.util.SystemInfo @@ -30,7 +32,7 @@ import java.awt.KeyboardFocusManager import java.nio.file.FileSystems import java.nio.file.Path -internal class FileChooserDialogHelper(private val descriptor: FileChooserDescriptor) { +internal class FileChooserDialogHelper(private val descriptor: FileChooserDescriptor) : Disposable { @Suppress("SpellCheckingInspection") private val ZIP_FS_TYPE = "zipfs" @@ -46,10 +48,14 @@ internal class FileChooserDialogHelper(private val descriptor: FileChooserDescri val registry = CoreFileTypeRegistry() registry.registerFileType(ArchiveFileType.INSTANCE, "zip") registry.registerFileType(ArchiveFileType.INSTANCE, "jar") - FileTypeRegistry.setInstanceSupplier { registry } + FileTypeRegistry.setInstanceSupplier( { registry }, this) } } + override fun dispose() { + + } + fun setNativeDialogProperties() { if (SystemInfo.isWindows) { System.setProperty("sun.awt.windows.useCommonItemDialog", "true") @@ -74,6 +80,7 @@ internal class FileChooserDialogHelper(private val descriptor: FileChooserDescri IdeFocusManager.getGlobalInstance().doWhenFocusSettlesDown { previousFocusOwner.requestFocus() } } } + Disposer.dispose(this) } } diff --git a/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java b/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java index 9ccd2b540681..911a9da00585 100644 --- a/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeManagerImpl.java @@ -49,13 +49,13 @@ import org.jdom.Element; import org.jetbrains.annotations.*; import org.jetbrains.jps.model.fileTypes.FileNameMatcherFactory; +import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.*; import java.util.concurrent.CancellationException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Consumer; @@ -68,7 +68,7 @@ import static com.intellij.openapi.fileTypes.impl.AlarmAdapterKt.singleAlarm; storages = @Storage("filetypes.xml"), additionalExportDirectory = FileTypeManagerImpl.FILE_SPEC, category = SettingsCategory.CODE) -public class FileTypeManagerImpl extends FileTypeManagerEx implements PersistentStateComponent, Disposable, ExtensionPointListener { +public class FileTypeManagerImpl extends FileTypeManagerEx implements PersistentStateComponent, ExtensionPointListener { static final ExtensionPointName EP_NAME = new ExtensionPointName<>("com.intellij.fileType"); private static final Logger LOG = Logger.getInstance(FileTypeManagerImpl.class); @@ -135,7 +135,9 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent private final Map standardFileTypes = new LinkedHashMap<>(); private final SchemeManager schemeManager; - protected FileTypeManagerImpl(@NotNull CoroutineScope coroutineScope) { + protected FileTypeManagerImpl(@NotNull CoroutineScope coroutineScope) throws IOException { + myDetectionService = new FileTypeDetectionService(this, coroutineScope); + NonLazySchemeProcessor abstractTypesProcessor = new NonLazySchemeProcessor<>() { @Override public @NotNull FileTypeWithDescriptor readScheme(@NotNull Element element, boolean duringLoad) { @@ -198,14 +200,25 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent } }; schemeManager = SchemeManagerFactory.getInstance().create(FILE_SPEC, abstractTypesProcessor, null, null, SettingsCategory.CODE); - - myDetectionService = new FileTypeDetectionService(this, coroutineScope); Disposer.register(this, myDetectionService); - EP_NAME.addExtensionPointListener(this, this); checkDuplicatedAlarm = singleAlarm(400, coroutineScope, this::checkUnique); + EP_NAME.addExtensionPointListener(this, this); + FileTypeOverrider.EP_NAME.addExtensionPointListener(new ExtensionPointListener<>() { + @Override + public void extensionAdded(FileTypeOverrider extension, @NotNull PluginDescriptor pluginDescriptor) { + fileTypeOverriderCache = null; + } + + @Override + public void extensionRemoved(FileTypeOverrider extension, @NotNull PluginDescriptor pluginDescriptor) { + fileTypeOverriderCache = null; + } + }); } + private FileTypeOverrider[] fileTypeOverriderCache; + public void extensionAdded(@NotNull FileTypeBean fileTypeBean, @NotNull PluginDescriptor pluginDescriptor) { fireBeforeFileTypesChanged(); initializeMatchers(pluginDescriptor, fileTypeBean); @@ -363,6 +376,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent @TestOnly void clearStandardFileTypesBeforeTest() { + assert ApplicationManager.getApplication().isUnitTestMode(); myPendingInitializationLock.writeLock().lock(); try { pendingAssociations.clear(); @@ -473,22 +487,27 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent } private @NotNull FileType mergeOrInstantiateFileTypeBean(@NotNull FileTypeBean fileTypeBean) { - StandardFileType type = withReadLock(() -> standardFileTypes.get(fileTypeBean.name)); - if (type == null) { - return getFileTypeOrUnknown(instantiateFileTypeBean(fileTypeBean)); + myPendingInitializationLock.writeLock().lock(); + try { + StandardFileType type = standardFileTypes.get(fileTypeBean.name); + if (type == null) { + return getFileTypeOrUnknown(instantiateFileTypeBean(fileTypeBean)); + } + type.matchers.addAll(fileTypeBean.getMatchers()); + for (FileNameMatcher matcher : fileTypeBean.getMatchers()) { + patternsTable.addAssociation(matcher, type.getDescriptor()); + } + return type.fileType; } - type.matchers.addAll(fileTypeBean.getMatchers()); - for (FileNameMatcher matcher : fileTypeBean.getMatchers()) { - patternsTable.addAssociation(matcher, type.getDescriptor()); + finally { + myPendingInitializationLock.writeLock().unlock(); } - return type.fileType; } final Set INSTANTIATED = PluginManagerCore.isUnitTestMode ? ContainerUtil.newConcurrentSet() : null; private @Nullable FileTypeWithDescriptor instantiateFileTypeBean(@NotNull FileTypeBean bean) { - Lock writeLock = myPendingInitializationLock.writeLock(); - writeLock.lock(); + myPendingInitializationLock.writeLock().lock(); try { String fileTypeName = bean.name; // DCL @@ -569,7 +588,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent return new FileTypeWithDescriptor(fileType, bean.getPluginDescriptor()); } finally { - writeLock.unlock(); + myPendingInitializationLock.writeLock().unlock(); } } @@ -740,38 +759,44 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent } @Override - public boolean isFileOfType(@NotNull VirtualFile file, @NotNull FileType requestedFileType) { + public boolean isFileOfType(@NotNull VirtualFile virtualFile, @NotNull FileType requestedFileType) { //MAYBE RC: shouldn't we first check file.isValid() and return false eagerly? - FileType temporarilyFixedFileType = getTemporarilyFixedFileType(file); + FileType temporarilyFixedFileType = getTemporarilyFixedFileType(virtualFile); if (temporarilyFixedFileType != null) return temporarilyFixedFileType.equals(requestedFileType); - FileType overriddenFileType = FileTypeOverrider.EP_NAME.computeSafeIfAny(overrider -> overrider.getOverriddenFileType(file)); - if (overriddenFileType != null) { - return overriddenFileType.equals(requestedFileType); + FileTypeOverrider[] overriders = fileTypeOverriderCache; + if (overriders == null) { + fileTypeOverriderCache = overriders = FileTypeOverrider.EP_NAME.getExtensions(); + } + for (FileTypeOverrider overrider : overriders) { + FileType overriddenFileType = overrider.getOverriddenFileType(virtualFile); + if (overriddenFileType != null) { + return overriddenFileType.equals(requestedFileType); + } } - if (file instanceof VirtualFileWithAssignedFileType) { - FileType fileType = ((VirtualFileWithAssignedFileType)file).getAssignedFileType(); + if (virtualFile instanceof VirtualFileWithAssignedFileType) { + FileType fileType = ((VirtualFileWithAssignedFileType)virtualFile).getAssignedFileType(); if (fileType != null) { return fileType.equals(requestedFileType); } } if (requestedFileType instanceof FileTypeIdentifiableByVirtualFile - && ((FileTypeIdentifiableByVirtualFile)requestedFileType).isMyFileType(file)) { + && ((FileTypeIdentifiableByVirtualFile)requestedFileType).isMyFileType(virtualFile)) { return true; } // otherwise, we can skip all the mySpecialFileTypes because it's certain this file type is not one of them - FileType fileType = getFileTypeByFileName(file.getNameSequence()); + FileType fileType = getFileTypeByFileName(virtualFile.getNameSequence()); if (fileType == UnknownFileType.INSTANCE) { fileType = null; } - if (fileType == null && file instanceof FakeVirtualFile && ScratchUtil.isScratch(file.getParent())) { + if (fileType == null && virtualFile instanceof FakeVirtualFile && ScratchUtil.isScratch(virtualFile.getParent())) { return PlainTextFileType.INSTANCE.equals(requestedFileType); } if (fileType == null || fileType == DetectedByContentFileType.INSTANCE) { - FileType detected = myDetectionService.getOrDetectFromContent(file, null, fileType); + FileType detected = myDetectionService.getOrDetectFromContent(virtualFile, null, fileType); if (detected == UnknownFileType.INSTANCE && fileType == DetectedByContentFileType.INSTANCE) { return DetectedByContentFileType.INSTANCE.equals(requestedFileType); } @@ -805,27 +830,33 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent } } @Override - public @NotNull FileType getFileTypeByFile(@NotNull VirtualFile file, byte @Nullable [] content) { - FileType temporarilyFixedFileType = getTemporarilyFixedFileType(file); + public @NotNull FileType getFileTypeByFile(@NotNull VirtualFile virtualFile, byte @Nullable [] content) { + FileType temporarilyFixedFileType = getTemporarilyFixedFileType(virtualFile); if (temporarilyFixedFileType != null) return temporarilyFixedFileType; - FileType overriddenFileType = FileTypeOverrider.EP_NAME.computeSafeIfAny(overrider -> overrider.getOverriddenFileType(file)); - if (overriddenFileType != null) { - return overriddenFileType; + FileTypeOverrider[] overriders = fileTypeOverriderCache; + if (overriders == null) { + fileTypeOverriderCache = overriders = FileTypeOverrider.EP_NAME.getExtensions(); + } + for (FileTypeOverrider overrider : overriders) { + FileType overriddenFileType = overrider.getOverriddenFileType(virtualFile); + if (overriddenFileType != null) { + return overriddenFileType; + } } - FileType fileType = getByFile(file); - if (file instanceof StubVirtualFile) { - if (fileType == null && content == null && file instanceof FakeVirtualFile) { - if (ScratchUtil.isScratch(file.getParent())) return PlainTextFileType.INSTANCE; + FileType fileType = getByFile(virtualFile); + if (virtualFile instanceof StubVirtualFile) { + if (fileType == null && content == null && virtualFile instanceof FakeVirtualFile) { + if (ScratchUtil.isScratch(virtualFile.getParent())) return PlainTextFileType.INSTANCE; } } else if (fileType == null || fileType == DetectedByContentFileType.INSTANCE) { - fileType = internalContinueToDetectFileTypeByFile(file, content, fileType); + fileType = internalContinueToDetectFileTypeByFile(virtualFile, content, fileType); } FileType result = fileType == null ? UnknownFileType.INSTANCE : fileType; CachedFileTypes cached = CACHED; - if (cached != null && file instanceof VirtualFileWithId vfid) { + if (cached != null && virtualFile instanceof VirtualFileWithId vfid) { cached.fileTypes().put(vfid.getId(), result); } return result; @@ -840,8 +871,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent private FileType getTemporarilyFixedFileType(@NotNull VirtualFile file) { CachedFileTypes cached = CACHED; - FileType fileType = cached != null && file instanceof VirtualFileWithId vfid ? cached.fileTypes().get(vfid.getId()) : null; - return fileType; + return cached != null && file instanceof VirtualFileWithId vfid ? cached.fileTypes().get(vfid.getId()) : null; } // null means all conventional detect methods returned UnknownFileType.INSTANCE, have to detect from content @@ -1002,9 +1032,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent FileTypeWithDescriptor ftd = FileTypeWithDescriptor.allFor(fileType); List matchers = patternsTable.getAssociations(ftd); // delete all records "extension xxx is removed from standard type X because the new fileType grabbed it to itself" - removedMappingTracker.removeIf(mapping -> { - return mapping.getFileTypeName().equals(fileType.getName()) || matchers.contains(mapping.getFileNameMatcher()); - }); + removedMappingTracker.removeIf(mapping -> mapping.getFileTypeName().equals(fileType.getName()) || matchers.contains(mapping.getFileNameMatcher())); patternsTable.removeAllAssociations(ftd); initialAssociations.removeAllAssociations(fileType); schemeManager.removeScheme(fileType.getName()); @@ -1018,14 +1046,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent @Override public FileType @NotNull [] getRegisteredFileTypes() { - instantiatePendingFileTypes(); - Collection descriptors = getAllFileTypeWithDescriptors(); - FileType[] result = new FileType[descriptors.size()]; - int i = 0; - for (FileTypeManagerImpl.FileTypeWithDescriptor descriptor : descriptors) { - result[i++] = descriptor.fileType(); - } - return result; + return ContainerUtil.map2Array(getRegisteredFileTypeWithDescriptors(), FileType.class, ftd->ftd.fileType); } @NotNull List getRegisteredFileTypeWithDescriptors() { @@ -1326,65 +1347,67 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent @Override public @NotNull Element getState() { - Element state = new Element("state"); + return withReadLock(() -> { + Element state = new Element("state"); - List ignoreFiles = new ArrayList<>(ignoredPatterns.getIgnoreMasks()); - ignoreFiles.sort(null); - if (!isEqualToDefaultIgnoreMasks(ignoreFiles)) { - // empty means empty list - we need to distinguish null and empty to apply or not to apply default value - state.addContent(new Element(ELEMENT_IGNORE_FILES).setAttribute(ATTRIBUTE_LIST, String.join(";", ignoreFiles))); - } - - Element extensionMap = new Element(ELEMENT_EXTENSION_MAP); - - List notExternalizableFileTypes = new ArrayList<>(); - for (FileTypeWithDescriptor fileTypeDescriptor: getAllFileTypeWithDescriptors()) { - if (!(fileTypeDescriptor.fileType() instanceof AbstractFileType) || defaultTypes.contains(fileTypeDescriptor)) { - notExternalizableFileTypes.add(fileTypeDescriptor); + List ignoreFiles = new ArrayList<>(ignoredPatterns.getIgnoreMasks()); + ignoreFiles.sort(null); + if (!isEqualToDefaultIgnoreMasks(ignoreFiles)) { + // empty means empty list - we need to distinguish null and empty to apply or not to apply default value + state.addContent(new Element(ELEMENT_IGNORE_FILES).setAttribute(ATTRIBUTE_LIST, String.join(";", ignoreFiles))); } - } - if (!notExternalizableFileTypes.isEmpty()) { - notExternalizableFileTypes.sort(Comparator.comparing(it -> it.fileType().getName())); - for (FileTypeWithDescriptor ftd : notExternalizableFileTypes) { - writeExtensionsMap(extensionMap, ftd, true); - } - } - // https://youtrack.jetbrains.com/issue/IDEA-138366 - removedMappingTracker.save(extensionMap); + Element extensionMap = new Element(ELEMENT_EXTENSION_MAP); - if (!unresolvedMappings.isEmpty()) { - List> entries = new ArrayList<>(unresolvedMappings.entrySet()); - entries.sort(Comparator.comparing(e -> e.getKey().getPresentableString())); - for (Map.Entry entry : entries) { - FileNameMatcher fileNameMatcher = entry.getKey(); - String typeName = entry.getValue(); - Element content = AbstractFileType.writeMapping(typeName, fileNameMatcher, true); - if (content != null) { - extensionMap.addContent(content); + List notExternalizableFileTypes = new ArrayList<>(); + for (FileTypeWithDescriptor fileTypeDescriptor: getAllFileTypeWithDescriptors()) { + if (!(fileTypeDescriptor.fileType() instanceof AbstractFileType) || defaultTypes.contains(fileTypeDescriptor)) { + notExternalizableFileTypes.add(fileTypeDescriptor); } } - } - if (!unresolvedHashBangs.isEmpty()) { - List> entries = new ArrayList<>(unresolvedHashBangs.entrySet()); - entries.sort(Map.Entry.comparingByKey()); - - for (Map.Entry entry : entries) { - String pattern = entry.getKey(); - String typeName = entry.getValue(); - writeHashBang(extensionMap, pattern, typeName); + if (!notExternalizableFileTypes.isEmpty()) { + notExternalizableFileTypes.sort(Comparator.comparing(it -> it.fileType().getName())); + for (FileTypeWithDescriptor ftd : notExternalizableFileTypes) { + writeExtensionsMap(extensionMap, ftd, true); + } } - } - if (!extensionMap.getChildren().isEmpty()) { - state.addContent(extensionMap); - } + // https://youtrack.jetbrains.com/issue/IDEA-138366 + removedMappingTracker.save(extensionMap); - if (!state.getChildren().isEmpty()) { - state.setAttribute(ATTRIBUTE_VERSION, String.valueOf(VERSION)); - } + if (!unresolvedMappings.isEmpty()) { + List> entries = new ArrayList<>(unresolvedMappings.entrySet()); + entries.sort(Comparator.comparing(e -> e.getKey().getPresentableString())); + for (Map.Entry entry : entries) { + FileNameMatcher fileNameMatcher = entry.getKey(); + String typeName = entry.getValue(); + Element content = AbstractFileType.writeMapping(typeName, fileNameMatcher, true); + if (content != null) { + extensionMap.addContent(content); + } + } + } + if (!unresolvedHashBangs.isEmpty()) { + List> entries = new ArrayList<>(unresolvedHashBangs.entrySet()); + entries.sort(Map.Entry.comparingByKey()); - return state; + for (Map.Entry entry : entries) { + String pattern = entry.getKey(); + String typeName = entry.getValue(); + writeHashBang(extensionMap, pattern, typeName); + } + } + + if (!extensionMap.getChildren().isEmpty()) { + state.addContent(extensionMap); + } + + if (!state.getChildren().isEmpty()) { + state.setAttribute(ATTRIBUTE_VERSION, String.valueOf(VERSION)); + } + + return state; + }); } private static boolean isEqualToDefaultIgnoreMasks(@NotNull List newList) { @@ -1634,33 +1657,39 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent if (isDefault && fileTypeDescr != null && fileTypeDescr.contains("syntax highlighting only")) { fileTypeDescr = fileTypeDescr.replace("syntax highlighting only", FileTypesBundle.message("filetype.default.syntax.highlighting.only.description")); } - String iconPath = typeElement.getAttributeValue("icon"); - setFileTypeAttributes(type, fileTypeName, fileTypeDescr, iconPath); - registerFileTypeWithoutNotification(type, - pluginDescriptor, - parseExtensions(context, extensionsStr), - pluginAdvertiserExtensionsStateService, - isDefault); + myPendingInitializationLock.writeLock().lock(); + try { + String iconPath = typeElement.getAttributeValue("icon"); + setFileTypeAttributes(type, fileTypeName, fileTypeDescr, iconPath); + registerFileTypeWithoutNotification(type, + pluginDescriptor, + parseExtensions(context, extensionsStr), + pluginAdvertiserExtensionsStateService, + isDefault); - if (isDefault) { - defaultTypes.add(new FileTypeWithDescriptor(type, pluginDescriptor)); - if (type instanceof ExternalizableFileType) { - ((ExternalizableFileType)type).markDefaultSettings(); - } - } - else { - Element extensions = typeElement.getChild(ELEMENT_EXTENSION_MAP); - if (extensions != null) { - FileTypeWithDescriptor newFtd = new FileTypeWithDescriptor(type, pluginDescriptor); - for (Pair association : AbstractFileType.readAssociations(extensions)) { - associate(newFtd, association.getFirst(), false); - } - for (RemovedMappingTracker.RemovedMapping removedAssociation : RemovedMappingTracker.readRemovedMappings(extensions)) { - removeAssociation(newFtd, removedAssociation.getFileNameMatcher(), false); + if (isDefault) { + defaultTypes.add(new FileTypeWithDescriptor(type, pluginDescriptor)); + if (type instanceof ExternalizableFileType) { + ((ExternalizableFileType)type).markDefaultSettings(); } } + else { + Element extensions = typeElement.getChild(ELEMENT_EXTENSION_MAP); + if (extensions != null) { + FileTypeWithDescriptor newFtd = new FileTypeWithDescriptor(type, pluginDescriptor); + for (Pair association : AbstractFileType.readAssociations(extensions)) { + associate(newFtd, association.getFirst(), false); + } + for (RemovedMappingTracker.RemovedMapping removedAssociation : RemovedMappingTracker.readRemovedMappings(extensions)) { + removeAssociation(newFtd, removedAssociation.getFileNameMatcher(), false); + } + } + } + return type; + } + finally { + myPendingInitializationLock.writeLock().unlock(); } - return type; } private @NotNull String filterAlreadyRegisteredExtensions(@NotNull String semicolonDelimited) { @@ -1705,9 +1734,6 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent return patternsTable; } - @Override - public void dispose() { } - void setPatternsTable(@NotNull Set fileTypes, @NotNull FileTypeAssocTable assocTable) { Map removedMappings = getExtensionMap().getRemovedMappings(assocTable, fileTypes); String message = "set patterns table called: file types " + fileTypes + ", ass. table:" + assocTable; @@ -1770,15 +1796,21 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent } private void registerReDetectedMappings(@NotNull StandardFileType pair) { - FileType fileType = pair.fileType; - if (fileType == PlainTextFileType.INSTANCE) return; - for (FileNameMatcher matcher : pair.matchers) { - registerReDetectedMapping(fileType.getName(), matcher); - if (matcher instanceof ExtensionFileNameMatcher extMatcher) { - // also check exact file name matcher - registerReDetectedMapping(fileType.getName(), new ExactFileNameMatcher("." + extMatcher.getExtension())); + myPendingInitializationLock.writeLock().lock(); + try { + FileType fileType = pair.fileType; + if (fileType == PlainTextFileType.INSTANCE) return; + for (FileNameMatcher matcher : pair.matchers) { + registerReDetectedMapping(fileType.getName(), matcher); + if (matcher instanceof ExtensionFileNameMatcher extMatcher) { + // also check exact file name matcher + registerReDetectedMapping(fileType.getName(), new ExactFileNameMatcher("." + extMatcher.getExtension())); + } } } + finally { + myPendingInitializationLock.writeLock().unlock(); + } } private void registerReDetectedMapping(@NotNull String fileTypeName, @NotNull FileNameMatcher matcher) { @@ -1870,8 +1902,37 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent } @TestOnly - public void setConflictResultConsumer(@Nullable Consumer consumer) { + void setConflictResultConsumer(@Nullable Consumer consumer) { if (!ApplicationManager.getApplication().isUnitTestMode()) throw new IllegalStateException(); conflictResultConsumer = consumer; } + + @Override + public String toString() { + return super.toString() + " FileTypeManagerImpl{" + + "myDetectionService=" + myDetectionService + + ", CACHED=" + CACHED + + ", defaultTypes=" + defaultTypes + + ", specialFileTypes=" + Arrays.toString(specialFileTypes) + + ", patternsTable=" + patternsTable + + ", ignoredPatterns=" + ignoredPatterns + + ", myIgnoredFileCache=" + myIgnoredFileCache + + ", initialAssociations=" + initialAssociations + + ", unresolvedMappings=" + unresolvedMappings + + ", unresolvedHashBangs=" + unresolvedHashBangs + + ", removedMappingTracker=" + removedMappingTracker + + ", conflictingMappingTracker=" + conflictingMappingTracker + + ", pendingFileTypes=" + pendingFileTypes + + ", pendingAssociations=" + pendingAssociations + + ", myPendingInitializationLock=" + myPendingInitializationLock + + ", fileTypesPerPlugin=" + fileTypesPerPlugin + + ", checkDuplicatedAlarm=" + checkDuplicatedAlarm + + ", conflictResultConsumer=" + conflictResultConsumer + + ", standardFileTypes=" + standardFileTypes + + ", schemeManager=" + schemeManager + + ", fileTypeOverriderCache=" + Arrays.toString(fileTypeOverriderCache) + + ", INSTANTIATED=" + INSTANTIATED + + ", toLog=" + toLog + + '}'; + } } diff --git a/platform/platform-tests/testSrc/com/intellij/openapi/fileTypes/impl/FileTypesTest.java b/platform/platform-tests/testSrc/com/intellij/openapi/fileTypes/impl/FileTypesTest.java index 40b438fb9bb7..4c4cf2c355af 100644 --- a/platform/platform-tests/testSrc/com/intellij/openapi/fileTypes/impl/FileTypesTest.java +++ b/platform/platform-tests/testSrc/com/intellij/openapi/fileTypes/impl/FileTypesTest.java @@ -7,7 +7,6 @@ import com.intellij.ide.highlighter.ArchiveFileType; import com.intellij.ide.highlighter.ModuleFileType; import com.intellij.ide.highlighter.ProjectFileType; import com.intellij.ide.highlighter.WorkspaceFileType; -import com.intellij.ide.highlighter.custom.SyntaxTable; import com.intellij.ide.plugins.IdeaPluginDescriptorImpl; import com.intellij.ide.plugins.PluginDescriptorTestKt; import com.intellij.ide.plugins.PluginManagerCore; @@ -1086,7 +1085,6 @@ public class FileTypesTest extends HeavyPlatformTestCase { bean.setPluginDescriptor(pluginDescriptor); Disposable disposable = Disposer.newDisposable(); WriteAction.run(() -> FileTypeManagerImpl.EP_NAME.getPoint().registerExtension(bean, disposable)); - ((FileTypeManagerImpl)FileTypeManager.getInstance()).extensionRemoved(bean, pluginDescriptor); // avoid contamination of production FileTypeManager return disposable; } @@ -1324,11 +1322,6 @@ public class FileTypesTest extends HeavyPlatformTestCase { assertInstanceOf(myFileTypeManager.getFileTypeByFileName("foo.hs"), MyHaskellFileType.class); WriteAction.run(() -> Disposer.dispose(disposable)); - - // todo restore old AbstractFileType automatically? - AbstractFileType old = new AbstractFileType(new SyntaxTable()); - old.setName(MyHaskellFileType.NAME); - myFileTypeManager.registerFileType(old, List.of(), myFileTypeManager, FileTypeManagerImpl.coreIdeaPluginDescriptor()); } private static class MyCustomImageFileType implements FileType { diff --git a/plugins/toml/core/src/main/kotlin/org/toml/lang/psi/ElementTypes.kt b/plugins/toml/core/src/main/kotlin/org/toml/lang/psi/ElementTypes.kt index d7db5b809a45..09a18bd39e48 100644 --- a/plugins/toml/core/src/main/kotlin/org/toml/lang/psi/ElementTypes.kt +++ b/plugins/toml/core/src/main/kotlin/org/toml/lang/psi/ElementTypes.kt @@ -7,6 +7,7 @@ package org.toml.lang.psi import com.intellij.openapi.fileTypes.LanguageFileType import com.intellij.openapi.fileTypes.ex.FileTypeIdentifiableByVirtualFile +import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.TokenSet @@ -27,7 +28,7 @@ object TomlFileType : LanguageFileType(TomlLanguage), FileTypeIdentifiableByVirt override fun getCharset(file: VirtualFile, content: ByteArray): String = "UTF-8" override fun isMyFileType(file: VirtualFile): Boolean { - return file.name == "config" && file.parent?.name == ".cargo" + return StringUtil.equal(file.nameSequence, "config", true) && file.parent?.name == ".cargo" } }