mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-03-22 04:40:30 -04:00
Merge from master
This commit is contained in:
@@ -3,40 +3,39 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as network from 'vs/base/common/network';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import * as nls from 'vs/nls';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { EditorInput, IEditor } from 'vs/workbench/common/editor';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IPreferencesService, IPreferencesEditorModel, ISetting, getSettingsTargetName, FOLDER_SETTINGS_PATH, DEFAULT_SETTINGS_EDITOR_SETTING } from 'vs/workbench/services/preferences/common/preferences';
|
||||
import { SettingsEditorModel, DefaultSettingsEditorModel, DefaultKeybindingsEditorModel, defaultKeybindingsContents, DefaultSettings, WorkspaceConfigurationEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { DefaultPreferencesEditorInput, PreferencesEditorInput, KeybindingsEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { Position, IPosition } from 'vs/editor/common/core/position';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { parse } from 'vs/base/common/json';
|
||||
import { ICodeEditor, getCodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import * as network from 'vs/base/common/network';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { getCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { EditOperation } from 'vs/editor/common/core/editOperation';
|
||||
import { IPosition, Position } from 'vs/editor/common/core/position';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import * as nls from 'vs/nls';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { EditorInput, IEditor } from 'vs/workbench/common/editor';
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroup, IEditorGroupsService, GroupDirection } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { IUriDisplayService } from 'vs/platform/uriDisplay/common/uriDisplay';
|
||||
import { GroupDirection, IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { DEFAULT_SETTINGS_EDITOR_SETTING, FOLDER_SETTINGS_PATH, getSettingsTargetName, IPreferencesEditorModel, IPreferencesService, ISetting, ISettingsEditorOptions, SettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences';
|
||||
import { DefaultPreferencesEditorInput, KeybindingsEditorInput, PreferencesEditorInput, SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput';
|
||||
import { defaultKeybindingsContents, DefaultKeybindingsEditorModel, DefaultSettings, DefaultSettingsEditorModel, Settings2EditorModel, SettingsEditorModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels';
|
||||
|
||||
const emptyEditableSettingsContent = '{\n}';
|
||||
|
||||
@@ -44,7 +43,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
private lastOpenedSettingsInput: PreferencesEditorInput = null;
|
||||
private lastOpenedSettingsInput: PreferencesEditorInput | null = null;
|
||||
|
||||
private readonly _onDispose: Emitter<void> = new Emitter<void>();
|
||||
|
||||
@@ -70,7 +69,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
@IModelService private modelService: IModelService,
|
||||
@IJSONEditingService private jsonEditingService: IJSONEditingService,
|
||||
@IModeService private modeService: IModeService,
|
||||
@IUriDisplayService private uriDisplayService: IUriDisplayService
|
||||
@ILabelService private labelService: ILabelService
|
||||
) {
|
||||
super();
|
||||
// The default keybindings.json updates based on keyboard layouts, so here we make sure
|
||||
@@ -96,16 +95,20 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
return this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE);
|
||||
}
|
||||
|
||||
get settingsEditor2Input(): SettingsEditor2Input {
|
||||
return this.instantiationService.createInstance(SettingsEditor2Input);
|
||||
}
|
||||
|
||||
getFolderSettingsResource(resource: URI): URI {
|
||||
return this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, resource);
|
||||
}
|
||||
|
||||
resolveModel(uri: URI): TPromise<ITextModel> {
|
||||
resolveModel(uri: URI): Thenable<ITextModel> {
|
||||
if (this.isDefaultSettingsResource(uri)) {
|
||||
|
||||
const target = this.getConfigurationTargetFromDefaultSettingsResource(uri);
|
||||
const mode = this.modeService.getOrCreateMode('jsonc');
|
||||
const model = this._register(this.modelService.createModel('', mode, uri));
|
||||
const languageSelection = this.modeService.create('jsonc');
|
||||
const model = this._register(this.modelService.createModel('', languageSelection, uri));
|
||||
|
||||
let defaultSettings: DefaultSettings;
|
||||
this.configurationService.onDidChangeConfiguration(e => {
|
||||
@@ -116,7 +119,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
return;
|
||||
}
|
||||
defaultSettings = this.getDefaultSettings(target);
|
||||
this.modelService.updateModel(model, defaultSettings.parse());
|
||||
this.modelService.updateModel(model, defaultSettings.getContent(true));
|
||||
defaultSettings._onDidChange.fire();
|
||||
}
|
||||
});
|
||||
@@ -124,30 +127,30 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
// Check if Default settings is already created and updated in above promise
|
||||
if (!defaultSettings) {
|
||||
defaultSettings = this.getDefaultSettings(target);
|
||||
this.modelService.updateModel(model, defaultSettings.parse());
|
||||
this.modelService.updateModel(model, defaultSettings.getContent(true));
|
||||
}
|
||||
|
||||
return TPromise.as(model);
|
||||
return Promise.resolve(model);
|
||||
}
|
||||
|
||||
if (this.defaultSettingsRawResource.toString() === uri.toString()) {
|
||||
let defaultSettings: DefaultSettings = this.getDefaultSettings(ConfigurationTarget.USER);
|
||||
const mode = this.modeService.getOrCreateMode('jsonc');
|
||||
const model = this._register(this.modelService.createModel(defaultSettings.raw, mode, uri));
|
||||
return TPromise.as(model);
|
||||
const languageSelection = this.modeService.create('jsonc');
|
||||
const model = this._register(this.modelService.createModel(defaultSettings.raw, languageSelection, uri));
|
||||
return Promise.resolve(model);
|
||||
}
|
||||
|
||||
if (this.defaultKeybindingsResource.toString() === uri.toString()) {
|
||||
const defaultKeybindingsEditorModel = this.instantiationService.createInstance(DefaultKeybindingsEditorModel, uri);
|
||||
const mode = this.modeService.getOrCreateMode('jsonc');
|
||||
const model = this._register(this.modelService.createModel(defaultKeybindingsEditorModel.content, mode, uri));
|
||||
return TPromise.as(model);
|
||||
const languageSelection = this.modeService.create('jsonc');
|
||||
const model = this._register(this.modelService.createModel(defaultKeybindingsEditorModel.content, languageSelection, uri));
|
||||
return Promise.resolve(model);
|
||||
}
|
||||
|
||||
return TPromise.as(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
createPreferencesEditorModel(uri: URI): TPromise<IPreferencesEditorModel<any>> {
|
||||
createPreferencesEditorModel(uri: URI): Thenable<IPreferencesEditorModel<any>> {
|
||||
if (this.isDefaultSettingsResource(uri)) {
|
||||
return this.createDefaultSettingsEditorModel(uri);
|
||||
}
|
||||
@@ -165,45 +168,78 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
return this.createEditableSettingsEditorModel(ConfigurationTarget.WORKSPACE_FOLDER, uri);
|
||||
}
|
||||
|
||||
return TPromise.wrap<IPreferencesEditorModel<any>>(null);
|
||||
return Promise.resolve<IPreferencesEditorModel<any>>(null);
|
||||
}
|
||||
|
||||
openRawDefaultSettings(): TPromise<IEditor> {
|
||||
openRawDefaultSettings(): Thenable<IEditor> {
|
||||
return this.editorService.openEditor({ resource: this.defaultSettingsRawResource });
|
||||
}
|
||||
|
||||
openRawUserSettings(): TPromise<IEditor> {
|
||||
openRawUserSettings(): Thenable<IEditor> {
|
||||
return this.editorService.openEditor({ resource: this.userSettingsResource });
|
||||
}
|
||||
|
||||
openSettings(): TPromise<IEditor> {
|
||||
openSettings(jsonEditor?: boolean): Thenable<IEditor> {
|
||||
jsonEditor = typeof jsonEditor === 'undefined' ?
|
||||
this.configurationService.getValue('workbench.settings.editor') === 'json' :
|
||||
jsonEditor;
|
||||
|
||||
if (!jsonEditor) {
|
||||
return this.openSettings2();
|
||||
}
|
||||
|
||||
const editorInput = this.getActiveSettingsEditorInput() || this.lastOpenedSettingsInput;
|
||||
const resource = editorInput ? editorInput.master.getResource() : this.userSettingsResource;
|
||||
const target = this.getConfigurationTargetFromSettingsResource(resource);
|
||||
return this.openOrSwitchSettings(target, resource);
|
||||
}
|
||||
|
||||
openGlobalSettings(options?: IEditorOptions, group?: IEditorGroup): TPromise<IEditor> {
|
||||
return this.openOrSwitchSettings(ConfigurationTarget.USER, this.userSettingsResource, options, group);
|
||||
private openSettings2(): Thenable<IEditor> {
|
||||
const input = this.settingsEditor2Input;
|
||||
return this.editorGroupService.activeGroup.openEditor(input)
|
||||
.then(() => this.editorGroupService.activeGroup.activeControl);
|
||||
}
|
||||
|
||||
openSettings2(): TPromise<IEditor> {
|
||||
return this.editorService.openEditor(this.instantiationService.createInstance(SettingsEditor2Input), { pinned: true }).then(() => null);
|
||||
openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable<IEditor> {
|
||||
jsonEditor = typeof jsonEditor === 'undefined' ?
|
||||
this.configurationService.getValue('workbench.settings.editor') === 'json' :
|
||||
jsonEditor;
|
||||
|
||||
return jsonEditor ?
|
||||
this.openOrSwitchSettings(ConfigurationTarget.USER, this.userSettingsResource, options, group) :
|
||||
this.openOrSwitchSettings2(ConfigurationTarget.USER, undefined, options, group);
|
||||
}
|
||||
|
||||
openWorkspaceSettings(options?: IEditorOptions, group?: IEditorGroup): TPromise<IEditor> {
|
||||
openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable<IEditor> {
|
||||
jsonEditor = typeof jsonEditor === 'undefined' ?
|
||||
this.configurationService.getValue('workbench.settings.editor') === 'json' :
|
||||
jsonEditor;
|
||||
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) {
|
||||
this.notificationService.info(nls.localize('openFolderFirst', "Open a folder first to create workspace settings"));
|
||||
return TPromise.as(null);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
return this.openOrSwitchSettings(ConfigurationTarget.WORKSPACE, this.workspaceSettingsResource, options, group);
|
||||
|
||||
return jsonEditor ?
|
||||
this.openOrSwitchSettings(ConfigurationTarget.WORKSPACE, this.workspaceSettingsResource, options, group) :
|
||||
this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE, undefined, options, group);
|
||||
}
|
||||
|
||||
openFolderSettings(folder: URI, options?: IEditorOptions, group?: IEditorGroup): TPromise<IEditor> {
|
||||
return this.openOrSwitchSettings(ConfigurationTarget.WORKSPACE_FOLDER, this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, folder), options, group);
|
||||
openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable<IEditor> {
|
||||
jsonEditor = typeof jsonEditor === 'undefined' ?
|
||||
this.configurationService.getValue('workbench.settings.editor') === 'json' :
|
||||
jsonEditor;
|
||||
|
||||
return jsonEditor ?
|
||||
this.openOrSwitchSettings(ConfigurationTarget.WORKSPACE_FOLDER, this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE_FOLDER, folder), options, group) :
|
||||
this.openOrSwitchSettings2(ConfigurationTarget.WORKSPACE_FOLDER, folder, options, group);
|
||||
}
|
||||
|
||||
switchSettings(target: ConfigurationTarget, resource: URI): TPromise<void> {
|
||||
switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Thenable<void> {
|
||||
if (!jsonEditor) {
|
||||
return this.doOpenSettings2(target, resource).then(() => null);
|
||||
}
|
||||
|
||||
const activeControl = this.editorService.activeControl;
|
||||
if (activeControl && activeControl.input instanceof PreferencesEditorInput) {
|
||||
return this.doSwitchSettings(target, resource, activeControl.input, activeControl.group).then(() => null);
|
||||
@@ -212,7 +248,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
}
|
||||
}
|
||||
|
||||
openGlobalKeybindingSettings(textual: boolean): TPromise<void> {
|
||||
openGlobalKeybindingSettings(textual: boolean): Thenable<void> {
|
||||
/* __GDPR__
|
||||
"openKeybindings" : {
|
||||
"textual" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }
|
||||
@@ -220,7 +256,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
*/
|
||||
this.telemetryService.publicLog('openKeybindings', { textual });
|
||||
if (textual) {
|
||||
const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to overwrite the defaults") + '\n[\n]';
|
||||
const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to override the defaults") + '\n[\n]';
|
||||
const editableKeybindings = URI.file(this.environmentService.appKeybindingsPath);
|
||||
const openDefaultKeybindings = !!this.configurationService.getValue('workbench.settings.openDefaultKeybindings');
|
||||
|
||||
@@ -229,33 +265,34 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
if (openDefaultKeybindings) {
|
||||
const activeEditorGroup = this.editorGroupService.activeGroup;
|
||||
const sideEditorGroup = this.editorGroupService.addGroup(activeEditorGroup.id, GroupDirection.RIGHT);
|
||||
return TPromise.join([
|
||||
this.editorService.openEditor({ resource: this.defaultKeybindingsResource, options: { pinned: true, preserveFocus: true }, label: nls.localize('defaultKeybindings', "Default Keybindings"), description: '' }),
|
||||
this.editorService.openEditor({ resource: editableKeybindings, options: { pinned: true } }, sideEditorGroup.id)
|
||||
return Promise.all([
|
||||
this.editorService.openEditor({ resource: this.defaultKeybindingsResource, options: { pinned: true, preserveFocus: true, revealIfOpened: true }, label: nls.localize('defaultKeybindings', "Default Keybindings"), description: '' }),
|
||||
this.editorService.openEditor({ resource: editableKeybindings, options: { pinned: true, revealIfOpened: true } }, sideEditorGroup.id)
|
||||
]).then(editors => void 0);
|
||||
} else {
|
||||
return this.editorService.openEditor({ resource: editableKeybindings, options: { pinned: true } }).then(() => void 0);
|
||||
return this.editorService.openEditor({ resource: editableKeybindings, options: { pinned: true, revealIfOpened: true } }).then(() => void 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { pinned: true }).then(() => null);
|
||||
return this.editorService.openEditor(this.instantiationService.createInstance(KeybindingsEditorInput), { pinned: true, revealIfOpened: true }).then(() => null);
|
||||
}
|
||||
|
||||
openDefaultKeybindingsFile(): TPromise<IEditor> {
|
||||
return this.editorService.openEditor({ resource: this.defaultKeybindingsResource });
|
||||
openDefaultKeybindingsFile(): Thenable<IEditor> {
|
||||
return this.editorService.openEditor({ resource: this.defaultKeybindingsResource, label: nls.localize('defaultKeybindings', "Default Keybindings") });
|
||||
}
|
||||
|
||||
configureSettingsForLanguage(language: string): void {
|
||||
this.openGlobalSettings()
|
||||
this.openGlobalSettings(true)
|
||||
.then(editor => this.createPreferencesEditorModel(this.userSettingsResource)
|
||||
.then((settingsModel: IPreferencesEditorModel<ISetting>) => {
|
||||
const codeEditor = getCodeEditor(editor.getControl());
|
||||
if (codeEditor) {
|
||||
this.getPosition(language, settingsModel, codeEditor)
|
||||
this.addLanguageOverrideEntry(language, settingsModel, codeEditor)
|
||||
.then(position => {
|
||||
if (codeEditor) {
|
||||
codeEditor.setPosition(position);
|
||||
codeEditor.revealLine(position.lineNumber);
|
||||
codeEditor.focus();
|
||||
}
|
||||
});
|
||||
@@ -263,15 +300,19 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
}));
|
||||
}
|
||||
|
||||
private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: IEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): TPromise<IEditor> {
|
||||
private openOrSwitchSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Thenable<IEditor> {
|
||||
const editorInput = this.getActiveSettingsEditorInput(group);
|
||||
if (editorInput && editorInput.master.getResource().fsPath !== resource.fsPath) {
|
||||
return this.doSwitchSettings(configurationTarget, resource, editorInput, group);
|
||||
return this.doSwitchSettings(configurationTarget, resource, editorInput, group, options);
|
||||
}
|
||||
return this.doOpenSettings(configurationTarget, resource, options, group);
|
||||
}
|
||||
|
||||
private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: IEditorOptions, group?: IEditorGroup): TPromise<IEditor> {
|
||||
private openOrSwitchSettings2(configurationTarget: ConfigurationTarget, folderUri?: URI, options?: ISettingsEditorOptions, group: IEditorGroup = this.editorGroupService.activeGroup): Thenable<IEditor> {
|
||||
return this.doOpenSettings2(configurationTarget, folderUri, options, group);
|
||||
}
|
||||
|
||||
private doOpenSettings(configurationTarget: ConfigurationTarget, resource: URI, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable<IEditor> {
|
||||
const openDefaultSettings = !!this.configurationService.getValue(DEFAULT_SETTINGS_EDITOR_SETTING);
|
||||
return this.getOrCreateEditableSettingsEditorInput(configurationTarget, resource)
|
||||
.then(editableSettingsEditorInput => {
|
||||
@@ -285,13 +326,28 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
const defaultPreferencesEditorInput = this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.getDefaultSettingsResource(configurationTarget));
|
||||
const preferencesEditorInput = new PreferencesEditorInput(this.getPreferencesEditorInputName(configurationTarget, resource), editableSettingsEditorInput.getDescription(), defaultPreferencesEditorInput, <EditorInput>editableSettingsEditorInput);
|
||||
this.lastOpenedSettingsInput = preferencesEditorInput;
|
||||
return this.editorService.openEditor(preferencesEditorInput, options, group);
|
||||
return this.editorService.openEditor(preferencesEditorInput, SettingsEditorOptions.create(options), group);
|
||||
}
|
||||
return this.editorService.openEditor(editableSettingsEditorInput, options, group);
|
||||
return this.editorService.openEditor(editableSettingsEditorInput, SettingsEditorOptions.create(options), group);
|
||||
});
|
||||
}
|
||||
|
||||
private doSwitchSettings(target: ConfigurationTarget, resource: URI, input: PreferencesEditorInput, group: IEditorGroup): TPromise<IEditor> {
|
||||
public createSettings2EditorModel(): Settings2EditorModel {
|
||||
return this.instantiationService.createInstance(Settings2EditorModel, this.getDefaultSettings(ConfigurationTarget.USER));
|
||||
}
|
||||
|
||||
private doOpenSettings2(target: ConfigurationTarget, folderUri: URI | undefined, options?: IEditorOptions, group?: IEditorGroup): Thenable<IEditor> {
|
||||
const input = this.settingsEditor2Input;
|
||||
const settingsOptions: ISettingsEditorOptions = {
|
||||
...options,
|
||||
target,
|
||||
folderUri
|
||||
};
|
||||
|
||||
return this.editorService.openEditor(input, SettingsEditorOptions.create(settingsOptions), group);
|
||||
}
|
||||
|
||||
private doSwitchSettings(target: ConfigurationTarget, resource: URI, input: PreferencesEditorInput, group: IEditorGroup, options?: ISettingsEditorOptions): Thenable<IEditor> {
|
||||
return this.getOrCreateEditableSettingsEditorInput(target, this.getEditableSettingsURI(target, resource))
|
||||
.then(toInput => {
|
||||
return group.openEditor(input).then(() => {
|
||||
@@ -299,7 +355,8 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
|
||||
return group.replaceEditors([{
|
||||
editor: input,
|
||||
replacement: replaceWith
|
||||
replacement: replaceWith,
|
||||
options: SettingsEditorOptions.create(options)
|
||||
}]).then(() => {
|
||||
this.lastOpenedSettingsInput = replaceWith;
|
||||
return group.activeControl;
|
||||
@@ -365,12 +422,12 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
return target === ConfigurationTarget.WORKSPACE_FOLDER ? nls.localize('folderSettingsName', "{0} (Folder Settings)", name) : name;
|
||||
}
|
||||
|
||||
private getOrCreateEditableSettingsEditorInput(target: ConfigurationTarget, resource: URI): TPromise<EditorInput> {
|
||||
private getOrCreateEditableSettingsEditorInput(target: ConfigurationTarget, resource: URI): Thenable<EditorInput> {
|
||||
return this.createSettingsIfNotExists(target, resource)
|
||||
.then(() => <EditorInput>this.editorService.createInput({ resource }));
|
||||
}
|
||||
|
||||
private createEditableSettingsEditorModel(configurationTarget: ConfigurationTarget, resource: URI): TPromise<SettingsEditorModel> {
|
||||
private createEditableSettingsEditorModel(configurationTarget: ConfigurationTarget, resource: URI): Thenable<SettingsEditorModel> {
|
||||
const settingsUri = this.getEditableSettingsURI(configurationTarget, resource);
|
||||
if (settingsUri) {
|
||||
const workspace = this.contextService.getWorkspace();
|
||||
@@ -381,10 +438,10 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
return this.textModelResolverService.createModelReference(settingsUri)
|
||||
.then(reference => this.instantiationService.createInstance(SettingsEditorModel, reference, configurationTarget));
|
||||
}
|
||||
return TPromise.wrap<SettingsEditorModel>(null);
|
||||
return Promise.resolve<SettingsEditorModel>(null);
|
||||
}
|
||||
|
||||
private createDefaultSettingsEditorModel(defaultSettingsUri: URI): TPromise<DefaultSettingsEditorModel> {
|
||||
private createDefaultSettingsEditorModel(defaultSettingsUri: URI): Thenable<DefaultSettingsEditorModel> {
|
||||
return this.textModelResolverService.createModelReference(defaultSettingsUri)
|
||||
.then(reference => {
|
||||
const target = this.getConfigurationTargetFromDefaultSettingsResource(defaultSettingsUri);
|
||||
@@ -428,9 +485,14 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
return null;
|
||||
}
|
||||
|
||||
private createSettingsIfNotExists(target: ConfigurationTarget, resource: URI): TPromise<void> {
|
||||
private createSettingsIfNotExists(target: ConfigurationTarget, resource: URI): Thenable<void> {
|
||||
if (this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE && target === ConfigurationTarget.WORKSPACE) {
|
||||
return this.fileService.resolveContent(this.contextService.getWorkspace().configuration)
|
||||
const workspaceConfig = this.contextService.getWorkspace().configuration;
|
||||
if (!workspaceConfig) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
return this.fileService.resolveContent(workspaceConfig)
|
||||
.then(content => {
|
||||
if (Object.keys(parse(content.value)).indexOf('settings') === -1) {
|
||||
return this.jsonEditingService.write(resource, { key: 'settings', value: {} }, true).then(null, () => { });
|
||||
@@ -441,15 +503,15 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
return this.createIfNotExists(resource, emptyEditableSettingsContent).then(() => { });
|
||||
}
|
||||
|
||||
private createIfNotExists(resource: URI, contents: string): TPromise<any> {
|
||||
private createIfNotExists(resource: URI, contents: string): Thenable<any> {
|
||||
return this.fileService.resolveContent(resource, { acceptTextOnly: true }).then(null, error => {
|
||||
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) {
|
||||
return this.fileService.updateContent(resource, contents).then(null, error => {
|
||||
return TPromise.wrapError(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", this.uriDisplayService.getLabel(resource, true), error)));
|
||||
return Promise.reject(new Error(nls.localize('fail.createSettings', "Unable to create '{0}' ({1}).", this.labelService.getUriLabel(resource, { relative: true }), error)));
|
||||
});
|
||||
}
|
||||
|
||||
return TPromise.wrapError(error);
|
||||
return Promise.reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -469,26 +531,18 @@ export class PreferencesService extends Disposable implements IPreferencesServic
|
||||
];
|
||||
}
|
||||
|
||||
private getPosition(language: string, settingsModel: IPreferencesEditorModel<ISetting>, codeEditor: ICodeEditor): TPromise<IPosition> {
|
||||
private addLanguageOverrideEntry(language: string, settingsModel: IPreferencesEditorModel<ISetting>, codeEditor: ICodeEditor): Thenable<IPosition> {
|
||||
const languageKey = `[${language}]`;
|
||||
let setting = settingsModel.getPreference(languageKey);
|
||||
const model = codeEditor.getModel();
|
||||
const configuration = this.configurationService.getValue<{ editor: { tabSize: number; insertSpaces: boolean }, files: { eol: string } }>();
|
||||
const eol = configuration.files && configuration.files.eol;
|
||||
const configuration = this.configurationService.getValue<{ editor: { tabSize: number; insertSpaces: boolean } }>();
|
||||
const eol = model.getEOL();
|
||||
if (setting) {
|
||||
if (setting.overrides.length) {
|
||||
const lastSetting = setting.overrides[setting.overrides.length - 1];
|
||||
let content;
|
||||
if (lastSetting.valueRange.endLineNumber === setting.range.endLineNumber) {
|
||||
content = ',' + eol + this.spaces(2, configuration.editor) + eol + this.spaces(1, configuration.editor);
|
||||
} else {
|
||||
content = ',' + eol + this.spaces(2, configuration.editor);
|
||||
}
|
||||
const editOperation = EditOperation.insert(new Position(lastSetting.valueRange.endLineNumber, lastSetting.valueRange.endColumn), content);
|
||||
model.pushEditOperations([], [editOperation], () => []);
|
||||
return TPromise.as({ lineNumber: lastSetting.valueRange.endLineNumber + 1, column: model.getLineMaxColumn(lastSetting.valueRange.endLineNumber + 1) });
|
||||
return Promise.resolve({ lineNumber: lastSetting.valueRange.endLineNumber, column: model.getLineMaxColumn(lastSetting.valueRange.endLineNumber) });
|
||||
}
|
||||
return TPromise.as({ lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 });
|
||||
return Promise.resolve({ lineNumber: setting.valueRange.startLineNumber, column: setting.valueRange.startColumn + 1 });
|
||||
}
|
||||
return this.configurationService.updateValue(languageKey, {}, ConfigurationTarget.USER)
|
||||
.then(() => {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import { OperatingSystem, language, LANGUAGE_DEFAULT } from 'vs/base/common/platform';
|
||||
@@ -15,7 +14,6 @@ import { AriaLabelProvider, UserSettingsLabelProvider, UILabelProvider, Modifier
|
||||
import { MenuRegistry, ILocalizedString, ICommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { EditorModel } from 'vs/workbench/common/editor';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
|
||||
import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver';
|
||||
@@ -80,8 +78,7 @@ export class KeybindingsEditorModel extends EditorModel {
|
||||
|
||||
constructor(
|
||||
os: OperatingSystem,
|
||||
@IKeybindingService private keybindingsService: IKeybindingService,
|
||||
@IExtensionService private extensionService: IExtensionService
|
||||
@IKeybindingService private keybindingsService: IKeybindingService
|
||||
) {
|
||||
super();
|
||||
this.modifierLabels = {
|
||||
@@ -157,35 +154,32 @@ export class KeybindingsEditorModel extends EditorModel {
|
||||
}
|
||||
|
||||
private splitKeybindingWords(wordsSeparatedBySpaces: string[]): string[] {
|
||||
const result = [];
|
||||
const result: string[] = [];
|
||||
for (const word of wordsSeparatedBySpaces) {
|
||||
result.push(...word.split('+').filter(w => !!w));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public resolve(editorActionsLabels: { [id: string]: string; }): TPromise<EditorModel> {
|
||||
return this.extensionService.whenInstalledExtensionsRegistered()
|
||||
.then(() => {
|
||||
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
public resolve(editorActionsLabels: { [id: string]: string; }): Thenable<EditorModel> {
|
||||
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
|
||||
this._keybindingItemsSortedByPrecedence = [];
|
||||
const boundCommands: Map<string, boolean> = new Map<string, boolean>();
|
||||
for (const keybinding of this.keybindingsService.getKeybindings()) {
|
||||
if (keybinding.command) { // Skip keybindings without commands
|
||||
this._keybindingItemsSortedByPrecedence.push(KeybindingsEditorModel.toKeybindingEntry(keybinding.command, keybinding, workbenchActionsRegistry, editorActionsLabels));
|
||||
boundCommands.set(keybinding.command, true);
|
||||
}
|
||||
}
|
||||
this._keybindingItemsSortedByPrecedence = [];
|
||||
const boundCommands: Map<string, boolean> = new Map<string, boolean>();
|
||||
for (const keybinding of this.keybindingsService.getKeybindings()) {
|
||||
if (keybinding.command) { // Skip keybindings without commands
|
||||
this._keybindingItemsSortedByPrecedence.push(KeybindingsEditorModel.toKeybindingEntry(keybinding.command, keybinding, workbenchActionsRegistry, editorActionsLabels));
|
||||
boundCommands.set(keybinding.command, true);
|
||||
}
|
||||
}
|
||||
|
||||
const commandsWithDefaultKeybindings = this.keybindingsService.getDefaultKeybindings().map(keybinding => keybinding.command);
|
||||
for (const command of KeybindingResolver.getAllUnboundCommands(boundCommands)) {
|
||||
const keybindingItem = new ResolvedKeybindingItem(null, command, null, null, commandsWithDefaultKeybindings.indexOf(command) === -1);
|
||||
this._keybindingItemsSortedByPrecedence.push(KeybindingsEditorModel.toKeybindingEntry(command, keybindingItem, workbenchActionsRegistry, editorActionsLabels));
|
||||
}
|
||||
this._keybindingItems = this._keybindingItemsSortedByPrecedence.slice(0).sort((a, b) => KeybindingsEditorModel.compareKeybindingData(a, b));
|
||||
return this;
|
||||
});
|
||||
const commandsWithDefaultKeybindings = this.keybindingsService.getDefaultKeybindings().map(keybinding => keybinding.command);
|
||||
for (const command of KeybindingResolver.getAllUnboundCommands(boundCommands)) {
|
||||
const keybindingItem = new ResolvedKeybindingItem(null, command, null, null, commandsWithDefaultKeybindings.indexOf(command) === -1);
|
||||
this._keybindingItemsSortedByPrecedence.push(KeybindingsEditorModel.toKeybindingEntry(command, keybindingItem, workbenchActionsRegistry, editorActionsLabels));
|
||||
}
|
||||
this._keybindingItems = this._keybindingItemsSortedByPrecedence.slice(0).sort((a, b) => KeybindingsEditorModel.compareKeybindingData(a, b));
|
||||
return Promise.resolve(this);
|
||||
}
|
||||
|
||||
private static getId(keybindingItem: IKeybindingItem): string {
|
||||
@@ -254,12 +248,12 @@ export class KeybindingsEditorModel extends EditorModel {
|
||||
|
||||
class KeybindingItemMatches {
|
||||
|
||||
public readonly commandIdMatches: IMatch[] = null;
|
||||
public readonly commandLabelMatches: IMatch[] = null;
|
||||
public readonly commandDefaultLabelMatches: IMatch[] = null;
|
||||
public readonly sourceMatches: IMatch[] = null;
|
||||
public readonly whenMatches: IMatch[] = null;
|
||||
public readonly keybindingMatches: KeybindingMatches = null;
|
||||
public readonly commandIdMatches: IMatch[] | null = null;
|
||||
public readonly commandLabelMatches: IMatch[] | null = null;
|
||||
public readonly commandDefaultLabelMatches: IMatch[] | null = null;
|
||||
public readonly sourceMatches: IMatch[] | null = null;
|
||||
public readonly whenMatches: IMatch[] | null = null;
|
||||
public readonly keybindingMatches: KeybindingMatches | null = null;
|
||||
|
||||
constructor(private modifierLabels: ModifierLabels, keybindingItem: IKeybindingItem, searchValue: string, words: string[], keybindingWords: string[], completeMatch: boolean) {
|
||||
if (!completeMatch) {
|
||||
@@ -304,7 +298,7 @@ class KeybindingItemMatches {
|
||||
private matchesKeybinding(keybinding: ResolvedKeybinding, searchValue: string, words: string[], completeMatch: boolean): KeybindingMatches {
|
||||
const [firstPart, chordPart] = keybinding.getParts();
|
||||
|
||||
if (strings.compareIgnoreCase(searchValue, keybinding.getAriaLabel()) === 0 || strings.compareIgnoreCase(searchValue, keybinding.getLabel()) === 0) {
|
||||
if (strings.compareIgnoreCase(searchValue, keybinding.getUserSettingsLabel()) === 0 || strings.compareIgnoreCase(searchValue, keybinding.getAriaLabel()) === 0 || strings.compareIgnoreCase(searchValue, keybinding.getLabel()) === 0) {
|
||||
return {
|
||||
firstPart: this.createCompleteMatch(firstPart),
|
||||
chordPart: this.createCompleteMatch(chordPart)
|
||||
@@ -314,9 +308,9 @@ class KeybindingItemMatches {
|
||||
let firstPartMatch: KeybindingMatch = {};
|
||||
let chordPartMatch: KeybindingMatch = {};
|
||||
|
||||
const matchedWords = [];
|
||||
let firstPartMatchedWords = [];
|
||||
let chordPartMatchedWords = [];
|
||||
const matchedWords: number[] = [];
|
||||
let firstPartMatchedWords: number[] = [];
|
||||
let chordPartMatchedWords: number[] = [];
|
||||
let matchFirstPart = true;
|
||||
for (let index = 0; index < words.length; index++) {
|
||||
const word = words[index];
|
||||
|
||||
@@ -3,21 +3,35 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IEditor } from 'vs/workbench/common/editor';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { join } from 'vs/base/common/paths';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { join } from 'vs/base/common/paths';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IRange } from 'vs/editor/common/core/range';
|
||||
import { ITextModel } from 'vs/editor/common/model';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { EditorOptions, IEditor } from 'vs/workbench/common/editor';
|
||||
import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels';
|
||||
|
||||
export enum SettingValueType {
|
||||
Null = 'null',
|
||||
Enum = 'enum',
|
||||
String = 'string',
|
||||
Integer = 'integer',
|
||||
Number = 'number',
|
||||
Boolean = 'boolean',
|
||||
Exclude = 'exclude',
|
||||
Complex = 'complex',
|
||||
NullableInteger = 'nullable-integer',
|
||||
NullableNumber = 'nullable-number'
|
||||
}
|
||||
|
||||
export interface ISettingsGroup {
|
||||
id: string;
|
||||
@@ -41,15 +55,19 @@ export interface ISetting {
|
||||
value: any;
|
||||
valueRange: IRange;
|
||||
description: string[];
|
||||
descriptionIsMarkdown: boolean;
|
||||
descriptionRanges: IRange[];
|
||||
overrides?: ISetting[];
|
||||
overrideOf?: ISetting;
|
||||
deprecationMessage?: string;
|
||||
|
||||
// TODO@roblou maybe need new type and new EditorModel for GUI editor instead of ISetting which is used for text settings editor
|
||||
scope?: ConfigurationScope;
|
||||
type?: string | string[];
|
||||
enum?: string[];
|
||||
enumDescriptions?: string[];
|
||||
enumDescriptionsAreMarkdown?: boolean;
|
||||
tags?: string[];
|
||||
validator?: (value: any) => string;
|
||||
}
|
||||
|
||||
export interface IExtensionSetting extends ISetting {
|
||||
@@ -116,7 +134,7 @@ export interface IFilterMetadata {
|
||||
}
|
||||
|
||||
export interface IPreferencesEditorModel<T> {
|
||||
uri: URI;
|
||||
uri?: URI;
|
||||
getPreference(key: string): T;
|
||||
dispose(): void;
|
||||
}
|
||||
@@ -132,6 +150,45 @@ export interface ISettingsEditorModel extends IPreferencesEditorModel<ISetting>
|
||||
updateResultGroup(id: string, resultGroup: ISearchResultGroup): IFilterResult;
|
||||
}
|
||||
|
||||
export interface ISettingsEditorOptions extends IEditorOptions {
|
||||
target?: ConfigurationTarget;
|
||||
folderUri?: URI;
|
||||
query?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO Why do we need this class?
|
||||
*/
|
||||
export class SettingsEditorOptions extends EditorOptions implements ISettingsEditorOptions {
|
||||
|
||||
target?: ConfigurationTarget;
|
||||
folderUri?: URI;
|
||||
query?: string;
|
||||
|
||||
static create(settings: ISettingsEditorOptions): SettingsEditorOptions {
|
||||
if (!settings) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const options = new SettingsEditorOptions();
|
||||
|
||||
options.target = settings.target;
|
||||
options.folderUri = settings.folderUri;
|
||||
options.query = settings.query;
|
||||
|
||||
// IEditorOptions
|
||||
options.preserveFocus = settings.preserveFocus;
|
||||
options.forceReload = settings.forceReload;
|
||||
options.revealIfVisible = settings.revealIfVisible;
|
||||
options.revealIfOpened = settings.revealIfOpened;
|
||||
options.pinned = settings.pinned;
|
||||
options.index = settings.index;
|
||||
options.inactive = settings.inactive;
|
||||
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
export interface IKeybindingsEditorModel<T> extends IPreferencesEditorModel<T> {
|
||||
}
|
||||
|
||||
@@ -144,18 +201,18 @@ export interface IPreferencesService {
|
||||
workspaceSettingsResource: URI;
|
||||
getFolderSettingsResource(resource: URI): URI;
|
||||
|
||||
resolveModel(uri: URI): TPromise<ITextModel>;
|
||||
createPreferencesEditorModel<T>(uri: URI): TPromise<IPreferencesEditorModel<T>>;
|
||||
resolveModel(uri: URI): Thenable<ITextModel>;
|
||||
createPreferencesEditorModel<T>(uri: URI): Thenable<IPreferencesEditorModel<T>>;
|
||||
createSettings2EditorModel(): Settings2EditorModel; // TODO
|
||||
|
||||
openRawDefaultSettings(): TPromise<IEditor>;
|
||||
openSettings(): TPromise<IEditor>;
|
||||
openSettings2(): TPromise<IEditor>;
|
||||
openGlobalSettings(options?: IEditorOptions, group?: IEditorGroup): TPromise<IEditor>;
|
||||
openWorkspaceSettings(options?: IEditorOptions, group?: IEditorGroup): TPromise<IEditor>;
|
||||
openFolderSettings(folder: URI, options?: IEditorOptions, group?: IEditorGroup): TPromise<IEditor>;
|
||||
switchSettings(target: ConfigurationTarget, resource: URI): TPromise<void>;
|
||||
openGlobalKeybindingSettings(textual: boolean): TPromise<void>;
|
||||
openDefaultKeybindingsFile(): TPromise<IEditor>;
|
||||
openRawDefaultSettings(): Thenable<IEditor>;
|
||||
openSettings(jsonEditor?: boolean): Thenable<IEditor>;
|
||||
openGlobalSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable<IEditor>;
|
||||
openWorkspaceSettings(jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable<IEditor>;
|
||||
openFolderSettings(folder: URI, jsonEditor?: boolean, options?: ISettingsEditorOptions, group?: IEditorGroup): Thenable<IEditor>;
|
||||
switchSettings(target: ConfigurationTarget, resource: URI, jsonEditor?: boolean): Thenable<void>;
|
||||
openGlobalKeybindingSettings(textual: boolean): Thenable<void>;
|
||||
openDefaultKeybindingsFile(): Thenable<IEditor>;
|
||||
|
||||
configureSettingsForLanguage(language: string): void;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { OS } from 'vs/base/common/platform';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
import * as nls from 'vs/nls';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -13,8 +12,8 @@ import { EditorInput, SideBySideEditorInput, Verbosity } from 'vs/workbench/comm
|
||||
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
|
||||
import { IHashService } from 'vs/workbench/services/hash/common/hashService';
|
||||
import { KeybindingsEditorModel } from 'vs/workbench/services/preferences/common/keybindingsEditorModel';
|
||||
import { IPreferencesService } from './preferences';
|
||||
import { DefaultSettingsEditorModel } from './preferencesModels';
|
||||
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
|
||||
import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels';
|
||||
|
||||
export class PreferencesEditorInput extends SideBySideEditorInput {
|
||||
public static readonly ID: string = 'workbench.editorinputs.preferencesEditorInput';
|
||||
@@ -70,8 +69,8 @@ export class KeybindingsEditorInput extends EditorInput {
|
||||
return nls.localize('keybindingsInputName', "Keyboard Shortcuts");
|
||||
}
|
||||
|
||||
resolve(): TPromise<KeybindingsEditorModel> {
|
||||
return TPromise.as(this.keybindingsModel);
|
||||
resolve(): Thenable<KeybindingsEditorModel> {
|
||||
return Promise.resolve(this.keybindingsModel);
|
||||
}
|
||||
|
||||
matches(otherInput: any): boolean {
|
||||
@@ -82,11 +81,22 @@ export class KeybindingsEditorInput extends EditorInput {
|
||||
export class SettingsEditor2Input extends EditorInput {
|
||||
|
||||
public static readonly ID: string = 'workbench.input.settings2';
|
||||
private readonly _settingsModel: Settings2EditorModel;
|
||||
private resource: URI = URI.from({
|
||||
scheme: 'vscode-settings',
|
||||
path: `settingseditor`
|
||||
});
|
||||
|
||||
constructor(
|
||||
@IPreferencesService private preferencesService: IPreferencesService
|
||||
@IPreferencesService _preferencesService: IPreferencesService,
|
||||
) {
|
||||
super();
|
||||
|
||||
this._settingsModel = _preferencesService.createSettings2EditorModel();
|
||||
}
|
||||
|
||||
matches(otherInput: any): boolean {
|
||||
return otherInput instanceof SettingsEditor2Input;
|
||||
}
|
||||
|
||||
getTypeId(): string {
|
||||
@@ -94,14 +104,14 @@ export class SettingsEditor2Input extends EditorInput {
|
||||
}
|
||||
|
||||
getName(): string {
|
||||
return nls.localize('settingsEditor2InputName', "Settings (Preview)");
|
||||
return nls.localize('settingsEditor2InputName', "Settings");
|
||||
}
|
||||
|
||||
resolve(): TPromise<DefaultSettingsEditorModel> {
|
||||
return <TPromise<DefaultSettingsEditorModel>>this.preferencesService.createPreferencesEditorModel(URI.parse('vscode://defaultsettings/0/settings.json'));
|
||||
resolve(): Thenable<Settings2EditorModel> {
|
||||
return Promise.resolve(this._settingsModel);
|
||||
}
|
||||
|
||||
matches(otherInput: any): boolean {
|
||||
return otherInput instanceof SettingsEditor2Input;
|
||||
public getResource(): URI {
|
||||
return this.resource;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,20 +3,20 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { flatten, tail } from 'vs/base/common/arrays';
|
||||
import { flatten, tail, find } from 'vs/base/common/arrays';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { JSONVisitor, visit } from 'vs/base/common/json';
|
||||
import { Disposable, IReference } from 'vs/base/common/lifecycle';
|
||||
import * as map from 'vs/base/common/map';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import URI from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IRange, Range } from 'vs/editor/common/core/range';
|
||||
import { Selection } from 'vs/editor/common/core/selection';
|
||||
import { IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model';
|
||||
import { ITextEditorModel } from 'vs/editor/common/services/resolverService';
|
||||
import * as nls from 'vs/nls';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ConfigurationScope, Extensions, IConfigurationNode, IConfigurationPropertySchema, IConfigurationRegistry, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
@@ -204,11 +204,52 @@ export class SettingsEditorModel extends AbstractSettingsModel implements ISetti
|
||||
}
|
||||
}
|
||||
|
||||
export class Settings2EditorModel extends AbstractSettingsModel implements ISettingsEditorModel {
|
||||
private readonly _onDidChangeGroups: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChangeGroups: Event<void> = this._onDidChangeGroups.event;
|
||||
|
||||
private dirty = false;
|
||||
|
||||
constructor(
|
||||
private _defaultSettings: DefaultSettings,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
) {
|
||||
super();
|
||||
|
||||
configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.source === ConfigurationTarget.DEFAULT) {
|
||||
this.dirty = true;
|
||||
this._onDidChangeGroups.fire();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected get filterGroups(): ISettingsGroup[] {
|
||||
// Don't filter "commonly used"
|
||||
return this.settingsGroups.slice(1);
|
||||
}
|
||||
|
||||
public get settingsGroups(): ISettingsGroup[] {
|
||||
const groups = this._defaultSettings.getSettingsGroups(this.dirty);
|
||||
this.dirty = false;
|
||||
return groups;
|
||||
}
|
||||
|
||||
public findValueMatches(filter: string, setting: ISetting): IRange[] {
|
||||
// TODO @roblou
|
||||
return [];
|
||||
}
|
||||
|
||||
protected update(): IFilterResult {
|
||||
throw new Error('Not supported');
|
||||
}
|
||||
}
|
||||
|
||||
function parse(model: ITextModel, isSettingsProperty: (currentProperty: string, previousParents: string[]) => boolean): ISettingsGroup[] {
|
||||
const settings: ISetting[] = [];
|
||||
let overrideSetting: ISetting = null;
|
||||
let overrideSetting: ISetting | null = null;
|
||||
|
||||
let currentProperty: string = null;
|
||||
let currentProperty: string | null = null;
|
||||
let currentParent: any = [];
|
||||
let previousParents: any[] = [];
|
||||
let settingsPropertyIndex: number = -1;
|
||||
@@ -267,6 +308,7 @@ function parse(model: ITextModel, isSettingsProperty: (currentProperty: string,
|
||||
let settingStartPosition = model.getPositionAt(offset);
|
||||
const setting: ISetting = {
|
||||
description: [],
|
||||
descriptionIsMarkdown: false,
|
||||
key: name,
|
||||
keyRange: {
|
||||
startLineNumber: settingStartPosition.lineNumber,
|
||||
@@ -409,45 +451,53 @@ export class DefaultSettings extends Disposable {
|
||||
super();
|
||||
}
|
||||
|
||||
get content(): string {
|
||||
if (!this._content) {
|
||||
this.parse();
|
||||
getContent(forceUpdate = false): string {
|
||||
if (!this._content || forceUpdate) {
|
||||
this._content = this.toContent(true, this.getSettingsGroups(forceUpdate));
|
||||
}
|
||||
|
||||
return this._content;
|
||||
}
|
||||
|
||||
get settingsGroups(): ISettingsGroup[] {
|
||||
if (!this._allSettingsGroups) {
|
||||
this.parse();
|
||||
getSettingsGroups(forceUpdate = false): ISettingsGroup[] {
|
||||
if (!this._allSettingsGroups || forceUpdate) {
|
||||
this._allSettingsGroups = this.parse();
|
||||
}
|
||||
|
||||
return this._allSettingsGroups;
|
||||
}
|
||||
|
||||
parse(): string {
|
||||
private parse(): ISettingsGroup[] {
|
||||
const settingsGroups = this.getRegisteredGroups();
|
||||
this.initAllSettingsMap(settingsGroups);
|
||||
const mostCommonlyUsed = this.getMostCommonlyUsedSettings(settingsGroups);
|
||||
this._allSettingsGroups = [mostCommonlyUsed, ...settingsGroups];
|
||||
this._content = this.toContent(true, this._allSettingsGroups);
|
||||
return this._content;
|
||||
return [mostCommonlyUsed, ...settingsGroups];
|
||||
}
|
||||
|
||||
get raw(): string {
|
||||
if (!DefaultSettings._RAW) {
|
||||
DefaultSettings._RAW = this.toContent(false, this.getRegisteredGroups());
|
||||
}
|
||||
return DefaultSettings._RAW;
|
||||
}
|
||||
|
||||
getSettingByName(name: string): ISetting {
|
||||
return this._settingsByName && this._settingsByName.get(name);
|
||||
return DefaultSettings._RAW;
|
||||
}
|
||||
|
||||
private getRegisteredGroups(): ISettingsGroup[] {
|
||||
const configurations = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurations().slice();
|
||||
return this.removeEmptySettingsGroups(configurations.sort(this.compareConfigurationNodes)
|
||||
const groups = this.removeEmptySettingsGroups(configurations.sort(this.compareConfigurationNodes)
|
||||
.reduce((result, config, index, array) => this.parseConfig(config, result, array), []));
|
||||
|
||||
return this.sortGroups(groups);
|
||||
}
|
||||
|
||||
private sortGroups(groups: ISettingsGroup[]): ISettingsGroup[] {
|
||||
groups.forEach(group => {
|
||||
group.sections.forEach(section => {
|
||||
section.settings.sort((a, b) => a.key.localeCompare(b.key));
|
||||
});
|
||||
});
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
private initAllSettingsMap(allSettingsGroups: ISettingsGroup[]): void {
|
||||
@@ -472,6 +522,7 @@ export class DefaultSettings extends Disposable {
|
||||
range: null,
|
||||
valueRange: null,
|
||||
overrides: [],
|
||||
scope: ConfigurationScope.RESOURCE,
|
||||
type: setting.type,
|
||||
enum: setting.enum,
|
||||
enumDescriptions: setting.enumDescriptions
|
||||
@@ -497,14 +548,14 @@ export class DefaultSettings extends Disposable {
|
||||
seenSettings = seenSettings ? seenSettings : {};
|
||||
let title = config.title;
|
||||
if (!title) {
|
||||
const configWithTitleAndSameId = configurations.filter(c => c.id === config.id && c.title)[0];
|
||||
const configWithTitleAndSameId = find(configurations, c => (c.id === config.id) && c.title);
|
||||
if (configWithTitleAndSameId) {
|
||||
title = configWithTitleAndSameId.title;
|
||||
}
|
||||
}
|
||||
if (title) {
|
||||
if (!settingsGroup) {
|
||||
settingsGroup = result.filter(g => g.title === title)[0];
|
||||
settingsGroup = find(result, g => g.title === title);
|
||||
if (!settingsGroup) {
|
||||
settingsGroup = { sections: [{ settings: [] }], id: config.id, title: title, titleRange: null, range: null, contributedByExtension: !!config.contributedByExtension };
|
||||
result.push(settingsGroup);
|
||||
@@ -526,7 +577,6 @@ export class DefaultSettings extends Disposable {
|
||||
}
|
||||
}
|
||||
if (configurationSettings.length) {
|
||||
configurationSettings.sort((a, b) => a.key.localeCompare(b.key));
|
||||
settingsGroup.sections[settingsGroup.sections.length - 1].settings = configurationSettings;
|
||||
}
|
||||
}
|
||||
@@ -537,7 +587,7 @@ export class DefaultSettings extends Disposable {
|
||||
}
|
||||
|
||||
private removeEmptySettingsGroups(settingsGroups: ISettingsGroup[]): ISettingsGroup[] {
|
||||
const result = [];
|
||||
const result: ISettingsGroup[] = [];
|
||||
for (const settingsGroup of settingsGroups) {
|
||||
settingsGroup.sections = settingsGroup.sections.filter(section => section.settings.length > 0);
|
||||
if (settingsGroup.sections.length) {
|
||||
@@ -551,23 +601,28 @@ export class DefaultSettings extends Disposable {
|
||||
let result: ISetting[] = [];
|
||||
for (let key in settingsObject) {
|
||||
const prop = settingsObject[key];
|
||||
if (!prop.deprecationMessage && this.matchesScope(prop)) {
|
||||
if (this.matchesScope(prop)) {
|
||||
const value = prop.default;
|
||||
const description = (prop.description || '').split('\n');
|
||||
const description = (prop.description || prop.markdownDescription || '').split('\n');
|
||||
const overrides = OVERRIDE_PROPERTY_PATTERN.test(key) ? this.parseOverrideSettings(prop.default) : [];
|
||||
result.push({
|
||||
key,
|
||||
value,
|
||||
description,
|
||||
descriptionIsMarkdown: !prop.description,
|
||||
range: null,
|
||||
keyRange: null,
|
||||
valueRange: null,
|
||||
descriptionRanges: [],
|
||||
overrides,
|
||||
scope: prop.scope,
|
||||
type: prop.type,
|
||||
enum: prop.enum,
|
||||
enumDescriptions: prop.enumDescriptions,
|
||||
tags: prop.tags
|
||||
enumDescriptions: prop.enumDescriptions || prop.markdownEnumDescriptions,
|
||||
enumDescriptionsAreMarkdown: !prop.enumDescriptions,
|
||||
tags: prop.tags,
|
||||
deprecationMessage: prop.deprecationMessage,
|
||||
validator: createValidator(prop)
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -575,7 +630,17 @@ export class DefaultSettings extends Disposable {
|
||||
}
|
||||
|
||||
private parseOverrideSettings(overrideSettings: any): ISetting[] {
|
||||
return Object.keys(overrideSettings).map((key) => ({ key, value: overrideSettings[key], description: [], range: null, keyRange: null, valueRange: null, descriptionRanges: [], overrides: [] }));
|
||||
return Object.keys(overrideSettings).map((key) => ({
|
||||
key,
|
||||
value: overrideSettings[key],
|
||||
description: [],
|
||||
descriptionIsMarkdown: false,
|
||||
range: null,
|
||||
keyRange: null,
|
||||
valueRange: null,
|
||||
descriptionRanges: [],
|
||||
overrides: []
|
||||
}));
|
||||
}
|
||||
|
||||
private matchesScope(property: IConfigurationNode): boolean {
|
||||
@@ -648,7 +713,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
|
||||
}
|
||||
|
||||
public get settingsGroups(): ISettingsGroup[] {
|
||||
return this.defaultSettings.settingsGroups;
|
||||
return this.defaultSettings.getSettingsGroups();
|
||||
}
|
||||
|
||||
protected get filterGroups(): ISettingsGroup[] {
|
||||
@@ -657,6 +722,10 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
|
||||
}
|
||||
|
||||
protected update(): IFilterResult {
|
||||
if (this._model.isDisposed()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Grab current result groups, only render non-empty groups
|
||||
const resultGroups = map
|
||||
.values(this._currentResultGroups)
|
||||
@@ -752,8 +821,9 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
|
||||
}
|
||||
|
||||
private copySetting(setting: ISetting): ISetting {
|
||||
return <ISetting>{
|
||||
return {
|
||||
description: setting.description,
|
||||
scope: setting.scope,
|
||||
type: setting.type,
|
||||
enum: setting.enum,
|
||||
enumDescriptions: setting.enumDescriptions,
|
||||
@@ -762,7 +832,12 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements
|
||||
range: setting.range,
|
||||
overrides: [],
|
||||
overrideOf: setting.overrideOf,
|
||||
tags: setting.tags
|
||||
tags: setting.tags,
|
||||
deprecationMessage: setting.deprecationMessage,
|
||||
keyRange: undefined,
|
||||
valueRange: undefined,
|
||||
descriptionIsMarkdown: undefined,
|
||||
descriptionRanges: undefined
|
||||
};
|
||||
}
|
||||
|
||||
@@ -839,7 +914,7 @@ class SettingsContentBuilder {
|
||||
|
||||
private _pushGroup(group: ISettingsGroup): ISetting {
|
||||
const indent = ' ';
|
||||
let lastSetting: ISetting = null;
|
||||
let lastSetting: ISetting | null = null;
|
||||
let groupStart = this.lineCountWithOffset + 1;
|
||||
for (const section of group.sections) {
|
||||
if (section.title) {
|
||||
@@ -869,16 +944,16 @@ class SettingsContentBuilder {
|
||||
|
||||
this.pushSettingDescription(setting, indent);
|
||||
|
||||
let preValueConent = indent;
|
||||
let preValueContent = indent;
|
||||
const keyString = JSON.stringify(setting.key);
|
||||
preValueConent += keyString;
|
||||
setting.keyRange = { startLineNumber: this.lineCountWithOffset + 1, startColumn: preValueConent.indexOf(setting.key) + 1, endLineNumber: this.lineCountWithOffset + 1, endColumn: setting.key.length };
|
||||
preValueContent += keyString;
|
||||
setting.keyRange = { startLineNumber: this.lineCountWithOffset + 1, startColumn: preValueContent.indexOf(setting.key) + 1, endLineNumber: this.lineCountWithOffset + 1, endColumn: setting.key.length };
|
||||
|
||||
preValueConent += ': ';
|
||||
preValueContent += ': ';
|
||||
const valueStart = this.lineCountWithOffset + 1;
|
||||
this.pushValue(setting, preValueConent, indent);
|
||||
this.pushValue(setting, preValueContent, indent);
|
||||
|
||||
setting.valueRange = { startLineNumber: valueStart, startColumn: preValueConent.length + 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length + 1 };
|
||||
setting.valueRange = { startLineNumber: valueStart, startColumn: preValueContent.length + 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length + 1 };
|
||||
this._contentByLines[this._contentByLines.length - 1] += ',';
|
||||
this._contentByLines.push('');
|
||||
setting.range = { startLineNumber: settingStart, startColumn: 1, endLineNumber: this.lineCountWithOffset, endColumn: this.lastLine.length };
|
||||
@@ -889,8 +964,7 @@ class SettingsContentBuilder {
|
||||
|
||||
setting.descriptionRanges = [];
|
||||
const descriptionPreValue = indent + '// ';
|
||||
for (let line of setting.description) {
|
||||
// Remove setting link tag
|
||||
for (let line of (setting.deprecationMessage ? [setting.deprecationMessage, ...setting.description] : setting.description)) {
|
||||
line = fixSettingLink(line);
|
||||
|
||||
this._contentByLines.push(descriptionPreValue + line);
|
||||
@@ -899,7 +973,7 @@ class SettingsContentBuilder {
|
||||
|
||||
if (setting.enumDescriptions && setting.enumDescriptions.some(desc => !!desc)) {
|
||||
setting.enumDescriptions.forEach((desc, i) => {
|
||||
const displayEnum = escapeInvisibleChars(setting.enum[i]);
|
||||
const displayEnum = escapeInvisibleChars(String(setting.enum[i]));
|
||||
const line = desc ?
|
||||
`${displayEnum}: ${fixSettingLink(desc)}` :
|
||||
displayEnum;
|
||||
@@ -943,6 +1017,112 @@ class SettingsContentBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
export function createValidator(prop: IConfigurationPropertySchema): ((value: any) => string) | null {
|
||||
return value => {
|
||||
let exclusiveMax: number | undefined;
|
||||
let exclusiveMin: number | undefined;
|
||||
|
||||
if (typeof prop.exclusiveMaximum === 'boolean') {
|
||||
exclusiveMax = prop.exclusiveMaximum ? prop.maximum : undefined;
|
||||
} else {
|
||||
exclusiveMax = prop.exclusiveMaximum;
|
||||
}
|
||||
|
||||
if (typeof prop.exclusiveMinimum === 'boolean') {
|
||||
exclusiveMin = prop.exclusiveMinimum ? prop.minimum : undefined;
|
||||
} else {
|
||||
exclusiveMin = prop.exclusiveMinimum;
|
||||
}
|
||||
|
||||
let patternRegex: RegExp | undefined;
|
||||
if (typeof prop.pattern === 'string') {
|
||||
patternRegex = new RegExp(prop.pattern);
|
||||
}
|
||||
|
||||
const type = Array.isArray(prop.type) ? prop.type : [prop.type];
|
||||
const canBeType = (t: string) => type.indexOf(t) > -1;
|
||||
|
||||
const isNullable = canBeType('null');
|
||||
const isNumeric = (canBeType('number') || canBeType('integer')) && (type.length === 1 || type.length === 2 && isNullable);
|
||||
const isIntegral = (canBeType('integer')) && (type.length === 1 || type.length === 2 && isNullable);
|
||||
|
||||
type Validator<T> = { enabled: boolean, isValid: (value: T) => boolean; message: string };
|
||||
|
||||
let numericValidations: Validator<number>[] = isNumeric ? [
|
||||
{
|
||||
enabled: exclusiveMax !== undefined && (prop.maximum === undefined || exclusiveMax <= prop.maximum),
|
||||
isValid: (value => value < exclusiveMax),
|
||||
message: nls.localize('validations.exclusiveMax', "Value must be strictly less than {0}.", exclusiveMax)
|
||||
},
|
||||
{
|
||||
enabled: exclusiveMin !== undefined && (prop.minimum === undefined || exclusiveMin >= prop.minimum),
|
||||
isValid: (value => value > exclusiveMin),
|
||||
message: nls.localize('validations.exclusiveMin', "Value must be strictly greater than {0}.", exclusiveMin)
|
||||
},
|
||||
|
||||
{
|
||||
enabled: prop.maximum !== undefined && (exclusiveMax === undefined || exclusiveMax > prop.maximum),
|
||||
isValid: (value => value <= prop.maximum),
|
||||
message: nls.localize('validations.max', "Value must be less than or equal to {0}.", prop.maximum)
|
||||
},
|
||||
{
|
||||
enabled: prop.minimum !== undefined && (exclusiveMin === undefined || exclusiveMin < prop.minimum),
|
||||
isValid: (value => value >= prop.minimum),
|
||||
message: nls.localize('validations.min', "Value must be greater than or equal to {0}.", prop.minimum)
|
||||
},
|
||||
{
|
||||
enabled: prop.multipleOf !== undefined,
|
||||
isValid: (value => value % prop.multipleOf === 0),
|
||||
message: nls.localize('validations.multipleOf', "Value must be a multiple of {0}.", prop.multipleOf)
|
||||
},
|
||||
{
|
||||
enabled: isIntegral,
|
||||
isValid: (value => value % 1 === 0),
|
||||
message: nls.localize('validations.expectedInteger', "Value must be an integer.")
|
||||
},
|
||||
].filter(validation => validation.enabled) : [];
|
||||
|
||||
let stringValidations: Validator<string>[] = [
|
||||
{
|
||||
enabled: prop.maxLength !== undefined,
|
||||
isValid: (value => value.length <= prop.maxLength),
|
||||
message: nls.localize('validations.maxLength', "Value must be {0} or fewer characters long.", prop.maxLength)
|
||||
},
|
||||
{
|
||||
enabled: prop.minLength !== undefined,
|
||||
isValid: (value => value.length >= prop.minLength),
|
||||
message: nls.localize('validations.minLength', "Value must be {0} or more characters long.", prop.minLength)
|
||||
},
|
||||
{
|
||||
enabled: patternRegex !== undefined,
|
||||
isValid: (value => patternRegex.test(value)),
|
||||
message: prop.patternErrorMessage || nls.localize('validations.regex', "Value must match regex `{0}`.", prop.pattern)
|
||||
},
|
||||
].filter(validation => validation.enabled);
|
||||
|
||||
if (prop.type === 'string' && stringValidations.length === 0) { return null; }
|
||||
if (isNullable && value === '') { return ''; }
|
||||
|
||||
let errors: string[] = [];
|
||||
|
||||
if (isNumeric) {
|
||||
if (value === '' || isNaN(+value)) {
|
||||
errors.push(nls.localize('validations.expectedNumeric', "Value must be a number."));
|
||||
} else {
|
||||
errors.push(...numericValidations.filter(validator => !validator.isValid(+value)).map(validator => validator.message));
|
||||
}
|
||||
}
|
||||
|
||||
if (prop.type === 'string') {
|
||||
errors.push(...stringValidations.filter(validator => !validator.isValid('' + value)).map(validator => validator.message));
|
||||
}
|
||||
if (errors.length) {
|
||||
return prop.errorMessage ? [prop.errorMessage, ...errors].join(' ') : errors.join(' ');
|
||||
}
|
||||
return '';
|
||||
};
|
||||
}
|
||||
|
||||
function escapeInvisibleChars(enumValue: string): string {
|
||||
return enumValue && enumValue
|
||||
.replace(/\n/g, '\\n')
|
||||
@@ -950,7 +1130,7 @@ function escapeInvisibleChars(enumValue: string): string {
|
||||
}
|
||||
|
||||
export function defaultKeybindingsContents(keybindingService: IKeybindingService): string {
|
||||
const defaultsHeader = '// ' + nls.localize('defaultKeybindingsHeader', "Overwrite key bindings by placing them into your key bindings file.");
|
||||
const defaultsHeader = '// ' + nls.localize('defaultKeybindingsHeader', "Override key bindings by placing them into your key bindings file.");
|
||||
return defaultsHeader + '\n' + keybindingService.getDefaultKeybindingsContent();
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ class AnAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
suite('Keybindings Editor Model test', () => {
|
||||
suite('KeybindingsEditorModel test', () => {
|
||||
|
||||
let instantiationService: TestInstantiationService;
|
||||
let testObject: KeybindingsEditorModel;
|
||||
@@ -51,31 +51,29 @@ suite('Keybindings Editor Model test', () => {
|
||||
CommandsRegistry.registerCommand('command_without_keybinding', () => { });
|
||||
});
|
||||
|
||||
test('fetch returns default keybindings', () => {
|
||||
test('fetch returns default keybindings', async () => {
|
||||
const expected = prepareKeybindingService(
|
||||
aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape } }),
|
||||
aResolvedKeybindingItem({ command: 'b' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } })
|
||||
);
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actuals = asResolvedKeybindingItems(testObject.fetch(''));
|
||||
assertKeybindingItems(actuals, expected);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actuals = asResolvedKeybindingItems(testObject.fetch(''));
|
||||
assertKeybindingItems(actuals, expected);
|
||||
});
|
||||
|
||||
test('fetch returns default keybindings at the top', () => {
|
||||
test('fetch returns default keybindings at the top', async () => {
|
||||
const expected = prepareKeybindingService(
|
||||
aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape } }),
|
||||
aResolvedKeybindingItem({ command: 'b' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } })
|
||||
);
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actuals = asResolvedKeybindingItems(testObject.fetch('').slice(0, 2), true);
|
||||
assertKeybindingItems(actuals, expected);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actuals = asResolvedKeybindingItems(testObject.fetch('').slice(0, 2), true);
|
||||
assertKeybindingItems(actuals, expected);
|
||||
});
|
||||
|
||||
test('fetch returns default keybindings sorted by command id', () => {
|
||||
test('fetch returns default keybindings sorted by command id', async () => {
|
||||
const keybindings = prepareKeybindingService(
|
||||
aResolvedKeybindingItem({ command: 'b' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape } }),
|
||||
aResolvedKeybindingItem({ command: 'c' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } }),
|
||||
@@ -83,13 +81,12 @@ suite('Keybindings Editor Model test', () => {
|
||||
);
|
||||
const expected = [keybindings[2], keybindings[0], keybindings[1]];
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actuals = asResolvedKeybindingItems(testObject.fetch(''));
|
||||
assertKeybindingItems(actuals, expected);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actuals = asResolvedKeybindingItems(testObject.fetch(''));
|
||||
assertKeybindingItems(actuals, expected);
|
||||
});
|
||||
|
||||
test('fetch returns user keybinding first if default and user has same id', () => {
|
||||
test('fetch returns user keybinding first if default and user has same id', async () => {
|
||||
const sameId = 'b' + uuid.generateUuid();
|
||||
const keybindings = prepareKeybindingService(
|
||||
aResolvedKeybindingItem({ command: sameId, firstPart: { keyCode: KeyCode.Escape } }),
|
||||
@@ -97,13 +94,12 @@ suite('Keybindings Editor Model test', () => {
|
||||
);
|
||||
const expected = [keybindings[1], keybindings[0]];
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actuals = asResolvedKeybindingItems(testObject.fetch(''));
|
||||
assertKeybindingItems(actuals, expected);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actuals = asResolvedKeybindingItems(testObject.fetch(''));
|
||||
assertKeybindingItems(actuals, expected);
|
||||
});
|
||||
|
||||
test('fetch returns keybinding with titles first', () => {
|
||||
test('fetch returns keybinding with titles first', async () => {
|
||||
const keybindings = prepareKeybindingService(
|
||||
aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape } }),
|
||||
aResolvedKeybindingItem({ command: 'b' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } }),
|
||||
@@ -118,13 +114,12 @@ suite('Keybindings Editor Model test', () => {
|
||||
instantiationService.stub(IKeybindingService, 'getKeybindings', () => keybindings);
|
||||
instantiationService.stub(IKeybindingService, 'getDefaultKeybindings', () => keybindings);
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actuals = asResolvedKeybindingItems(testObject.fetch(''));
|
||||
assertKeybindingItems(actuals, expected);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actuals = asResolvedKeybindingItems(testObject.fetch(''));
|
||||
assertKeybindingItems(actuals, expected);
|
||||
});
|
||||
|
||||
test('fetch returns keybinding with user first if title and id matches', () => {
|
||||
test('fetch returns keybinding with user first if title and id matches', async () => {
|
||||
const sameId = 'b' + uuid.generateUuid();
|
||||
const keybindings = prepareKeybindingService(
|
||||
aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape } }),
|
||||
@@ -137,465 +132,441 @@ suite('Keybindings Editor Model test', () => {
|
||||
registerCommandWithTitle(keybindings[3].command, 'Same Title');
|
||||
const expected = [keybindings[3], keybindings[1], keybindings[0], keybindings[2]];
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actuals = asResolvedKeybindingItems(testObject.fetch(''));
|
||||
assertKeybindingItems(actuals, expected);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actuals = asResolvedKeybindingItems(testObject.fetch(''));
|
||||
assertKeybindingItems(actuals, expected);
|
||||
});
|
||||
|
||||
test('fetch returns default keybindings sorted by precedence', () => {
|
||||
test('fetch returns default keybindings sorted by precedence', async () => {
|
||||
const expected = prepareKeybindingService(
|
||||
aResolvedKeybindingItem({ command: 'b' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape } }),
|
||||
aResolvedKeybindingItem({ command: 'c' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, chordPart: { keyCode: KeyCode.Escape } }),
|
||||
aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Backspace } })
|
||||
);
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actuals = asResolvedKeybindingItems(testObject.fetch('', true));
|
||||
assertKeybindingItems(actuals, expected);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actuals = asResolvedKeybindingItems(testObject.fetch('', true));
|
||||
assertKeybindingItems(actuals, expected);
|
||||
});
|
||||
|
||||
test('convert keybinding without title to entry', () => {
|
||||
test('convert keybinding without title to entry', async () => {
|
||||
const expected = aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2' });
|
||||
prepareKeybindingService(expected);
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('')[0];
|
||||
assert.equal(actual.keybindingItem.command, expected.command);
|
||||
assert.equal(actual.keybindingItem.commandLabel, '');
|
||||
assert.equal(actual.keybindingItem.commandDefaultLabel, null);
|
||||
assert.equal(actual.keybindingItem.keybinding.getAriaLabel(), expected.resolvedKeybinding.getAriaLabel());
|
||||
assert.equal(actual.keybindingItem.when, expected.when.serialize());
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('')[0];
|
||||
assert.equal(actual.keybindingItem.command, expected.command);
|
||||
assert.equal(actual.keybindingItem.commandLabel, '');
|
||||
assert.equal(actual.keybindingItem.commandDefaultLabel, null);
|
||||
assert.equal(actual.keybindingItem.keybinding.getAriaLabel(), expected.resolvedKeybinding.getAriaLabel());
|
||||
assert.equal(actual.keybindingItem.when, expected.when.serialize());
|
||||
});
|
||||
|
||||
test('convert keybinding with title to entry', () => {
|
||||
test('convert keybinding with title to entry', async () => {
|
||||
const expected = aResolvedKeybindingItem({ command: 'a' + uuid.generateUuid(), firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2' });
|
||||
prepareKeybindingService(expected);
|
||||
registerCommandWithTitle(expected.command, 'Some Title');
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('')[0];
|
||||
assert.equal(actual.keybindingItem.command, expected.command);
|
||||
assert.equal(actual.keybindingItem.commandLabel, 'Some Title');
|
||||
assert.equal(actual.keybindingItem.commandDefaultLabel, null);
|
||||
assert.equal(actual.keybindingItem.keybinding.getAriaLabel(), expected.resolvedKeybinding.getAriaLabel());
|
||||
assert.equal(actual.keybindingItem.when, expected.when.serialize());
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('')[0];
|
||||
assert.equal(actual.keybindingItem.command, expected.command);
|
||||
assert.equal(actual.keybindingItem.commandLabel, 'Some Title');
|
||||
assert.equal(actual.keybindingItem.commandDefaultLabel, null);
|
||||
assert.equal(actual.keybindingItem.keybinding.getAriaLabel(), expected.resolvedKeybinding.getAriaLabel());
|
||||
assert.equal(actual.keybindingItem.when, expected.when.serialize());
|
||||
});
|
||||
|
||||
test('convert without title and binding to entry', () => {
|
||||
test('convert without title and binding to entry', async () => {
|
||||
CommandsRegistry.registerCommand('command_without_keybinding', () => { });
|
||||
prepareKeybindingService();
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('').filter(element => element.keybindingItem.command === 'command_without_keybinding')[0];
|
||||
assert.equal(actual.keybindingItem.command, 'command_without_keybinding');
|
||||
assert.equal(actual.keybindingItem.commandLabel, '');
|
||||
assert.equal(actual.keybindingItem.commandDefaultLabel, null);
|
||||
assert.equal(actual.keybindingItem.keybinding, null);
|
||||
assert.equal(actual.keybindingItem.when, '');
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('').filter(element => element.keybindingItem.command === 'command_without_keybinding')[0];
|
||||
assert.equal(actual.keybindingItem.command, 'command_without_keybinding');
|
||||
assert.equal(actual.keybindingItem.commandLabel, '');
|
||||
assert.equal(actual.keybindingItem.commandDefaultLabel, null);
|
||||
assert.equal(actual.keybindingItem.keybinding, null);
|
||||
assert.equal(actual.keybindingItem.when, '');
|
||||
});
|
||||
|
||||
test('convert with title and without binding to entry', () => {
|
||||
test('convert with title and without binding to entry', async () => {
|
||||
const id = 'a' + uuid.generateUuid();
|
||||
registerCommandWithTitle(id, 'some title');
|
||||
prepareKeybindingService();
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('').filter(element => element.keybindingItem.command === id)[0];
|
||||
assert.equal(actual.keybindingItem.command, id);
|
||||
assert.equal(actual.keybindingItem.commandLabel, 'some title');
|
||||
assert.equal(actual.keybindingItem.commandDefaultLabel, null);
|
||||
assert.equal(actual.keybindingItem.keybinding, null);
|
||||
assert.equal(actual.keybindingItem.when, '');
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('').filter(element => element.keybindingItem.command === id)[0];
|
||||
assert.equal(actual.keybindingItem.command, id);
|
||||
assert.equal(actual.keybindingItem.commandLabel, 'some title');
|
||||
assert.equal(actual.keybindingItem.commandDefaultLabel, null);
|
||||
assert.equal(actual.keybindingItem.keybinding, null);
|
||||
assert.equal(actual.keybindingItem.when, '');
|
||||
});
|
||||
|
||||
test('filter by command id', () => {
|
||||
test('filter by command id', async () => {
|
||||
const id = 'workbench.action.increaseViewSize';
|
||||
registerCommandWithTitle(id, 'some title');
|
||||
prepareKeybindingService();
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('workbench action view size').filter(element => element.keybindingItem.command === id)[0];
|
||||
assert.ok(actual);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('workbench action view size').filter(element => element.keybindingItem.command === id)[0];
|
||||
assert.ok(actual);
|
||||
});
|
||||
|
||||
test('filter by command title', () => {
|
||||
test('filter by command title', async () => {
|
||||
const id = 'a' + uuid.generateUuid();
|
||||
registerCommandWithTitle(id, 'Increase view size');
|
||||
prepareKeybindingService();
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('increase size').filter(element => element.keybindingItem.command === id)[0];
|
||||
assert.ok(actual);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('increase size').filter(element => element.keybindingItem.command === id)[0];
|
||||
assert.ok(actual);
|
||||
});
|
||||
|
||||
test('filter by default source', () => {
|
||||
test('filter by default source', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2' });
|
||||
prepareKeybindingService(expected);
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('default').filter(element => element.keybindingItem.command === command)[0];
|
||||
assert.ok(actual);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('default').filter(element => element.keybindingItem.command === command)[0];
|
||||
assert.ok(actual);
|
||||
});
|
||||
|
||||
test('filter by user source', () => {
|
||||
test('filter by user source', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2', isDefault: false });
|
||||
prepareKeybindingService(expected);
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('user').filter(element => element.keybindingItem.command === command)[0];
|
||||
assert.ok(actual);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('user').filter(element => element.keybindingItem.command === command)[0];
|
||||
assert.ok(actual);
|
||||
});
|
||||
|
||||
test('filter by default source with "@source: " prefix', () => {
|
||||
test('filter by default source with "@source: " prefix', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2', isDefault: true });
|
||||
prepareKeybindingService(expected);
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('@source: default').filter(element => element.keybindingItem.command === command)[0];
|
||||
assert.ok(actual);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('@source: default').filter(element => element.keybindingItem.command === command)[0];
|
||||
assert.ok(actual);
|
||||
});
|
||||
|
||||
test('filter by user source with "@source: " prefix', () => {
|
||||
test('filter by user source with "@source: " prefix', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, when: 'context1 && context2', isDefault: false });
|
||||
prepareKeybindingService(expected);
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('@source: user').filter(element => element.keybindingItem.command === command)[0];
|
||||
assert.ok(actual);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('@source: user').filter(element => element.keybindingItem.command === command)[0];
|
||||
assert.ok(actual);
|
||||
});
|
||||
|
||||
test('filter by when context', () => {
|
||||
test('filter by when context', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected);
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('when context').filter(element => element.keybindingItem.command === command)[0];
|
||||
assert.ok(actual);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('when context').filter(element => element.keybindingItem.command === command)[0];
|
||||
assert.ok(actual);
|
||||
});
|
||||
|
||||
test('filter by cmd key', () => {
|
||||
test('filter by cmd key', async () => {
|
||||
testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh);
|
||||
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected);
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('cmd').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('cmd').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter by meta key', () => {
|
||||
test('filter by meta key', async () => {
|
||||
testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh);
|
||||
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('meta').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('meta').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter by command key', () => {
|
||||
test('filter by command key', async () => {
|
||||
testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh);
|
||||
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('command').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('command').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter by windows key', () => {
|
||||
test('filter by windows key', async () => {
|
||||
testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Windows);
|
||||
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('windows').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('windows').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter by alt key', () => {
|
||||
test('filter by alt key', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('alt').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { altKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('alt').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { altKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter by option key', () => {
|
||||
test('filter by option key', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('option').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { altKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('option').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { altKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter by ctrl key', () => {
|
||||
test('filter by ctrl key', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('ctrl').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { ctrlKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('ctrl').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { ctrlKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter by control key', () => {
|
||||
test('filter by control key', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('control').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { ctrlKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('control').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { ctrlKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter by shift key', () => {
|
||||
test('filter by shift key', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('shift').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { shiftKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('shift').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { shiftKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter by arrow', () => {
|
||||
test('filter by arrow', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { shiftKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('arrow').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('arrow').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter by modifier and key', () => {
|
||||
test('filter by modifier and key', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('alt right').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { altKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('alt right').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { altKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter by key and modifier', () => {
|
||||
test('filter by key and modifier', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { altKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.RightArrow, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('right alt').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(0, actual.length);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('right alt').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(0, actual.length);
|
||||
});
|
||||
|
||||
test('filter by modifiers and key', () => {
|
||||
test('filter by modifiers and key', async () => {
|
||||
testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh);
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { altKey: true, metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('alt cmd esc').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { altKey: true, metaKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('alt cmd esc').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { altKey: true, metaKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter by modifiers in random order and key', () => {
|
||||
test('filter by modifiers in random order and key', async () => {
|
||||
testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh);
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('cmd shift esc').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true, shiftKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('cmd shift esc').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true, shiftKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter by first part', () => {
|
||||
test('filter by first part', async () => {
|
||||
testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh);
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.Delete }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('cmd shift esc').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true, shiftKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('cmd shift esc').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true, shiftKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter matches in chord part', () => {
|
||||
test('filter matches in chord part', async () => {
|
||||
testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh);
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.Delete }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('cmd del').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, { keyCode: true });
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('cmd del').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { metaKey: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, { keyCode: true });
|
||||
});
|
||||
|
||||
test('filter matches first part and in chord part', () => {
|
||||
test('filter matches first part and in chord part', async () => {
|
||||
testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh);
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.Delete }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.UpArrow }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('cmd shift esc del').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { shiftKey: true, metaKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, { keyCode: true });
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('cmd shift esc del').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { shiftKey: true, metaKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, { keyCode: true });
|
||||
});
|
||||
|
||||
test('filter exact matches', () => {
|
||||
test('filter exact matches', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('"ctrl c"').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { ctrlKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('"ctrl c"').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { ctrlKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter exact matches with first and chord part', () => {
|
||||
test('filter exact matches with first and chord part', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('"shift meta escape ctrl c"').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { shiftKey: true, metaKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, { ctrlKey: true, keyCode: true });
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('"shift meta escape ctrl c"').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { shiftKey: true, metaKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, { ctrlKey: true, keyCode: true });
|
||||
});
|
||||
|
||||
test('filter exact matches with first and chord part no results', () => {
|
||||
test('filter exact matches with first and chord part no results', async () => {
|
||||
testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh);
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.Delete, modifiers: { metaKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.UpArrow }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('"cmd shift esc del"').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(0, actual.length);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('"cmd shift esc del"').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(0, actual.length);
|
||||
});
|
||||
|
||||
test('filter matches with + separator', () => {
|
||||
test('filter matches with + separator', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('"control+c"').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { ctrlKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('"control+c"').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { ctrlKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, {});
|
||||
});
|
||||
|
||||
test('filter matches with + separator in first and chord parts', () => {
|
||||
test('filter matches with + separator in first and chord parts', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Escape, modifiers: { shiftKey: true, metaKey: true } }, chordPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.KEY_C, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('"shift+meta+escape ctrl+c"').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { shiftKey: true, metaKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, { keyCode: true, ctrlKey: true });
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('"shift+meta+escape ctrl+c"').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { shiftKey: true, metaKey: true, keyCode: true });
|
||||
assert.deepEqual(actual[0].keybindingMatches.chordPart, { keyCode: true, ctrlKey: true });
|
||||
});
|
||||
|
||||
test('filter exact matches with space #32993', () => {
|
||||
test('filter exact matches with space #32993', async () => {
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Space, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.Backspace, modifiers: { ctrlKey: true } }, when: 'whenContext1 && whenContext2', isDefault: false }));
|
||||
|
||||
return testObject.resolve({}).then(() => {
|
||||
const actual = testObject.fetch('"ctrl+space"').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
});
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('"ctrl+space"').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
});
|
||||
|
||||
test('filter exact matches with user settings label', async () => {
|
||||
testObject = instantiationService.createInstance(KeybindingsEditorModel, OperatingSystem.Macintosh);
|
||||
const command = 'a' + uuid.generateUuid();
|
||||
const expected = aResolvedKeybindingItem({ command, firstPart: { keyCode: KeyCode.DownArrow } });
|
||||
prepareKeybindingService(expected, aResolvedKeybindingItem({ command: 'down', firstPart: { keyCode: KeyCode.Escape } }));
|
||||
|
||||
await testObject.resolve({});
|
||||
const actual = testObject.fetch('"down"').filter(element => element.keybindingItem.command === command);
|
||||
assert.equal(1, actual.length);
|
||||
assert.deepEqual(actual[0].keybindingMatches.firstPart, { keyCode: true });
|
||||
});
|
||||
|
||||
function prepareKeybindingService(...keybindingItems: ResolvedKeybindingItem[]): ResolvedKeybindingItem[] {
|
||||
@@ -607,7 +578,7 @@ suite('Keybindings Editor Model test', () => {
|
||||
|
||||
function registerCommandWithTitle(command: string, title: string): void {
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(AnAction, command, title, { primary: null }), '');
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(AnAction, command, title, { primary: 0 }), '');
|
||||
}
|
||||
|
||||
function assertKeybindingItems(actual: ResolvedKeybindingItem[], expected: ResolvedKeybindingItem[]) {
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { createValidator } from 'vs/workbench/services/preferences/common/preferencesModels';
|
||||
import { IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
|
||||
|
||||
suite('Preferences Model test', () => {
|
||||
class Tester {
|
||||
private validator: (value: any) => string;
|
||||
|
||||
constructor(private settings: IConfigurationPropertySchema) {
|
||||
this.validator = createValidator(settings);
|
||||
}
|
||||
|
||||
public accepts(input) {
|
||||
assert.equal(this.validator(input), '', `Expected ${JSON.stringify(this.settings)} to accept \`${input}\`. Got ${this.validator(input)}.`);
|
||||
}
|
||||
|
||||
public rejects(input) {
|
||||
assert.notEqual(this.validator(input), '', `Expected ${JSON.stringify(this.settings)} to reject \`${input}\`.`);
|
||||
return {
|
||||
withMessage:
|
||||
(message) => assert(this.validator(input).indexOf(message) > -1,
|
||||
`Expected error of ${JSON.stringify(this.settings)} on \`${input}\` to contain ${message}. Got ${this.validator(input)}.`)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
public validatesNumeric() {
|
||||
this.accepts('3');
|
||||
this.accepts('3.');
|
||||
this.accepts('.0');
|
||||
this.accepts('3.0');
|
||||
this.accepts(' 3.0');
|
||||
this.accepts(' 3.0 ');
|
||||
this.rejects('3f');
|
||||
}
|
||||
|
||||
public validatesNullableNumeric() {
|
||||
this.validatesNumeric();
|
||||
this.accepts('');
|
||||
}
|
||||
|
||||
public validatesNonNullableNumeric() {
|
||||
this.validatesNumeric();
|
||||
this.rejects('');
|
||||
}
|
||||
|
||||
public validatesString() {
|
||||
this.accepts('3');
|
||||
this.accepts('3.');
|
||||
this.accepts('.0');
|
||||
this.accepts('3.0');
|
||||
this.accepts(' 3.0');
|
||||
this.accepts(' 3.0 ');
|
||||
this.accepts('');
|
||||
this.accepts('3f');
|
||||
this.accepts('hello');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test('exclusive max and max work together properly', () => {
|
||||
{
|
||||
const justMax = new Tester({ maximum: 5, type: 'number' });
|
||||
justMax.validatesNonNullableNumeric();
|
||||
justMax.rejects('5.1');
|
||||
justMax.accepts('5.0');
|
||||
}
|
||||
{
|
||||
const justEMax = new Tester({ exclusiveMaximum: 5, type: 'number' });
|
||||
justEMax.validatesNonNullableNumeric();
|
||||
justEMax.rejects('5.1');
|
||||
justEMax.rejects('5.0');
|
||||
justEMax.accepts('4.999');
|
||||
}
|
||||
{
|
||||
const bothNumeric = new Tester({ exclusiveMaximum: 5, maximum: 4, type: 'number' });
|
||||
bothNumeric.validatesNonNullableNumeric();
|
||||
bothNumeric.rejects('5.1');
|
||||
bothNumeric.rejects('5.0');
|
||||
bothNumeric.rejects('4.999');
|
||||
bothNumeric.accepts('4');
|
||||
}
|
||||
{
|
||||
const bothNumeric = new Tester({ exclusiveMaximum: 5, maximum: 6, type: 'number' });
|
||||
bothNumeric.validatesNonNullableNumeric();
|
||||
bothNumeric.rejects('5.1');
|
||||
bothNumeric.rejects('5.0');
|
||||
bothNumeric.accepts('4.999');
|
||||
}
|
||||
});
|
||||
|
||||
test('exclusive min and min work together properly', () => {
|
||||
{
|
||||
const justMin = new Tester({ minimum: -5, type: 'number' });
|
||||
justMin.validatesNonNullableNumeric();
|
||||
justMin.rejects('-5.1');
|
||||
justMin.accepts('-5.0');
|
||||
}
|
||||
{
|
||||
const justEMin = new Tester({ exclusiveMinimum: -5, type: 'number' });
|
||||
justEMin.validatesNonNullableNumeric();
|
||||
justEMin.rejects('-5.1');
|
||||
justEMin.rejects('-5.0');
|
||||
justEMin.accepts('-4.999');
|
||||
}
|
||||
{
|
||||
const bothNumeric = new Tester({ exclusiveMinimum: -5, minimum: -4, type: 'number' });
|
||||
bothNumeric.validatesNonNullableNumeric();
|
||||
bothNumeric.rejects('-5.1');
|
||||
bothNumeric.rejects('-5.0');
|
||||
bothNumeric.rejects('-4.999');
|
||||
bothNumeric.accepts('-4');
|
||||
}
|
||||
{
|
||||
const bothNumeric = new Tester({ exclusiveMinimum: -5, minimum: -6, type: 'number' });
|
||||
bothNumeric.validatesNonNullableNumeric();
|
||||
bothNumeric.rejects('-5.1');
|
||||
bothNumeric.rejects('-5.0');
|
||||
bothNumeric.accepts('-4.999');
|
||||
}
|
||||
});
|
||||
|
||||
test('multiple of works for both integers and fractions', () => {
|
||||
{
|
||||
const onlyEvens = new Tester({ multipleOf: 2, type: 'number' });
|
||||
onlyEvens.accepts('2.0');
|
||||
onlyEvens.accepts('2');
|
||||
onlyEvens.accepts('-4');
|
||||
onlyEvens.accepts('0');
|
||||
onlyEvens.accepts('100');
|
||||
onlyEvens.rejects('100.1');
|
||||
onlyEvens.rejects('');
|
||||
onlyEvens.rejects('we');
|
||||
}
|
||||
{
|
||||
const hackyIntegers = new Tester({ multipleOf: 1, type: 'number' });
|
||||
hackyIntegers.accepts('2.0');
|
||||
hackyIntegers.rejects('.5');
|
||||
}
|
||||
{
|
||||
const halfIntegers = new Tester({ multipleOf: 0.5, type: 'number' });
|
||||
halfIntegers.accepts('0.5');
|
||||
halfIntegers.accepts('1.5');
|
||||
halfIntegers.rejects('1.51');
|
||||
}
|
||||
});
|
||||
|
||||
test('integer type correctly adds a validation', () => {
|
||||
{
|
||||
const integers = new Tester({ multipleOf: 1, type: 'integer' });
|
||||
integers.accepts('02');
|
||||
integers.accepts('2');
|
||||
integers.accepts('20');
|
||||
integers.rejects('.5');
|
||||
integers.rejects('2j');
|
||||
integers.rejects('');
|
||||
}
|
||||
});
|
||||
|
||||
test('null is allowed only when expected', () => {
|
||||
{
|
||||
const nullableIntegers = new Tester({ type: ['integer', 'null'] });
|
||||
nullableIntegers.accepts('2');
|
||||
nullableIntegers.rejects('.5');
|
||||
nullableIntegers.accepts('2.0');
|
||||
nullableIntegers.rejects('2j');
|
||||
nullableIntegers.accepts('');
|
||||
}
|
||||
{
|
||||
const nonnullableIntegers = new Tester({ type: ['integer'] });
|
||||
nonnullableIntegers.accepts('2');
|
||||
nonnullableIntegers.rejects('.5');
|
||||
nonnullableIntegers.accepts('2.0');
|
||||
nonnullableIntegers.rejects('2j');
|
||||
nonnullableIntegers.rejects('');
|
||||
}
|
||||
{
|
||||
const nullableNumbers = new Tester({ type: ['number', 'null'] });
|
||||
nullableNumbers.accepts('2');
|
||||
nullableNumbers.accepts('.5');
|
||||
nullableNumbers.accepts('2.0');
|
||||
nullableNumbers.rejects('2j');
|
||||
nullableNumbers.accepts('');
|
||||
}
|
||||
{
|
||||
const nonnullableNumbers = new Tester({ type: ['number'] });
|
||||
nonnullableNumbers.accepts('2');
|
||||
nonnullableNumbers.accepts('.5');
|
||||
nonnullableNumbers.accepts('2.0');
|
||||
nonnullableNumbers.rejects('2j');
|
||||
nonnullableNumbers.rejects('');
|
||||
}
|
||||
});
|
||||
|
||||
test('string max min length work', () => {
|
||||
{
|
||||
const min = new Tester({ minLength: 4, type: 'string' });
|
||||
min.rejects('123');
|
||||
min.accepts('1234');
|
||||
min.accepts('12345');
|
||||
}
|
||||
{
|
||||
const max = new Tester({ maxLength: 6, type: 'string' });
|
||||
max.accepts('12345');
|
||||
max.accepts('123456');
|
||||
max.rejects('1234567');
|
||||
}
|
||||
{
|
||||
const minMax = new Tester({ minLength: 4, maxLength: 6, type: 'string' });
|
||||
minMax.rejects('123');
|
||||
minMax.accepts('1234');
|
||||
minMax.accepts('12345');
|
||||
minMax.accepts('123456');
|
||||
minMax.rejects('1234567');
|
||||
}
|
||||
});
|
||||
|
||||
test('patterns work', () => {
|
||||
{
|
||||
const urls = new Tester({ pattern: '^(hello)*$', type: 'string' });
|
||||
urls.accepts('');
|
||||
urls.rejects('hel');
|
||||
urls.accepts('hello');
|
||||
urls.rejects('hellohel');
|
||||
urls.accepts('hellohello');
|
||||
}
|
||||
{
|
||||
const urls = new Tester({ pattern: '^(hello)*$', type: 'string', patternErrorMessage: 'err: must be friendly' });
|
||||
urls.accepts('');
|
||||
urls.rejects('hel').withMessage('err: must be friendly');
|
||||
urls.accepts('hello');
|
||||
urls.rejects('hellohel').withMessage('err: must be friendly');
|
||||
urls.accepts('hellohello');
|
||||
}
|
||||
});
|
||||
|
||||
test('custom error messages are shown', () => {
|
||||
const withMessage = new Tester({ minLength: 1, maxLength: 0, type: 'string', errorMessage: 'always error!' });
|
||||
withMessage.rejects('').withMessage('always error!');
|
||||
withMessage.rejects(' ').withMessage('always error!');
|
||||
withMessage.rejects('1').withMessage('always error!');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user