From ebef455b079749588cd195a0391cb26b54bdc634 Mon Sep 17 00:00:00 2001 From: Nicolas QUINQUENEL Date: Mon, 26 Aug 2024 16:33:58 +0200 Subject: [PATCH] SLI-1556 In Rider, all solution's files should be analyzed --- .../common/analysis/FilesContributor.java | 13 ++++ .../intellij/common/util/FileUtils.kt | 31 ++++++++++ .../intellij/rider/RiderFilesContributor.kt | 39 ++++++++++++ .../intellij/SonarLintIntelliJClient.kt | 48 ++++----------- .../intellij/fs/EditorFileChangeListener.kt | 6 +- .../org/sonarlint/intellij/util/FileUtils.kt | 61 ------------------- .../intellij/util/SonarLintAppUtils.java | 20 +----- src/main/resources/META-INF/plugin-rider.xml | 3 +- src/main/resources/META-INF/plugin.xml | 1 + 9 files changed, 104 insertions(+), 118 deletions(-) create mode 100644 common/src/main/java/org/sonarlint/intellij/common/analysis/FilesContributor.java create mode 100644 common/src/main/java/org/sonarlint/intellij/common/util/FileUtils.kt create mode 100644 rider/src/main/java/org/sonarlint/intellij/rider/RiderFilesContributor.kt delete mode 100644 src/main/java/org/sonarlint/intellij/util/FileUtils.kt diff --git a/common/src/main/java/org/sonarlint/intellij/common/analysis/FilesContributor.java b/common/src/main/java/org/sonarlint/intellij/common/analysis/FilesContributor.java new file mode 100644 index 0000000000..b7be69b0c2 --- /dev/null +++ b/common/src/main/java/org/sonarlint/intellij/common/analysis/FilesContributor.java @@ -0,0 +1,13 @@ +package org.sonarlint.intellij.common.analysis; + +import com.intellij.openapi.extensions.ExtensionPointName; +import com.intellij.openapi.module.Module; +import com.intellij.openapi.vfs.VirtualFile; +import java.util.Set; + +public interface FilesContributor { + // Name is constructed from plugin-id.extension-point-name + ExtensionPointName EP_NAME = ExtensionPointName.create("org.sonarlint.idea.filesContributor"); + + Set listFiles(Module module); +} diff --git a/common/src/main/java/org/sonarlint/intellij/common/util/FileUtils.kt b/common/src/main/java/org/sonarlint/intellij/common/util/FileUtils.kt new file mode 100644 index 0000000000..67d3f5ee73 --- /dev/null +++ b/common/src/main/java/org/sonarlint/intellij/common/util/FileUtils.kt @@ -0,0 +1,31 @@ +package org.sonarlint.intellij.common.util + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.GeneratedSourcesFilter.isGeneratedSourceByAnyFilter +import com.intellij.openapi.roots.ProjectFileIndex +import com.intellij.openapi.util.io.FileUtilRt +import com.intellij.openapi.vfs.VirtualFile +import org.sonarlint.intellij.common.ui.ReadActionUtils.Companion.computeReadActionSafely +import org.sonarlint.intellij.common.ui.SonarLintConsole + +class FileUtils { + + companion object { + fun isFileValidForSonarLint(file: VirtualFile, project: Project): Boolean { + try { + val toSkip = computeReadActionSafely(project) { + isGeneratedSourceByAnyFilter(file, project) + || ProjectFileIndex.getInstance(project).isExcluded(file) + || ProjectFileIndex.getInstance(project).isInLibrary(file) + || (!ApplicationManager.getApplication().isUnitTestMode && FileUtilRt.isTooLarge(file.length)) + } + return false == toSkip + } catch (e: Exception) { + SonarLintConsole.get(project).error("Error while visiting a file, reason: " + e.message) + return false + } + } + } + +} diff --git a/rider/src/main/java/org/sonarlint/intellij/rider/RiderFilesContributor.kt b/rider/src/main/java/org/sonarlint/intellij/rider/RiderFilesContributor.kt new file mode 100644 index 0000000000..6ee0fca94b --- /dev/null +++ b/rider/src/main/java/org/sonarlint/intellij/rider/RiderFilesContributor.kt @@ -0,0 +1,39 @@ +package org.sonarlint.intellij.rider + +import com.intellij.openapi.module.Module +import com.intellij.openapi.vfs.VirtualFile +import com.jetbrains.rider.projectView.workspace.ProjectModelEntity +import com.jetbrains.rider.projectView.workspace.ProjectModelEntityVisitor +import com.jetbrains.rider.projectView.workspace.getVirtualFileAsContentRoot +import com.jetbrains.rider.projectView.workspace.isProjectFile +import org.sonarlint.intellij.common.analysis.FilesContributor +import org.sonarlint.intellij.common.util.FileUtils.Companion.isFileValidForSonarLint + +class RiderFilesContributor : FilesContributor { + + override fun listFiles(module: Module): MutableSet { + val filesInContentRoots = mutableSetOf() + + val visitor = object : ProjectModelEntityVisitor() { + override fun visitProjectFile(entity: ProjectModelEntity): Result { + if (module.isDisposed) { + return Result.Stop + } + + if (entity.isProjectFile()) { + entity.getVirtualFileAsContentRoot()?.let { + if (!it.isDirectory && it.isValid && isFileValidForSonarLint(it, module.project)) { + filesInContentRoots.add(it) + } + } + } + + return Result.Continue + } + } + visitor.visit(module.project) + + return filesInContentRoots + } + +} diff --git a/src/main/java/org/sonarlint/intellij/SonarLintIntelliJClient.kt b/src/main/java/org/sonarlint/intellij/SonarLintIntelliJClient.kt index 6a2705e6c5..e0db6cd9aa 100644 --- a/src/main/java/org/sonarlint/intellij/SonarLintIntelliJClient.kt +++ b/src/main/java/org/sonarlint/intellij/SonarLintIntelliJClient.kt @@ -67,6 +67,7 @@ import org.sonarlint.intellij.analysis.AnalysisSubmitter import org.sonarlint.intellij.analysis.AnalysisSubmitter.collectContributedLanguages import org.sonarlint.intellij.analysis.OpenInIdeFindingCache import org.sonarlint.intellij.analysis.RunningAnalysesTracker +import org.sonarlint.intellij.common.analysis.FilesContributor import org.sonarlint.intellij.common.ui.ReadActionUtils.Companion.computeReadActionSafely import org.sonarlint.intellij.common.ui.SonarLintConsole import org.sonarlint.intellij.common.util.SonarLintUtils.getService @@ -647,17 +648,7 @@ object SonarLintIntelliJClient : SonarLintRpcClientDelegate { override fun listFiles(configScopeId: String): List { val listClientFiles = BackendService.findModule(configScopeId)?.let { module -> - val listModulesFiles = if (isRider()) { - val listFiles = listModuleFilesForRider(module, configScopeId) - computeRiderSharedConfiguration(module.project, configScopeId)?.let { - listFiles.add(it) - } - listFiles - } else { - listModuleFiles(module, configScopeId) - } - - listModulesFiles + listModuleFiles(module, configScopeId) } ?: findProject(configScopeId)?.let { project -> val listProjectFiles = listProjectFiles(project, configScopeId) @@ -701,9 +692,13 @@ object SonarLintIntelliJClient : SonarLintRpcClientDelegate { private fun listModuleFiles(module: Module, configScopeId: String): MutableList { val filesInContentRoots = listFilesInContentRoots(module) + FilesContributor.EP_NAME.extensionList.forEach { + filesInContentRoots.addAll(it.listFiles(module)) + } + val forcedLanguages = collectContributedLanguages(module, filesInContentRoots) - return filesInContentRoots.mapNotNull { file -> + val clientFiles = filesInContentRoots.mapNotNull { file -> val forcedLanguage = forcedLanguages[file]?.let { fl -> Language.valueOf(fl.name) } getRelativePathForAnalysis(module, file)?.let { relativePath -> toClientFileDto( @@ -715,31 +710,14 @@ object SonarLintIntelliJClient : SonarLintRpcClientDelegate { ) } }.toMutableList() - } - private fun listModuleFilesForRider(module: Module, configScopeId: String): MutableList { - val filesInContentRoots = mutableSetOf() - module.project.guessProjectDir()?.children?.forEach { contentRoot -> - if (module.isDisposed) { - return@forEach + if (isRider()) { + computeRiderSharedConfiguration(module.project, configScopeId)?.let { + clientFiles.add(it) } - filesInContentRoots.addAll(visitAndAddFiles(contentRoot, module)) } - val forcedLanguages = collectContributedLanguages(module, filesInContentRoots) - - return filesInContentRoots.mapNotNull { file -> - val forcedLanguage = forcedLanguages[file]?.let { fl -> Language.valueOf(fl.name) } - getRelativePathForAnalysis(module, file)?.let { relativePath -> - toClientFileDto( - module.project, - configScopeId, - file, - relativePath, - forcedLanguage - ) - } - }.toMutableList() + return clientFiles } private fun listProjectFiles(project: Project, configScopeId: String): MutableList { @@ -789,7 +767,7 @@ object SonarLintIntelliJClient : SonarLintRpcClientDelegate { private fun listFilesInContentRoots( module: Module, - ): Set { + ): MutableSet { val files = mutableListOf() ModuleRootManager.getInstance(module).contentRoots.forEach { contentRoot -> if (module.isDisposed) { @@ -797,7 +775,7 @@ object SonarLintIntelliJClient : SonarLintRpcClientDelegate { } files.addAll(visitAndAddFiles(contentRoot, module)) } - return files.toSet() + return files.toMutableSet() } // useful for Rider where the files to find are not located in content roots diff --git a/src/main/java/org/sonarlint/intellij/fs/EditorFileChangeListener.kt b/src/main/java/org/sonarlint/intellij/fs/EditorFileChangeListener.kt index cfaeaab064..e6405b017c 100644 --- a/src/main/java/org/sonarlint/intellij/fs/EditorFileChangeListener.kt +++ b/src/main/java/org/sonarlint/intellij/fs/EditorFileChangeListener.kt @@ -30,11 +30,11 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.project.ProjectLocator import com.intellij.openapi.vfs.VirtualFile import java.time.Duration +import org.sonarlint.intellij.common.util.FileUtils import org.sonarlint.intellij.common.util.SonarLintUtils.getService import org.sonarlint.intellij.core.BackendService import org.sonarlint.intellij.util.Alarm import org.sonarlint.intellij.util.SonarLintAppUtils -import org.sonarlint.intellij.util.SonarLintAppUtils.isFileValidForSonarLint import org.sonarsource.sonarlint.plugin.api.module.file.ModuleFileEvent const val DEBOUNCE_DELAY_MS = 1000L @@ -79,7 +79,7 @@ class EditorFileChangeListener : BulkAwareDocumentListener.Simple, Disposable { val filesToSendPerModule = HashMap>() changedFiles - .filter { it.isValid && isFileValidForSonarLint(it, project) } + .filter { it.isValid && FileUtils.Companion.isFileValidForSonarLint(it, project) } .forEach { file -> val module = SonarLintAppUtils.findModuleForFile(file, project) ?: return@forEach filesToSendPerModule.computeIfAbsent(module) { mutableListOf() }.add(VirtualFileEvent(ModuleFileEvent.Type.MODIFIED, file)) @@ -94,4 +94,4 @@ class EditorFileChangeListener : BulkAwareDocumentListener.Simple, Disposable { changedFiles.clear() triggerAlarm.shutdown() } -} \ No newline at end of file +} diff --git a/src/main/java/org/sonarlint/intellij/util/FileUtils.kt b/src/main/java/org/sonarlint/intellij/util/FileUtils.kt deleted file mode 100644 index 5dc290e86c..0000000000 --- a/src/main/java/org/sonarlint/intellij/util/FileUtils.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * SonarLint for IntelliJ IDEA - * Copyright (C) 2015-2024 SonarSource - * sonarlint@sonarsource.com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 - */ -package org.sonarlint.intellij.util - -import java.io.IOException -import java.nio.file.Files -import java.nio.file.Path -import org.apache.commons.io.file.PathUtils - -class FileUtils { - companion object { - - /** - * Creates a directory by creating all nonexistent parent directories first. - * - * @param path the directory to create - */ - @JvmStatic - fun mkdirs(path: Path) { - try { - Files.createDirectories(path) - } catch (e: IOException) { - throw IllegalStateException("Unable to create directory: $path", e) - } - } - - /** - * Deletes recursively the specified file or directory tree. - * - * @param path - */ - @JvmStatic - fun deleteRecursively(path: Path) { - if (!path.toFile().exists()) { - return - } - try { - PathUtils.deleteDirectory(path) - } catch (e: IOException) { - throw java.lang.IllegalStateException("Unable to delete directory $path", e) - } - } - } -} \ No newline at end of file diff --git a/src/main/java/org/sonarlint/intellij/util/SonarLintAppUtils.java b/src/main/java/org/sonarlint/intellij/util/SonarLintAppUtils.java index a286596ef1..777a954934 100644 --- a/src/main/java/org/sonarlint/intellij/util/SonarLintAppUtils.java +++ b/src/main/java/org/sonarlint/intellij/util/SonarLintAppUtils.java @@ -20,7 +20,6 @@ package org.sonarlint.intellij.util; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; @@ -28,7 +27,6 @@ import com.intellij.openapi.project.ProjectUtil; import com.intellij.openapi.roots.ModuleRootManager; import com.intellij.openapi.roots.ProjectFileIndex; -import com.intellij.openapi.util.io.FileUtilRt; import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFileVisitor; @@ -40,9 +38,8 @@ import java.util.List; import javax.annotation.CheckForNull; import org.jetbrains.annotations.Nullable; -import org.sonarlint.intellij.common.ui.SonarLintConsole; +import org.sonarlint.intellij.common.util.FileUtils; -import static com.intellij.openapi.roots.GeneratedSourcesFilter.isGeneratedSourceByAnyFilter; import static com.intellij.openapi.vfs.VirtualFileVisitor.NO_FOLLOW_SYMLINKS; import static org.sonarlint.intellij.common.ui.ReadActionUtils.computeReadActionSafely; @@ -168,7 +165,7 @@ public static List visitAndAddFiles(VirtualFile file, Module module VfsUtilCore.visitChildrenRecursively(file, new VirtualFileVisitor<>(NO_FOLLOW_SYMLINKS) { @Override public boolean visitFile(VirtualFile file) { - if (!isFileValidForSonarLint(file, module.getProject())) { + if (!FileUtils.Companion.isFileValidForSonarLint(file, module.getProject())) { return false; } if (!file.isDirectory() && file.isValid()) { @@ -180,19 +177,6 @@ public boolean visitFile(VirtualFile file) { return filesToAdd; } - public static boolean isFileValidForSonarLint(VirtualFile file, Project project) { - try { - var toSkip = computeReadActionSafely(project, () -> isGeneratedSourceByAnyFilter(file, project) - || ProjectFileIndex.getInstance(project).isExcluded(file) - || ProjectFileIndex.getInstance(project).isInLibrary(file) - || (!ApplicationManager.getApplication().isUnitTestMode() && FileUtilRt.isTooLarge(file.getLength()))); - return Boolean.FALSE == toSkip; - } catch (Exception e) { - SonarLintConsole.get(project).error("Error while visiting a file, reason: " + e.getMessage()); - return false; - } - } - @CheckForNull private static String getPathRelativeToContentRoot(Module module, VirtualFile file) { var moduleRootManager = ModuleRootManager.getInstance(module); diff --git a/src/main/resources/META-INF/plugin-rider.xml b/src/main/resources/META-INF/plugin-rider.xml index 3a4e4bb02d..ef3c470dd4 100644 --- a/src/main/resources/META-INF/plugin-rider.xml +++ b/src/main/resources/META-INF/plugin-rider.xml @@ -19,8 +19,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 --> - + + diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index ccc851874b..1d6ec82923 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -276,6 +276,7 @@ +