@theia/cpp
Version:
Theia - Cpp Extension
230 lines (201 loc) • 9.14 kB
text/typescript
/********************************************************************************
* Copyright (C) 2018 Ericsson and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { Command, CommandContribution, CommandRegistry, CommandService } from '@theia/core';
import { injectable, inject } from 'inversify';
import { QuickOpenService } from '@theia/core/lib/browser/quick-open/quick-open-service';
import { FileSystem } from '@theia/filesystem/lib/common';
import URI from '@theia/core/lib/common/uri';
import { PreferenceScope, PreferenceService } from '@theia/preferences/lib/browser';
import { CppBuildConfigurationManager, CPP_BUILD_CONFIGURATIONS_PREFERENCE_KEY, isCppBuildConfiguration, equals } from './cpp-build-configurations';
import { EditorManager } from '@theia/editor/lib/browser';
import { CommonCommands, LabelProvider } from '@theia/core/lib/browser';
import { QuickPickService, QuickPickItem } from '@theia/core/lib/common/quick-pick-service';
import { WorkspaceService } from '@theia/workspace/lib/browser';
import { CppBuildConfiguration } from '../common/cpp-build-configuration-protocol';
export class CppBuildConfigurationChanger {
protected readonly commandService: CommandService;
protected readonly cppBuildConfigurations: CppBuildConfigurationManager;
protected readonly editorManager: EditorManager;
protected readonly fileSystem: FileSystem;
protected readonly labelProvider: LabelProvider;
protected readonly quickPick: QuickPickService;
protected readonly quickOpenService: QuickOpenService;
protected readonly preferenceService: PreferenceService;
protected readonly workspaceService: WorkspaceService;
/**
* Item used to trigger creation of a new build configuration.
*/
protected readonly createItem: QuickPickItem<'createNew'> = ({
label: 'Create New',
value: 'createNew',
description: 'Create a new build configuration',
iconClass: 'fa fa-plus'
});
/**
* Item used to trigger reset of the active build configuration.
*/
protected readonly resetItem: QuickPickItem<'reset'> = ({
label: 'None',
value: 'reset',
description: 'Reset the active build configuration',
iconClass: 'fa fa-times'
});
/**
* Change the build configuration for a given root.
* If multiple roots are available, prompt users a first time to select their desired root.
* Once a root is determined, prompt users to select an active build configuration if applicable.
*/
async change(): Promise<void> {
// Prompt users to determine working root.
const root = await this.selectWorkspaceRoot();
if (!root) {
return;
}
// Prompt users to determine action (set active config, reset active config, create new config).
const action = await this.selectCppAction(root);
if (!action) {
return;
}
// Perform desired action.
if (action === 'createNew') {
this.commandService.executeCommand(CPP_CREATE_NEW_BUILD_CONFIGURATION.id);
}
if (action === 'reset') {
this.cppBuildConfigurations.setActiveConfig(undefined, root);
}
if (action && isCppBuildConfiguration(action)) {
this.cppBuildConfigurations.setActiveConfig(action, root);
}
}
/**
* Pick a workspace root using the quick open menu.
*/
protected async selectWorkspaceRoot(): Promise<string | undefined> {
const roots = this.workspaceService.tryGetRoots();
return this.quickPick.show(roots.map(({ uri: root }) => {
const active = this.cppBuildConfigurations.getActiveConfig(root);
return {
// See: WorkspaceUriLabelProviderContribution
// It will transform the path to a prettier display (adding a ~, etc).
label: this.labelProvider.getName(new URI(root).withScheme('file')),
description: active ? active.name : 'undefined',
value: root,
};
}), { placeholder: 'Select workspace root' });
}
/**
* Lists the different options for a given root if specified, first else.
* In this case, the options are to set/unset/create a build configuration.
*
* @param root the workspace root.
*/
protected async selectCppAction(root: string | undefined): Promise<string | CppBuildConfiguration | undefined> {
const items: QuickPickItem<'createNew' | 'reset' | CppBuildConfiguration>[] = [];
// Add the 'Create New' item at all times.
items.push(this.createItem);
// Add the 'Reset' item if there currently is an active config.
if (this.cppBuildConfigurations.getActiveConfig(root)) {
items.push(this.resetItem);
}
// Display all valid configurations for a given root.
const configs = this.cppBuildConfigurations.getValidConfigs(root);
const active = this.cppBuildConfigurations.getActiveConfig(root);
configs.map(config => {
items.push({
label: config.name,
description: config.directory,
iconClass: active && equals(config, active) ? 'fa fa-check' : 'fa fa-empty-item',
value: {
name: config.name,
directory: config.directory,
commands: config.commands
},
});
});
return this.quickPick.show(items, { placeholder: 'Select action' });
}
/** Create a new build configuration with placeholder values. */
async createConfig(): Promise<void> {
this.commandService.executeCommand(CommonCommands.OPEN_PREFERENCES.id, PreferenceScope.Workspace);
const configs = this.cppBuildConfigurations.getConfigs().slice(0);
configs.push({ name: '', directory: '' });
await this.preferenceService.set(CPP_BUILD_CONFIGURATIONS_PREFERENCE_KEY, configs, PreferenceScope.Workspace);
}
}
export const CPP_CATEGORY = 'C/C++';
/**
* Reset active build configuration if applicable.
* Set active build configuration to `None`.
*/
export const CPP_RESET_BUILD_CONFIGURATION: Command = {
id: 'cpp.resetBuildConfiguration',
category: CPP_CATEGORY,
label: 'Reset Build Configuration'
};
/**
* Create a new build configuration, and trigger opening the preferences widget.
*/
export const CPP_CREATE_NEW_BUILD_CONFIGURATION: Command = {
id: 'cpp.createNewBuildConfiguration',
category: CPP_CATEGORY,
label: 'Create New Build Configuration'
};
/**
* Open the quick open menu to let the user change the active build configuration.
*/
export const CPP_CHANGE_BUILD_CONFIGURATION: Command = {
id: 'cpp.change-build-configuration',
category: CPP_CATEGORY,
label: 'Change Build Configuration'
};
export class CppBuildConfigurationsContributions implements CommandContribution {
protected readonly cppChangeBuildConfiguration: CppBuildConfigurationChanger;
protected readonly cppManager: CppBuildConfigurationManager;
/**
* Register build configurations commands for C/C++.
* Available commands include:
* - Resetting C/C++ build configurations.
* - Creating new C/C++ build configurations.
* - Updating C/C++ build configurations.
* @param commands the command registry.
*/
registerCommands(commands: CommandRegistry): void {
commands.registerCommand(CPP_RESET_BUILD_CONFIGURATION, {
isEnabled: () => !!this.cppManager.getActiveConfig(),
isVisible: () => !!this.cppManager.getActiveConfig(),
execute: () => this.cppManager.setActiveConfig(undefined)
});
commands.registerCommand(CPP_CREATE_NEW_BUILD_CONFIGURATION, {
execute: () => this.cppChangeBuildConfiguration.createConfig()
});
commands.registerCommand(CPP_CHANGE_BUILD_CONFIGURATION, {
execute: () => this.cppChangeBuildConfiguration.change()
});
}
}