support debug & tasks writing (for #8937)

This commit is contained in:
Benjamin Pasero 2016-09-15 08:45:37 +02:00
parent a4a100213d
commit 91115aa714
4 changed files with 93 additions and 54 deletions

View file

@ -33,6 +33,6 @@ export class MainThreadConfiguration extends MainThreadConfigurationShape {
}
$updateConfigurationOption(target: ConfigurationTarget, key: string, value: any): TPromise<void> {
return this._configurationEditingService.writeConfiguration(target, [{ key, value }]);
return this._configurationEditingService.writeConfiguration(target, { key, value });
}
}

View file

@ -16,6 +16,11 @@ export enum ConfigurationEditingErrorCode {
*/
ERROR_UNKNOWN_KEY,
/**
* Error when trying to write to user target but not supported for provided key.
*/
ERROR_INVALID_TARGET,
/**
* Error when trying to write to the workspace configuration without having a workspace opened.
*/
@ -63,5 +68,5 @@ export interface IConfigurationEditingService {
* Allows to write to either the user or workspace configuration file. The returned promise will be
* in error state in any of the error cases from [ConfigurationEditingErrorCode](#ConfigurationEditingErrorCode)
*/
writeConfiguration(target: ConfigurationTarget, values: IConfigurationValue[]): TPromise<void>;
writeConfiguration(target: ConfigurationTarget, value: IConfigurationValue): TPromise<void>;
}

View file

@ -21,6 +21,16 @@ import {IConfigurationService} from 'vs/platform/configuration/common/configurat
import {WORKSPACE_CONFIG_DEFAULT_PATH} from 'vs/workbench/services/configuration/common/configuration';
import {IConfigurationEditingService, ConfigurationEditingErrorCode, IConfigurationEditingError, ConfigurationTarget, IConfigurationValue} from 'vs/workbench/services/configuration/common/configurationEditing';
export const WORKSPACE_STANDALONE_CONFIGURATIONS = {
'tasks': '.vscode/tasks.json',
'launch': '.vscode/launch.json'
};
interface IConfigurationEditOperation extends IConfigurationValue {
target: URI;
isWorkspaceStandalone?: boolean;
}
interface IValidationResult {
error?: ConfigurationEditingErrorCode;
exists?: boolean;
@ -39,16 +49,17 @@ export class ConfigurationEditingService implements IConfigurationEditingService
) {
}
public writeConfiguration(target: ConfigurationTarget, values: IConfigurationValue[]): TPromise<void> {
public writeConfiguration(target: ConfigurationTarget, value: IConfigurationValue): TPromise<void> {
const operation = this.getConfigurationEditOperation(target, value);
// First validate before making any edits
return this.validate(target, values).then(validation => {
return this.validate(target, operation).then(validation => {
if (typeof validation.error === 'number') {
return this.wrapError(validation.error);
}
// Create configuration file if missing
const resource = this.getConfigurationResource(target);
const resource = operation.target;
let ensureConfigurationFile = TPromise.as(null);
let contents: string;
if (!validation.exists) {
@ -61,7 +72,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService
return ensureConfigurationFile.then(() => {
// Apply all edits to the configuration file
const result = this.applyEdits(contents, values);
const result = this.applyEdits(contents, [operation]);
return pfs.writeFile(resource.fsPath, result, encoding.UTF8).then(() => {
@ -85,6 +96,7 @@ export class ConfigurationEditingService implements IConfigurationEditingService
private toErrorMessage(error: ConfigurationEditingErrorCode): string {
switch (error) {
case ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY: return nls.localize('errorUnknownKey', "Unable to write to the configuration file (Unknown Key)");
case ConfigurationEditingErrorCode.ERROR_INVALID_TARGET: return nls.localize('errorInvalidTarget', "Unable to write to the configuration file (Invalid Target)");
case ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED: return nls.localize('errorWorkspaceOpened', "Unable to write to the configuration file (No Workspace Opened)");
case ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION: return nls.localize('errorInvalidConfiguration', "Unable to write to the configuration file (Invalid Configuration Found)");
case ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY: return nls.localize('errorConfigurationFileDirty', "Unable to write to the configuration file (Configuration File Dirty)");
@ -105,27 +117,34 @@ export class ConfigurationEditingService implements IConfigurationEditingService
return content;
}
private validate(target: ConfigurationTarget, values: IConfigurationValue[]): TPromise<IValidationResult> {
private validate(target: ConfigurationTarget, operation: IConfigurationEditOperation): TPromise<IValidationResult> {
// 1.) Any key must be a known setting from the registry
const validKeys = getConfigurationKeys();
if (values.some(v => validKeys.indexOf(v.key) < 0)) {
return TPromise.as({ error: ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY });
// Any key must be a known setting from the registry (unless this is a standalone config)
if (!operation.isWorkspaceStandalone) {
const validKeys = getConfigurationKeys();
if (validKeys.indexOf(operation.key) < 0) {
return TPromise.as({ error: ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY });
}
}
// 2.) Target cannot be workspace if no workspace opened
// Target cannot be user if is standalone
if (operation.isWorkspaceStandalone && target === ConfigurationTarget.USER) {
return TPromise.as({ error: ConfigurationEditingErrorCode.ERROR_INVALID_TARGET });
}
// Target cannot be workspace if no workspace opened
if (target === ConfigurationTarget.WORKSPACE && !this.contextService.getWorkspace()) {
return TPromise.as({ error: ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED });
}
// 3.) Target cannot be dirty
const resource = this.getConfigurationResource(target);
// Target cannot be dirty
const resource = operation.target;
return this.editorService.createInput({ resource }).then(typedInput => {
if (typedInput.isDirty()) {
return { error: ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY };
}
// 4.) Target cannot contain JSON errors
// Target cannot contain JSON errors
return pfs.exists(resource.fsPath).then(exists => {
if (!exists) {
return { exists };
@ -146,11 +165,26 @@ export class ConfigurationEditingService implements IConfigurationEditingService
});
}
private getConfigurationResource(target: ConfigurationTarget): URI {
if (target === ConfigurationTarget.USER) {
return URI.file(this.environmentService.appSettingsPath);
private getConfigurationEditOperation(target: ConfigurationTarget, config: IConfigurationValue): IConfigurationEditOperation {
// Check for standalone workspace configurations
if (config.key) {
const standaloneConfigurationKeys = Object.keys(WORKSPACE_STANDALONE_CONFIGURATIONS);
for (let i = 0; i < standaloneConfigurationKeys.length; i++) {
const key = standaloneConfigurationKeys[i];
const keyPrefix = `${key}.`;
const target = this.contextService.toResource(WORKSPACE_STANDALONE_CONFIGURATIONS[key]);
if (config.key.indexOf(keyPrefix) === 0) {
return { key: config.key.substr(keyPrefix.length), value: config.value, target, isWorkspaceStandalone: true };
}
}
}
return this.contextService.toResource(WORKSPACE_CONFIG_DEFAULT_PATH);
if (target === ConfigurationTarget.USER) {
return { key: config.key, value: config.value, target: URI.file(this.environmentService.appSettingsPath) };
}
return { key: config.key, value: config.value, target: this.contextService.toResource(WORKSPACE_CONFIG_DEFAULT_PATH) };
}
}

View file

@ -21,7 +21,7 @@ import uuid = require('vs/base/common/uuid');
import {IConfigurationRegistry, Extensions as ConfigurationExtensions} from 'vs/platform/configuration/common/configurationRegistry';
import {WorkspaceConfigurationService} from 'vs/workbench/services/configuration/node/configurationService';
import URI from 'vs/base/common/uri';
import {ConfigurationEditingService} from 'vs/workbench/services/configuration/node/configurationEditingService';
import {ConfigurationEditingService, WORKSPACE_STANDALONE_CONFIGURATIONS} from 'vs/workbench/services/configuration/node/configurationEditingService';
import {ConfigurationTarget, IConfigurationEditingError, ConfigurationEditingErrorCode} from 'vs/workbench/services/configuration/common/configurationEditing';
import {IResourceInput} from 'vs/platform/editor/common/editor';
@ -111,7 +111,7 @@ suite('WorkspaceConfigurationEditingService - Node', () => {
test('errors cases - invalid key', (done: () => void) => {
createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => {
return createServices(workspaceDir, globalSettingsFile, false, true /* no workspace */).then(services => {
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, [{ key: 'unknown.key', value: 'value' }]).then(res => {
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, { key: 'unknown.key', value: 'value' }).then(res => {
}, (error:IConfigurationEditingError) => {
assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_UNKNOWN_KEY);
services.configurationService.dispose();
@ -121,10 +121,23 @@ suite('WorkspaceConfigurationEditingService - Node', () => {
});
});
test('errors cases - invalid target', (done: () => void) => {
createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => {
return createServices(workspaceDir, globalSettingsFile).then(services => {
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: 'tasks.something', value: 'value' }).then(res => {
}, (error:IConfigurationEditingError) => {
assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_INVALID_TARGET);
services.configurationService.dispose();
cleanUp(done);
});
});
});
});
test('errors cases - no workspace', (done: () => void) => {
createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => {
return createServices(workspaceDir, globalSettingsFile, false, true /* no workspace */).then(services => {
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, [{ key: 'configurationEditing.service.testSetting', value: 'value' }]).then(res => {
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, { key: 'configurationEditing.service.testSetting', value: 'value' }).then(res => {
}, (error: IConfigurationEditingError) => {
assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_NO_WORKSPACE_OPENED);
services.configurationService.dispose();
@ -139,7 +152,7 @@ suite('WorkspaceConfigurationEditingService - Node', () => {
return createServices(workspaceDir, globalSettingsFile).then(services => {
fs.writeFileSync(globalSettingsFile, ',,,,,,,,,,,,,,');
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, [{ key: 'configurationEditing.service.testSetting', value: 'value' }]).then(res => {
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: 'configurationEditing.service.testSetting', value: 'value' }).then(res => {
}, (error: IConfigurationEditingError) => {
assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_INVALID_CONFIGURATION);
services.configurationService.dispose();
@ -152,7 +165,7 @@ suite('WorkspaceConfigurationEditingService - Node', () => {
test('errors cases - dirty', (done: () => void) => {
createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => {
return createServices(workspaceDir, globalSettingsFile, true).then(services => {
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, [{ key: 'configurationEditing.service.testSetting', value: 'value' }]).then(res => {
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: 'configurationEditing.service.testSetting', value: 'value' }).then(res => {
}, (error: IConfigurationEditingError) => {
assert.equal(error.code, ConfigurationEditingErrorCode.ERROR_CONFIGURATION_FILE_DIRTY);
services.configurationService.dispose();
@ -165,7 +178,7 @@ suite('WorkspaceConfigurationEditingService - Node', () => {
test('write one setting - empty file', (done: () => void) => {
createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => {
return createServices(workspaceDir, globalSettingsFile).then(services => {
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, [{ key: 'configurationEditing.service.testSetting', value: 'value' }]).then(res => {
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: 'configurationEditing.service.testSetting', value: 'value' }).then(res => {
const contents = fs.readFileSync(globalSettingsFile).toString('utf8');
const parsed = json.parse(contents);
assert.equal(parsed['configurationEditing.service.testSetting'], 'value');
@ -183,7 +196,7 @@ suite('WorkspaceConfigurationEditingService - Node', () => {
return createServices(workspaceDir, globalSettingsFile).then(services => {
fs.writeFileSync(globalSettingsFile, '{ "my.super.setting": "my.super.value" }');
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, [{ key: 'configurationEditing.service.testSetting', value: 'value' }]).then(res => {
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, { key: 'configurationEditing.service.testSetting', value: 'value' }).then(res => {
const contents = fs.readFileSync(globalSettingsFile).toString('utf8');
const parsed = json.parse(contents);
assert.equal(parsed['configurationEditing.service.testSetting'], 'value');
@ -199,22 +212,15 @@ suite('WorkspaceConfigurationEditingService - Node', () => {
});
});
test('write multiple settings - empty file', (done: () => void) => {
test('write workspace standalone setting - empty file', (done: () => void) => {
createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => {
return createServices(workspaceDir, globalSettingsFile).then(services => {
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, [
{ key: 'configurationEditing.service.testSetting', value: 'value' },
{ key: 'configurationEditing.service.testSettingTwo', value: { complex: { value: true } } },
{ key: 'configurationEditing.service.testSettingThree', value: 55 }
]).then(res => {
const contents = fs.readFileSync(globalSettingsFile).toString('utf8');
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, { key: 'tasks.service.testSetting', value: 'value' }).then(res => {
const target = path.join(workspaceDir, WORKSPACE_STANDALONE_CONFIGURATIONS['tasks']);
const contents = fs.readFileSync(target).toString('utf8');
const parsed = json.parse(contents);
assert.equal(parsed['configurationEditing.service.testSetting'], 'value');
assert.equal(parsed['configurationEditing.service.testSettingTwo'].complex.value, true);
assert.equal(parsed['configurationEditing.service.testSettingThree'], 55);
assert.equal(services.configurationService.lookup('configurationEditing.service.testSetting').value, 'value');
assert.equal(services.configurationService.lookup('configurationEditing.service.testSettingThree').value, 55);
assert.equal(parsed['service.testSetting'], 'value');
assert.equal(services.configurationService.lookup('tasks.service.testSetting').value, 'value');
services.configurationService.dispose();
cleanUp(done);
@ -223,27 +229,21 @@ suite('WorkspaceConfigurationEditingService - Node', () => {
});
});
test('write multiple settings - existing file', (done: () => void) => {
test('write workspace standalone setting - existing file', (done: () => void) => {
createWorkspace((workspaceDir, globalSettingsFile, cleanUp) => {
return createServices(workspaceDir, globalSettingsFile).then(services => {
fs.writeFileSync(globalSettingsFile, '// some comment from me\n{ "my.super.setting": "my.super.value" }\n\n// more comments');
const target = path.join(workspaceDir, WORKSPACE_STANDALONE_CONFIGURATIONS['launch']);
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.USER, [
{ key: 'configurationEditing.service.testSetting', value: 'value' },
{ key: 'configurationEditing.service.testSettingTwo', value: { complex: { value: true } } },
{ key: 'configurationEditing.service.testSettingThree', value: 55 }
]).then(res => {
const contents = fs.readFileSync(globalSettingsFile).toString('utf8');
fs.writeFileSync(target, '{ "my.super.setting": "my.super.value" }');
return services.configurationEditingService.writeConfiguration(ConfigurationTarget.WORKSPACE, { key: 'launch.service.testSetting', value: 'value' }).then(res => {
const contents = fs.readFileSync(target).toString('utf8');
const parsed = json.parse(contents);
assert.equal(parsed['configurationEditing.service.testSetting'], 'value');
assert.equal(parsed['configurationEditing.service.testSettingTwo'].complex.value, true);
assert.equal(parsed['configurationEditing.service.testSettingThree'], 55);
assert.equal(parsed['service.testSetting'], 'value');
assert.equal(parsed['my.super.setting'], 'my.super.value');
assert.equal(services.configurationService.lookup('my.super.setting').value, 'my.super.value');
assert.equal(services.configurationService.lookup('configurationEditing.service.testSetting').value, 'value');
assert.equal(services.configurationService.lookup('configurationEditing.service.testSettingThree').value, 55);
assert.equal(services.configurationService.lookup('launch.service.testSetting').value, 'value');
assert.equal(services.configurationService.lookup('launch.my.super.setting').value, 'my.super.value');
services.configurationService.dispose();
cleanUp(done);