Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* SonarLint Language Server
* Copyright (C) 2009-2024 SonarSource SA
* mailto:info AT sonarsource DOT 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 02110-1301, USA.
*/
package org.sonarsource.sonarlint.ls.settings;
import com.google.common.collect.Maps;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import java.net.URI;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.lsp4j.ConfigurationItem;
import org.eclipse.lsp4j.ConfigurationParams;
import org.sonarsource.sonarlint.core.commons.RuleKey;
import org.sonarsource.sonarlint.core.rpc.protocol.backend.analysis.DidChangeClientNodeJsPathParams;
import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.StandaloneRuleConfigDto;
import org.sonarsource.sonarlint.ls.SonarLintExtendedLanguageClient;
import org.sonarsource.sonarlint.ls.backend.BackendServiceFacade;
import org.sonarsource.sonarlint.ls.folders.WorkspaceFolderLifecycleListener;
import org.sonarsource.sonarlint.ls.folders.WorkspaceFolderWrapper;
import org.sonarsource.sonarlint.ls.folders.WorkspaceFoldersManager;
import org.sonarsource.sonarlint.ls.log.LanguageClientLogger;
import org.sonarsource.sonarlint.ls.util.Utils;
import static java.lang.String.format;
import static java.util.Arrays.stream;
import static org.apache.commons.lang3.StringUtils.defaultIfBlank;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.sonarsource.sonarlint.ls.backend.BackendService.ROOT_CONFIGURATION_SCOPE;
import static org.sonarsource.sonarlint.ls.util.Utils.interrupted;
public class SettingsManager implements WorkspaceFolderLifecycleListener {
private static Path sonarLintUserHomeOverride = null;
private static final String ORGANIZATION_KEY = "organizationKey";
private static final String DISABLE_NOTIFICATIONS = "disableNotifications";
private static final String PROJECT = "project";
public static final String DEFAULT_CONNECTION_ID = "";
private static final String SERVER_URL = "serverUrl";
private static final String SERVER_ID = "serverId";
private static final String TOKEN = "token";
private static final String CONNECTION_ID = "connectionId";
public static final String SONARLINT_CONFIGURATION_NAMESPACE = "sonarlint";
public static final String DOTNET_DEFAULT_SOLUTION_PATH = "dotnet.defaultSolution";
public static final String OMNISHARP_USE_MODERN_NET = "omnisharp.useModernNet";
public static final String OMNISHARP_LOAD_PROJECT_ON_DEMAND = "omnisharp.enableMsBuildLoadProjectsOnDemand";
public static final String OMNISHARP_PROJECT_LOAD_TIMEOUT = "omnisharp.projectLoadTimeout";
public static final String VSCODE_FILE_EXCLUDES = "files.exclude";
private static final String DISABLE_TELEMETRY = "disableTelemetry";
public static final String ANALYSIS_EXCLUDES = "analysisExcludesStandalone";
private static final String RULES = "rules";
private static final String TEST_FILE_PATTERN = "testFilePattern";
static final String ANALYZER_PROPERTIES = "analyzerProperties";
private static final String OUTPUT = "output";
private static final String SHOW_ANALYZER_LOGS = "showAnalyzerLogs";
private static final String SHOW_VERBOSE_LOGS = "showVerboseLogs";
private static final String PATH_TO_NODE_EXECUTABLE = "pathToNodeExecutable";
private static final String PATH_TO_COMPILE_COMMANDS = "pathToCompileCommands";
private static final String FOCUS_ON_NEW_CODE = "focusOnNewCode";
private static final String WORKSPACE_FOLDER_VARIABLE = "${workspaceFolder}";
public static final String SONAR_CS_FILE_SUFFIXES = "sonar.cs.file.suffixes";
private final SonarLintExtendedLanguageClient client;
private final WorkspaceFoldersManager foldersManager;
private WorkspaceSettings currentSettings = null;
private final CountDownLatch initLatch = new CountDownLatch(1);
// Setting that are normally specific per workspace folder, but we also keep a cache of global values to analyze files outside any
// workspace
private WorkspaceFolderSettings currentDefaultSettings = null;
private final ExecutorService executor;
private final List globalListeners = new ArrayList<>();
private final List folderListeners = new ArrayList<>();
private final BackendServiceFacade backendServiceFacade;
private final LanguageClientLogger logOutput;
public SettingsManager(SonarLintExtendedLanguageClient client, WorkspaceFoldersManager foldersManager,
BackendServiceFacade backendServiceFacade, LanguageClientLogger logOutput) {
this(client, foldersManager, Executors.newCachedThreadPool(Utils.threadFactory("SonarLint settings manager", false)), backendServiceFacade, logOutput);
}
SettingsManager(SonarLintExtendedLanguageClient client, WorkspaceFoldersManager foldersManager,
ExecutorService executor, BackendServiceFacade backendServiceFacade, LanguageClientLogger logOutput) {
this.client = client;
this.foldersManager = foldersManager;
this.executor = executor;
this.backendServiceFacade = backendServiceFacade;
this.logOutput = logOutput;
}
public static void setSonarLintUserHomeOverride(Path sonarLintUserHomeOverride) {
SettingsManager.sonarLintUserHomeOverride = sonarLintUserHomeOverride;
}
public static Path getSonarLintUserHomeOverride() {
return sonarLintUserHomeOverride;
}
/**
* Get workspace level settings, waiting for them to be initialized
*/
public WorkspaceSettings getCurrentSettings() {
try {
if (initLatch.await(1, TimeUnit.MINUTES)) {
return currentSettings;
}
} catch (InterruptedException e) {
interrupted(e, logOutput);
}
throw new IllegalStateException("Unable to get settings in time");
}
public Map getStandaloneRuleConfigByKey() {
var standaloneRuleConfigByKey = new HashMap();
currentSettings.getIncludedRules().forEach((ruleKey -> addRulesToConfig(ruleKey, standaloneRuleConfigByKey, true)));
currentSettings.getExcludedRules().forEach((ruleKey -> addRulesToConfig(ruleKey, standaloneRuleConfigByKey, false)));
return standaloneRuleConfigByKey;
}
private void addRulesToConfig(RuleKey ruleKey, HashMap standaloneRuleConfigByKey, boolean isActive) {
var params = currentSettings.getRuleParameters().get(ruleKey);
var sanitizedParams = params != null ? params : Map.of();
standaloneRuleConfigByKey.put(ruleKey.toString(), new StandaloneRuleConfigDto(isActive, sanitizedParams));
}
/**
* Get default workspace folder level settings, waiting for them to be initialized
*/
public WorkspaceFolderSettings getCurrentDefaultFolderSettings() {
try {
if (initLatch.await(1, TimeUnit.MINUTES)) {
return currentDefaultSettings;
}
} catch (InterruptedException e) {
interrupted(e, logOutput);
}
throw new IllegalStateException("Unable to get settings in time");
}
public void didChangeConfiguration() {
executor.execute(() -> {
try {
var workspaceSettingsMap = requestSonarLintAndOmnisharpConfigurationAsync(null).get(1, TimeUnit.MINUTES);
var newWorkspaceSettings = parseSettings(workspaceSettingsMap);
var oldWorkspaceSettings = currentSettings;
this.currentSettings = newWorkspaceSettings;
var newDefaultFolderSettings = parseFolderSettings(workspaceSettingsMap, null);
var oldDefaultFolderSettings = currentDefaultSettings;
this.currentDefaultSettings = newDefaultFolderSettings;
if (initLatch.getCount() != 0) {
initLatch.countDown();
backendServiceFacade.initialize(getCurrentSettings().getServerConnections());
} else {
notifyChangeClientNodeJsPathIfNeeded(oldWorkspaceSettings, newWorkspaceSettings);
backendServiceFacade.getBackendService().didChangeConnections(getCurrentSettings().getServerConnections());
backendServiceFacade.getBackendService().updateStandaloneRulesConfiguration(getStandaloneRuleConfigByKey());
}
foldersManager.getAll().forEach(f -> updateWorkspaceFolderSettings(f, true));
notifyListeners(newWorkspaceSettings, oldWorkspaceSettings, newDefaultFolderSettings, oldDefaultFolderSettings);
} catch (InterruptedException e) {
interrupted(e, logOutput);
} catch (Exception e) {
logOutput.errorWithStackTrace("Unable to update configuration.", e);
} finally {
client.readyForTests();
}
});
}
private void notifyChangeClientNodeJsPathIfNeeded(WorkspaceSettings oldWorkspaceSettings, WorkspaceSettings newWorkspaceSettings) {
var hasNodeJsPathChanged = !Objects.equals(oldWorkspaceSettings.pathToNodeExecutable(), newWorkspaceSettings.pathToNodeExecutable());
if (hasNodeJsPathChanged) {
backendServiceFacade.getBackendService().didChangeClientNodeJsPath(new DidChangeClientNodeJsPathParams(Path.of(newWorkspaceSettings.pathToNodeExecutable())));
}
}
private void notifyAnalyzerPropertiesChangeIfNeeded(@Nullable WorkspaceFolderSettings oldDefaultSettings,
WorkspaceFolderSettings newDefaultSettings, String configurationScopeId) {
var hasAnalyzerPropertiesChanged = oldDefaultSettings != null && !Objects.equals(oldDefaultSettings.getAnalyzerProperties(), newDefaultSettings.getAnalyzerProperties());
var hasPathToCompileCommandsChanged = oldDefaultSettings != null
&& !Objects.equals(oldDefaultSettings.getPathToCompileCommands(), newDefaultSettings.getPathToCompileCommands());
if (hasPathToCompileCommandsChanged) {
backendServiceFacade.getBackendService().didChangePathToCompileCommands(configurationScopeId, newDefaultSettings.getPathToCompileCommands());
}
if (hasAnalyzerPropertiesChanged || hasPathToCompileCommandsChanged) {
backendServiceFacade.getBackendService().didSetUserAnalysisProperties(configurationScopeId, newDefaultSettings.getAnalyzerProperties());
}
}
private void notifyListeners(WorkspaceSettings newWorkspaceSettings, WorkspaceSettings oldWorkspaceSettings, WorkspaceFolderSettings newDefaultFolderSettings,
WorkspaceFolderSettings oldDefaultFolderSettings) {
if (!Objects.equals(oldWorkspaceSettings, newWorkspaceSettings)) {
logOutput.debug(format("Global settings updated: %s", newWorkspaceSettings));
globalListeners.forEach(l -> l.onChange(oldWorkspaceSettings, newWorkspaceSettings));
}
if (!Objects.equals(oldDefaultFolderSettings, newDefaultFolderSettings)) {
logOutput.debug(format("Default settings updated: %s", newDefaultFolderSettings));
notifyAnalyzerPropertiesChangeIfNeeded(oldDefaultFolderSettings, newDefaultFolderSettings, ROOT_CONFIGURATION_SCOPE);
folderListeners.forEach(l -> l.onChange(null, oldDefaultFolderSettings, newDefaultFolderSettings));
}
}
// Visible for testing
CompletableFuture