IDEA-258255 Warn users when they open a file that should be associated with a plugin but it's ignored or plain-text

GitOrigin-RevId: db4e2351f1dcdb0dc1601fdbfb59c07ba61956de
This commit is contained in:
Alexey Kudravtsev
2022-03-04 16:59:19 +01:00
committed by intellij-monorepo-bot
parent 1eb5648354
commit fd2becf61e
8 changed files with 195 additions and 1 deletions

View File

@@ -0,0 +1,46 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeInsight.daemon;
import com.intellij.codeInsight.daemon.DaemonAnalyzerTestCase;
import com.intellij.codeInsight.daemon.impl.HighlightInfo;
import com.intellij.codeInspection.InspectionsBundle;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.ReassignedToPlainTextInspection;
import com.intellij.lang.annotation.HighlightSeverity;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.fileTypes.ExactFileNameMatcher;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.fileTypes.FileTypes;
import com.intellij.openapi.fileTypes.PlainTextFileType;
import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
public class ReassignedToPlainTextTest extends DaemonAnalyzerTestCase {
@Override
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new ReassignedToPlainTextInspection()};
}
public void testText() throws Exception {
FileTypeManager fileTypeManager = FileTypeManager.getInstance();
String name = "xx.xxxx";
assertEquals(FileTypes.UNKNOWN, fileTypeManager.getFileTypeByFileName(name));
WriteAction.run(() -> fileTypeManager.associate(PlainTextFileType.INSTANCE, new ExactFileNameMatcher(name)));
assertEquals(PlainTextFileType.INSTANCE, fileTypeManager.getFileTypeByFileName(name));
VirtualFile file = getVirtualFile(createTempFile(name, "xxx"));
configureByExistingFile(file);
assertEquals(PlainTextFileType.INSTANCE, file.getFileType());
Collection<HighlightInfo> infos = doHighlighting(HighlightSeverity.WARNING);
HighlightInfo info = assertOneElement(infos);
assertEquals("This file was explicitly re-assigned to plain text", info.getDescription());
findAndInvokeIntentionAction(infos, InspectionsBundle.message("reassigned.to.plain.text.inspection.fix.remove.name"), getEditor(), getFile());
assertEquals(FileTypes.UNKNOWN, fileTypeManager.getFileTypeByFileName(name));
assertEquals(PlainTextFileType.INSTANCE, file.getFileType());
}
}

View File

@@ -308,4 +308,25 @@ public final class FileTypeAssocTable<T> {
myExactFileNameMappings.clear();
myExactFileNameAnyCaseMappings.clear();
}
void removeAssociationsForFile(@NotNull CharSequence fileName, @NotNull T association) {
T t = myExactFileNameMappings.get(fileName);
if (association.equals(t)) {
myExactFileNameMappings.remove(fileName);
}
t = myExactFileNameAnyCaseMappings.get(fileName);
if (association.equals(t)) {
myExactFileNameAnyCaseMappings.remove(fileName);
}
myMatchingMappings.removeIf(pair -> association.equals(pair.second)
&& pair.getFirst().acceptsCharSequence(fileName));
CharSequence extension = FileUtilRt.getExtension(fileName);
t = myExtensionMappings.get(extension);
if (association.equals(t)) {
myExtensionMappings.remove(extension);
}
}
}

View File

@@ -307,3 +307,8 @@ long.range.set.presentation.range.with.mod={0}; {1}
inspection.results=Inspection Results
inspections.results=Inspections Results
notification.group.inspection.results=Code inspection completed
reassigned.to.plain.text.inspection.name=Reassigned to plain text
reassigned.to.plain.text.inspection.message=This file was explicitly re-assigned to plain text
reassigned.to.plain.text.inspection.fix.remove.name=Remove association
reassigned.to.plain.text.inspection.fix.edit.name=Edit File Types

View File

