Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configuration UI - V2 #3732

Merged
merged 13 commits into from
Jun 7, 2019
208 changes: 117 additions & 91 deletions Extension/src/LanguageServer/configurations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -227,22 +227,6 @@ export class CppProperties {

private onSelectionChanged(): void {
this.selectionChanged.fire(this.CurrentConfigurationIndex);
if (this.settingsPanel) {
this.ensurePropertiesFile().then(() => {
if (this.propertiesFile) {
// Clear out any modifications we may have made internally by parsing the json file
if (this.parsePropertiesFile(false)) {
// Update the UI with new selected configuration
this.settingsPanel.updateConfigUI(
this.configurationJson.configurations[this.currentConfigurationIndex.Value],
this.getErrorsForConfigUI());
} else {
// Parse failed, open json file
vscode.workspace.openTextDocument(this.propertiesFile);
}
}
});
}
this.handleSquiggles();
}

Expand Down Expand Up @@ -273,53 +257,57 @@ export class CppProperties {
private applyDefaultIncludePathsAndFrameworks(): void {
if (this.configurationIncomplete && this.defaultIncludes && this.defaultFrameworks && this.vcpkgPathReady) {
let configuration: Configuration = this.CurrentConfiguration;
let settings: CppSettings = new CppSettings(this.rootUri);
let isUnset: (input: any) => boolean = (input: any) => {
this.applyDefaultConfigurationValues(configuration);
this.configurationIncomplete = false;
}
}

private applyDefaultConfigurationValues(configuration: Configuration): void {
let settings: CppSettings = new CppSettings(this.rootUri);
let isUnset: (input: any) => boolean = (input: any) => {
// default values for "default" config settings is null.
return input === null;
};
};

// Anything that has a vscode setting for it will be resolved in updateServerOnFolderSettingsChange.
// So if a property is currently unset, but has a vscode setting, don't set it yet, otherwise the linkage
// to the setting will be lost if this configuration is saved into a c_cpp_properties.json file.
// Anything that has a vscode setting for it will be resolved in updateServerOnFolderSettingsChange.
// So if a property is currently unset, but has a vscode setting, don't set it yet, otherwise the linkage
// to the setting will be lost if this configuration is saved into a c_cpp_properties.json file.

// Only add settings from the default compiler if user hasn't explicitly set the corresponding VS Code setting.
// Only add settings from the default compiler if user hasn't explicitly set the corresponding VS Code setting.

if (isUnset(settings.defaultIncludePath)) {
// We don't add system includes to the includePath anymore. The language server has this information.
let abTestSettings: ABTestSettings = getABTestSettings();
let rootFolder: string = abTestSettings.UseRecursiveIncludes ? "${workspaceFolder}/**" : "${workspaceFolder}";
configuration.includePath = [rootFolder].concat(this.vcpkgIncludes);
}
// browse.path is not set by default anymore. When it is not set, the includePath will be used instead.
if (isUnset(settings.defaultDefines)) {
configuration.defines = (process.platform === 'win32') ? ["_DEBUG", "UNICODE", "_UNICODE"] : [];
}
if (isUnset(settings.defaultMacFrameworkPath) && process.platform === 'darwin') {
configuration.macFrameworkPath = this.defaultFrameworks;
}
if (isUnset(settings.defaultWindowsSdkVersion) && this.defaultWindowsSdkVersion && process.platform === 'win32') {
configuration.windowsSdkVersion = this.defaultWindowsSdkVersion;
}
if (isUnset(settings.defaultCompilerPath) && this.defaultCompilerPath &&
isUnset(settings.defaultCompileCommands) && !configuration.compileCommands) {
// compile_commands.json already specifies a compiler. compilerPath overrides the compile_commands.json compiler so
// don't set a default when compileCommands is in use.
configuration.compilerPath = this.defaultCompilerPath;
}
if (this.knownCompilers) {
configuration.knownCompilers = this.knownCompilers;
}
if (isUnset(settings.defaultCStandard) && this.defaultCStandard) {
configuration.cStandard = this.defaultCStandard;
}
if (isUnset(settings.defaultCppStandard) && this.defaultCppStandard) {
configuration.cppStandard = this.defaultCppStandard;
}
if (isUnset(settings.defaultIntelliSenseMode)) {
configuration.intelliSenseMode = this.defaultIntelliSenseMode;
}
this.configurationIncomplete = false;
if (isUnset(settings.defaultIncludePath)) {
// We don't add system includes to the includePath anymore. The language server has this information.
let abTestSettings: ABTestSettings = getABTestSettings();
let rootFolder: string = abTestSettings.UseRecursiveIncludes ? "${workspaceFolder}/**" : "${workspaceFolder}";
configuration.includePath = [rootFolder].concat(this.vcpkgIncludes);
}
// browse.path is not set by default anymore. When it is not set, the includePath will be used instead.
if (isUnset(settings.defaultDefines)) {
configuration.defines = (process.platform === 'win32') ? ["_DEBUG", "UNICODE", "_UNICODE"] : [];
}
if (isUnset(settings.defaultMacFrameworkPath) && process.platform === 'darwin') {
configuration.macFrameworkPath = this.defaultFrameworks;
}
if (isUnset(settings.defaultWindowsSdkVersion) && this.defaultWindowsSdkVersion && process.platform === 'win32') {
configuration.windowsSdkVersion = this.defaultWindowsSdkVersion;
}
if (isUnset(settings.defaultCompilerPath) && this.defaultCompilerPath &&
isUnset(settings.defaultCompileCommands) && !configuration.compileCommands) {
// compile_commands.json already specifies a compiler. compilerPath overrides the compile_commands.json compiler so
// don't set a default when compileCommands is in use.
configuration.compilerPath = this.defaultCompilerPath;
}
if (this.knownCompilers) {
configuration.knownCompilers = this.knownCompilers;
}
if (isUnset(settings.defaultCStandard) && this.defaultCStandard) {
configuration.cStandard = this.defaultCStandard;
}
if (isUnset(settings.defaultCppStandard) && this.defaultCppStandard) {
configuration.cppStandard = this.defaultCppStandard;
}
if (isUnset(settings.defaultIntelliSenseMode)) {
configuration.intelliSenseMode = this.defaultIntelliSenseMode;
}
}

Expand Down Expand Up @@ -397,20 +385,20 @@ export class CppProperties {
}
}

private isCompilerIntelliSenseModeCompatible(): boolean {
private isCompilerIntelliSenseModeCompatible(configuration: Configuration): boolean {
// Check if intelliSenseMode and compilerPath are compatible
// cl.exe and msvc mode should be used together
// Ignore if compiler path is not set or intelliSenseMode is not set
if (this.CurrentConfiguration.compilerPath === undefined ||
this.CurrentConfiguration.compilerPath === "" ||
this.CurrentConfiguration.compilerPath === "${default}" ||
this.CurrentConfiguration.intelliSenseMode === undefined ||
this.CurrentConfiguration.intelliSenseMode === "" ||
this.CurrentConfiguration.intelliSenseMode === "${default}") {
if (configuration.compilerPath === undefined ||
configuration.compilerPath === "" ||
configuration.compilerPath === "${default}" ||
configuration.intelliSenseMode === undefined ||
configuration.intelliSenseMode === "" ||
configuration.intelliSenseMode === "${default}") {
return true;
}
let compilerPathAndArgs: util.CompilerPathAndArgs = util.extractCompilerPathAndArgs(this.CurrentConfiguration.compilerPath);
return compilerPathAndArgs.compilerPath.endsWith("cl.exe") === (this.CurrentConfiguration.intelliSenseMode === "msvc-x64");
let compilerPathAndArgs: util.CompilerPathAndArgs = util.extractCompilerPathAndArgs(configuration.compilerPath);
return compilerPathAndArgs.compilerPath.endsWith("cl.exe") === (configuration.intelliSenseMode === "msvc-x64");
}

public addToIncludePathCommand(path: string): void {
Expand Down Expand Up @@ -635,25 +623,33 @@ export class CppProperties {
});
}

private ensureSettingsPanelInitlialized(): void {
if (this.settingsPanel === undefined) {
let settings: CppSettings = new CppSettings(this.rootUri);
this.settingsPanel = new SettingsPanel(); // set initial this.currentConfigurationIndex.Value
this.settingsPanel.setKnownCompilers(this.knownCompilers, settings.preferredPathSeparator);
this.settingsPanel.SettingsPanelActivated(() => this.onSettingsPanelActivated());
this.settingsPanel.ConfigValuesChanged(() => this.saveConfigurationUI());
this.settingsPanel.ConfigSelectionChanged(() => this.onConfigSelectionChanged());
this.settingsPanel.AddConfigRequested((e) => this.onAddConfigRequested(e));
this.disposables.push(this.settingsPanel);
}
}

public handleConfigurationEditUICommand(onCreation: () => void, showDocument: (document: vscode.TextDocument) => void): void {
this.ensurePropertiesFile().then(() => {
if (this.propertiesFile) {
if (onCreation) {
onCreation();
}
if (this.parsePropertiesFile(false)) {
// Parse successful, show UI
if (this.settingsPanel === undefined) {
let settings: CppSettings = new CppSettings(this.rootUri);
this.settingsPanel = new SettingsPanel();
this.settingsPanel.setKnownCompilers(this.knownCompilers, settings.preferredPathSeparator);
this.settingsPanel.SettingsPanelActivated(() => this.onSettingsPanelActivated());
this.settingsPanel.ConfigValuesChanged(() => this.saveConfigurationUI());
this.disposables.push(this.settingsPanel);
}
this.settingsPanel.createOrShow(
this.configurationJson.configurations[this.currentConfigurationIndex.Value],
this.getErrorsForConfigUI());
this.ensureSettingsPanelInitlialized();

// Use the active configuration as the default selected configuration to load on UI editor
this.settingsPanel.selectedConfigIndex = this.currentConfigurationIndex.Value;
this.settingsPanel.createOrShow(this.ConfigurationNames,
this.configurationJson.configurations[this.settingsPanel.selectedConfigIndex],
this.getErrorsForConfigUI(this.settingsPanel.selectedConfigIndex));
} else {
// Parse failed, open json file
vscode.workspace.openTextDocument(this.propertiesFile).then((document: vscode.TextDocument) => {
Expand All @@ -673,9 +669,12 @@ export class CppProperties {
if (this.parsePropertiesFile(false)) {
// The settings UI became visible or active.
// Ensure settingsPanel has copy of latest current configuration
this.settingsPanel.updateConfigUI(
this.configurationJson.configurations[this.currentConfigurationIndex.Value],
this.getErrorsForConfigUI());
if (this.settingsPanel.selectedConfigIndex >= this.configurationJson.configurations.length) {
this.settingsPanel.selectedConfigIndex = this.currentConfigurationIndex.Value;
}
this.settingsPanel.updateConfigUI(this.ConfigurationNames,
this.configurationJson.configurations[this.settingsPanel.selectedConfigIndex],
this.getErrorsForConfigUI(this.settingsPanel.selectedConfigIndex));
} else {
// Parse failed, open json file
vscode.workspace.openTextDocument(this.propertiesFile);
Expand All @@ -688,8 +687,33 @@ export class CppProperties {
private saveConfigurationUI(): void {
this.parsePropertiesFile(false); // Clear out any modifications we may have made internally.
let config: Configuration = this.settingsPanel.getLastValuesFromConfigUI();
this.configurationJson.configurations[this.currentConfigurationIndex.Value] = config;
this.settingsPanel.updateErrors(this.getErrorsForConfigUI());
this.configurationJson.configurations[this.settingsPanel.selectedConfigIndex] = config;
this.settingsPanel.updateErrors(this.getErrorsForConfigUI(this.settingsPanel.selectedConfigIndex));
this.writeToJson();
}

private onConfigSelectionChanged(): void {
this.settingsPanel.updateConfigUI(this.ConfigurationNames,
this.configurationJson.configurations[this.settingsPanel.selectedConfigIndex],
this.getErrorsForConfigUI(this.settingsPanel.selectedConfigIndex));
}

private onAddConfigRequested(configName: string): void {
this.parsePropertiesFile(false); // Clear out any modifications we may have made internally.

// Create default config and add to list of configurations
let newConfig: Configuration = { name: configName };
this.applyDefaultConfigurationValues(newConfig);
delete newConfig.knownCompilers;
this.configurationJson.configurations.push(newConfig);

// Update UI
this.settingsPanel.selectedConfigIndex = this.configurationJson.configurations.length - 1;
this.settingsPanel.updateConfigUI(this.ConfigurationNames,
this.configurationJson.configurations[this.settingsPanel.selectedConfigIndex],
null);

// Save new config to file
this.writeToJson();
}

Expand Down Expand Up @@ -885,12 +909,14 @@ export class CppProperties {
return result;
}

private getErrorsForConfigUI(): ConfigurationErrors {
private getErrorsForConfigUI(configIndex: number): ConfigurationErrors {
let errors: ConfigurationErrors = {};
const isWindows: boolean = os.platform() === 'win32';

let config: Configuration = this.configurationJson.configurations[configIndex];

// Validate compilerPath
let resolvedCompilerPath: string = this.resolvePath(this.CurrentConfiguration.compilerPath, isWindows);
let resolvedCompilerPath: string = this.resolvePath(config.compilerPath, isWindows);
let compilerPathAndArgs: util.CompilerPathAndArgs = util.extractCompilerPathAndArgs(resolvedCompilerPath);
if (resolvedCompilerPath &&
// Don't error cl.exe paths because it could be for an older preview build.
Expand Down Expand Up @@ -949,8 +975,8 @@ export class CppProperties {

// Validate includePath
let includePathErrors: string[] = [];
if (this.CurrentConfiguration.includePath) {
for (let includePath of this.CurrentConfiguration.includePath) {
if (config.includePath) {
for (let includePath of config.includePath) {
let pathExists: boolean = true;
let resolvedIncludePath: string = this.resolvePath(includePath, isWindows);
if (!resolvedIncludePath) {
Expand Down Expand Up @@ -987,8 +1013,8 @@ export class CppProperties {
}

// Validate intelliSenseMode
if (isWindows && !this.isCompilerIntelliSenseModeCompatible()) {
errors.intelliSenseMode = `IntelliSense mode ${this.CurrentConfiguration.intelliSenseMode} is incompatible with compiler path.`;
if (isWindows && !this.isCompilerIntelliSenseModeCompatible(config)) {
errors.intelliSenseMode = `IntelliSense mode ${config.intelliSenseMode} is incompatible with compiler path.`;
}

return errors;
Expand Down Expand Up @@ -1055,7 +1081,7 @@ export class CppProperties {
const intelliSenseModeValueStart: number = curText.indexOf('"', curText.indexOf(":", intelliSenseModeStart));
const intelliSenseModeValueEnd: number = intelliSenseModeStart === -1 ? -1 : curText.indexOf('"', intelliSenseModeValueStart + 1) + 1;

if (!this.isCompilerIntelliSenseModeCompatible()) {
if (!this.isCompilerIntelliSenseModeCompatible(this.CurrentConfiguration)) {
let message: string = `intelliSenseMode ${this.CurrentConfiguration.intelliSenseMode} is incompatible with compilerPath.`;
let diagnostic: vscode.Diagnostic = new vscode.Diagnostic(
new vscode.Range(document.positionAt(curTextStartOffset + intelliSenseModeValueStart),
Expand Down
Loading