提交 ba1f61cc 编写于 作者: S Sandeep Somavarapu

#15361 Refactoring

- Create a model for default settings and key bindings
- Move default settings actions as editor contributions
上级 0e294079
......@@ -6,38 +6,45 @@
import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import * as network from 'vs/base/common/network';
import * as strings from 'vs/base/common/strings';
import { hasClass, getDomNodePagePosition } from 'vs/base/browser/dom';
import { parse } from 'vs/base/common/json';
import { Disposable } from 'vs/base/common/lifecycle';
import { IAction } from 'vs/base/common/actions';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import Event, { Emitter } from 'vs/base/common/event';
import { LinkedMap as Map } from 'vs/base/common/map';
import { Registry } from 'vs/platform/platform';
import { EditorOptions, EditorInput, } from 'vs/workbench/common/editor';
import { StringEditorInput } from 'vs/workbench/common/editor/stringEditorInput';
import { ICommonCodeEditor, IEditorViewState } from 'vs/editor/common/editorCommon';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { StringEditor } from 'vs/workbench/browser/parts/editor/stringEditor';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { IMessageService } from 'vs/platform/message/common/message';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEventService } from 'vs/platform/event/common/event';
import { IThemeService } from 'vs/workbench/services/themes/common/themeService';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IFoldingController, ID as FoldingContributionId } from 'vs/editor/contrib/folding/common/folding';
import { IConfigurationNode, IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import { Registry } from 'vs/platform/platform';
import { IOpenSettingsService, ISettingsGroup, ISetting } from 'vs/workbench/parts/settings/common/openSettings';
import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions';
import { ICodeEditor, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
export class AbstractSettingsInput extends StringEditorInput {
private _willDispose = new Emitter<void>();
public willDispose: Event<void> = this._willDispose.event;
public get resource(): URI {
return this.getResource();
constructor(
name: string,
description: string,
value: string,
private _resource: URI,
modeId: string,
singleton: boolean,
@IInstantiationService instantiationService: IInstantiationService
) {
super(name, description, value, modeId, singleton, instantiationService);
}
public getResource(): URI {
return this._resource;
}
public dispose() {
......@@ -47,177 +54,39 @@ export class AbstractSettingsInput extends StringEditorInput {
}
}
interface ISettingsGroup {
title: string;
sections: ISettingsSection[];
}
interface ISettingsSection {
description?: string;
settings: ISetting[];
}
interface ISetting {
key: string;
value: any;
description?: string;
}
class SettingsModel {
private settingsGroups: ISettingsGroup[];
private indent: string;
constructor( @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService) {
const editorConfig = this.configurationService.getConfiguration<any>();
this.indent = editorConfig.editor.insertSpaces ? strings.repeat(' ', editorConfig.editor.tabSize) : '\t';
const configurations = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurations();
this.settingsGroups = configurations.sort(this.compareConfigurationNodes).reduce((result, config) => this.parseConfig(config, result), []);
}
private parseConfig(config: IConfigurationNode, result: ISettingsGroup[], settingsGroup?: ISettingsGroup): ISettingsGroup[] {
if (config.title) {
if (!settingsGroup) {
settingsGroup = result.filter(g => g.title === config.title)[0];
if (!settingsGroup) {
settingsGroup = { sections: [{ settings: [] }], title: config.title };
result.push(settingsGroup);
}
} else {
settingsGroup.sections[settingsGroup.sections.length - 1].description = config.title;
}
}
if (config.properties) {
if (!settingsGroup) {
settingsGroup = { sections: [{ settings: [] }], title: config.id };
result.push(settingsGroup);
}
const configurationSettings = Object.keys(config.properties).map((key) => {
const prop = config.properties[key];
const value = prop.default;
const description = prop.description || '';
return { key, value, description };
});
settingsGroup.sections[settingsGroup.sections.length - 1].settings.push(...configurationSettings);
}
if (config.allOf) {
config.allOf.forEach(c => this.parseConfig(c, result, settingsGroup));
}
return result;
}
private compareConfigurationNodes(c1: IConfigurationNode, c2: IConfigurationNode): number {
if (typeof c1.order !== 'number') {
return 1;
}
if (typeof c2.order !== 'number') {
return -1;
}
if (c1.order === c2.order) {
const title1 = c1.title || '';
const title2 = c2.title || '';
return title1.localeCompare(title2);
}
return c1.order - c2.order;
}
public toContent(): string {
let defaultsHeader = '// ' + nls.localize('defaultSettingsHeader', "Overwrite settings by placing them into your settings file.\n");
defaultsHeader += '// ' + nls.localize('defaultSettingsHeader2', "See http://go.microsoft.com/fwlink/?LinkId=808995 for the most commonly used settings.\n\n");
let lastEntry = -1;
const result: string[] = [];
result.push('{');
for (const group of this.settingsGroups) {
result.push('// ' + group.title);
for (const section of group.sections) {
if (section.description) {
result.push(this.indent + '// ' + section.description);
}
for (const setting of section.settings) {
result.push(this.indent + '// ' + setting.description);
let valueString = JSON.stringify(setting.value, null, this.indent);
if (valueString && (typeof setting.value === 'object')) {
valueString = valueString.split('\n').join('\n' + this.indent);
}
if (lastEntry !== -1) {
result[lastEntry] += ',';
}
lastEntry = result.length;
result.push(this.indent + JSON.stringify(setting.key) + ': ' + valueString);
result.push('');
}
}
}
result.push('}');
return defaultsHeader + result.join('\n');
}
}
export class DefaultSettingsInput extends AbstractSettingsInput {
static uri: URI = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/settings.json' }); // URI is used to register JSON schema support
private static INSTANCE: DefaultSettingsInput;
public static getInstance(instantiationService: IInstantiationService, configurationService: IWorkspaceConfigurationService): DefaultSettingsInput {
public static getInstance(instantiationService: IInstantiationService, openSettingsService: IOpenSettingsService): DefaultSettingsInput {
if (!DefaultSettingsInput.INSTANCE) {
const content = instantiationService.createInstance(SettingsModel).toContent();
DefaultSettingsInput.INSTANCE = instantiationService.createInstance(DefaultSettingsInput, nls.localize('defaultName', "Default Settings"), null, content, 'application/json', false);
const defaultSettings = openSettingsService.defaultSettings;
DefaultSettingsInput.INSTANCE = instantiationService.createInstance(DefaultSettingsInput, nls.localize('defaultName', "Default Settings"), null, defaultSettings.content, defaultSettings.uri, 'application/json', false);
}
return DefaultSettingsInput.INSTANCE;
}
protected getResource(): URI {
return DefaultSettingsInput.uri;
}
}
export class DefaultKeybindingsInput extends AbstractSettingsInput {
private static INSTANCE: DefaultKeybindingsInput;
public static getInstance(instantiationService: IInstantiationService, keybindingService: IKeybindingService): DefaultKeybindingsInput {
public static getInstance(instantiationService: IInstantiationService, openSettingsService: IOpenSettingsService): DefaultKeybindingsInput {
if (!DefaultKeybindingsInput.INSTANCE) {
const defaultsHeader = '// ' + nls.localize('defaultKeybindingsHeader', "Overwrite key bindings by placing them into your key bindings file.");
const defaultContents = keybindingService.getDefaultKeybindings();
DefaultKeybindingsInput.INSTANCE = instantiationService.createInstance(DefaultKeybindingsInput, nls.localize('defaultKeybindings', "Default Keyboard Shortcuts"), null, defaultsHeader + '\n' + defaultContents, 'application/json', false);
const defaultKeybindings = openSettingsService.defaultKeybindings;
DefaultKeybindingsInput.INSTANCE = instantiationService.createInstance(DefaultKeybindingsInput, nls.localize('defaultKeybindings', "Default Keyboard Shortcuts"), null, defaultKeybindings.content, defaultKeybindings.uri, 'application/json', false);
}
return DefaultKeybindingsInput.INSTANCE;
}
protected getResource(): URI {
return URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/keybindings.json' }); // URI is used to register JSON schema support
}
}
export class DefaultSettingsEditor extends StringEditor {
public static ID = 'workbench.editors.defaultSettingsEditor';
private static VIEW_STATE: Map<URI, IEditorViewState> = new Map<URI, IEditorViewState>();
private static VIEW_STATE: Map<URI, editorCommon.IEditorViewState> = new Map<URI, editorCommon.IEditorViewState>();
private inputDisposeListener;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@IInstantiationService instantiationService: IInstantiationService,
@IWorkspaceContextService contextService: IWorkspaceContextService,
@IStorageService storageService: IStorageService,
@IMessageService messageService: IMessageService,
@IConfigurationService configurationService: IConfigurationService,
@IEventService eventService: IEventService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService,
@IThemeService themeService: IThemeService,
@ICommandService private commandService: ICommandService,
@IUntitledEditorService untitledEditorService: IUntitledEditorService
) {
super(telemetryService, instantiationService, contextService, storageService,
messageService, configurationService, eventService, editorService, themeService, untitledEditorService);
}
public getId(): string {
return DefaultSettingsEditor.ID;
}
......@@ -236,7 +105,7 @@ export class DefaultSettingsEditor extends StringEditor {
}
protected restoreViewState(input: EditorInput) {
const viewState = DefaultSettingsEditor.VIEW_STATE.get(this.getResource(<AbstractSettingsInput>input));
const viewState = DefaultSettingsEditor.VIEW_STATE.get((<AbstractSettingsInput>input).getResource());
if (viewState) {
this.getControl().restoreViewState(viewState);
} else if (input instanceof DefaultSettingsInput) {
......@@ -247,7 +116,7 @@ export class DefaultSettingsEditor extends StringEditor {
private saveState(input: AbstractSettingsInput) {
const state = this.getControl().saveViewState();
if (state) {
const resource = this.getResource(input);
const resource = input.getResource();
if (DefaultSettingsEditor.VIEW_STATE.has(resource)) {
DefaultSettingsEditor.VIEW_STATE.delete(resource);
}
......@@ -255,12 +124,8 @@ export class DefaultSettingsEditor extends StringEditor {
}
}
private getResource(input: AbstractSettingsInput): URI {
return input.resource;
}
private foldAll() {
const foldingController = (<ICommonCodeEditor>this.getControl()).getContribution<IFoldingController>(FoldingContributionId);
const foldingController = (<editorCommon.ICommonCodeEditor>this.getControl()).getContribution<IFoldingController>(FoldingContributionId);
foldingController.foldAll();
}
......@@ -272,4 +137,178 @@ export class DefaultSettingsEditor extends StringEditor {
this.inputDisposeListener = (<AbstractSettingsInput>input).willDispose(() => this.saveState(<AbstractSettingsInput>input));
}
}
}
@editorContribution
export class DefaultSettingsContribution extends Disposable implements editorCommon.IEditorContribution {
private settingsActions: SettingsActionsDecorators = null;
constructor(private editor: ICodeEditor,
@IInstantiationService private instantiationService: IInstantiationService,
@IOpenSettingsService private openSettingsService: IOpenSettingsService
) {
super();
this._register(editor.onDidChangeModel(() => this.onModelChanged()));
}
public getId(): string {
return 'editor.contrib.settings';
}
private onModelChanged(): void {
const model = this.editor.getModel();
if (!this.canHandle(model)) {
if (this.settingsActions) {
this.settingsActions.dispose();
this.settingsActions = null;
}
return;
}
if (model.uri.fsPath === this.openSettingsService.defaultSettings.uri.fsPath) {
this.styleDefaultSettings(model);
}
}
private canHandle(model: editorCommon.IModel) {
if (model) {
if (model.uri.fsPath === this.openSettingsService.defaultSettings.uri.fsPath) {
return true;
}
}
return false;
}
private styleDefaultSettings(model: editorCommon.IModel) {
this.renderDecorations(model);
}
private renderDecorations(model: editorCommon.IModel) {
this.settingsActions = this.instantiationService.createInstance(SettingsActionsDecorators, this.editor);
this.settingsActions.render(this.openSettingsService.defaultSettings.getSettingsGroups());
}
}
export class SettingsActionsDecorators extends Disposable {
private decorationIds: string[] = [];
constructor(private editor: ICodeEditor,
@IOpenSettingsService private settingsService: IOpenSettingsService,
@IContextMenuService private contextMenuService: IContextMenuService
) {
super();
this._register(editor.onMouseUp(e => this.onEditorMouseUp(e)));
}
public render(settingGroups: ISettingsGroup[]): void {
const model = this.editor.getModel();
model.changeDecorations(changeAccessor => {
this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, []);
});
model.changeDecorations(changeAccessor => {
this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, this.createDecorations(settingGroups, model));
});
}
private createDecorations(settingsGroups: ISettingsGroup[], model: editorCommon.IModel): editorCommon.IModelDeltaDecoration[] {
let result: editorCommon.IModelDeltaDecoration[] = [];
for (const settingsGroup of settingsGroups) {
for (const settingsSection of settingsGroup.sections) {
for (const setting of settingsSection.settings) {
result.push(this.createDecoration(setting, model));
}
}
}
return result;
}
private createDecoration(setting: ISetting, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration {
const jsonSchema: IJSONSchema = this.getConfigurationsMap()[setting.key];
const maxColumn = model.getLineMaxColumn(setting.range.startLineNumber + 1);
const range = {
startLineNumber: setting.range.startLineNumber + 1,
startColumn: maxColumn,
endLineNumber: setting.range.startLineNumber + 1,
endColumn: maxColumn
};
return {
range, options: {
afterContentClassName: `copySetting${(jsonSchema.enum || jsonSchema.type === 'boolean') ? '.select' : ''}`,
}
};
}
private onEditorMouseUp(e: IEditorMouseEvent): void {
let range = e.target.range;
if (!range || !range.isEmpty) {
return;
}
if (!e.event.leftButton) {
return;
}
switch (e.target.type) {
case editorCommon.MouseTargetType.CONTENT_EMPTY:
if (hasClass(<HTMLElement>e.target.element, 'copySetting')) {
this.onClick(e);
}
return;
default:
return;
}
}
private getConfigurationsMap(): { [qualifiedKey: string]: IJSONSchema } {
return Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
}
private onClick(e: IEditorMouseEvent) {
const model = this.editor.getModel();
const setting = parse('{' + model.getLineContent(e.target.range.startLineNumber) + '}');
const key = Object.keys(setting)[0];
let value = setting[key];
let jsonSchema: IJSONSchema = this.getConfigurationsMap()[key];
const actions = this.getActions(key, jsonSchema);
if (actions) {
let elementPosition = getDomNodePagePosition(<HTMLElement>e.target.element);
const anchor = { x: elementPosition.left + elementPosition.width, y: elementPosition.top + elementPosition.height + 10 };
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => TPromise.wrap(actions)
});
return;
}
this.settingsService.copyConfiguration({ key, value });
}
private getActions(key: string, jsonSchema: IJSONSchema): IAction[] {
if (jsonSchema.type === 'boolean') {
return [<IAction>{
id: 'truthyValue',
label: 'true',
enabled: true,
run: () => this.settingsService.copyConfiguration({ key, value: true })
}, <IAction>{
id: 'falsyValue',
label: 'false',
enabled: true,
run: () => this.settingsService.copyConfiguration({ key, value: false })
}];
}
if (jsonSchema.enum) {
return jsonSchema.enum.map(value => {
return <IAction>{
id: value,
label: value,
enabled: true,
run: () => this.settingsService.copyConfiguration({ key, value })
};
});
}
return null;
}
}
\ No newline at end of file
......@@ -8,13 +8,8 @@ import * as nls from 'vs/nls';
import URI from 'vs/base/common/uri';
import * as labels from 'vs/base/common/labels';
import { Delayer } from 'vs/base/common/async';
import { Disposable, dispose } from 'vs/base/common/lifecycle';
import { JSONVisitor, visit, parse, parseTree, findNodeAtLocation } from 'vs/base/common/json';
import { Registry } from 'vs/platform/platform';
import { hasClass, getDomNodePagePosition } from 'vs/base/browser/dom';
import { IAction } from 'vs/base/common/actions';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { Extensions } from 'vs/workbench/common/actionRegistry';
import { Disposable } from 'vs/base/common/lifecycle';
import { parseTree, findNodeAtLocation } from 'vs/base/common/json';
import { asFileEditorInput } from 'vs/workbench/common/editor';
import { StringEditorInput } from 'vs/workbench/common/editor/stringEditorInput';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
......@@ -25,18 +20,19 @@ import { IEditorGroupService } from 'vs/workbench/services/group/common/groupSer
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IFileService, IFileOperationResult, FileOperationResult } from 'vs/platform/files/common/files';
import { IMessageService, Severity, IChoiceService } from 'vs/platform/message/common/message';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ICodeEditor, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { IConfigurationEditingService, ConfigurationTarget, IConfigurationValue } from 'vs/workbench/services/configuration/common/configurationEditing';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IOpenSettingsService } from 'vs/workbench/parts/settings/common/openSettings';
import { DefaultSettingsInput, DefaultKeybindingsInput } from 'vs/workbench/parts/settings/browser/defaultSettingsEditors';
import { IOpenSettingsService, IDefaultSettings, IDefaultKeybindings } from 'vs/workbench/parts/settings/common/openSettings';
import { DefaultSettings, DefaultKeybindings } from 'vs/workbench/parts/settings/common/defaultSettings';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { ITextModelContentProvider } from 'vs/platform/textmodelResolver/common/resolver';
import { IModelService } from 'vs/editor/common/services/modelService';
import { IModeService } from 'vs/editor/common/services/modeService';
import { DefaultSettingsInput, DefaultKeybindingsInput } from 'vs/workbench/parts/settings/browser/defaultSettingsEditors';
const SETTINGS_INFO_IGNORE_KEY = 'settings.workspace.info.ignore';
......@@ -53,9 +49,9 @@ export class OpenSettingsService extends Disposable implements IOpenSettingsServ
_serviceBrand: any;
private configurationTarget: ConfigurationTarget = null;
private defaultSettingsActionsRenderer: SettingsActionsRenderer;
private userSettingsActionsRenderer: SettingsActionsRenderer;
private workspaceSettingsActionsRenderer: SettingsActionsRenderer;
private _defaultSettings: IDefaultSettings;
private _defaultKeybindings: IDefaultKeybindings;
constructor(
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
......@@ -65,7 +61,6 @@ export class OpenSettingsService extends Disposable implements IOpenSettingsServ
@IMessageService private messageService: IMessageService,
@IChoiceService private choiceService: IChoiceService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IKeybindingService private keybindingService: IKeybindingService,
@IInstantiationService private instantiationService: IInstantiationService,
@IStorageService private storageService: IStorageService,
@IEnvironmentService private environmentService: IEnvironmentService,
......@@ -87,6 +82,20 @@ export class OpenSettingsService extends Disposable implements IOpenSettingsServ
}));
}
public get defaultSettings(): IDefaultSettings {
if (!this._defaultSettings) {
this._defaultSettings = this.instantiationService.createInstance(DefaultSettings);
}
return this._defaultSettings;
}
public get defaultKeybindings(): IDefaultKeybindings {
if (!this._defaultKeybindings) {
this._defaultKeybindings = this.instantiationService.createInstance(DefaultKeybindings);
}
return this._defaultKeybindings;
}
openGlobalSettings(): TPromise<void> {
if (this.configurationService.hasWorkspaceConfiguration() && !this.storageService.getBoolean(SETTINGS_INFO_IGNORE_KEY, StorageScope.WORKSPACE)) {
this.promptToOpenWorkspaceSettings();
......@@ -105,7 +114,7 @@ export class OpenSettingsService extends Disposable implements IOpenSettingsServ
openGlobalKeybindingSettings(): TPromise<void> {
const emptyContents = '// ' + nls.localize('emptyKeybindingsHeader', "Place your key bindings in this file to overwrite the defaults") + '\n[\n]';
return this.openTwoEditors(DefaultKeybindingsInput.getInstance(this.instantiationService, this.keybindingService), URI.file(this.environmentService.appKeybindingsPath), emptyContents).then(() => null);
return this.openTwoEditors(DefaultKeybindingsInput.getInstance(this.instantiationService, this), URI.file(this.environmentService.appKeybindingsPath), emptyContents).then(() => null);
}
openEditableSettings(configurationTarget: ConfigurationTarget, showVisibleEditor: boolean = false): TPromise<IEditor> {
......@@ -126,6 +135,22 @@ export class OpenSettingsService extends Disposable implements IOpenSettingsServ
}));
}
public copyConfiguration(configurationValue: IConfigurationValue): void {
this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [configurationValue.key] });
this.openEditableSettings(this.configurationTarget, true).then(editor => {
const editorControl = <ICodeEditor>editor.getControl();
const disposable = editorControl.onDidChangeModelContent(() => {
new Delayer(100).trigger((): any => {
editorControl.focus();
editorControl.setSelection(this.getSelectionRange(configurationValue.key, editorControl.getModel()));
});
disposable.dispose();
});
this.configurationEditingService.writeConfiguration(this.configurationTarget, configurationValue)
.then(null, error => this.messageService.show(Severity.Error, error));
});
}
private isEditorFor(editor: IEditor, configurationTarget: ConfigurationTarget): boolean {
const fileEditorInput = asFileEditorInput(editor.input);
return !!fileEditorInput && fileEditorInput.getResource().fsPath === this.getEditableSettingsURI(configurationTarget).fsPath;
......@@ -177,8 +202,7 @@ export class OpenSettingsService extends Disposable implements IOpenSettingsServ
if (openDefaultSettings) {
const emptySettingsContents = this.getEmptyEditableSettingsContent(configurationTarget);
const settingsResource = this.getEditableSettingsURI(configurationTarget);
return this.openTwoEditors(DefaultSettingsInput.getInstance(this.instantiationService, this.configurationService), settingsResource, emptySettingsContents)
.then(editors => this.renderActionsForDefaultSettings(editors[0]));
return this.openTwoEditors(DefaultSettingsInput.getInstance(this.instantiationService, this), settingsResource, emptySettingsContents).then(() => null);
}
return this.openEditableSettings(configurationTarget).then(() => null);
}
......@@ -217,61 +241,6 @@ export class OpenSettingsService extends Disposable implements IOpenSettingsServ
resource.fsPath === this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE).fsPath ? ConfigurationTarget.WORKSPACE : null;
}
private renderActionsForDefaultSettings(defaultSettingsEditor: IEditor) {
const defaultSettingsEditorControl = <ICodeEditor>defaultSettingsEditor.getControl();
if (!this.defaultSettingsActionsRenderer) {
this.defaultSettingsActionsRenderer = this.instantiationService.createInstance(SettingsActionsRenderer, defaultSettingsEditorControl, this.copyConfiguration.bind(this));
const disposable = defaultSettingsEditorControl.getModel().onWillDispose(() => {
this.defaultSettingsActionsRenderer.dispose();
this.defaultSettingsActionsRenderer = null;
dispose(disposable);
});
}
this.defaultSettingsActionsRenderer.render();
}
protected renderActionsForUserSettingsEditor(settingsEditor: IEditor) {
const settingsEditorControl = <ICodeEditor>settingsEditor.getControl();
if (!this.userSettingsActionsRenderer) {
this.userSettingsActionsRenderer = this.instantiationService.createInstance(SettingsActionsRenderer, settingsEditorControl, this.copyConfiguration.bind(this));
const disposable = settingsEditorControl.getModel().onWillDispose(() => {
this.userSettingsActionsRenderer.dispose();
this.userSettingsActionsRenderer = null;
dispose(disposable);
});
}
this.userSettingsActionsRenderer.render();
}
protected renderActionsForWorkspaceSettingsEditor(settingsEditor: IEditor) {
const settingsEditorControl = <ICodeEditor>settingsEditor.getControl();
if (!this.workspaceSettingsActionsRenderer) {
this.workspaceSettingsActionsRenderer = this.instantiationService.createInstance(SettingsActionsRenderer, settingsEditorControl, this.copyConfiguration.bind(this));
const disposable = settingsEditorControl.getModel().onWillDispose(() => {
this.workspaceSettingsActionsRenderer.dispose();
this.workspaceSettingsActionsRenderer = null;
dispose(disposable);
});
}
this.workspaceSettingsActionsRenderer.render();
}
private copyConfiguration(configurationValue: IConfigurationValue) {
this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [configurationValue.key] });
this.openEditableSettings(this.configurationTarget, true).then(editor => {
const editorControl = <ICodeEditor>editor.getControl();
const disposable = editorControl.onDidChangeModelContent(() => {
new Delayer(100).trigger((): any => {
editorControl.focus();
editorControl.setSelection(this.getSelectionRange(configurationValue.key, editorControl.getModel()));
});
disposable.dispose();
});
this.configurationEditingService.writeConfiguration(this.configurationTarget, configurationValue)
.then(null, error => this.messageService.show(Severity.Error, error));
});
}
private getSelectionRange(setting: string, model: editorCommon.IModel): editorCommon.IRange {
const tree = parseTree(model.getValue());
const node = findNodeAtLocation(tree, [setting]);
......@@ -285,155 +254,28 @@ export class OpenSettingsService extends Disposable implements IOpenSettingsServ
}
}
class SettingsActionsRenderer extends Disposable {
private decorationIds: string[] = [];
export class SettingsContentProvider implements ITextModelContentProvider {
constructor(private settingsEditor: ICodeEditor,
private copyConfiguration: (configurationValue: IConfigurationValue) => void,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IWorkspaceContextService private contextService: IWorkspaceContextService,
@IConfigurationService private configurationService: IConfigurationService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IMessageService private messageService: IMessageService
constructor(
@IOpenSettingsService private openSettingsService: IOpenSettingsService,
@IModelService private modelService: IModelService,
@IModeService private modeService: IModeService
) {
super();
this._register(this.settingsEditor.onMouseUp(e => this.onEditorMouseUp(e)));
this._register(this.settingsEditor.getModel().onDidChangeContent(() => this.render()));
}
public render(): void {
const defaultSettingsModel = this.settingsEditor.getModel();
if (defaultSettingsModel) {
defaultSettingsModel.changeDecorations(changeAccessor => {
this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, []);
});
defaultSettingsModel.changeDecorations(changeAccessor => {
this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, this.createDecorations());
});
}
}
private createDecorations(): editorCommon.IModelDeltaDecoration[] {
const settingsModel = this.settingsEditor.getModel();
let result: editorCommon.IModelDeltaDecoration[] = [];
let parsingConfigurations = false;
let parsingConfiguration = false;
let visitor: JSONVisitor = {
onObjectBegin: (offset: number, length: number) => {
if (parsingConfigurations) {
parsingConfiguration = true;
} else {
parsingConfigurations = true;
}
},
onObjectProperty: (property: string, offset: number, length: number) => {
if (!parsingConfiguration) {
result.push(this.createDecoration(property, offset, settingsModel));
}
},
onObjectEnd: () => {
if (parsingConfiguration) {
parsingConfiguration = false;
} else {
parsingConfigurations = false;
}
},
};
visit(settingsModel.getValue(), visitor);
return result;
}
private createDecoration(property: string, offset: number, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration {
const jsonSchema: IJSONSchema = this.getConfigurationsMap()[property];
const position = model.getPositionAt(offset);
const maxColumn = model.getLineMaxColumn(position.lineNumber);
const range = {
startLineNumber: position.lineNumber,
startColumn: maxColumn,
endLineNumber: position.lineNumber,
endColumn: maxColumn
};
return {
range, options: {
afterContentClassName: `copySetting${(jsonSchema.enum || jsonSchema.type === 'boolean') ? '.select' : ''}`,
}
};
}
private onEditorMouseUp(e: IEditorMouseEvent): void {
let range = e.target.range;
if (!range || !range.isEmpty) {
return;
}
if (!e.event.leftButton) {
return;
}
switch (e.target.type) {
case editorCommon.MouseTargetType.CONTENT_EMPTY:
if (hasClass(<HTMLElement>e.target.element, 'copySetting')) {
this.onClick(e);
}
return;
default:
return;
}
}
private getConfigurationsMap(): { [qualifiedKey: string]: IJSONSchema } {
return Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration).getConfigurationProperties();
}
private onClick(e: IEditorMouseEvent) {
const model = this.settingsEditor.getModel();
const setting = parse('{' + model.getLineContent(e.target.range.startLineNumber) + '}');
const key = Object.keys(setting)[0];
let value = setting[key];
let jsonSchema: IJSONSchema = this.getConfigurationsMap()[key];
const actions = this.getActions(key, jsonSchema);
if (actions) {
let elementPosition = getDomNodePagePosition(<HTMLElement>e.target.element);
const anchor = { x: elementPosition.left + elementPosition.width, y: elementPosition.top + elementPosition.height + 10 };
this.contextMenuService.showContextMenu({
getAnchor: () => anchor,
getActions: () => TPromise.wrap(actions)
});
return;
public provideTextContent(uri: URI): TPromise<editorCommon.IModel> {
if (uri.scheme !== 'vscode') {
return null;
}
this.copyConfiguration({ key, value });
}
private getActions(key: string, jsonSchema: IJSONSchema): IAction[] {
if (jsonSchema.type === 'boolean') {
return [<IAction>{
id: 'truthyValue',
label: 'true',
enabled: true,
run: () => {
this.copyConfiguration({ key, value: true });
}
}, <IAction>{
id: 'falsyValue',
label: 'false',
enabled: true,
run: () => {
this.copyConfiguration({ key, value: false });
}
}];
const defaultSettings = this.openSettingsService.defaultSettings;
if (defaultSettings.uri.fsPath === uri.fsPath) {
let mode = this.modeService.getOrCreateMode('application/json');
return TPromise.as(this.modelService.createModel(defaultSettings.content, mode, uri));
}
if (jsonSchema.enum) {
return jsonSchema.enum.map(value => {
return <IAction>{
id: value,
label: value,
enabled: true,
run: () => {
this.copyConfiguration({ key, value });
}
};
});
const defaultKeybindings = this.openSettingsService.defaultKeybindings;
if (defaultKeybindings.uri.fsPath === uri.fsPath) {
let mode = this.modeService.getOrCreateMode('application/json');
return TPromise.as(this.modelService.createModel(defaultKeybindings.content, mode, uri));
}
return null;
}
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as network from 'vs/base/common/network';
import * as strings from 'vs/base/common/strings';
import URI from 'vs/base/common/uri';
import { Registry } from 'vs/platform/platform';
import { IConfigurationNode, IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import { IDefaultSettings, IDefaultKeybindings, ISettingsGroup } from 'vs/workbench/parts/settings/common/openSettings';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
export class DefaultSettings implements IDefaultSettings {
private _uri: URI = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/settings.json' });
private _content: string;
private _settingsGroups: ISettingsGroup[];
private indent: string;
constructor( @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService) {
const editorConfig = this.configurationService.getConfiguration<any>();
this.indent = editorConfig.editor.insertSpaces ? strings.repeat(' ', editorConfig.editor.tabSize) : '\t';
const configurations = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurations();
this._settingsGroups = configurations.sort(this.compareConfigurationNodes).reduce((result, config) => this.parseConfig(config, result), []);
this._content = this.toContent();
}
public get uri(): URI {
return this._uri;
}
public get content(): string {
return this._content;
}
public getSettingsGroups(): ISettingsGroup[] {
return this._settingsGroups;
}
private parseConfig(config: IConfigurationNode, result: ISettingsGroup[], settingsGroup?: ISettingsGroup): ISettingsGroup[] {
if (config.title) {
if (!settingsGroup) {
settingsGroup = result.filter(g => g.title === config.title)[0];
if (!settingsGroup) {
settingsGroup = { sections: [{ settings: [] }], title: config.title };
result.push(settingsGroup);
}
} else {
settingsGroup.sections[settingsGroup.sections.length - 1].description = config.title;
}
}
if (config.properties) {
if (!settingsGroup) {
settingsGroup = { sections: [{ settings: [] }], title: config.id };
result.push(settingsGroup);
}
const configurationSettings = Object.keys(config.properties).map((key) => {
const prop = config.properties[key];
const value = prop.default;
const description = prop.description || '';
return { key, value, description };
});
settingsGroup.sections[settingsGroup.sections.length - 1].settings.push(...configurationSettings);
}
if (config.allOf) {
config.allOf.forEach(c => this.parseConfig(c, result, settingsGroup));
}
return result;
}
private compareConfigurationNodes(c1: IConfigurationNode, c2: IConfigurationNode): number {
if (typeof c1.order !== 'number') {
return 1;
}
if (typeof c2.order !== 'number') {
return -1;
}
if (c1.order === c2.order) {
const title1 = c1.title || '';
const title2 = c2.title || '';
return title1.localeCompare(title2);
}
return c1.order - c2.order;
}
private toContent(): string {
let defaultsHeader = '// ' + nls.localize('defaultSettingsHeader', "Overwrite settings by placing them into your settings file.\n");
defaultsHeader += '// ' + nls.localize('defaultSettingsHeader2', "See http://go.microsoft.com/fwlink/?LinkId=808995 for the most commonly used settings.\n\n");
let lastEntry = -1;
const result: string[] = [];
result.push('{');
let lineNumber = 4; // Beginning of settings
for (const group of this._settingsGroups) {
result.push('// ' + group.title);
lineNumber++;
group.range = { startLineNumber: lineNumber, startColumn: 1, endLineNumber: lineNumber, endColumn: 1 };
for (const section of group.sections) {
if (section.description) {
result.push(this.indent + '// ' + section.description);
lineNumber++;
section.range = { startLineNumber: lineNumber, startColumn: 1, endLineNumber: lineNumber, endColumn: 1 };
}
for (const setting of section.settings) {
result.push(this.indent + '// ' + setting.description);
lineNumber++;
const settingStart = lineNumber;
let valueString = JSON.stringify(setting.value, null, this.indent);
let valueLines = 1;
if (valueString && (typeof setting.value === 'object')) {
const mulitLineValue = valueString.split('\n');
valueString = mulitLineValue.join('\n' + this.indent);
valueLines = mulitLineValue.length;
}
if (lastEntry !== -1) {
result[lastEntry] += ',';
}
lastEntry = result.length;
result.push(this.indent + JSON.stringify(setting.key) + ': ' + valueString);
lineNumber += valueLines;
setting.range = { startLineNumber: settingStart, startColumn: 1, endLineNumber: lineNumber, endColumn: 1 };
result.push('');
lineNumber++;
}
}
}
result.push('}');
return defaultsHeader + result.join('\n');
}
}
export class DefaultKeybindings implements IDefaultKeybindings {
private _uri: URI;
private _content: string;
constructor( @IKeybindingService keybindingService: IKeybindingService) {
this._uri = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/keybindings.json' });
const defaultsHeader = '// ' + nls.localize('defaultKeybindingsHeader', "Overwrite key bindings by placing them into your key bindings file.");
this._content = defaultsHeader + '\n' + keybindingService.getDefaultKeybindings();
}
public get uri(): URI {
return this._uri;
}
public get content(): string {
return this._content;
}
}
\ No newline at end of file
......@@ -5,14 +5,51 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IRange } from 'vs/editor/common/editorCommon';
import URI from 'vs/base/common/uri';
import { IConfigurationValue } from 'vs/workbench/services/configuration/common/configurationEditing';
export interface ISettingsGroup {
range?: IRange;
title: string;
sections: ISettingsSection[];
}
export interface ISettingsSection {
range?: IRange;
description?: string;
settings: ISetting[];
}
export interface ISetting {
range?: IRange;
key: string;
value: any;
description?: string;
}
export interface IDefaultSettings {
uri: URI;
content: string;
getSettingsGroups(): ISettingsGroup[];
}
export interface IDefaultKeybindings {
uri: URI;
content: string;
}
export const IOpenSettingsService = createDecorator<IOpenSettingsService>('openSettingsService');
export interface IOpenSettingsService {
_serviceBrand: any;
defaultSettings: IDefaultSettings;
defaultKeybindings: IDefaultKeybindings;
openGlobalSettings(): TPromise<void>;
openWorkspaceSettings(): TPromise<void>;
openGlobalKeybindingSettings(): TPromise<void>;
copyConfiguration(configurationValue: IConfigurationValue): void;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册