@@ -0,0 +1,96 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection;
import com.intellij.lang.injection.InjectedLanguageManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.openapi.fileTypes.FileTypeManager;
import com.intellij.openapi.fileTypes.PlainTextFileType;
import com.intellij.openapi.fileTypes.impl.FileTypeManagerImpl;
import com.intellij.openapi.fileTypes.impl.FileTypeSelectable;
import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.options.SearchableConfigurable;
import com.intellij.openapi.options.ShowSettingsUtil;
import com.intellij.openapi.options.ex.ConfigurableWrapper;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.FileViewProvider;
import com.intellij.psi.PsiFile;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
// this file is assigned to "Plain text" file type even though there's a plugin supporting this specific extension/file pattern
public class ReassignedToPlainTextInspection extends LocalInspectionTool {
@Override
@NonNls
@NotNull
public String getShortName() {
return "ReassignedToPlainText";
}
@Override
public ProblemDescriptor @Nullable [] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) {
if (InjectedLanguageManager.getInstance(file.getProject()).isInjectedFragment(file)) return null;
if (!file.isPhysical()) return null;
FileViewProvider viewProvider = file.getViewProvider();
if (viewProvider.getBaseLanguage() != file.getLanguage()) return null;
VirtualFile virtualFile = file.getVirtualFile();
if (virtualFile == null) return null;
FileType fileType = virtualFile.getFileType();
if (fileType != PlainTextFileType.INSTANCE) {
return null;
}
FileType assigned = FileTypeManager.getInstance().getFileTypeByFileName(virtualFile.getNameSequence());
if (assigned == PlainTextFileType.INSTANCE) {
LocalQuickFix removeFix = new LocalQuickFix() {
@Nls
@NotNull
@Override
public String getFamilyName() {
return InspectionsBundle.message("reassigned.to.plain.text.inspection.fix.remove.name");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
((FileTypeManagerImpl)FileTypeManager.getInstance()).removePlainTextAssociationsForFile(descriptor.getPsiElement().getContainingFile().getName());
}
};
LocalQuickFix editFix = new LocalQuickFix() {
@Nls
@NotNull
@Override
public String getFamilyName() {
return InspectionsBundle.message("reassigned.to.plain.text.inspection.fix.edit.name");
}
@Override
public boolean startInWriteAction() {
return false;
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
editFileType(project, PlainTextFileType.INSTANCE);
}
};
ProblemDescriptor descriptor = manager.createProblemDescriptor(file, InspectionsBundle.message("reassigned.to.plain.text.inspection.message"), new LocalQuickFix[]{removeFix, editFix},
ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
true, false);
return new ProblemDescriptor[]{descriptor};
}
return null;
}
private static void editFileType(@Nullable Project project, @NotNull FileType fileType) {
ShowSettingsUtil.getInstance().showSettingsDialog(project,
configurable -> configurable instanceof SearchableConfigurable && ((SearchableConfigurable)configurable).getId().equals("preferences.fileTypes"),
configurable -> {
if (configurable instanceof ConfigurableWrapper) {
configurable = (Configurable)((ConfigurableWrapper)configurable).getConfigurable();
}
FileTypeSelectable fileTypeSelectable = (FileTypeSelectable)configurable;
fileTypeSelectable.selectFileType(fileType);
});
}
}

View File

@@ -26,6 +26,7 @@ import com.intellij.openapi.vfs.*;
import com.intellij.openapi.vfs.newvfs.FileAttribute;
import com.intellij.openapi.vfs.newvfs.FileSystemInterface;
import com.intellij.openapi.vfs.newvfs.events.*;
import com.intellij.testFramework.LightVirtualFile;
import com.intellij.util.*;
import com.intellij.util.concurrency.AppExecutorUtil;
import com.intellij.util.concurrency.BoundedTaskExecutor;
@@ -322,7 +323,7 @@ final class FileTypeDetectionService implements Disposable {
return !file.isDirectory()
&& file.isValid()
&& !file.is(VFileProperty.SPECIAL)
&& file.getFileSystem() instanceof FileSystemInterface;
&& (file.getFileSystem() instanceof FileSystemInterface || file instanceof LightVirtualFile);
}
// read auto-detection flags from the persistent FS file attributes. If file attributes are absent, return 0 for flags

View File

@@ -636,6 +636,18 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent
return getFileTypeOrUnknown(ftd);
}
public void removePlainTextAssociationsForFile(@NotNull CharSequence fileName) {
ApplicationManager.getApplication().assertIsDispatchThread();
myPendingInitializationLock.writeLock().lock();
try {
makeFileTypesChange("removePlainTextAssociationsForFile("+fileName+")", () ->
myPatternsTable.removeAssociationsForFile(fileName, FileTypeWithDescriptor.allFor(PlainTextFileType.INSTANCE)));
}
finally {
myPendingInitializationLock.writeLock().unlock();
}
}
@Override
public void freezeFileTypeTemporarilyIn(@NotNull VirtualFile file, @NotNull Runnable runnable) {
FileType fileType = file.isDirectory() ? null : file.getFileType();

View File

@@ -0,0 +1,10 @@
<html>
<body>
Reports files that were explicitly re-assigned to Plain Text File Type.
This association is unnecessary because the platform auto-detects text files by content automatically.
<p>You can dismiss this warning by removing the file type association
in <b>Settings | Editor | File Types | Text</b>.</p>
</body>
</html>

View File

@@ -985,6 +985,9 @@
<localInspection shortName="LossyEncoding" bundle="messages.LangBundle" key="lossy.encoding"
groupBundle="messages.InspectionsBundle" groupKey="group.names.internationalization.issues" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.LossyEncodingInspection"/>
<localInspection shortName="ReassignedToPlainText" bundle="messages.InspectionsBundle" key="reassigned.to.plain.text.inspection.name"
groupBundle="messages.InspectionsBundle" groupKey="inspection.general.tools.group.name" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.ReassignedToPlainTextInspection"/>
<localInspection shortName="NonAsciiCharacters" bundle="messages.LangBundle" key="non.ascii.characters"
groupBundle="messages.InspectionsBundle" groupKey="group.names.internationalization.issues" enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.NonAsciiCharactersInspection"/>