From 115cff4c4e3cfd7b60c6f9086f287727f7d2a284 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 2 Feb 2017 11:13:45 +0100 Subject: [PATCH] Refactor preferences editor - separate renderers from editors --- .../preferences/browser/preferencesEditor.ts | 876 +---------------- .../browser/preferencesRenderers.ts | 885 ++++++++++++++++++ 2 files changed, 892 insertions(+), 869 deletions(-) create mode 100644 src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index 58dca47cf40..c1c90dec013 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -10,11 +10,6 @@ import * as DOM from 'vs/base/browser/dom'; import { Delayer } from 'vs/base/common/async'; import { Dimension, Builder } from 'vs/base/browser/builder'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; -import { flatten, distinct } from 'vs/base/common/arrays'; -import { ArrayNavigator, IIterator } from 'vs/base/common/iterator'; -import { IAction } from 'vs/base/common/actions'; -import { IJSONSchema } from 'vs/base/common/jsonSchema'; -import Event, { Emitter } from 'vs/base/common/event'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor'; import { Registry } from 'vs/platform/platform'; @@ -23,22 +18,19 @@ import { BaseEditor, EditorDescriptor } from 'vs/workbench/browser/parts/editor/ import { ResourceEditorModel } from 'vs/workbench/common/editor/resourceEditorModel'; import { IEditorControl, IEditor } from 'vs/platform/editor/common/editor'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { CodeEditor } from 'vs/editor/browser/codeEditor'; -import { Range } from 'vs/editor/common/core/range'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { - IPreferencesService, ISettingsGroup, ISetting, IPreferencesEditorModel, IFilterResult, CONTEXT_DEFAULT_SETTINGS_EDITOR, + IPreferencesService, ISettingsGroup, ISetting, IFilterResult, CONTEXT_DEFAULT_SETTINGS_EDITOR, DEFAULT_EDITOR_COMMAND_COLLAPSE_ALL, DEFAULT_EDITOR_COMMAND_FOCUS_SEARCH, ISettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferences'; import { SettingsEditorModel, DefaultSettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels'; import { editorContribution } from 'vs/editor/browser/editorBrowserExtensions'; -import { ICodeEditor, IEditorMouseEvent, IEditorContributionCtor } from 'vs/editor/browser/editorBrowser'; -import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/browser/contextView'; -import { SearchWidget, SettingsTabsWidget, SettingsGroupTitleWidget, EditPreferenceWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; -import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ICodeEditor, IEditorContributionCtor } from 'vs/editor/browser/editorBrowser'; +import { SearchWidget, SettingsTabsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { CommonEditorRegistry, EditorCommand, Command } from 'vs/editor/common/editorCommonExtensions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; @@ -46,18 +38,14 @@ import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IMessageService, Severity } from 'vs/platform/message/common/message'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService'; import { ITextModelResolverService } from 'vs/editor/common/services/resolverService'; -import { RangeHighlightDecorations } from 'vs/workbench/common/editor/rangeDecorations'; -import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { VSash } from 'vs/base/browser/ui/sash/sash'; import { Widget } from 'vs/base/browser/ui/widget'; -import { overrideIdentifierFromKey } from 'vs/platform/configuration/common/model'; -import { IMarkerService, IMarkerData } from 'vs/platform/markers/common/markers'; -import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { IPreferencesRenderer, DefaultSettingsRenderer, UserSettingsRenderer, WorkspaceSettingsRenderer } from 'vs/workbench/parts/preferences/browser/preferencesRenderers'; // Ignore following contributions import { FoldingController } from 'vs/editor/contrib/folding/browser/folding'; @@ -506,19 +494,6 @@ class DefaultPreferencesCodeEditor extends CodeEditor { } } -export interface IPreferencesRenderer extends IDisposable { - iterator: IIterator; - onFocusPreference: Event; - onClearFocusPreference: Event; - onUpdatePreference: Event<{ key: string, value: any, source: T }>; - preferencesModel: IPreferencesEditorModel; - render(): void; - updatePreference(key: string, value: any, source: T): void; - filterPreferences(filterResult: IFilterResult): void; - focusPreference(setting: T): void; - clearFocus(setting: T): void; -} - export abstract class PreferencesEditorContribution extends Disposable implements editorCommon.IEditorContribution { private preferencesRenderer: IPreferencesRenderer; @@ -573,7 +548,7 @@ export class DefaultSettingsEditorContribution extends PreferencesEditorContribu return this.preferencesService.resolvePreferencesEditorModel(this.editor.getModel().uri) .then(editorModel => { if (editorModel instanceof DefaultSettingsEditorModel) { - return this.instantiationService.createInstance(DefaultSettingsRenderer, this.editor, editorModel); + return this.instantiationService.createInstance(DefaultSettingsRenderer, this.editor, editorModel, () => (this.editor).settingsModel); } return null; }); @@ -607,843 +582,6 @@ export class SettingsEditorContribution extends PreferencesEditorContribution { - - private settingHighlighter: SettingHighlighter; - private editSettingActionRenderer: EditSettingRenderer; - private highlightPreferencesRenderer: HighlightPreferencesRenderer; - private modelChangeDelayer: Delayer = new Delayer(200); - - private _onFocusPreference: Emitter = new Emitter(); - public readonly onFocusPreference: Event = this._onFocusPreference.event; - - private _onUpdatePreference: Emitter<{ key: string, value: any, source: ISetting }> = new Emitter<{ key: string, value: any, source: ISetting }>(); - public readonly onUpdatePreference: Event<{ key: string, value: any, source: ISetting }> = this._onUpdatePreference.event; - - private _onClearFocusPreference: Emitter = new Emitter(); - public readonly onClearFocusPreference: Event = this._onClearFocusPreference.event; - - private filterResult: IFilterResult; - - constructor(protected editor: ICodeEditor, public readonly preferencesModel: SettingsEditorModel, private defaultPreferencesModel: IPreferencesEditorModel, - @IPreferencesService protected preferencesService: IPreferencesService, - @ITelemetryService private telemetryService: ITelemetryService, - @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, - @IMessageService private messageService: IMessageService, - @IInstantiationService protected instantiationService: IInstantiationService - ) { - super(); - this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor, this._onFocusPreference, this._onClearFocusPreference)); - this.highlightPreferencesRenderer = this._register(instantiationService.createInstance(HighlightPreferencesRenderer, editor)); - this.editSettingActionRenderer = this._register(this.instantiationService.createInstance(EditSettingRenderer, this.editor, this.preferencesModel, () => defaultPreferencesModel, this.settingHighlighter)); - this._register(this.editSettingActionRenderer.onUpdateSetting(({key, value, source}) => this.updatePreference(key, value, source))); - this._register(this.editor.getModel().onDidChangeContent(() => this.modelChangeDelayer.trigger(() => this.onModelChanged()))); - } - - public get iterator(): IIterator { - return null; - } - - public render(): void { - this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups); - if (this.filterResult) { - this.filterPreferences(this.filterResult); - } - } - - public updatePreference(key: string, value: any, source: ISetting): void { - this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [key] }); - const overrideIdentifier = source.overrideOf ? overrideIdentifierFromKey(source.overrideOf.key) : null; - this.configurationEditingService.writeConfiguration(this.preferencesModel.configurationTarget, { key, value, overrideIdentifier }, { writeToBuffer: true, autoSave: true }) - .then(() => this.onSettingUpdated(source), error => this.messageService.show(Severity.Error, error)); - } - - private onModelChanged(): void { - if (!this.editor.getModel()) { - // model could have been disposed during the delay - return; - } - this.render(); - } - - private onSettingUpdated(setting: ISetting) { - this.editor.focus(); - setting = this.getSetting(setting); - if (setting) { - // TODO:@sandy Selection range should be template range - this.editor.setSelection(setting.valueRange); - this.settingHighlighter.highlight(setting, true); - } - } - - private getSetting(setting: ISetting): ISetting { - const {key, overrideOf} = setting; - if (overrideOf) { - const setting = this.getSetting(overrideOf); - for (const override of setting.overrides) { - if (override.key === key) { - return override; - } - } - return null; - } - return this.preferencesModel.getPreference(key); - } - - public filterPreferences(filterResult: IFilterResult): void { - this.filterResult = filterResult; - this.highlightPreferencesRenderer.render([]); - this.settingHighlighter.clear(true); - if (this.defaultPreferencesModel && filterResult) { - const settings = distinct(filterResult.filteredGroups.reduce((settings: ISetting[], settingsGroup: ISettingsGroup) => { - for (const section of settingsGroup.sections) { - for (const setting of section.settings) { - const s = this.getSetting(setting); - if (s) { - settings.push(s); - } - } - } - return settings; - }, [])); - this.highlightPreferencesRenderer.render(settings); - } - } - - public focusPreference(setting: ISetting): void { - const s = this.getSetting(setting); - if (s) { - this.settingHighlighter.highlight(s, true); - } else { - this.settingHighlighter.clear(true); - } - } - - public clearFocus(setting: ISetting): void { - this.settingHighlighter.clear(true); - } -} - -class WorkspaceSettingsRenderer extends UserSettingsRenderer implements IPreferencesRenderer { - - private untrustedSettingRenderer: UnTrustedWorkspaceSettingsRenderer; - - constructor(editor: ICodeEditor, preferencesModel: SettingsEditorModel, defaultPreferencesModel: IPreferencesEditorModel, - @IPreferencesService preferencesService: IPreferencesService, - @ITelemetryService telemetryService: ITelemetryService, - @IConfigurationEditingService configurationEditingService: IConfigurationEditingService, - @IMessageService messageService: IMessageService, - @IInstantiationService instantiationService: IInstantiationService - ) { - super(editor, preferencesModel, defaultPreferencesModel, preferencesService, telemetryService, configurationEditingService, messageService, instantiationService); - this.untrustedSettingRenderer = this._register(instantiationService.createInstance(UnTrustedWorkspaceSettingsRenderer, editor, preferencesModel)); - } - - public render(): void { - super.render(); - this.untrustedSettingRenderer.render(); - } -} - -export class DefaultSettingsRenderer extends Disposable implements IPreferencesRenderer { - - private defaultSettingsEditorContextKey: IContextKey; - - private settingHighlighter: SettingHighlighter; - private settingsGroupTitleRenderer: SettingsGroupTitleRenderer; - private filteredMatchesRenderer: FilteredMatchesRenderer; - private filteredSettingsNavigationRenderer: FilteredSettingsNavigationRenderer; - private hiddenAreasRenderer: HiddenAreasRenderer; - private editSettingActionRenderer: EditSettingRenderer; - - private _onUpdatePreference: Emitter<{ key: string, value: any, source: ISetting }> = new Emitter<{ key: string, value: any, source: ISetting }>(); - public readonly onUpdatePreference: Event<{ key: string, value: any, source: ISetting }> = this._onUpdatePreference.event; - - private _onFocusPreference: Emitter = new Emitter(); - public readonly onFocusPreference: Event = this._onFocusPreference.event; - - private _onClearFocusPreference: Emitter = new Emitter(); - public readonly onClearFocusPreference: Event = this._onClearFocusPreference.event; - - constructor(protected editor: ICodeEditor, public readonly preferencesModel: DefaultSettingsEditorModel, - @IPreferencesService protected preferencesService: IPreferencesService, - @IContextKeyService contextKeyService: IContextKeyService, - @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @IInstantiationService protected instantiationService: IInstantiationService - ) { - super(); - this.defaultSettingsEditorContextKey = CONTEXT_DEFAULT_SETTINGS_EDITOR.bindTo(contextKeyService); - this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor, this._onFocusPreference, this._onClearFocusPreference)); - this.settingsGroupTitleRenderer = this._register(instantiationService.createInstance(SettingsGroupTitleRenderer, editor)); - this.filteredMatchesRenderer = this._register(instantiationService.createInstance(FilteredMatchesRenderer, editor)); - this.filteredSettingsNavigationRenderer = this._register(instantiationService.createInstance(FilteredSettingsNavigationRenderer, editor, this.settingHighlighter)); - this.editSettingActionRenderer = this._register(instantiationService.createInstance(EditSettingRenderer, editor, preferencesModel, () => (this.editor).settingsModel, this.settingHighlighter)); - this._register(this.editSettingActionRenderer.onUpdateSetting(e => this._onUpdatePreference.fire(e))); - const paranthesisHidingRenderer = this._register(instantiationService.createInstance(StaticContentHidingRenderer, editor, preferencesModel.settingsGroups)); - this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, paranthesisHidingRenderer])); - - this._register(this.settingsGroupTitleRenderer.onHiddenAreasChanged(() => this.hiddenAreasRenderer.render())); - } - - public get iterator(): IIterator { - return this.filteredSettingsNavigationRenderer; - } - - public render() { - this.defaultSettingsEditorContextKey.set(true); - this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups); - this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups); - this.hiddenAreasRenderer.render(); - this.filteredSettingsNavigationRenderer.render([]); - this.settingsGroupTitleRenderer.showGroup(1); - this.hiddenAreasRenderer.render(); - } - - public filterPreferences(filterResult: IFilterResult): void { - if (!filterResult) { - this.filteredSettingsNavigationRenderer.render([]); - this.filteredMatchesRenderer.render(null); - this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups); - this.settingsGroupTitleRenderer.showGroup(1); - } else { - this.filteredMatchesRenderer.render(filterResult); - this.settingsGroupTitleRenderer.render(filterResult.filteredGroups); - this.filteredSettingsNavigationRenderer.render(filterResult.filteredGroups); - } - this.hiddenAreasRenderer.render(); - } - - public focusPreference(setting: ISetting): void { - this.settingsGroupTitleRenderer.showSetting(setting); - this.settingHighlighter.highlight(setting, true); - } - - public clearFocus(setting: ISetting): void { - this.settingHighlighter.clear(true); - } - - public collapseAll() { - this.settingsGroupTitleRenderer.collapseAll(); - } - - public updatePreference(key: string, value: any, source: ISetting): void { - } - - dispose() { - this.defaultSettingsEditorContextKey.set(false); - super.dispose(); - } -} - -export interface HiddenAreasProvider { - hiddenAreas: editorCommon.IRange[]; -} - -export class StaticContentHidingRenderer extends Disposable implements HiddenAreasProvider { - - constructor(private editor: ICodeEditor, private settingsGroups: ISettingsGroup[] - ) { - super(); - } - - get hiddenAreas(): editorCommon.IRange[] { - const model = this.editor.getModel(); - return [ - { - startLineNumber: 1, - startColumn: model.getLineMinColumn(1), - endLineNumber: 2, - endColumn: model.getLineMaxColumn(2) - }, - { - startLineNumber: this.settingsGroups[0].range.endLineNumber + 1, - startColumn: model.getLineMinColumn(this.settingsGroups[0].range.endLineNumber + 1), - endLineNumber: this.settingsGroups[0].range.endLineNumber + 4, - endColumn: model.getLineMaxColumn(this.settingsGroups[0].range.endLineNumber + 4) - }, - { - startLineNumber: model.getLineCount() - 1, - startColumn: model.getLineMinColumn(model.getLineCount() - 1), - endLineNumber: model.getLineCount(), - endColumn: model.getLineMaxColumn(model.getLineCount()) - } - ]; - } - -} - -export class SettingsGroupTitleRenderer extends Disposable implements HiddenAreasProvider { - - private _onHiddenAreasChanged: Emitter = new Emitter(); - get onHiddenAreasChanged(): Event { return this._onHiddenAreasChanged.event; }; - - private settingsGroups: ISettingsGroup[]; - private hiddenGroups: ISettingsGroup[] = []; - private settingsGroupTitleWidgets: SettingsGroupTitleWidget[]; - private disposables: IDisposable[] = []; - - constructor(private editor: ICodeEditor, - @IInstantiationService private instantiationService: IInstantiationService - ) { - super(); - } - - public get hiddenAreas(): editorCommon.IRange[] { - const hiddenAreas: editorCommon.IRange[] = []; - for (const group of this.hiddenGroups) { - hiddenAreas.push(group.range); - } - return hiddenAreas; - } - - public render(settingsGroups: ISettingsGroup[]) { - this.disposeWidgets(); - this.settingsGroups = settingsGroups.slice(); - this.settingsGroupTitleWidgets = []; - for (const group of this.settingsGroups.slice().reverse()) { - const settingsGroupTitleWidget = this.instantiationService.createInstance(SettingsGroupTitleWidget, this.editor, group); - settingsGroupTitleWidget.render(); - this.settingsGroupTitleWidgets.push(settingsGroupTitleWidget); - this.disposables.push(settingsGroupTitleWidget); - this.disposables.push(settingsGroupTitleWidget.onToggled(collapsed => this.onToggled(collapsed, settingsGroupTitleWidget.settingsGroup))); - } - this.settingsGroupTitleWidgets.reverse(); - } - - public showGroup(group: number) { - this.hiddenGroups = this.settingsGroups.filter((g, i) => i !== group - 1); - for (const groupTitleWidget of this.settingsGroupTitleWidgets.filter((g, i) => i !== group - 1)) { - groupTitleWidget.toggleCollapse(true); - } - this._onHiddenAreasChanged.fire(); - } - - public showSetting(setting: ISetting): void { - const settingsGroupTitleWidget = this.settingsGroupTitleWidgets.filter(widget => Range.containsRange(widget.settingsGroup.range, setting.range))[0]; - if (settingsGroupTitleWidget && settingsGroupTitleWidget.isCollapsed()) { - settingsGroupTitleWidget.toggleCollapse(false); - this.hiddenGroups.splice(this.hiddenGroups.indexOf(settingsGroupTitleWidget.settingsGroup), 1); - this._onHiddenAreasChanged.fire(); - } - } - - public collapseAll() { - this.editor.setPosition({ lineNumber: 1, column: 1 }); - this.hiddenGroups = this.settingsGroups.slice(); - for (const groupTitleWidget of this.settingsGroupTitleWidgets) { - groupTitleWidget.toggleCollapse(true); - } - this._onHiddenAreasChanged.fire(); - } - - private onToggled(collapsed: boolean, group: ISettingsGroup) { - const index = this.hiddenGroups.indexOf(group); - if (collapsed) { - const currentPosition = this.editor.getPosition(); - if (group.range.startLineNumber <= currentPosition.lineNumber && group.range.endLineNumber >= currentPosition.lineNumber) { - this.editor.setPosition({ lineNumber: group.range.startLineNumber - 1, column: 1 }); - } - this.hiddenGroups.push(group); - } else { - this.hiddenGroups.splice(index, 1); - } - this._onHiddenAreasChanged.fire(); - } - - private disposeWidgets() { - this.hiddenGroups = []; - this.disposables = dispose(this.disposables); - } - - public dispose() { - this.disposeWidgets(); - super.dispose(); - } -} - -export class HiddenAreasRenderer extends Disposable { - - constructor(private editor: ICodeEditor, private hiddenAreasProviders: HiddenAreasProvider[], - @IInstantiationService private instantiationService: IInstantiationService - ) { - super(); - } - - public render() { - const ranges: editorCommon.IRange[] = []; - for (const hiddenAreaProvider of this.hiddenAreasProviders) { - ranges.push(...hiddenAreaProvider.hiddenAreas); - } - this.editor.setHiddenAreas(ranges); - } - - public dispose() { - this.editor.setHiddenAreas([]); - super.dispose(); - } -} - -export class FilteredMatchesRenderer extends Disposable implements HiddenAreasProvider { - - private decorationIds: string[] = []; - public hiddenAreas: editorCommon.IRange[] = []; - - constructor(private editor: ICodeEditor, - @IInstantiationService private instantiationService: IInstantiationService - ) { - super(); - } - - public render(result: IFilterResult): void { - const model = this.editor.getModel(); - this.hiddenAreas = []; - this.editor.changeDecorations(changeAccessor => { - this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, []); - }); - if (result) { - this.hiddenAreas = this.computeHiddenRanges(result.filteredGroups, result.allGroups, model); - this.editor.changeDecorations(changeAccessor => { - this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, flatten(result.matches.values()).map(match => this.createDecoration(match, model))); - }); - } - } - - private createDecoration(range: editorCommon.IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration { - return { - range, - options: { - stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - className: 'findMatch' - } - }; - } - - private computeHiddenRanges(filteredGroups: ISettingsGroup[], allSettingsGroups: ISettingsGroup[], model: editorCommon.IModel): editorCommon.IRange[] { - const notMatchesRanges: editorCommon.IRange[] = []; - for (const group of allSettingsGroups) { - const filteredGroup = filteredGroups.filter(g => g.title === group.title)[0]; - if (!filteredGroup) { - notMatchesRanges.push({ - startLineNumber: group.range.startLineNumber - 1, - startColumn: model.getLineMinColumn(group.range.startLineNumber - 1), - endLineNumber: group.range.endLineNumber, - endColumn: model.getLineMaxColumn(group.range.endLineNumber), - }); - } else { - for (const section of group.sections) { - if (section.titleRange) { - if (!this.containsLine(section.titleRange.startLineNumber, filteredGroup)) { - notMatchesRanges.push(this.createCompleteRange(section.titleRange, model)); - } - } - for (const setting of section.settings) { - if (!this.containsLine(setting.range.startLineNumber, filteredGroup)) { - notMatchesRanges.push(this.createCompleteRange(setting.range, model)); - } - } - } - } - } - return notMatchesRanges; - } - - private containsLine(lineNumber: number, settingsGroup: ISettingsGroup): boolean { - if (settingsGroup.titleRange && lineNumber >= settingsGroup.titleRange.startLineNumber && lineNumber <= settingsGroup.titleRange.endLineNumber) { - return true; - } - - for (const section of settingsGroup.sections) { - if (section.titleRange && lineNumber >= section.titleRange.startLineNumber && lineNumber <= section.titleRange.endLineNumber) { - return true; - } - - for (const setting of section.settings) { - if (lineNumber >= setting.range.startLineNumber && lineNumber <= setting.range.endLineNumber) { - return true; - } - } - } - return false; - } - - private createCompleteRange(range: editorCommon.IRange, model: editorCommon.IModel): editorCommon.IRange { - return { - startLineNumber: range.startLineNumber, - startColumn: model.getLineMinColumn(range.startLineNumber), - endLineNumber: range.endLineNumber, - endColumn: model.getLineMaxColumn(range.endLineNumber) - }; - } - - public dispose() { - if (this.decorationIds) { - this.decorationIds = this.editor.changeDecorations(changeAccessor => { - return changeAccessor.deltaDecorations(this.decorationIds, []); - }); - } - super.dispose(); - } -} - -export class HighlightPreferencesRenderer extends Disposable { - - private decorationIds: string[] = []; - - constructor(private editor: ICodeEditor, - @IInstantiationService private instantiationService: IInstantiationService - ) { - super(); - } - - public render(settings: ISetting[]): void { - const model = this.editor.getModel(); - this.editor.changeDecorations(changeAccessor => { - this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, []); - }); - if (settings.length) { - this.editor.changeDecorations(changeAccessor => { - this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, settings.map(setting => this.createDecoration(setting.keyRange, model))); - }); - } - } - - private createDecoration(range: editorCommon.IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration { - return { - range, - options: { - stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - className: 'findMatch' - } - }; - } - - public dispose() { - if (this.decorationIds) { - this.decorationIds = this.editor.changeDecorations(changeAccessor => { - return changeAccessor.deltaDecorations(this.decorationIds, []); - }); - } - super.dispose(); - } -} - -class FilteredSettingsNavigationRenderer extends Disposable implements IIterator { - - private iterator: ArrayNavigator; - - constructor(private editor: ICodeEditor, private settingHighlighter: SettingHighlighter) { - super(); - } - - public next(): ISetting { - return this.iterator.next() || this.iterator.first(); - } - - public render(filteredGroups: ISettingsGroup[]) { - this.settingHighlighter.clear(true); - const settings: ISetting[] = []; - for (const group of filteredGroups) { - for (const section of group.sections) { - settings.push(...section.settings); - } - } - this.iterator = new ArrayNavigator(settings); - } -} - -class EditSettingRenderer extends Disposable { - - private editPreferenceWidgetForCusorPosition: EditPreferenceWidget; - private editPreferenceWidgetForMouseMove: EditPreferenceWidget; - - private settingsGroups: ISettingsGroup[]; - private toggleEditPreferencesForMouseMoveDelayer: Delayer; - - private _onUpdateSetting: Emitter<{ key: string, value: any, source: ISetting }> = new Emitter<{ key: string, value: any, source: ISetting }>(); - public readonly onUpdateSetting: Event<{ key: string, value: any, source: ISetting }> = this._onUpdateSetting.event; - - constructor(private editor: ICodeEditor, private masterSettingsModel: ISettingsEditorModel, - private otherSettingsModel: () => ISettingsEditorModel, - private settingHighlighter: SettingHighlighter, - @IPreferencesService private preferencesService: IPreferencesService, - @IInstantiationService private instantiationService: IInstantiationService, - @IContextMenuService private contextMenuService: IContextMenuService - ) { - super(); - - this.editPreferenceWidgetForCusorPosition = this._register(this.instantiationService.createInstance(EditPreferenceWidget, editor)); - this.editPreferenceWidgetForMouseMove = this._register(this.instantiationService.createInstance(EditPreferenceWidget, editor)); - this.toggleEditPreferencesForMouseMoveDelayer = new Delayer(75); - - this._register(this.editPreferenceWidgetForCusorPosition.onClick(setting => this.onEditSettingClicked(this.editPreferenceWidgetForCusorPosition))); - this._register(this.editPreferenceWidgetForMouseMove.onClick(setting => this.onEditSettingClicked(this.editPreferenceWidgetForMouseMove))); - - this._register(this.editPreferenceWidgetForCusorPosition.onMouseOver(setting => this.onMouseOver(this.editPreferenceWidgetForCusorPosition))); - this._register(this.editPreferenceWidgetForMouseMove.onMouseOver(setting => this.onMouseOver(this.editPreferenceWidgetForMouseMove))); - - this._register(this.editor.onDidChangeCursorPosition(positionChangeEvent => this.onPositionChanged(positionChangeEvent))); - this._register(this.editor.onMouseMove(mouseMoveEvent => this.onMouseMoved(mouseMoveEvent))); - this._register(this.editor.onDidChangeConfiguration(() => this.onConfigurationChanged())); - } - - public render(settingsGroups: ISettingsGroup[]): void { - this.editPreferenceWidgetForCusorPosition.hide(); - this.editPreferenceWidgetForMouseMove.hide(); - this.settingsGroups = settingsGroups; - - const settings = this.getSettings(this.editor.getPosition().lineNumber); - if (settings.length) { - this.showEditPreferencesWidget(this.editPreferenceWidgetForCusorPosition, settings); - } - } - - private isDefaultSettings(): boolean { - return this.masterSettingsModel instanceof DefaultSettingsEditorModel; - } - - private onConfigurationChanged(): void { - if (!this.editor.getRawConfiguration().glyphMargin) { - this.editPreferenceWidgetForCusorPosition.hide(); - this.editPreferenceWidgetForMouseMove.hide(); - } - } - - private onPositionChanged(positionChangeEvent: editorCommon.ICursorPositionChangedEvent) { - this.editPreferenceWidgetForMouseMove.hide(); - const settings = this.getSettings(positionChangeEvent.position.lineNumber); - if (settings.length) { - this.showEditPreferencesWidget(this.editPreferenceWidgetForCusorPosition, settings); - } else { - this.editPreferenceWidgetForCusorPosition.hide(); - } - } - - private onMouseMoved(mouseMoveEvent: IEditorMouseEvent): void { - const editPreferenceWidget = this.getEditPreferenceWidgetUnderMouse(mouseMoveEvent); - if (editPreferenceWidget) { - this.onMouseOver(editPreferenceWidget); - return; - } - this.settingHighlighter.clear(); - this.toggleEditPreferencesForMouseMoveDelayer.trigger(() => this.toggleEidtPreferenceWidgetForMouseMove(mouseMoveEvent)); - } - - private getEditPreferenceWidgetUnderMouse(mouseMoveEvent: IEditorMouseEvent): EditPreferenceWidget { - if (mouseMoveEvent.event.target === this.editPreferenceWidgetForMouseMove.getDomNode()) { - return this.editPreferenceWidgetForMouseMove; - } - if (mouseMoveEvent.event.target === this.editPreferenceWidgetForCusorPosition.getDomNode()) { - return this.editPreferenceWidgetForCusorPosition; - } - return null; - } - - private toggleEidtPreferenceWidgetForMouseMove(mouseMoveEvent: IEditorMouseEvent): void { - const settings = mouseMoveEvent.target.position ? this.getSettings(mouseMoveEvent.target.position.lineNumber) : null; - if (settings && settings.length) { - this.showEditPreferencesWidget(this.editPreferenceWidgetForMouseMove, settings); - } else { - this.editPreferenceWidgetForMouseMove.hide(); - } - } - - private showEditPreferencesWidget(editPreferencesWidget: EditPreferenceWidget, settings: ISetting[]) { - if (this.editor.getRawConfiguration().glyphMargin) { - editPreferencesWidget.show(settings[0].valueRange.startLineNumber, settings); - editPreferencesWidget.getDomNode().title = nls.localize('editTtile', "Edit"); - const editPreferenceWidgetToHide = editPreferencesWidget === this.editPreferenceWidgetForCusorPosition ? this.editPreferenceWidgetForMouseMove : this.editPreferenceWidgetForCusorPosition; - editPreferenceWidgetToHide.hide(); - } - } - - private getSettings(lineNumber: number): ISetting[] { - const configurationMap = this.getConfigurationsMap(); - return this.getSettingsAtLineNumber(lineNumber).filter(setting => { - let jsonSchema: IJSONSchema = configurationMap[setting.key]; - return jsonSchema && (this.isDefaultSettings() || jsonSchema.type === 'boolean' || jsonSchema.enum); - }); - } - - private getSettingsAtLineNumber(lineNumber: number): ISetting[] { - const settings = []; - for (const group of this.settingsGroups) { - if (group.range.startLineNumber > lineNumber) { - break; - } - if (lineNumber >= group.range.startLineNumber && lineNumber <= group.range.endLineNumber) { - for (const section of group.sections) { - for (const setting of section.settings) { - if (setting.range.startLineNumber > lineNumber) { - break; - } - if (lineNumber >= setting.range.startLineNumber && lineNumber <= setting.range.endLineNumber) { - if (setting.overrides.length > 0) { - // Only one level because override settings cannot have override settings - for (const overrideSetting of setting.overrides) { - if (lineNumber >= overrideSetting.range.startLineNumber && lineNumber <= overrideSetting.range.endLineNumber) { - settings.push(overrideSetting); - } - } - } else { - settings.push(setting); - } - } - } - } - } - } - return settings; - } - - private onMouseOver(editPreferenceWidget: EditPreferenceWidget): void { - this.settingHighlighter.highlight(editPreferenceWidget.preferences[0]); - } - - private onEditSettingClicked(editPreferenceWidget: EditPreferenceWidget): void { - const elementPosition = DOM.getDomNodePagePosition(editPreferenceWidget.getDomNode()); - const anchor = { x: elementPosition.left + elementPosition.width, y: elementPosition.top + elementPosition.height + 10 }; - const actions = this.getSettingsAtLineNumber(editPreferenceWidget.getLine()).length === 1 ? this.getActions(editPreferenceWidget.preferences[0], this.getConfigurationsMap()[editPreferenceWidget.preferences[0].key]) - : editPreferenceWidget.preferences.map(setting => new ContextSubMenu(setting.key, this.getActions(setting, this.getConfigurationsMap()[setting.key]))); - this.contextMenuService.showContextMenu({ - getAnchor: () => anchor, - getActions: () => TPromise.wrap(actions) - }); - } - - private getConfigurationsMap(): { [qualifiedKey: string]: IJSONSchema } { - return Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); - } - - private getActions(setting: ISetting, jsonSchema: IJSONSchema): IAction[] { - if (jsonSchema.type === 'boolean') { - return [{ - id: 'truthyValue', - label: 'true', - enabled: true, - run: () => this.updateSetting(setting.key, true, setting) - }, { - id: 'falsyValue', - label: 'false', - enabled: true, - run: () => this.updateSetting(setting.key, false, setting) - }]; - } - if (jsonSchema.enum) { - return jsonSchema.enum.map(value => { - return { - id: value, - label: JSON.stringify(value), - enabled: true, - run: () => this.updateSetting(setting.key, value, setting) - }; - }); - } - return this.getDefaultActions(setting); - } - - private getDefaultActions(setting: ISetting): IAction[] { - const settingInOtherModel = this.otherSettingsModel().getPreference(setting.key); - if (this.isDefaultSettings()) { - return [{ - id: 'setDefaultValue', - label: settingInOtherModel ? nls.localize('replaceDefaultValue', "Replace in Settings") : nls.localize('copyDefaultValue', "Copy to Settings"), - enabled: true, - run: () => this.updateSetting(setting.key, setting.value, setting) - }]; - } - return []; - } - - private updateSetting(key: string, value: any, source: ISetting): void { - this._onUpdateSetting.fire({ key, value, source }); - } -} - -class SettingHighlighter extends Disposable { - - private fixedHighlighter: RangeHighlightDecorations; - private volatileHighlighter: RangeHighlightDecorations; - private highlightedSetting: ISetting; - - constructor(private editor: editorCommon.ICommonCodeEditor, private focusEventEmitter: Emitter, private clearFocusEventEmitter: Emitter, - @IInstantiationService instantiationService: IInstantiationService - ) { - super(); - this.fixedHighlighter = this._register(instantiationService.createInstance(RangeHighlightDecorations)); - this.volatileHighlighter = this._register(instantiationService.createInstance(RangeHighlightDecorations)); - this.fixedHighlighter.onHighlghtRemoved(() => this.clearFocusEventEmitter.fire(this.highlightedSetting)); - this.volatileHighlighter.onHighlghtRemoved(() => this.clearFocusEventEmitter.fire(this.highlightedSetting)); - } - - highlight(setting: ISetting, fix: boolean = false) { - this.highlightedSetting = setting; - this.volatileHighlighter.removeHighlightRange(); - this.fixedHighlighter.removeHighlightRange(); - - const highlighter = fix ? this.fixedHighlighter : this.volatileHighlighter; - highlighter.highlightRange({ - range: setting.valueRange, - resource: this.editor.getModel().uri - }, this.editor); - - this.editor.revealLinesInCenterIfOutsideViewport(setting.valueRange.startLineNumber, setting.valueRange.endLineNumber - 1); - this.focusEventEmitter.fire(setting); - } - - clear(fix: boolean = false): void { - this.volatileHighlighter.removeHighlightRange(); - if (fix) { - this.fixedHighlighter.removeHighlightRange(); - } - this.clearFocusEventEmitter.fire(this.highlightedSetting); - } -} - -class UnTrustedWorkspaceSettingsRenderer extends Disposable { - - constructor(private editor: editorCommon.ICommonCodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel, - @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, - @IMarkerService private markerService: IMarkerService - ) { - super(); - } - - private getMarkerMessage(settingKey): string { - switch (settingKey) { - case 'php.validate.executablePath': - return nls.localize('unsupportedPHPExecutablePathSetting', "This setting must be a User Setting. To configure PHP for the workspace, open a PHP file and click on 'PHP Path' in the status bar."); - default: - return nls.localize('unsupportedWorkspaceSetting', "This setting must be a User Setting."); - } - } - - public render(): void { - const untrustedConfigurations = this.configurationService.getUntrustedConfigurations(); - if (untrustedConfigurations.length) { - const markerData: IMarkerData[] = []; - for (const untrustedConfiguration of untrustedConfigurations) { - const setting = this.workspaceSettingsEditorModel.getPreference(untrustedConfiguration); - if (setting) { - markerData.push({ - severity: Severity.Warning, - startLineNumber: setting.keyRange.startLineNumber, - startColumn: setting.keyRange.startColumn, - endLineNumber: setting.keyRange.endLineNumber, - endColumn: setting.keyRange.endColumn, - message: this.getMarkerMessage(untrustedConfiguration) - }); - } - } - this.markerService.changeOne('preferencesEditor', this.workspaceSettingsEditorModel.uri, markerData); - } - } - - public dispose(): void { - this.markerService.remove('preferencesEditor', [this.workspaceSettingsEditorModel.uri]); - super.dispose(); - } -} - const DefaultSettingsEditorCommand = EditorCommand.bindToContribution>((editor: editorCommon.ICommonCodeEditor) => >editor.getContribution(DefaultSettingsEditorContribution.ID)); CommonEditorRegistry.registerEditorCommand(new DefaultSettingsEditorCommand({ diff --git a/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts new file mode 100644 index 00000000000..1d7d235ed91 --- /dev/null +++ b/src/vs/workbench/parts/preferences/browser/preferencesRenderers.ts @@ -0,0 +1,885 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TPromise } from 'vs/base/common/winjs.base'; +import * as nls from 'vs/nls'; +import * as DOM from 'vs/base/browser/dom'; +import { Delayer } from 'vs/base/common/async'; +import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { flatten, distinct } from 'vs/base/common/arrays'; +import { ArrayNavigator, IIterator } from 'vs/base/common/iterator'; +import { IAction } from 'vs/base/common/actions'; +import { IJSONSchema } from 'vs/base/common/jsonSchema'; +import Event, { Emitter } from 'vs/base/common/event'; +import { Registry } from 'vs/platform/platform'; +import * as editorCommon from 'vs/editor/common/editorCommon'; +import { Range } from 'vs/editor/common/core/range'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IPreferencesService, ISettingsGroup, ISetting, IPreferencesEditorModel, IFilterResult, CONTEXT_DEFAULT_SETTINGS_EDITOR, ISettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferences'; +import { SettingsEditorModel, DefaultSettingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels'; +import { ICodeEditor, IEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; +import { IContextMenuService, ContextSubMenu } from 'vs/platform/contextview/browser/contextView'; +import { SettingsGroupTitleWidget, EditPreferenceWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { RangeHighlightDecorations } from 'vs/workbench/common/editor/rangeDecorations'; +import { IConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { overrideIdentifierFromKey } from 'vs/platform/configuration/common/model'; +import { IMarkerService, IMarkerData } from 'vs/platform/markers/common/markers'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { IMessageService, Severity } from 'vs/platform/message/common/message'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; + +export interface IPreferencesRenderer extends IDisposable { + iterator: IIterator; + onFocusPreference: Event; + onClearFocusPreference: Event; + onUpdatePreference: Event<{ key: string, value: any, source: T }>; + preferencesModel: IPreferencesEditorModel; + render(): void; + updatePreference(key: string, value: any, source: T): void; + filterPreferences(filterResult: IFilterResult): void; + focusPreference(setting: T): void; + clearFocus(setting: T): void; +} + + +export class UserSettingsRenderer extends Disposable implements IPreferencesRenderer { + + private settingHighlighter: SettingHighlighter; + private editSettingActionRenderer: EditSettingRenderer; + private highlightPreferencesRenderer: HighlightPreferencesRenderer; + private modelChangeDelayer: Delayer = new Delayer(200); + + private _onFocusPreference: Emitter = new Emitter(); + public readonly onFocusPreference: Event = this._onFocusPreference.event; + + private _onUpdatePreference: Emitter<{ key: string, value: any, source: ISetting }> = new Emitter<{ key: string, value: any, source: ISetting }>(); + public readonly onUpdatePreference: Event<{ key: string, value: any, source: ISetting }> = this._onUpdatePreference.event; + + private _onClearFocusPreference: Emitter = new Emitter(); + public readonly onClearFocusPreference: Event = this._onClearFocusPreference.event; + + private filterResult: IFilterResult; + + constructor(protected editor: ICodeEditor, public readonly preferencesModel: SettingsEditorModel, private defaultPreferencesModel: IPreferencesEditorModel, + @IPreferencesService protected preferencesService: IPreferencesService, + @ITelemetryService private telemetryService: ITelemetryService, + @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, + @IMessageService private messageService: IMessageService, + @IInstantiationService protected instantiationService: IInstantiationService + ) { + super(); + this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor, this._onFocusPreference, this._onClearFocusPreference)); + this.highlightPreferencesRenderer = this._register(instantiationService.createInstance(HighlightPreferencesRenderer, editor)); + this.editSettingActionRenderer = this._register(this.instantiationService.createInstance(EditSettingRenderer, this.editor, this.preferencesModel, () => this.defaultPreferencesModel, this.settingHighlighter)); + this._register(this.editSettingActionRenderer.onUpdateSetting(({key, value, source}) => this.updatePreference(key, value, source))); + this._register(this.editor.getModel().onDidChangeContent(() => this.modelChangeDelayer.trigger(() => this.onModelChanged()))); + } + + public get iterator(): IIterator { + return null; + } + + public render(): void { + this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups); + if (this.filterResult) { + this.filterPreferences(this.filterResult); + } + } + + public updatePreference(key: string, value: any, source: ISetting): void { + this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [key] }); + const overrideIdentifier = source.overrideOf ? overrideIdentifierFromKey(source.overrideOf.key) : null; + this.configurationEditingService.writeConfiguration(this.preferencesModel.configurationTarget, { key, value, overrideIdentifier }, { writeToBuffer: true, autoSave: true }) + .then(() => this.onSettingUpdated(source), error => this.messageService.show(Severity.Error, error)); + } + + private onModelChanged(): void { + if (!this.editor.getModel()) { + // model could have been disposed during the delay + return; + } + this.render(); + } + + private onSettingUpdated(setting: ISetting) { + this.editor.focus(); + setting = this.getSetting(setting); + if (setting) { + // TODO:@sandy Selection range should be template range + this.editor.setSelection(setting.valueRange); + this.settingHighlighter.highlight(setting, true); + } + } + + private getSetting(setting: ISetting): ISetting { + const {key, overrideOf} = setting; + if (overrideOf) { + const setting = this.getSetting(overrideOf); + for (const override of setting.overrides) { + if (override.key === key) { + return override; + } + } + return null; + } + return this.preferencesModel.getPreference(key); + } + + public filterPreferences(filterResult: IFilterResult): void { + this.filterResult = filterResult; + this.highlightPreferencesRenderer.render([]); + this.settingHighlighter.clear(true); + if (this.defaultPreferencesModel && filterResult) { + const settings = distinct(filterResult.filteredGroups.reduce((settings: ISetting[], settingsGroup: ISettingsGroup) => { + for (const section of settingsGroup.sections) { + for (const setting of section.settings) { + const s = this.getSetting(setting); + if (s) { + settings.push(s); + } + } + } + return settings; + }, [])); + this.highlightPreferencesRenderer.render(settings); + } + } + + public focusPreference(setting: ISetting): void { + const s = this.getSetting(setting); + if (s) { + this.settingHighlighter.highlight(s, true); + } else { + this.settingHighlighter.clear(true); + } + } + + public clearFocus(setting: ISetting): void { + this.settingHighlighter.clear(true); + } +} + +export class WorkspaceSettingsRenderer extends UserSettingsRenderer implements IPreferencesRenderer { + + private untrustedSettingRenderer: UnTrustedWorkspaceSettingsRenderer; + + constructor(editor: ICodeEditor, preferencesModel: SettingsEditorModel, defaultPreferencesModel: IPreferencesEditorModel, + @IPreferencesService preferencesService: IPreferencesService, + @ITelemetryService telemetryService: ITelemetryService, + @IConfigurationEditingService configurationEditingService: IConfigurationEditingService, + @IMessageService messageService: IMessageService, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(editor, preferencesModel, defaultPreferencesModel, preferencesService, telemetryService, configurationEditingService, messageService, instantiationService); + this.untrustedSettingRenderer = this._register(instantiationService.createInstance(UnTrustedWorkspaceSettingsRenderer, editor, preferencesModel)); + } + + public render(): void { + super.render(); + this.untrustedSettingRenderer.render(); + } +} + +export class DefaultSettingsRenderer extends Disposable implements IPreferencesRenderer { + + private defaultSettingsEditorContextKey: IContextKey; + + private settingHighlighter: SettingHighlighter; + private settingsGroupTitleRenderer: SettingsGroupTitleRenderer; + private filteredMatchesRenderer: FilteredMatchesRenderer; + private filteredSettingsNavigationRenderer: FilteredSettingsNavigationRenderer; + private hiddenAreasRenderer: HiddenAreasRenderer; + private editSettingActionRenderer: EditSettingRenderer; + + private _onUpdatePreference: Emitter<{ key: string, value: any, source: ISetting }> = new Emitter<{ key: string, value: any, source: ISetting }>(); + public readonly onUpdatePreference: Event<{ key: string, value: any, source: ISetting }> = this._onUpdatePreference.event; + + private _onFocusPreference: Emitter = new Emitter(); + public readonly onFocusPreference: Event = this._onFocusPreference.event; + + private _onClearFocusPreference: Emitter = new Emitter(); + public readonly onClearFocusPreference: Event = this._onClearFocusPreference.event; + + constructor(protected editor: ICodeEditor, public readonly preferencesModel: DefaultSettingsEditorModel, otherSettingsModel: () => IPreferencesEditorModel, + @IPreferencesService protected preferencesService: IPreferencesService, + @IContextKeyService contextKeyService: IContextKeyService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IInstantiationService protected instantiationService: IInstantiationService + ) { + super(); + this.defaultSettingsEditorContextKey = CONTEXT_DEFAULT_SETTINGS_EDITOR.bindTo(contextKeyService); + this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor, this._onFocusPreference, this._onClearFocusPreference)); + this.settingsGroupTitleRenderer = this._register(instantiationService.createInstance(SettingsGroupTitleRenderer, editor)); + this.filteredMatchesRenderer = this._register(instantiationService.createInstance(FilteredMatchesRenderer, editor)); + this.filteredSettingsNavigationRenderer = this._register(instantiationService.createInstance(FilteredSettingsNavigationRenderer, editor, this.settingHighlighter)); + this.editSettingActionRenderer = this._register(instantiationService.createInstance(EditSettingRenderer, editor, preferencesModel, otherSettingsModel, this.settingHighlighter)); + this._register(this.editSettingActionRenderer.onUpdateSetting(e => this._onUpdatePreference.fire(e))); + const paranthesisHidingRenderer = this._register(instantiationService.createInstance(StaticContentHidingRenderer, editor, preferencesModel.settingsGroups)); + this.hiddenAreasRenderer = this._register(instantiationService.createInstance(HiddenAreasRenderer, editor, [this.settingsGroupTitleRenderer, this.filteredMatchesRenderer, paranthesisHidingRenderer])); + + this._register(this.settingsGroupTitleRenderer.onHiddenAreasChanged(() => this.hiddenAreasRenderer.render())); + } + + public get iterator(): IIterator { + return this.filteredSettingsNavigationRenderer; + } + + public render() { + this.defaultSettingsEditorContextKey.set(true); + this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups); + this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups); + this.hiddenAreasRenderer.render(); + this.filteredSettingsNavigationRenderer.render([]); + this.settingsGroupTitleRenderer.showGroup(1); + this.hiddenAreasRenderer.render(); + } + + public filterPreferences(filterResult: IFilterResult): void { + if (!filterResult) { + this.filteredSettingsNavigationRenderer.render([]); + this.filteredMatchesRenderer.render(null); + this.settingsGroupTitleRenderer.render(this.preferencesModel.settingsGroups); + this.settingsGroupTitleRenderer.showGroup(1); + } else { + this.filteredMatchesRenderer.render(filterResult); + this.settingsGroupTitleRenderer.render(filterResult.filteredGroups); + this.filteredSettingsNavigationRenderer.render(filterResult.filteredGroups); + } + this.hiddenAreasRenderer.render(); + } + + public focusPreference(setting: ISetting): void { + this.settingsGroupTitleRenderer.showSetting(setting); + this.settingHighlighter.highlight(setting, true); + } + + public clearFocus(setting: ISetting): void { + this.settingHighlighter.clear(true); + } + + public collapseAll() { + this.settingsGroupTitleRenderer.collapseAll(); + } + + public updatePreference(key: string, value: any, source: ISetting): void { + } + + dispose() { + this.defaultSettingsEditorContextKey.set(false); + super.dispose(); + } +} + +export interface HiddenAreasProvider { + hiddenAreas: editorCommon.IRange[]; +} + +export class StaticContentHidingRenderer extends Disposable implements HiddenAreasProvider { + + constructor(private editor: ICodeEditor, private settingsGroups: ISettingsGroup[] + ) { + super(); + } + + get hiddenAreas(): editorCommon.IRange[] { + const model = this.editor.getModel(); + return [ + { + startLineNumber: 1, + startColumn: model.getLineMinColumn(1), + endLineNumber: 2, + endColumn: model.getLineMaxColumn(2) + }, + { + startLineNumber: this.settingsGroups[0].range.endLineNumber + 1, + startColumn: model.getLineMinColumn(this.settingsGroups[0].range.endLineNumber + 1), + endLineNumber: this.settingsGroups[0].range.endLineNumber + 4, + endColumn: model.getLineMaxColumn(this.settingsGroups[0].range.endLineNumber + 4) + }, + { + startLineNumber: model.getLineCount() - 1, + startColumn: model.getLineMinColumn(model.getLineCount() - 1), + endLineNumber: model.getLineCount(), + endColumn: model.getLineMaxColumn(model.getLineCount()) + } + ]; + } + +} + +export class SettingsGroupTitleRenderer extends Disposable implements HiddenAreasProvider { + + private _onHiddenAreasChanged: Emitter = new Emitter(); + get onHiddenAreasChanged(): Event { return this._onHiddenAreasChanged.event; }; + + private settingsGroups: ISettingsGroup[]; + private hiddenGroups: ISettingsGroup[] = []; + private settingsGroupTitleWidgets: SettingsGroupTitleWidget[]; + private disposables: IDisposable[] = []; + + constructor(private editor: ICodeEditor, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(); + } + + public get hiddenAreas(): editorCommon.IRange[] { + const hiddenAreas: editorCommon.IRange[] = []; + for (const group of this.hiddenGroups) { + hiddenAreas.push(group.range); + } + return hiddenAreas; + } + + public render(settingsGroups: ISettingsGroup[]) { + this.disposeWidgets(); + this.settingsGroups = settingsGroups.slice(); + this.settingsGroupTitleWidgets = []; + for (const group of this.settingsGroups.slice().reverse()) { + const settingsGroupTitleWidget = this.instantiationService.createInstance(SettingsGroupTitleWidget, this.editor, group); + settingsGroupTitleWidget.render(); + this.settingsGroupTitleWidgets.push(settingsGroupTitleWidget); + this.disposables.push(settingsGroupTitleWidget); + this.disposables.push(settingsGroupTitleWidget.onToggled(collapsed => this.onToggled(collapsed, settingsGroupTitleWidget.settingsGroup))); + } + this.settingsGroupTitleWidgets.reverse(); + } + + public showGroup(group: number) { + this.hiddenGroups = this.settingsGroups.filter((g, i) => i !== group - 1); + for (const groupTitleWidget of this.settingsGroupTitleWidgets.filter((g, i) => i !== group - 1)) { + groupTitleWidget.toggleCollapse(true); + } + this._onHiddenAreasChanged.fire(); + } + + public showSetting(setting: ISetting): void { + const settingsGroupTitleWidget = this.settingsGroupTitleWidgets.filter(widget => Range.containsRange(widget.settingsGroup.range, setting.range))[0]; + if (settingsGroupTitleWidget && settingsGroupTitleWidget.isCollapsed()) { + settingsGroupTitleWidget.toggleCollapse(false); + this.hiddenGroups.splice(this.hiddenGroups.indexOf(settingsGroupTitleWidget.settingsGroup), 1); + this._onHiddenAreasChanged.fire(); + } + } + + public collapseAll() { + this.editor.setPosition({ lineNumber: 1, column: 1 }); + this.hiddenGroups = this.settingsGroups.slice(); + for (const groupTitleWidget of this.settingsGroupTitleWidgets) { + groupTitleWidget.toggleCollapse(true); + } + this._onHiddenAreasChanged.fire(); + } + + private onToggled(collapsed: boolean, group: ISettingsGroup) { + const index = this.hiddenGroups.indexOf(group); + if (collapsed) { + const currentPosition = this.editor.getPosition(); + if (group.range.startLineNumber <= currentPosition.lineNumber && group.range.endLineNumber >= currentPosition.lineNumber) { + this.editor.setPosition({ lineNumber: group.range.startLineNumber - 1, column: 1 }); + } + this.hiddenGroups.push(group); + } else { + this.hiddenGroups.splice(index, 1); + } + this._onHiddenAreasChanged.fire(); + } + + private disposeWidgets() { + this.hiddenGroups = []; + this.disposables = dispose(this.disposables); + } + + public dispose() { + this.disposeWidgets(); + super.dispose(); + } +} + +export class HiddenAreasRenderer extends Disposable { + + constructor(private editor: ICodeEditor, private hiddenAreasProviders: HiddenAreasProvider[], + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(); + } + + public render() { + const ranges: editorCommon.IRange[] = []; + for (const hiddenAreaProvider of this.hiddenAreasProviders) { + ranges.push(...hiddenAreaProvider.hiddenAreas); + } + this.editor.setHiddenAreas(ranges); + } + + public dispose() { + this.editor.setHiddenAreas([]); + super.dispose(); + } +} + +export class FilteredMatchesRenderer extends Disposable implements HiddenAreasProvider { + + private decorationIds: string[] = []; + public hiddenAreas: editorCommon.IRange[] = []; + + constructor(private editor: ICodeEditor, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(); + } + + public render(result: IFilterResult): void { + const model = this.editor.getModel(); + this.hiddenAreas = []; + this.editor.changeDecorations(changeAccessor => { + this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, []); + }); + if (result) { + this.hiddenAreas = this.computeHiddenRanges(result.filteredGroups, result.allGroups, model); + this.editor.changeDecorations(changeAccessor => { + this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, flatten(result.matches.values()).map(match => this.createDecoration(match, model))); + }); + } + } + + private createDecoration(range: editorCommon.IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration { + return { + range, + options: { + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'findMatch' + } + }; + } + + private computeHiddenRanges(filteredGroups: ISettingsGroup[], allSettingsGroups: ISettingsGroup[], model: editorCommon.IModel): editorCommon.IRange[] { + const notMatchesRanges: editorCommon.IRange[] = []; + for (const group of allSettingsGroups) { + const filteredGroup = filteredGroups.filter(g => g.title === group.title)[0]; + if (!filteredGroup) { + notMatchesRanges.push({ + startLineNumber: group.range.startLineNumber - 1, + startColumn: model.getLineMinColumn(group.range.startLineNumber - 1), + endLineNumber: group.range.endLineNumber, + endColumn: model.getLineMaxColumn(group.range.endLineNumber), + }); + } else { + for (const section of group.sections) { + if (section.titleRange) { + if (!this.containsLine(section.titleRange.startLineNumber, filteredGroup)) { + notMatchesRanges.push(this.createCompleteRange(section.titleRange, model)); + } + } + for (const setting of section.settings) { + if (!this.containsLine(setting.range.startLineNumber, filteredGroup)) { + notMatchesRanges.push(this.createCompleteRange(setting.range, model)); + } + } + } + } + } + return notMatchesRanges; + } + + private containsLine(lineNumber: number, settingsGroup: ISettingsGroup): boolean { + if (settingsGroup.titleRange && lineNumber >= settingsGroup.titleRange.startLineNumber && lineNumber <= settingsGroup.titleRange.endLineNumber) { + return true; + } + + for (const section of settingsGroup.sections) { + if (section.titleRange && lineNumber >= section.titleRange.startLineNumber && lineNumber <= section.titleRange.endLineNumber) { + return true; + } + + for (const setting of section.settings) { + if (lineNumber >= setting.range.startLineNumber && lineNumber <= setting.range.endLineNumber) { + return true; + } + } + } + return false; + } + + private createCompleteRange(range: editorCommon.IRange, model: editorCommon.IModel): editorCommon.IRange { + return { + startLineNumber: range.startLineNumber, + startColumn: model.getLineMinColumn(range.startLineNumber), + endLineNumber: range.endLineNumber, + endColumn: model.getLineMaxColumn(range.endLineNumber) + }; + } + + public dispose() { + if (this.decorationIds) { + this.decorationIds = this.editor.changeDecorations(changeAccessor => { + return changeAccessor.deltaDecorations(this.decorationIds, []); + }); + } + super.dispose(); + } +} + +export class HighlightPreferencesRenderer extends Disposable { + + private decorationIds: string[] = []; + + constructor(private editor: ICodeEditor, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(); + } + + public render(settings: ISetting[]): void { + const model = this.editor.getModel(); + this.editor.changeDecorations(changeAccessor => { + this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, []); + }); + if (settings.length) { + this.editor.changeDecorations(changeAccessor => { + this.decorationIds = changeAccessor.deltaDecorations(this.decorationIds, settings.map(setting => this.createDecoration(setting.keyRange, model))); + }); + } + } + + private createDecoration(range: editorCommon.IRange, model: editorCommon.IModel): editorCommon.IModelDeltaDecoration { + return { + range, + options: { + stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + className: 'findMatch' + } + }; + } + + public dispose() { + if (this.decorationIds) { + this.decorationIds = this.editor.changeDecorations(changeAccessor => { + return changeAccessor.deltaDecorations(this.decorationIds, []); + }); + } + super.dispose(); + } +} + +class FilteredSettingsNavigationRenderer extends Disposable implements IIterator { + + private iterator: ArrayNavigator; + + constructor(private editor: ICodeEditor, private settingHighlighter: SettingHighlighter) { + super(); + } + + public next(): ISetting { + return this.iterator.next() || this.iterator.first(); + } + + public render(filteredGroups: ISettingsGroup[]) { + this.settingHighlighter.clear(true); + const settings: ISetting[] = []; + for (const group of filteredGroups) { + for (const section of group.sections) { + settings.push(...section.settings); + } + } + this.iterator = new ArrayNavigator(settings); + } +} + +class EditSettingRenderer extends Disposable { + + private editPreferenceWidgetForCusorPosition: EditPreferenceWidget; + private editPreferenceWidgetForMouseMove: EditPreferenceWidget; + + private settingsGroups: ISettingsGroup[]; + private toggleEditPreferencesForMouseMoveDelayer: Delayer; + + private _onUpdateSetting: Emitter<{ key: string, value: any, source: ISetting }> = new Emitter<{ key: string, value: any, source: ISetting }>(); + public readonly onUpdateSetting: Event<{ key: string, value: any, source: ISetting }> = this._onUpdateSetting.event; + + constructor(private editor: ICodeEditor, private masterSettingsModel: ISettingsEditorModel, + private otherSettingsModel: () => ISettingsEditorModel, + private settingHighlighter: SettingHighlighter, + @IPreferencesService private preferencesService: IPreferencesService, + @IInstantiationService private instantiationService: IInstantiationService, + @IContextMenuService private contextMenuService: IContextMenuService + ) { + super(); + + this.editPreferenceWidgetForCusorPosition = this._register(this.instantiationService.createInstance(EditPreferenceWidget, editor)); + this.editPreferenceWidgetForMouseMove = this._register(this.instantiationService.createInstance(EditPreferenceWidget, editor)); + this.toggleEditPreferencesForMouseMoveDelayer = new Delayer(75); + + this._register(this.editPreferenceWidgetForCusorPosition.onClick(setting => this.onEditSettingClicked(this.editPreferenceWidgetForCusorPosition))); + this._register(this.editPreferenceWidgetForMouseMove.onClick(setting => this.onEditSettingClicked(this.editPreferenceWidgetForMouseMove))); + + this._register(this.editPreferenceWidgetForCusorPosition.onMouseOver(setting => this.onMouseOver(this.editPreferenceWidgetForCusorPosition))); + this._register(this.editPreferenceWidgetForMouseMove.onMouseOver(setting => this.onMouseOver(this.editPreferenceWidgetForMouseMove))); + + this._register(this.editor.onDidChangeCursorPosition(positionChangeEvent => this.onPositionChanged(positionChangeEvent))); + this._register(this.editor.onMouseMove(mouseMoveEvent => this.onMouseMoved(mouseMoveEvent))); + this._register(this.editor.onDidChangeConfiguration(() => this.onConfigurationChanged())); + } + + public render(settingsGroups: ISettingsGroup[]): void { + this.editPreferenceWidgetForCusorPosition.hide(); + this.editPreferenceWidgetForMouseMove.hide(); + this.settingsGroups = settingsGroups; + + const settings = this.getSettings(this.editor.getPosition().lineNumber); + if (settings.length) { + this.showEditPreferencesWidget(this.editPreferenceWidgetForCusorPosition, settings); + } + } + + private isDefaultSettings(): boolean { + return this.masterSettingsModel instanceof DefaultSettingsEditorModel; + } + + private onConfigurationChanged(): void { + if (!this.editor.getRawConfiguration().glyphMargin) { + this.editPreferenceWidgetForCusorPosition.hide(); + this.editPreferenceWidgetForMouseMove.hide(); + } + } + + private onPositionChanged(positionChangeEvent: editorCommon.ICursorPositionChangedEvent) { + this.editPreferenceWidgetForMouseMove.hide(); + const settings = this.getSettings(positionChangeEvent.position.lineNumber); + if (settings.length) { + this.showEditPreferencesWidget(this.editPreferenceWidgetForCusorPosition, settings); + } else { + this.editPreferenceWidgetForCusorPosition.hide(); + } + } + + private onMouseMoved(mouseMoveEvent: IEditorMouseEvent): void { + const editPreferenceWidget = this.getEditPreferenceWidgetUnderMouse(mouseMoveEvent); + if (editPreferenceWidget) { + this.onMouseOver(editPreferenceWidget); + return; + } + this.settingHighlighter.clear(); + this.toggleEditPreferencesForMouseMoveDelayer.trigger(() => this.toggleEidtPreferenceWidgetForMouseMove(mouseMoveEvent)); + } + + private getEditPreferenceWidgetUnderMouse(mouseMoveEvent: IEditorMouseEvent): EditPreferenceWidget { + if (mouseMoveEvent.event.target === this.editPreferenceWidgetForMouseMove.getDomNode()) { + return this.editPreferenceWidgetForMouseMove; + } + if (mouseMoveEvent.event.target === this.editPreferenceWidgetForCusorPosition.getDomNode()) { + return this.editPreferenceWidgetForCusorPosition; + } + return null; + } + + private toggleEidtPreferenceWidgetForMouseMove(mouseMoveEvent: IEditorMouseEvent): void { + const settings = mouseMoveEvent.target.position ? this.getSettings(mouseMoveEvent.target.position.lineNumber) : null; + if (settings && settings.length) { + this.showEditPreferencesWidget(this.editPreferenceWidgetForMouseMove, settings); + } else { + this.editPreferenceWidgetForMouseMove.hide(); + } + } + + private showEditPreferencesWidget(editPreferencesWidget: EditPreferenceWidget, settings: ISetting[]) { + if (this.editor.getRawConfiguration().glyphMargin) { + editPreferencesWidget.show(settings[0].valueRange.startLineNumber, settings); + editPreferencesWidget.getDomNode().title = nls.localize('editTtile', "Edit"); + const editPreferenceWidgetToHide = editPreferencesWidget === this.editPreferenceWidgetForCusorPosition ? this.editPreferenceWidgetForMouseMove : this.editPreferenceWidgetForCusorPosition; + editPreferenceWidgetToHide.hide(); + } + } + + private getSettings(lineNumber: number): ISetting[] { + const configurationMap = this.getConfigurationsMap(); + return this.getSettingsAtLineNumber(lineNumber).filter(setting => { + let jsonSchema: IJSONSchema = configurationMap[setting.key]; + return jsonSchema && (this.isDefaultSettings() || jsonSchema.type === 'boolean' || jsonSchema.enum); + }); + } + + private getSettingsAtLineNumber(lineNumber: number): ISetting[] { + const settings = []; + for (const group of this.settingsGroups) { + if (group.range.startLineNumber > lineNumber) { + break; + } + if (lineNumber >= group.range.startLineNumber && lineNumber <= group.range.endLineNumber) { + for (const section of group.sections) { + for (const setting of section.settings) { + if (setting.range.startLineNumber > lineNumber) { + break; + } + if (lineNumber >= setting.range.startLineNumber && lineNumber <= setting.range.endLineNumber) { + if (setting.overrides.length > 0) { + // Only one level because override settings cannot have override settings + for (const overrideSetting of setting.overrides) { + if (lineNumber >= overrideSetting.range.startLineNumber && lineNumber <= overrideSetting.range.endLineNumber) { + settings.push(overrideSetting); + } + } + } else { + settings.push(setting); + } + } + } + } + } + } + return settings; + } + + private onMouseOver(editPreferenceWidget: EditPreferenceWidget): void { + this.settingHighlighter.highlight(editPreferenceWidget.preferences[0]); + } + + private onEditSettingClicked(editPreferenceWidget: EditPreferenceWidget): void { + const elementPosition = DOM.getDomNodePagePosition(editPreferenceWidget.getDomNode()); + const anchor = { x: elementPosition.left + elementPosition.width, y: elementPosition.top + elementPosition.height + 10 }; + const actions = this.getSettingsAtLineNumber(editPreferenceWidget.getLine()).length === 1 ? this.getActions(editPreferenceWidget.preferences[0], this.getConfigurationsMap()[editPreferenceWidget.preferences[0].key]) + : editPreferenceWidget.preferences.map(setting => new ContextSubMenu(setting.key, this.getActions(setting, this.getConfigurationsMap()[setting.key]))); + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => TPromise.wrap(actions) + }); + } + + private getConfigurationsMap(): { [qualifiedKey: string]: IJSONSchema } { + return Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); + } + + private getActions(setting: ISetting, jsonSchema: IJSONSchema): IAction[] { + if (jsonSchema.type === 'boolean') { + return [{ + id: 'truthyValue', + label: 'true', + enabled: true, + run: () => this.updateSetting(setting.key, true, setting) + }, { + id: 'falsyValue', + label: 'false', + enabled: true, + run: () => this.updateSetting(setting.key, false, setting) + }]; + } + if (jsonSchema.enum) { + return jsonSchema.enum.map(value => { + return { + id: value, + label: JSON.stringify(value), + enabled: true, + run: () => this.updateSetting(setting.key, value, setting) + }; + }); + } + return this.getDefaultActions(setting); + } + + private getDefaultActions(setting: ISetting): IAction[] { + const settingInOtherModel = this.otherSettingsModel().getPreference(setting.key); + if (this.isDefaultSettings()) { + return [{ + id: 'setDefaultValue', + label: settingInOtherModel ? nls.localize('replaceDefaultValue', "Replace in Settings") : nls.localize('copyDefaultValue', "Copy to Settings"), + enabled: true, + run: () => this.updateSetting(setting.key, setting.value, setting) + }]; + } + return []; + } + + private updateSetting(key: string, value: any, source: ISetting): void { + this._onUpdateSetting.fire({ key, value, source }); + } +} + +class SettingHighlighter extends Disposable { + + private fixedHighlighter: RangeHighlightDecorations; + private volatileHighlighter: RangeHighlightDecorations; + private highlightedSetting: ISetting; + + constructor(private editor: editorCommon.ICommonCodeEditor, private focusEventEmitter: Emitter, private clearFocusEventEmitter: Emitter, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(); + this.fixedHighlighter = this._register(instantiationService.createInstance(RangeHighlightDecorations)); + this.volatileHighlighter = this._register(instantiationService.createInstance(RangeHighlightDecorations)); + this.fixedHighlighter.onHighlghtRemoved(() => this.clearFocusEventEmitter.fire(this.highlightedSetting)); + this.volatileHighlighter.onHighlghtRemoved(() => this.clearFocusEventEmitter.fire(this.highlightedSetting)); + } + + highlight(setting: ISetting, fix: boolean = false) { + this.highlightedSetting = setting; + this.volatileHighlighter.removeHighlightRange(); + this.fixedHighlighter.removeHighlightRange(); + + const highlighter = fix ? this.fixedHighlighter : this.volatileHighlighter; + highlighter.highlightRange({ + range: setting.valueRange, + resource: this.editor.getModel().uri + }, this.editor); + + this.editor.revealLinesInCenterIfOutsideViewport(setting.valueRange.startLineNumber, setting.valueRange.endLineNumber - 1); + this.focusEventEmitter.fire(setting); + } + + clear(fix: boolean = false): void { + this.volatileHighlighter.removeHighlightRange(); + if (fix) { + this.fixedHighlighter.removeHighlightRange(); + } + this.clearFocusEventEmitter.fire(this.highlightedSetting); + } +} + +class UnTrustedWorkspaceSettingsRenderer extends Disposable { + + constructor(private editor: editorCommon.ICommonCodeEditor, private workspaceSettingsEditorModel: SettingsEditorModel, + @IWorkspaceConfigurationService private configurationService: IWorkspaceConfigurationService, + @IMarkerService private markerService: IMarkerService + ) { + super(); + } + + private getMarkerMessage(settingKey): string { + switch (settingKey) { + case 'php.validate.executablePath': + return nls.localize('unsupportedPHPExecutablePathSetting', "This setting must be a User Setting. To configure PHP for the workspace, open a PHP file and click on 'PHP Path' in the status bar."); + default: + return nls.localize('unsupportedWorkspaceSetting', "This setting must be a User Setting."); + } + } + + public render(): void { + const untrustedConfigurations = this.configurationService.getUntrustedConfigurations(); + if (untrustedConfigurations.length) { + const markerData: IMarkerData[] = []; + for (const untrustedConfiguration of untrustedConfigurations) { + const setting = this.workspaceSettingsEditorModel.getPreference(untrustedConfiguration); + if (setting) { + markerData.push({ + severity: Severity.Warning, + startLineNumber: setting.keyRange.startLineNumber, + startColumn: setting.keyRange.startColumn, + endLineNumber: setting.keyRange.endLineNumber, + endColumn: setting.keyRange.endColumn, + message: this.getMarkerMessage(untrustedConfiguration) + }); + } + } + this.markerService.changeOne('preferencesEditor', this.workspaceSettingsEditorModel.uri, markerData); + } + } + + public dispose(): void { + this.markerService.remove('preferencesEditor', [this.workspaceSettingsEditorModel.uri]); + super.dispose(); + } +} \ No newline at end of file -- GitLab