package com.jetbrains.lang.dart.ide.inspections; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.codeInspection.*; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.OpenFileDescriptor; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFile; import com.jetbrains.lang.dart.DartBundle; import com.jetbrains.lang.dart.flutter.FlutterUtil; import com.jetbrains.lang.dart.ide.actions.DartPubActionBase; import com.jetbrains.lang.dart.psi.DartFile; import com.jetbrains.lang.dart.sdk.DartSdk; import com.jetbrains.lang.dart.sdk.DartSdkLibUtil; import com.jetbrains.lang.dart.util.DartResolveUtil; import com.jetbrains.lang.dart.util.DotPackagesFileUtil; import com.jetbrains.lang.dart.util.PubspecYamlUtil; import gnu.trove.THashSet; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Set; public class DartOutdatedDependenciesInspection extends LocalInspectionTool { private final Set<String> myIgnoredPubspecPaths = new THashSet<>(); // remember for the current session only, do not serialize @Nullable @Override public ProblemDescriptor[] checkFile(@NotNull final PsiFile psiFile, @NotNull final InspectionManager manager, final boolean isOnTheFly) { if (!isOnTheFly) return null; if (!(psiFile instanceof DartFile)) return null; if (DartPubActionBase.isInProgress()) return null; final VirtualFile file = DartResolveUtil.getRealVirtualFile(psiFile); if (file == null || !file.isInLocalFileSystem()) return null; final Project project = psiFile.getProject(); if (!ProjectRootManager.getInstance(project).getFileIndex().isInContent(file)) return null; final DartSdk sdk = DartSdk.getDartSdk(project); final Module module = ModuleUtilCore.findModuleForFile(file, project); if (module == null || sdk == null || !DartSdkLibUtil.isDartSdkEnabled(module)) return null; if (FlutterUtil.isFlutterPluginInstalled() && FlutterUtil.isFlutterModule(module)) return null; final VirtualFile pubspecFile = PubspecYamlUtil.findPubspecYamlFile(project, file); if (pubspecFile == null || myIgnoredPubspecPaths.contains(pubspecFile.getPath())) return null; final String projectName = PubspecYamlUtil.getDartProjectName(pubspecFile); if (projectName == null || !StringUtil.isJavaIdentifier(projectName)) return null; // 'pub get' will fail anyway final VirtualFile dotPackagesFile = pubspecFile.getParent().findChild(DotPackagesFileUtil.DOT_PACKAGES); if (dotPackagesFile == null) { return createProblemDescriptors(manager, psiFile, pubspecFile, DartBundle.message("pub.get.never.done")); } if (FileDocumentManager.getInstance().isFileModified(pubspecFile) || pubspecFile.getTimeStamp() > dotPackagesFile.getTimeStamp()) { return createProblemDescriptors(manager, psiFile, pubspecFile, DartBundle.message("pubspec.edited")); } return null; } @NotNull private ProblemDescriptor[] createProblemDescriptors(@NotNull final InspectionManager manager, @NotNull final PsiFile psiFile, @NotNull final VirtualFile pubspecFile, @NotNull final String errorMessage) { final LocalQuickFix[] fixes = new LocalQuickFix[]{ new RunPubFix(DartBundle.message("get.dependencies"), "Dart.pub.get"), new RunPubFix(DartBundle.message("upgrade.dependencies"), "Dart.pub.upgrade"), new OpenPubspecFix(), new IgnoreWarningFix(myIgnoredPubspecPaths, pubspecFile.getPath())}; return new ProblemDescriptor[]{ manager.createProblemDescriptor(psiFile, errorMessage, true, fixes, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)}; } private static class RunPubFix extends IntentionAndQuickFixAction { private final String myFixName; private final String myActionId; private RunPubFix(@NotNull final String fixName, @NotNull final String actionId) { myFixName = fixName; myActionId = actionId; } @Override @NotNull public String getName() { return myFixName; } @Override @NotNull public String getFamilyName() { return getName(); } @Override public boolean startInWriteAction() { return false; } @Override public void applyFix(@NotNull final Project project, @NotNull final PsiFile psiFile, @Nullable final Editor editor) { final VirtualFile file = DartResolveUtil.getRealVirtualFile(psiFile); if (file == null || !file.isInLocalFileSystem()) return; final VirtualFile pubspecFile = PubspecYamlUtil.findPubspecYamlFile(project, file); if (pubspecFile == null) return; final Module module = ModuleUtilCore.findModuleForFile(file, project); if (module == null) return; final AnAction pubGetAction = ActionManager.getInstance().getAction(myActionId); if (pubGetAction instanceof DartPubActionBase) { ((DartPubActionBase)pubGetAction).performPubAction(module, pubspecFile, false); } } } private static class OpenPubspecFix extends IntentionAndQuickFixAction { @Override @NotNull public String getName() { return DartBundle.message("open.pubspec"); } @Override @NotNull public String getFamilyName() { return getName(); } @Override public boolean startInWriteAction() { return false; } @Override public void applyFix(@NotNull final Project project, @NotNull final PsiFile psiFile, @Nullable final Editor editor) { final VirtualFile file = DartResolveUtil.getRealVirtualFile(psiFile); if (file == null || !file.isInLocalFileSystem()) return; final VirtualFile pubspecFile = PubspecYamlUtil.findPubspecYamlFile(project, file); if (pubspecFile == null) return; new OpenFileDescriptor(project, pubspecFile).navigate(true); } } private static class IgnoreWarningFix extends IntentionAndQuickFixAction { @NotNull private final Set<String> myIgnoredPubspecPaths; @NotNull private final String myPubspecPath; public IgnoreWarningFix(@NotNull final Set<String> ignoredPubspecPaths, @NotNull final String pubspecPath) { myIgnoredPubspecPaths = ignoredPubspecPaths; myPubspecPath = pubspecPath; } @Override @NotNull public String getName() { return DartBundle.message("ignore.warning"); } @Override @NotNull public String getFamilyName() { return getName(); } @Override public boolean startInWriteAction() { return false; } @Override public void applyFix(@NotNull final Project project, @NotNull final PsiFile psiFile, @Nullable final Editor editor) { myIgnoredPubspecPaths.add(myPubspecPath); DaemonCodeAnalyzer.getInstance(project).restart(); } } }