diff --git a/src/vs/base/common/iterator.ts b/src/vs/base/common/iterator.ts index 45dd6ef4055..0b19d5e56ae 100644 --- a/src/vs/base/common/iterator.ts +++ b/src/vs/base/common/iterator.ts @@ -88,6 +88,7 @@ export interface INavigator extends IIterator { parent(): T; first(): T; last(): T; + next(): T; } export class MappedNavigator extends MappedIterator implements INavigator { @@ -101,4 +102,5 @@ export class MappedNavigator extends MappedIterator implements INavi parent() { return this.fn(this.navigator.parent()); } first() { return this.fn(this.navigator.first()); } last() { return this.fn(this.navigator.last()); } + next() { return this.fn(this.navigator.next()); } } diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 759fbd3cc5f..a6f4c935a75 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -452,8 +452,7 @@ export class VSCodeMenu { } private getPreferencesMenu(): Electron.MenuItem { - const userSettings = this.createMenuItem(nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&User Settings"), 'workbench.action.openGlobalSettings'); - const workspaceSettings = this.createMenuItem(nls.localize({ key: 'miOpenWorkspaceSettings', comment: ['&& denotes a mnemonic'] }, "&&Workspace Settings"), 'workbench.action.openWorkspaceSettings'); + const settings = this.createMenuItem(nls.localize({ key: 'miOpenSettings', comment: ['&& denotes a mnemonic'] }, "&&Settings"), 'workbench.action.openSettings'); const kebindingSettings = this.createMenuItem(nls.localize({ key: 'miOpenKeymap', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts"), 'workbench.action.openGlobalKeybindings'); const keymapExtensions = this.createMenuItem(nls.localize({ key: 'miOpenKeymapExtensions', comment: ['&& denotes a mnemonic'] }, "&&Keymap Extensions"), 'workbench.extensions.action.showRecommendedKeymapExtensions'); const snippetsSettings = this.createMenuItem(nls.localize({ key: 'miOpenSnippets', comment: ['&& denotes a mnemonic'] }, "User &&Snippets"), 'workbench.action.openSnippets'); @@ -461,8 +460,7 @@ export class VSCodeMenu { const iconThemeSelection = this.createMenuItem(nls.localize({ key: 'miSelectIconTheme', comment: ['&& denotes a mnemonic'] }, "File &&Icon Theme"), 'workbench.action.selectIconTheme'); const preferencesMenu = new Menu(); - preferencesMenu.append(userSettings); - preferencesMenu.append(workspaceSettings); + preferencesMenu.append(settings); preferencesMenu.append(__separator__()); preferencesMenu.append(kebindingSettings); preferencesMenu.append(keymapExtensions); diff --git a/src/vs/workbench/common/editor/rangeDecorations.ts b/src/vs/workbench/common/editor/rangeDecorations.ts index 62c1ccd9ec1..6a15a6e4988 100644 --- a/src/vs/workbench/common/editor/rangeDecorations.ts +++ b/src/vs/workbench/common/editor/rangeDecorations.ts @@ -5,6 +5,7 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; +import Event, { Emitter } from 'vs/base/common/event'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { toResource } from 'vs/workbench/common/editor'; @@ -21,12 +22,16 @@ export class RangeHighlightDecorations implements IDisposable { private editor: editorCommon.ICommonCodeEditor = null; private editorDisposables: IDisposable[] = []; + private _onHighlightRemoved: Emitter = new Emitter(); + public readonly onHighlghtRemoved: Event = this._onHighlightRemoved.event; + constructor( @IWorkbenchEditorService private editorService: IWorkbenchEditorService) { } public removeHighlightRange() { - if (this.editor && this.rangeHighlightDecorationId) { - this.doRemoveRangeHighlight(this.editor, this.rangeHighlightDecorationId); + if (this.editor && this.editor.getModel() && this.rangeHighlightDecorationId) { + this.editor.deltaDecorations([this.rangeHighlightDecorationId], []); + this._onHighlightRemoved.fire(); } this.rangeHighlightDecorationId = null; } @@ -67,12 +72,12 @@ export class RangeHighlightDecorations implements IDisposable { || e.reason === editorCommon.CursorChangeReason.Undo || e.reason === editorCommon.CursorChangeReason.Redo ) { - this.doRemoveRangeHighlight(this.editor, this.rangeHighlightDecorationId); + this.removeHighlightRange(); } })); - this.editorDisposables.push(this.editor.onDidChangeModel(() => { this.doRemoveRangeHighlight(this.editor, this.rangeHighlightDecorationId); })); + this.editorDisposables.push(this.editor.onDidChangeModel(() => { this.removeHighlightRange(); })); this.editorDisposables.push(this.editor.onDidDispose(() => { - this.doRemoveRangeHighlight(this.editor, this.rangeHighlightDecorationId); + this.removeHighlightRange(); this.editor = null; })); } @@ -83,10 +88,6 @@ export class RangeHighlightDecorations implements IDisposable { this.editorDisposables = []; } - private doRemoveRangeHighlight(model: editorCommon.ICommonCodeEditor, rangeHighlightDecorationId: string) { - model.deltaDecorations([rangeHighlightDecorationId], []); - } - private createRangeHighlightDecoration(isWholeLine: boolean = true): editorCommon.IModelDecorationOptions { return { stickiness: editorCommon.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, diff --git a/src/vs/workbench/parts/preferences/browser/media/preferences.css b/src/vs/workbench/parts/preferences/browser/media/preferences.css index b4c41817652..234f5b5cc96 100644 --- a/src/vs/workbench/parts/preferences/browser/media/preferences.css +++ b/src/vs/workbench/parts/preferences/browser/media/preferences.css @@ -3,57 +3,83 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.settings-header-widget { - border-bottom: 1px solid #efefef; +.preferences-editor > .preferences-header { + border-bottom: 1px solid #efeff2; + display: flex; + flex-wrap: wrap; + padding-left: 27px; + padding-right: 32px; + padding-bottom: 11px; + padding-top: 11px; } -.vs-dark .settings-header-widget { +.vs-dark .preferences-editor > .preferences-header { border-bottom: 1px solid #2d2d2d; } +.settings-tabs-widget { + display: flex; + flex-wrap: wrap; +} + +.settings-tabs-widget > .settings-tab { + text-transform: uppercase; + font-size: 12px; + font-weight: bold; + margin-left: 20px; + padding-top: 6px; + cursor: pointer; +} + +.settings-tabs-widget > .settings-tab.active { + border-bottom: 2px solid #ccc; +} + +.settings-tabs-widget > .settings-tab.disabled { + opacity: 0.5; + cursor: default; +} + +.preferences-header > .settings-header-widget { + flex: 1; + display: flex; + position: relative; +} + .settings-header-widget > .settings-header-container { padding-top: 8px; padding-left: 27px; padding-right: 32px; } +.settings-header-widget > .settings-count-widget { + margin: 5px 0px; + padding: 0px 8px; + position: absolute; + right: 10px; + border-radius: 2px; +} + +.settings-header-widget > .settings-count-widget:not(.no-results) { + background-color: #EFEFF2; +} + +.hc-black .settings-header-widget > .settings-count-widget:not(.no-results), +.vs-dark .settings-header-widget > .settings-count-widget:not(.no-results) { + background-color: #2D2D30; +} + +.settings-header-widget > .settings-count-widget.no-results { + background-color: rgba(255,0,0,0.5); +} + +.hc-black .settings-header-widget > .settings-count-widget.no-results, +.vs-dark .settings-header-widget > .settings-count-widget.no-results { + background-color: rgba(255,0,0,0.3); +} + .settings-header-widget > .settings-search-container { - padding-left: 27px; - padding-right: 32px; - padding-bottom: 11px; - padding-top: 11px; -} - -.vs .settings-header-widget > .settings-search-container { - background: #efeff2; -} -.vs-dark .settings-header-widget > .settings-search-container { - background: #2d2d30; -} - -.settings-header-widget > .settings-header-container > .settings-title-container { - display: flex -} - -.settings-header-widget > .settings-header-container > .settings-title-container > .settings-info-container { - white-space: pre-wrap; - margin-bottom: 8px; - font-size: 12px; -} - -.vs .settings-header-widget > .settings-header-container > .settings-title-container > .settings-info-container { - color: #6f6f6f; -} -.vs-dark .settings-header-widget > .settings-header-container > .settings-title-container > .settings-info-container { - color: #bbbbbb; -} -.hc-black .settings-header-widget > .settings-header-container > .settings-title-container > .settings-info-container { - color: white; -} - -.settings-header-widget > .settings-header-container > .settings-title-container > .settings-info-container .title-label { - text-transform: uppercase; - font-weight: bold; + flex: 1; } .settings-header-widget > .settings-search-container > .settings-search-input { @@ -62,6 +88,7 @@ .settings-header-widget > .settings-search-container > .settings-search-input > .monaco-inputbox { height: 30px; + min-width: 350px; } .vs .settings-header-widget > .settings-search-container > .settings-search-input > .monaco-inputbox { @@ -80,6 +107,14 @@ background-image: url('search_inverse.svg'); } +.vs-dark .preferences-editor .side-by-side-preferences-editor > .editable-preferences-editor-container { + box-shadow: -6px 0 5px -5px black; +} + +.preferences-editor .side-by-side-preferences-editor > .editable-preferences-editor-container { + box-shadow: -6px 0 5px -5px #DDD; +} + .monaco-editor .settings-group-title-widget { z-index: 1; } @@ -102,7 +137,6 @@ color: white; } - .monaco-editor.vs-dark .settings-group-title-widget .title-container.focused, .monaco-editor.vs .settings-group-title-widget .title-container.focused { outline: none !important; @@ -182,36 +216,4 @@ .vs-dark .title-actions .action-item .icon.collapseAll, .vs-dark .editor-actions .action-item .icon.collapseAll { background: url('collapseAll_inverse.svg') center center no-repeat; -} - -.monaco-editor .settings-count-widget { - padding: 4px 10px 4px 10px; - display: none; - position: absolute; - top: 4px; - right: 28px; - border-radius: 2px; - z-index: 1; -} - -.monaco-editor .settings-count-widget.show { - display: inherit; -} - -.monaco-editor .settings-count-widget.show:not(.no-results) { - background-color: #EFEFF2; -} - -.monaco-editor.hc-black .settings-count-widget.show:not(.no-results), -.monaco-editor.vs-dark .settings-count-widget.show:not(.no-results) { - background-color: #2D2D30; -} - -.monaco-editor .settings-count-widget.show.no-results { - background-color: rgba(255,0,0,0.5); -} - -.monaco-editor.hc-black .settings-count-widget.show.no-results, -.monaco-editor.vs-dark .settings-count-widget.show.no-results { - background-color: rgba(255,0,0,0.3); } \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts b/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts index a030a6fca95..12f385f87d3 100644 --- a/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/parts/preferences/browser/preferences.contribution.ts @@ -14,8 +14,8 @@ import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { DefaultPreferencesEditor, DefaultPreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; -import { OpenGlobalSettingsAction, OpenGlobalKeybindingsAction, OpenWorkspaceSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; +import { DefaultPreferencesEditorInput, PreferencesEditor, PreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; +import { OpenSettingsAction, OpenGlobalSettingsAction, OpenGlobalKeybindingsAction, OpenWorkspaceSettingsAction } from 'vs/workbench/parts/preferences/browser/preferencesActions'; import { IPreferencesService, CONTEXT_DEFAULT_SETTINGS_EDITOR, DEFAULT_EDITOR_COMMAND_COLLAPSE_ALL } from 'vs/workbench/parts/preferences/common/preferences'; import { PreferencesService } from 'vs/workbench/parts/preferences/browser/preferencesService'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; @@ -27,21 +27,84 @@ registerSingleton(IPreferencesService, PreferencesService); Registry.as(EditorExtensions.Editors).registerEditor( new EditorDescriptor( - DefaultPreferencesEditor.ID, + PreferencesEditor.ID, nls.localize('defaultPreferencesEditor', "Default Preferences Editor"), 'vs/workbench/parts/preferences/browser/preferencesEditor', - 'DefaultPreferencesEditor' + 'PreferencesEditor' ), [ - new SyncDescriptor(DefaultPreferencesEditorInput) + new SyncDescriptor(PreferencesEditorInput) ] ); +interface ISerializedPreferencesEditorInput { + name: string; + description: string; + + detailsSerialized: string; + masterSerialized: string; + + detailsTypeId: string; + masterTypeId: string; +} + +// Register Preferences Editor Input Factory +class PreferencesEditorInputFactory implements IEditorInputFactory { + + public serialize(editorInput: EditorInput): string { + const input = editorInput; + + if (input.details && input.master) { + const registry = Registry.as(EditorExtensions.Editors); + const detailsInputFactory = registry.getEditorInputFactory(input.details.getTypeId()); + const masterInputFactory = registry.getEditorInputFactory(input.master.getTypeId()); + + if (detailsInputFactory && masterInputFactory) { + const detailsSerialized = detailsInputFactory.serialize(input.details); + const masterSerialized = masterInputFactory.serialize(input.master); + + if (detailsSerialized && masterSerialized) { + return JSON.stringify({ + name: input.getName(), + description: input.getDescription(), + detailsSerialized, + masterSerialized, + detailsTypeId: input.details.getTypeId(), + masterTypeId: input.master.getTypeId() + }); + } + } + } + + return null; + } + + public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput { + const deserialized: ISerializedPreferencesEditorInput = JSON.parse(serializedEditorInput); + + const registry = Registry.as(EditorExtensions.Editors); + const detailsInputFactory = registry.getEditorInputFactory(deserialized.detailsTypeId); + const masterInputFactory = registry.getEditorInputFactory(deserialized.masterTypeId); + + if (detailsInputFactory && masterInputFactory) { + const detailsInput = detailsInputFactory.deserialize(instantiationService, deserialized.detailsSerialized); + const masterInput = masterInputFactory.deserialize(instantiationService, deserialized.masterSerialized); + + if (detailsInput && masterInput) { + return new PreferencesEditorInput(deserialized.name, deserialized.description, detailsInput, masterInput); + } + } + + return null; + } +} + + interface ISerializedDefaultPreferencesEditorInput { resource: string; } -// Register Editor Input Factory for Default Preferences Input +// Register Default Preferences Editor Input Factory class DefaultPreferencesEditorInputFactory implements IEditorInputFactory { public serialize(editorInput: EditorInput): string { @@ -59,17 +122,19 @@ class DefaultPreferencesEditorInputFactory implements IEditorInputFactory { } } +Registry.as(EditorExtensions.Editors).registerEditorInputFactory(PreferencesEditorInput.ID, PreferencesEditorInputFactory); Registry.as(EditorExtensions.Editors).registerEditorInputFactory(DefaultPreferencesEditorInput.ID, DefaultPreferencesEditorInputFactory); // Contribute Global Actions const category = nls.localize('preferences', "Preferences"); const registry = Registry.as(Extensions.WorkbenchActions); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL, { +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenSettingsAction, OpenSettingsAction.ID, OpenSettingsAction.LABEL, { primary: null, mac: { primary: KeyMod.CtrlCmd | KeyCode.US_COMMA } }), 'Preferences: Open User Settings', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalKeybindingsAction, OpenGlobalKeybindingsAction.ID, OpenGlobalKeybindingsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_S) }), 'Preferences: Open Keyboard Shortcuts', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalSettingsAction, OpenGlobalSettingsAction.ID, OpenGlobalSettingsAction.LABEL), 'Preferences: Open User Settings', category); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceSettingsAction, OpenWorkspaceSettingsAction.ID, OpenWorkspaceSettingsAction.LABEL), 'Preferences: Open Workspace Settings', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenGlobalKeybindingsAction, OpenGlobalKeybindingsAction.ID, OpenGlobalKeybindingsAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_S) }), 'Preferences: Open Keyboard Shortcuts', category); MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { diff --git a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts index 59c9ae66bd2..24be266eaf0 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesActions.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesActions.ts @@ -9,6 +9,24 @@ import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { IPreferencesService } from 'vs/workbench/parts/preferences/common/preferences'; +export class OpenSettingsAction extends Action { + + public static ID = 'workbench.action.openSettings'; + public static LABEL = nls.localize('openSettings', "Open Settings"); + + constructor( + id: string, + label: string, + @IPreferencesService private preferencesService: IPreferencesService + ) { + super(id, label); + } + + public run(event?: any): TPromise { + return this.preferencesService.openSettings(); + } +} + export class OpenGlobalSettingsAction extends Action { public static ID = 'workbench.action.openGlobalSettings'; diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index fdc91ef3a81..a2378268e0c 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -10,16 +10,18 @@ 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 } from 'vs/base/common/arrays'; -import { ArrayIterator } from 'vs/base/common/iterator'; +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'; -import { EditorOptions, toResource } from 'vs/workbench/common/editor'; +import { toResource, SideBySideEditorInput, EditorOptions, EditorInput, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor'; +import { BaseEditor, EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; 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'; @@ -35,7 +37,7 @@ import { SettingsEditorModel, DefaultSettingsEditorModel } from 'vs/workbench/pa 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 { DefaultSettingsHeaderWidget, SettingsGroupTitleWidget, SettingsCountWidget, EditPreferenceWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; +import { SearchWidget, SettingsTabsWidget, SettingsGroupTitleWidget, EditPreferenceWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { CommonEditorRegistry, EditorCommand, Command } from 'vs/editor/common/editorCommonExtensions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -49,21 +51,26 @@ import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/edi 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 } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationEditingService, 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'; // Ignore following contributions import { FoldingController } from 'vs/editor/contrib/folding/browser/folding'; import { FindController } from 'vs/editor/contrib/find/browser/find'; import { SelectionHighlighter } from 'vs/editor/contrib/find/common/findController'; +export class PreferencesEditorInput extends SideBySideEditorInput { + public static ID: string = 'workbench.editorinputs.preferencesEditorInput'; + + getTypeId(): string { + return PreferencesEditorInput.ID; + } +} export class DefaultPreferencesEditorInput extends ResourceEditorInput { - public static ID = 'workbench.editorinputs.defaultpreferences'; - - private _willDispose = new Emitter(); - public willDispose: Event = this._willDispose.event; - constructor(defaultSettingsResource: URI, @ITextModelResolverService textModelResolverService: ITextModelResolverService ) { @@ -74,10 +81,6 @@ export class DefaultPreferencesEditorInput extends ResourceEditorInput { return DefaultPreferencesEditorInput.ID; } - supportsSplitEditor(): boolean { - return false; - } - matches(other: any): boolean { if (!super.matches(other)) { return false; @@ -87,10 +90,268 @@ export class DefaultPreferencesEditorInput extends ResourceEditorInput { } return true; } +} - dispose() { - this._willDispose.fire(); - this._willDispose.dispose(); +export class PreferencesEditor extends BaseEditor { + + public static ID: string = 'workbench.editor.preferencesEditor'; + + private headerContainer: HTMLElement; + private searchWidget: SearchWidget; + private settingsTabsWidget: SettingsTabsWidget; + private sideBySidePreferencesWidget: SideBySidePreferencesWidget; + + private delayedFilterLogging: Delayer; + private disposablesByInput: IDisposable[] = []; + + constructor( + @IPreferencesService private preferencesService: IPreferencesService, + @IEnvironmentService private environmentService: IEnvironmentService, + @ITelemetryService telemetryService: ITelemetryService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService, + @IInstantiationService private instantiationService: IInstantiationService + ) { + super(PreferencesEditor.ID, telemetryService); + this.delayedFilterLogging = new Delayer(1000); + } + + public createEditor(parent: Builder): void { + const parentElement = parent.getHTMLElement(); + DOM.addClass(parentElement, 'preferences-editor'); + + this.headerContainer = DOM.append(parentElement, DOM.$('.preferences-header')); + + this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, this.headerContainer)); + this._register(this.searchWidget.onDidChange(value => this.filterPreferences(value.trim()))); + this._register(this.searchWidget.onEnter(value => this.focusNextPreference())); + + this.settingsTabsWidget = this._register(this.instantiationService.createInstance(SettingsTabsWidget, this.headerContainer)); + this._register(this.settingsTabsWidget.onSwitch(() => this.switchSettings())); + + const editorsContainer = DOM.append(parentElement, DOM.$('.preferences-editors-container')); + this.sideBySidePreferencesWidget = this._register(this.instantiationService.createInstance(SideBySidePreferencesWidget, editorsContainer)); + } + + public setInput(newInput: PreferencesEditorInput, options?: EditorOptions): TPromise { + const oldInput = this.input; + return super.setInput(newInput, options) + .then(() => this.updateInput(oldInput, newInput, options)); + } + + public layout(dimension: Dimension): void { + const headerHeight = DOM.getTotalHeight(this.headerContainer); + this.sideBySidePreferencesWidget.layout(new Dimension(dimension.width, dimension.height - headerHeight)); + } + + public getControl(): IEditorControl { + const editablePreferencesEditor = this.sideBySidePreferencesWidget.getEditablePreferencesEditor(); + return editablePreferencesEditor ? editablePreferencesEditor.getControl() : null; + } + + public focus(): void { + this.searchWidget.focus(); + } + + private updateInput(oldInput: PreferencesEditorInput, newInput: PreferencesEditorInput, options?: EditorOptions): TPromise { + const editablePreferencesUri = toResource(newInput.master); + this.settingsTabsWidget.show(editablePreferencesUri.toString() === this.preferencesService.userSettingsResource.toString() ? ConfigurationTarget.USER : ConfigurationTarget.WORKSPACE); + + this.disposablesByInput = dispose(this.disposablesByInput); + return this.sideBySidePreferencesWidget.setInput(newInput.details, newInput.master, options).then(() => { + this.showTotalCount(); + if (!this.getDefaultPreferencesRenderer()) { + return; + } + this.getDefaultPreferencesRenderer().onFocusPreference(setting => this.getEditablePreferencesRenderer().focusPreference(setting), this.disposablesByInput); + this.getDefaultPreferencesRenderer().onClearFocusPreference(setting => this.getEditablePreferencesRenderer().clearFocus(setting), this.disposablesByInput); + this.filterPreferences(this.searchWidget.value()); + }); + } + + private switchSettings(): void { + const promise = this.input.isDirty() ? this.input.save() : TPromise.as(true); + promise.done(value => this.preferencesService.switchSettings()); + } + + private filterPreferences(filter: string) { + if (!this.getDefaultPreferencesRenderer()) { + return; + } + const defaultPreferencesRenderer = this.getDefaultPreferencesRenderer(); + const editablePreferencesRender = this.getEditablePreferencesRenderer(); + if (filter) { + this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(filter)); + const filterResult = defaultPreferencesRenderer.preferencesModel.filterSettings(filter); + defaultPreferencesRenderer.filterPreferences(filterResult); + editablePreferencesRender.filterPreferences(filterResult); + const count = this.getCount(filterResult.filteredGroups); + this.searchWidget.showMessage(this.showSearchResultsMessage(count), count); + } else { + defaultPreferencesRenderer.filterPreferences(null); + editablePreferencesRender.filterPreferences(null); + this.showTotalCount(); + } + } + + private showTotalCount(): void { + if (this.getDefaultPreferencesRenderer()) { + const count = this.getCount(this.getDefaultPreferencesRenderer().preferencesModel.settingsGroups); + this.searchWidget.showMessage(nls.localize('totalSettingsMessage', "Total {0} Settings", count), count); + } + } + + private showSearchResultsMessage(count: number): string { + return count === 0 ? nls.localize('noSettingsFound', "No Settings matched") : + count === 1 ? nls.localize('oneSettingFound', "1 Setting matched") : + nls.localize('settingsFound', "{0} Settings matched", count); + } + + private focusNextPreference() { + const defaultPreferencesRenderer = this.getDefaultPreferencesRenderer(); + if (defaultPreferencesRenderer) { + const setting = defaultPreferencesRenderer.iterator.next(); + if (setting) { + defaultPreferencesRenderer.focusPreference(setting); + this.getEditablePreferencesRenderer().focusPreference(setting); + } + } + } + + private getDefaultPreferencesRenderer(): IPreferencesRenderer { + const detailsEditor = this.sideBySidePreferencesWidget.getDefaultPreferencesEditor(); + if (detailsEditor) { + return (this.sideBySidePreferencesWidget.getDefaultPreferencesEditor().getControl()).getContribution(DefaultSettingsEditorContribution.ID).getPreferencesRenderer(); + } + return null; + } + + private getEditablePreferencesRenderer(): IPreferencesRenderer { + if (this.sideBySidePreferencesWidget.getEditablePreferencesEditor()) { + return (this.sideBySidePreferencesWidget.getEditablePreferencesEditor().getControl()).getContribution(SettingsEditorContribution.ID).getPreferencesRenderer(); + } + return null; + } + + private reportFilteringUsed(filter: string): void { + let data = {}; + data['filter'] = filter; + this.telemetryService.publicLog('defaultSettings.filter', data); + } + + private getCount(settingsGroups: ISettingsGroup[]): number { + let count = 0; + for (const group of settingsGroups) { + for (const section of group.sections) { + count += section.settings.length; + } + } + return count; + } +} + +export class SideBySidePreferencesWidget extends Widget { + + private dimension: Dimension; + + private defaultPreferencesEditor: DefaultPreferencesEditor; + private defaultPreferencesEditorContainer: HTMLElement; + private editablePreferencesEditor: BaseEditor; + private editablePreferencesEditorContainer: HTMLElement; + + private sash: VSash; + + constructor(parent: HTMLElement, @IInstantiationService private instantiationService: IInstantiationService) { + super(); + this.create(parent); + } + + private create(parentElement: HTMLElement): void { + DOM.addClass(parentElement, 'side-by-side-preferences-editor'); + this.createSash(parentElement); + + this.defaultPreferencesEditorContainer = DOM.append(parentElement, DOM.$('.default-preferences-editor-container')); + this.defaultPreferencesEditorContainer.style.position = 'absolute'; + this.defaultPreferencesEditor = this.instantiationService.createInstance(DefaultPreferencesEditor); + this.defaultPreferencesEditor.create(new Builder(this.defaultPreferencesEditorContainer)); + + this.editablePreferencesEditorContainer = DOM.append(parentElement, DOM.$('.editable-preferences-editor-container')); + this.editablePreferencesEditorContainer.style.position = 'absolute'; + } + + public setInput(defaultPreferencesEditorInput: DefaultPreferencesEditorInput, editablePreferencesEditorInput: EditorInput, options?: EditorOptions): TPromise { + return this.getOrCreateEditablePreferencesEditor(editablePreferencesEditorInput) + .then(() => { + this.dolayout(this.sash.getVerticalSashLeft()); + return TPromise.join([this.defaultPreferencesEditor.updateInput(defaultPreferencesEditorInput, options, toResource(editablePreferencesEditorInput), this.editablePreferencesEditor), + this.editablePreferencesEditor.setInput(editablePreferencesEditorInput, options)]) + .then(() => null); + }); + } + + public layout(dimension: Dimension): void { + this.dimension = dimension; + this.sash.setDimenesion(this.dimension); + } + + public getEditablePreferencesEditor(): IEditor { + return this.editablePreferencesEditor; + } + + public getDefaultPreferencesEditor(): DefaultPreferencesEditor { + return this.defaultPreferencesEditor; + } + + private getOrCreateEditablePreferencesEditor(editorInput: EditorInput): TPromise { + if (this.editablePreferencesEditor) { + return TPromise.as(this.editablePreferencesEditor); + } + const descriptor = Registry.as(EditorExtensions.Editors).getEditor(editorInput); + return this.instantiationService.createInstance(descriptor) + .then((editor: BaseEditor) => { + this.editablePreferencesEditor = editor; + this.editablePreferencesEditor.create(new Builder(this.editablePreferencesEditorContainer)); + this.editablePreferencesEditor.setVisible(true); + return editor; + }); + } + + private createSash(parentElement: HTMLElement): void { + this.sash = this._register(new VSash(parentElement, 220)); + this._register(this.sash.onPositionChange(position => this.dolayout(position))); + } + + private dolayout(splitPoint: number): void { + if (!this.editablePreferencesEditor || !this.dimension) { + return; + } + const masterEditorWidth = this.dimension.width - splitPoint; + const detailsEditorWidth = this.dimension.width - masterEditorWidth; + + this.defaultPreferencesEditorContainer.style.width = `${detailsEditorWidth}px`; + this.defaultPreferencesEditorContainer.style.height = `${this.dimension.height}px`; + this.defaultPreferencesEditorContainer.style.left = '0px'; + + this.editablePreferencesEditorContainer.style.width = `${masterEditorWidth}px`; + this.editablePreferencesEditorContainer.style.height = `${this.dimension.height}px`; + this.editablePreferencesEditorContainer.style.left = `${splitPoint}px`; + + this.defaultPreferencesEditor.layout(new Dimension(detailsEditorWidth, this.dimension.height)); + this.editablePreferencesEditor.layout(new Dimension(masterEditorWidth, this.dimension.height)); + } + + private disposeEditors(): void { + if (this.defaultPreferencesEditor) { + this.defaultPreferencesEditor.dispose(); + this.defaultPreferencesEditor = null; + } + if (this.editablePreferencesEditor) { + this.editablePreferencesEditor.dispose(); + this.editablePreferencesEditor = null; + } + } + + public dispose(): void { + this.disposeEditors(); super.dispose(); } } @@ -99,9 +360,6 @@ export class DefaultPreferencesEditor extends BaseTextEditor { public static ID: string = 'workbench.editor.defaultPreferences'; - private defaultSettingHeaderWidget: DefaultSettingsHeaderWidget; - private delayedFilterLogging: Delayer; - constructor( @ITelemetryService telemetryService: ITelemetryService, @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @@ -115,19 +373,10 @@ export class DefaultPreferencesEditor extends BaseTextEditor { @IModeService modeService: IModeService, ) { super(DefaultPreferencesEditor.ID, telemetryService, instantiationService, storageService, configurationService, themeService, modeService); - this.delayedFilterLogging = new Delayer(1000); } public createEditorControl(parent: Builder, configuration: editorCommon.IEditorOptions): editorCommon.IEditor { - const parentContainer = parent.getHTMLElement(); - - this.defaultSettingHeaderWidget = this._register(this.instantiationService.createInstance(DefaultSettingsHeaderWidget, parentContainer)); - this._register(this.defaultSettingHeaderWidget.onDidChange(value => this.filterPreferences(value))); - this._register(this.defaultSettingHeaderWidget.onEnter(value => this.focusNextPreference())); - - const defaultPreferencesEditor = this.instantiationService.createInstance(DefaultPreferencesCodeEditor, parentContainer, configuration); - - return defaultPreferencesEditor; + return this.instantiationService.createInstance(DefaultPreferencesCodeEditor, parent.getHTMLElement(), configuration); } protected getConfigurationOverrides(): editorCommon.IEditorOptions { @@ -146,66 +395,30 @@ export class DefaultPreferencesEditor extends BaseTextEditor { return options; } - setInput(input: DefaultPreferencesEditorInput, options: EditorOptions): TPromise { - return super.setInput(input, options).then(() => this.updateInput()); + updateInput(input: DefaultPreferencesEditorInput, options: EditorOptions, editablePreferencesUri: URI, settingsEditor: BaseEditor): TPromise { + return this.setInput(input, options) + .then(() => this.input.resolve() + .then(editorModel => TPromise.join([ + editorModel.load(), + this.preferencesService.resolvePreferencesEditorModel(editablePreferencesUri) + ])) + .then(([editorModel, preferencesModel]) => (this.getControl()).setModels((editorModel).textEditorModel, preferencesModel, settingsEditor))); } public layout(dimension: Dimension) { - this.defaultSettingHeaderWidget.layout(dimension); - const headerHeight = DOM.getTotalHeight(this.defaultSettingHeaderWidget.domNode); - this.getControl().layout({ - height: dimension.height - headerHeight, - width: dimension.width - }); - } - - public focus(): void { - if (this.input) { - this.defaultSettingHeaderWidget.focus(); - } else { - super.focus(); - } - } - - private updateInput(): TPromise { - return this.input.resolve() - .then(editorModel => TPromise.join([ - editorModel.load(), - // Default preferences editor is always part of side by side editor hence getting the master preferences model from active editor - // TODO:@sandy check with Ben - this.preferencesService.resolvePreferencesEditorModel(toResource(this.editorService.getActiveEditorInput(), { supportSideBySide: true })) - ])) - .then(([editorModel, preferencesModel]) => (this.getControl()).setModels((editorModel).textEditorModel, preferencesModel)); - } - - private filterPreferences(filter: string) { - this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(filter)); - (this.getDefaultPreferencesContribution().getPreferencesRenderer()).filterPreferences(filter.trim()); - } - - private focusNextPreference() { - (this.getDefaultPreferencesContribution().getPreferencesRenderer()).focusNextSetting(); + this.getControl().layout(dimension); } public clearInput(): void { this.getControl().setModel(null); super.clearInput(); } - - private getDefaultPreferencesContribution(): PreferencesEditorContribution { - return (this.getControl()).getContribution(DefaultSettingsEditorContribution.ID); - } - - private reportFilteringUsed(filter: string): void { - let data = {}; - data['filter'] = filter; - this.telemetryService.publicLog('defaultSettings.filter', data); - } } class DefaultPreferencesCodeEditor extends CodeEditor { private _settingsModel: SettingsEditorModel; + private _settingsEditor: BaseEditor; protected _getContributions(): IEditorContributionCtor[] { let contributions = super._getContributions(); @@ -215,19 +428,31 @@ class DefaultPreferencesCodeEditor extends CodeEditor { return contributions; } - setModels(model: editorCommon.IModel, settingsModel: SettingsEditorModel): void { + setModels(model: editorCommon.IModel, settingsModel: SettingsEditorModel, settingsEditor: BaseEditor): void { this._settingsModel = settingsModel; + this._settingsEditor = settingsEditor; return super.setModel(model); } get settingsModel(): SettingsEditorModel { return this._settingsModel; } + + get settingsEditor(): BaseEditor { + return this._settingsEditor; + } } export interface IPreferencesRenderer { - render(); + iterator: IIterator; + onFocusPreference: Event; + onClearFocusPreference: Event; + preferencesModel: ISettingsEditorModel; + render(): void; updatePreference(setting: ISetting, value: any): void; + filterPreferences(filterResult: IFilterResult): void; + focusPreference(setting: ISetting): void; + clearFocus(setting: ISetting): void; dispose(); } @@ -285,7 +510,7 @@ export class DefaultSettingsEditorContribution extends PreferencesEditorContribu protected createPreferencesRenderer(editorModel: IPreferencesEditorModel): IPreferencesRenderer { if (editorModel instanceof DefaultSettingsEditorModel) { - return this.instantiationService.createInstance(DefaultSettingsRenderer, this.editor, editorModel, (this.editor).settingsModel); + return this.instantiationService.createInstance(DefaultSettingsRenderer, this.editor, editorModel); } return null; } @@ -317,9 +542,17 @@ export class SettingsRenderer extends Disposable implements IPreferencesRenderer private initializationPromise: TPromise; private settingHighlighter: SettingHighlighter; private editSettingActionRenderer: EditSettingRenderer; + private highlightPreferencesRenderer: HighlightPreferencesRenderer; + private defaultSettingsModel: DefaultSettingsEditorModel; private modelChangeDelayer: Delayer = new Delayer(200); - constructor(protected editor: ICodeEditor, protected settingsEditorModel: SettingsEditorModel, + 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: SettingsEditorModel, @IPreferencesService protected preferencesService: IPreferencesService, @ITelemetryService private telemetryService: ITelemetryService, @IConfigurationEditingService private configurationEditingService: IConfigurationEditingService, @@ -327,18 +560,26 @@ export class SettingsRenderer extends Disposable implements IPreferencesRenderer @IInstantiationService protected instantiationService: IInstantiationService ) { super(); - this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor)); + this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor, this._onFocusPreference, this._onClearFocusPreference)); + this.highlightPreferencesRenderer = this._register(instantiationService.createInstance(HighlightPreferencesRenderer, editor)); this.initializationPromise = this.initialize(); } + public get iterator(): IIterator { + return null; + } + public render(): void { - this.initializationPromise.then(() => this.editSettingActionRenderer.render(this.settingsEditorModel.settingsGroups)); + this.initializationPromise.then(() => { + this.editSettingActionRenderer.render(this.preferencesModel.settingsGroups); + }); } private initialize(): TPromise { return this.preferencesService.createDefaultPreferencesEditorModel(this.preferencesService.defaultSettingsResource) .then(defaultSettingsModel => { - this.editSettingActionRenderer = this._register(this.instantiationService.createInstance(EditSettingRenderer, this.editor, this.settingsEditorModel, defaultSettingsModel, this.settingHighlighter)); + this.defaultSettingsModel = defaultSettingsModel; + this.editSettingActionRenderer = this._register(this.instantiationService.createInstance(EditSettingRenderer, this.editor, this.preferencesModel, () => defaultSettingsModel, this.settingHighlighter)); this._register(this.editor.getModel().onDidChangeContent(() => this.modelChangeDelayer.trigger(() => this.onModelChanged()))); this._register(this.editSettingActionRenderer.onUpdateSetting(({setting, value}) => this.updatePreference(setting, value))); return null; @@ -355,16 +596,48 @@ export class SettingsRenderer extends Disposable implements IPreferencesRenderer public updatePreference(setting: ISetting, value: any): void { this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [setting.key] }); - this.configurationEditingService.writeConfiguration(this.settingsEditorModel.configurationTarget, { key: setting.key, value }, { writeToBuffer: true, autoSave: true }) + this.configurationEditingService.writeConfiguration(this.preferencesModel.configurationTarget, { key: setting.key, value }, { writeToBuffer: true, autoSave: true }) .then(() => this.onSettingUpdated(setting), error => this.messageService.show(Severity.Error, error)); } private onSettingUpdated(setting: ISetting) { this.editor.focus(); - setting = this.settingsEditorModel.getSetting(setting.key); + setting = this.preferencesModel.getSetting(setting.key); // TODO:@sandy Selection range should be template range this.editor.setSelection(setting.valueRange); - this.settingHighlighter.highlight(this.settingsEditorModel.getSetting(setting.key), true); + this.settingHighlighter.highlight(this.preferencesModel.getSetting(setting.key), true); + } + + public filterPreferences(filterResult: IFilterResult): void { + this.highlightPreferencesRenderer.render([]); + this.settingHighlighter.clear(true); + if (this.defaultSettingsModel && 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.preferencesModel.getSetting(setting.key); + if (s) { + settings.push(s); + } + } + } + return settings; + }, [])); + this.highlightPreferencesRenderer.render(settings); + } + } + + public focusPreference(setting: ISetting): void { + const s = this.preferencesModel.getSetting(setting.key); + if (s) { + this.settingHighlighter.highlight(s, true); + } else { + this.settingHighlighter.clear(true); + } + } + + public clearFocus(setting: ISetting): void { + this.settingHighlighter.clear(true); } } @@ -378,10 +651,14 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR private filteredSettingsNavigationRenderer: FilteredSettingsNavigationRenderer; private hiddenAreasRenderer: HiddenAreasRenderer; private editSettingActionRenderer: EditSettingRenderer; - private settingsCountWidget: SettingsCountWidget; - constructor(protected editor: ICodeEditor, protected defaultSettingsEditorModel: DefaultSettingsEditorModel, - private settingsEditorModel: SettingsEditorModel, + 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, @@ -389,50 +666,53 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR ) { super(); this.defaultSettingsEditorContextKey = CONTEXT_DEFAULT_SETTINGS_EDITOR.bindTo(contextKeyService); - this.settingHighlighter = this._register(instantiationService.createInstance(SettingHighlighter, editor)); + 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, defaultSettingsEditorModel, settingsEditorModel, this.settingHighlighter)); + this.editSettingActionRenderer = this._register(instantiationService.createInstance(EditSettingRenderer, editor, preferencesModel, () => (this.editor).settingsModel, this.settingHighlighter)); this._register(this.editSettingActionRenderer.onUpdateSetting(({setting, value}) => this.updatePreference(setting, value))); - this.settingsCountWidget = this._register(instantiationService.createInstance(SettingsCountWidget, editor, this.getCount(defaultSettingsEditorModel.settingsGroups))); - const paranthesisHidingRenderer = this._register(instantiationService.createInstance(StaticContentHidingRenderer, editor, defaultSettingsEditorModel.settingsGroups)); + 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.defaultSettingsEditorModel.settingsGroups); - this.editSettingActionRenderer.render(this.defaultSettingsEditorModel.settingsGroups); - this.settingsCountWidget.render(); + 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(filter: string) { - const filterResult = this.defaultSettingsEditorModel.filterSettings(filter); - this.filteredMatchesRenderer.render(filterResult); - this.settingsGroupTitleRenderer.render(filterResult.filteredGroups); - this.settingsCountWidget.show(this.getCount(filterResult.filteredGroups)); - - if (!filter) { + 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 focusNextSetting(): void { - const setting = this.filteredSettingsNavigationRenderer.next(); - if (setting) { - this.settingsGroupTitleRenderer.showSetting(setting); - } + 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() { @@ -447,23 +727,7 @@ export class DefaultSettingsRenderer extends Disposable implements IPreferencesR } private getEditableSettingsEditor(): editorCommon.ICommonCodeEditor { - return this.editorService.getVisibleEditors() - .filter(editor => { - if (editorCommon.isCommonCodeEditor(editor.getControl())) { - return (editor.getControl()).getModel().uri.fsPath === this.settingsEditorModel.uri.fsPath; - } - }) - .map(editor => editor.getControl())[0]; - } - - private getCount(settingsGroups: ISettingsGroup[]): number { - let count = 0; - for (const group of settingsGroups) { - for (const section of group.sections) { - count += section.settings.length; - } - } - return count; + return (this.editor).settingsEditor.getControl(); } dispose() { @@ -722,21 +986,58 @@ export class FilteredMatchesRenderer extends Disposable implements HiddenAreasPr } } -class FilteredSettingsNavigationRenderer extends Disposable { +export class HighlightPreferencesRenderer extends Disposable { - private iterator: ArrayIterator; + 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 { - let setting = this.iterator.next() || this.iterator.first(); - if (setting) { - this.settingHighlighter.highlight(setting, true); - return setting; - } - return null; + return this.iterator.next() || this.iterator.first(); } public render(filteredGroups: ISettingsGroup[]) { @@ -747,7 +1048,7 @@ class FilteredSettingsNavigationRenderer extends Disposable { settings.push(...section.settings); } } - this.iterator = new ArrayIterator(settings); + this.iterator = new ArrayNavigator(settings); } } @@ -763,7 +1064,7 @@ class EditSettingRenderer extends Disposable { public readonly onUpdateSetting: Event<{ setting: ISetting, value: any }> = this._onUpdateSetting.event; constructor(private editor: ICodeEditor, private masterSettingsModel: ISettingsEditorModel, - private otherSettingsModel: ISettingsEditorModel, + private otherSettingsModel: () => ISettingsEditorModel, private settingHighlighter: SettingHighlighter, @IPreferencesService private preferencesService: IPreferencesService, @IInstantiationService private instantiationService: IInstantiationService, @@ -921,7 +1222,7 @@ class EditSettingRenderer extends Disposable { } private getDefaultActions(setting: ISetting): IAction[] { - const settingInOtherModel = this.otherSettingsModel.getSetting(setting.key); + const settingInOtherModel = this.otherSettingsModel().getSetting(setting.key); if (this.isDefaultSettings()) { return [{ id: 'setDefaultValue', @@ -942,14 +1243,20 @@ class SettingHighlighter extends Disposable { private fixedHighlighter: RangeHighlightDecorations; private volatileHighlighter: RangeHighlightDecorations; + private highlightedSetting: ISetting; - constructor(private editor: editorCommon.ICommonCodeEditor, @IInstantiationService instantiationService: IInstantiationService) { + 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(); @@ -960,6 +1267,7 @@ class SettingHighlighter extends Disposable { }, this.editor); this.editor.revealLinesInCenterIfOutsideViewport(setting.valueRange.startLineNumber, setting.valueRange.endLineNumber - 1); + this.focusEventEmitter.fire(setting); } clear(fix: boolean = false): void { @@ -967,6 +1275,7 @@ class SettingHighlighter extends Disposable { if (fix) { this.fixedHighlighter.removeHighlightRange(); } + this.clearFocusEventEmitter.fire(this.highlightedSetting); } } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index 24d871f3a58..83d9b8b0192 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -10,13 +10,13 @@ import URI from 'vs/base/common/uri'; import { LinkedMap as Map } from 'vs/base/common/map'; import * as labels from 'vs/base/common/labels'; import { Disposable } from 'vs/base/common/lifecycle'; -import { SideBySideEditorInput, EditorInput } from 'vs/workbench/common/editor'; +import { EditorInput, toResource } from 'vs/workbench/common/editor'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IWorkspaceConfigurationService, WORKSPACE_CONFIG_DEFAULT_PATH } from 'vs/workbench/services/configuration/common/configuration'; -import { Position, IEditor } from 'vs/platform/editor/common/editor'; +import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; +import { Position } from 'vs/platform/editor/common/editor'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IStorageService } 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 { IExtensionService } from 'vs/platform/extensions/common/extensions'; @@ -26,11 +26,9 @@ import { IConfigurationEditingService, ConfigurationTarget } from 'vs/workbench/ import { IPreferencesService, IPreferencesEditorModel } from 'vs/workbench/parts/preferences/common/preferences'; import { SettingsEditorModel, DefaultSettingsEditorModel, DefaultKeybindingsEditorModel } from 'vs/workbench/parts/preferences/common/preferencesModels'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { DefaultPreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; +import { DefaultPreferencesEditorInput, PreferencesEditorInput } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; import { ITextModelResolverService } from 'vs/editor/common/services/resolverService'; -const SETTINGS_INFO_IGNORE_KEY = 'settings.workspace.info.ignore'; - interface IWorkbenchSettingsConfiguration { workbench: { settings: { @@ -45,8 +43,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic // TODO:@sandy merge these models into editor inputs by extending resource editor model private defaultPreferencesEditorModels: Map; - private defaultSettingsEditorInputForUser: DefaultPreferencesEditorInput; - private defaultSettingsEditorInputForWorkspace: DefaultPreferencesEditorInput; + private lastOpenedSettingsInput: PreferencesEditorInput = null; constructor( @IWorkbenchEditorService private editorService: IWorkbenchEditorService, @@ -66,11 +63,25 @@ export class PreferencesService extends Disposable implements IPreferencesServic ) { super(); this.defaultPreferencesEditorModels = new Map(); + this.editorGroupService.onEditorsChanged(() => { + const activeEditorInput = this.editorService.getActiveEditorInput(); + if (activeEditorInput instanceof PreferencesEditorInput) { + this.lastOpenedSettingsInput = activeEditorInput; + } + }); } readonly defaultSettingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/settings.json' }); readonly defaultKeybindingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/keybindings.json' }); + get userSettingsResource(): URI { + return this.getEditableSettingsURI(ConfigurationTarget.USER); + } + + get workspaceSettingsResource(): URI { + return this.getEditableSettingsURI(ConfigurationTarget.WORKSPACE); + } + createDefaultPreferencesEditorModel(uri: URI): TPromise { const editorModel = this.defaultPreferencesEditorModels.get(uri); if (editorModel) { @@ -114,12 +125,12 @@ export class PreferencesService extends Disposable implements IPreferencesServic return TPromise.wrap(null); } + openSettings(): TPromise { + return this.doOpenSettings(this.getSettingsConfigurationTarget(this.lastOpenedSettingsInput), false); + } + openGlobalSettings(): TPromise { - if (this.configurationService.hasWorkspaceConfiguration() && !this.storageService.getBoolean(SETTINGS_INFO_IGNORE_KEY, StorageScope.WORKSPACE)) { - this.promptToOpenWorkspaceSettings(); - } - // Open settings - return this.openSettings(ConfigurationTarget.USER); + return this.doOpenSettings(ConfigurationTarget.USER); } openWorkspaceSettings(): TPromise { @@ -127,7 +138,27 @@ export class PreferencesService extends Disposable implements IPreferencesServic this.messageService.show(Severity.Info, nls.localize('openFolderFirst', "Open a folder first to create workspace settings")); return TPromise.as(null); } - return this.openSettings(ConfigurationTarget.WORKSPACE); + return this.doOpenSettings(ConfigurationTarget.WORKSPACE); + } + + switchSettings(): TPromise { + const activeEditorInput = this.editorService.getActiveEditorInput(); + if (activeEditorInput instanceof PreferencesEditorInput) { + const fromTarget = this.getSettingsConfigurationTarget(activeEditorInput); + const toTarget = ConfigurationTarget.USER === fromTarget ? ConfigurationTarget.WORKSPACE : ConfigurationTarget.USER; + return this.getOrCreateEditableSettingsEditorInput(toTarget) + .then(toInput => { + const replaceWith = new PreferencesEditorInput(toInput.getName(), toInput.getDescription(), this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.defaultSettingsResource), toInput); + return this.editorService.replaceEditors([{ + toReplace: this.lastOpenedSettingsInput, + replaceWith + }]).then(() => { + this.lastOpenedSettingsInput = replaceWith; + }); + }); + } else { + this.openSettings(); + } } openGlobalKeybindingSettings(): TPromise { @@ -135,13 +166,25 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.openTwoEditors(this.defaultKeybindingsResource, URI.file(this.environmentService.appKeybindingsPath), emptyContents).then(() => null); } - private openEditableSettings(configurationTarget: ConfigurationTarget): TPromise { - const emptySettingsContents = this.getEmptyEditableSettingsContent(configurationTarget); - const settingsResource = this.getEditableSettingsURI(configurationTarget); - return this.createIfNotExists(settingsResource, emptySettingsContents).then(() => this.editorService.openEditor({ - resource: settingsResource, - options: { pinned: true } - })); + private doOpenSettings(configurationTarget: ConfigurationTarget, checkToOpenDefaultSettings: boolean = true): TPromise { + const openDefaultSettings = !checkToOpenDefaultSettings || !!this.configurationService.getConfiguration().workbench.settings.openDefaultSettings; + return this.getOrCreateEditableSettingsEditorInput(configurationTarget) + .then(editableSettingsEditorInput => { + if (openDefaultSettings) { + const defaultPreferencesEditorInput = this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.defaultSettingsResource); + const preferencesEditorInput = new PreferencesEditorInput(editableSettingsEditorInput.getName(), editableSettingsEditorInput.getDescription(), defaultPreferencesEditorInput, editableSettingsEditorInput); + this.lastOpenedSettingsInput = preferencesEditorInput; + return this.editorService.openEditor(preferencesEditorInput, { pinned: true }); + } + return this.editorService.openEditor(editableSettingsEditorInput, { pinned: true }); + }).then(() => null); + } + + private getOrCreateEditableSettingsEditorInput(configurationTarget: ConfigurationTarget): TPromise { + const resource = this.getEditableSettingsURI(configurationTarget); + const editableSettingsEmptyContent = this.getEmptyEditableSettingsContent(configurationTarget); + return this.createIfNotExists(resource, editableSettingsEmptyContent) + .then(() => this.editorService.createInput({ resource })); } private resolveSettingsEditorModel(configurationTarget: ConfigurationTarget): TPromise { @@ -179,58 +222,6 @@ export class PreferencesService extends Disposable implements IPreferencesServic return null; } - private promptToOpenWorkspaceSettings() { - this.choiceService.choose(Severity.Info, nls.localize('workspaceHasSettings', "The currently opened folder contains workspace settings that may override user settings"), - [nls.localize('openWorkspaceSettings', "Open Workspace Settings"), nls.localize('neverShowAgain', "Don't show again"), nls.localize('close', "Close")] - ).then(choice => { - switch (choice) { - case 0: - const editorCount = this.editorService.getVisibleEditors().length; - return this.editorService.openEditor({ resource: this.contextService.toResource(WORKSPACE_CONFIG_DEFAULT_PATH), options: { pinned: true } }, editorCount === 2 ? Position.THREE : editorCount === 1 ? Position.TWO : void 0); - case 1: - this.storageService.store(SETTINGS_INFO_IGNORE_KEY, true, StorageScope.WORKSPACE); - default: - return TPromise.as(true); - } - }); - } - - private openSettings(configurationTarget: ConfigurationTarget): TPromise { - const openDefaultSettings = !!this.configurationService.getConfiguration().workbench.settings.openDefaultSettings; - if (openDefaultSettings) { - const emptySettingsContents = this.getEmptyEditableSettingsContent(configurationTarget); - const settingsResource = this.getEditableSettingsURI(configurationTarget); - return this.openSideBySideEditor(this.getDefaultSettingsEditorInput(configurationTarget), settingsResource, emptySettingsContents); - } - return this.openEditableSettings(configurationTarget).then(() => null); - } - - private getDefaultSettingsEditorInput(configurationTarget: ConfigurationTarget): DefaultPreferencesEditorInput { - switch (configurationTarget) { - case ConfigurationTarget.USER: - if (!this.defaultSettingsEditorInputForUser) { - this.defaultSettingsEditorInputForUser = this._register(this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.defaultSettingsResource)); - } - return this.defaultSettingsEditorInputForUser; - case ConfigurationTarget.WORKSPACE: - if (!this.defaultSettingsEditorInputForWorkspace) { - this.defaultSettingsEditorInputForWorkspace = this._register(this.instantiationService.createInstance(DefaultPreferencesEditorInput, this.defaultSettingsResource)); - } - return this.defaultSettingsEditorInputForWorkspace; - } - } - - private openSideBySideEditor(leftHandDefaultInput: EditorInput, editableResource: URI, defaultEditableContents: string): TPromise { - // Create as needed and open in editor - return this.createIfNotExists(editableResource, defaultEditableContents).then(() => { - return this.editorService.createInput({ resource: editableResource }).then(typedRightHandEditableInput => { - const sideBySideInput = new SideBySideEditorInput(typedRightHandEditableInput.getName(), typedRightHandEditableInput.getDescription(), leftHandDefaultInput, typedRightHandEditableInput); - this.editorService.openEditor(sideBySideInput, { pinned: true }); - return; - }); - }); - } - private openTwoEditors(leftHandDefault: URI, editableResource: URI, defaultEditableContents: string): TPromise { // Create as needed and open in editor return this.createIfNotExists(editableResource, defaultEditableContents).then(() => { @@ -255,6 +246,14 @@ export class PreferencesService extends Disposable implements IPreferencesServic }); } + private getSettingsConfigurationTarget(preferencesEditorInput: PreferencesEditorInput): ConfigurationTarget { + if (preferencesEditorInput) { + const resource = toResource(preferencesEditorInput.master); + return resource.toString() === this.userSettingsResource.toString() ? ConfigurationTarget.USER : ConfigurationTarget.WORKSPACE; + } + return ConfigurationTarget.USER; + } + private fetchMostCommonlyUsedSettings(): TPromise { return TPromise.wrap([ 'editor.fontSize', diff --git a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts index a6260610aec..4c5e4e1b579 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesWidgets.ts @@ -5,7 +5,6 @@ import { localize } from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; -import { Dimension } from 'vs/base/browser/builder'; import { Widget } from 'vs/base/browser/ui/widget'; import Event, { Emitter } from 'vs/base/common/event'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; @@ -17,6 +16,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IContextViewService, IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ISettingsGroup } from 'vs/workbench/parts/preferences/common/preferences'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; export class SettingsGroupTitleWidget extends Widget implements IViewZone { @@ -176,11 +177,51 @@ export class SettingsGroupTitleWidget extends Widget implements IViewZone { } } -export class DefaultSettingsHeaderWidget extends Widget { +export class SettingsTabsWidget extends Widget { + + private userSettingsTab: HTMLElement; + private workspaceSettingsTab: HTMLElement; + + private _onSwitch: Emitter = new Emitter(); + public readonly onSwitch: Event = this._onSwitch.event; + + constructor(parent: HTMLElement, @IWorkspaceContextService private contextService: IWorkspaceContextService, ) { + super(); + this.create(parent); + } + + private create(parent: HTMLElement): void { + const settingsTabsWidget = DOM.append(parent, DOM.$('.settings-tabs-widget')); + this.userSettingsTab = DOM.append(settingsTabsWidget, DOM.$('.settings-tab')); + DOM.append(this.userSettingsTab, DOM.$('')).textContent = localize('userSettings', "User Settings"); + this.onclick(this.userSettingsTab, () => this.onClick(this.userSettingsTab)); + + this.workspaceSettingsTab = DOM.append(settingsTabsWidget, DOM.$('.settings-tab')); + DOM.append(this.workspaceSettingsTab, DOM.$('')).textContent = localize('workspaceSettings', "Workspace Settings"); + if (!this.contextService.hasWorkspace()) { + DOM.addClass(this.workspaceSettingsTab, 'disabled'); + } else { + this.onclick(this.workspaceSettingsTab, () => this.onClick(this.workspaceSettingsTab)); + } + } + + public show(configurationTarget: ConfigurationTarget): void { + DOM.toggleClass(this.userSettingsTab, 'active', ConfigurationTarget.USER === configurationTarget); + DOM.toggleClass(this.workspaceSettingsTab, 'active', ConfigurationTarget.WORKSPACE === configurationTarget); + } + + private onClick(element: HTMLElement): void { + if (!DOM.hasClass(element, 'active')) { + this._onSwitch.fire(); + } + } +} + +export class SearchWidget extends Widget { public domNode: HTMLElement; - private headerContainer: HTMLElement; + private countElement: HTMLElement; private searchContainer: HTMLElement; private inputBox: InputBox; @@ -201,40 +242,38 @@ export class DefaultSettingsHeaderWidget extends Widget { private create(parent: HTMLElement) { this.domNode = DOM.append(parent, DOM.$('div.settings-header-widget')); - this.headerContainer = DOM.append(this.domNode, DOM.$('div.settings-header-container')); - const titleContainer = DOM.append(this.headerContainer, DOM.$('div.settings-title-container')); - this.createInfoContainer(DOM.append(titleContainer, DOM.$('div.settings-info-container'))); this.createSearchContainer(DOM.append(this.domNode, DOM.$('div.settings-search-container'))); - } - - private createInfoContainer(infoContainer: HTMLElement) { - DOM.append(infoContainer, DOM.$('span.title-label')).textContent = localize('defaultSettingsTitle', "Default Settings"); - DOM.append(infoContainer, DOM.$('span')).textContent = localize('defaultSettingsInfo', " - Overwrite these by placing them into your settings file to the right"); + this.countElement = DOM.append(this.domNode, DOM.$('.settings-count-widget')); } private createSearchContainer(searchContainer: HTMLElement) { this.searchContainer = searchContainer; const searchInput = DOM.append(this.searchContainer, DOM.$('div.settings-search-input')); this.inputBox = this._register(new InputBox(searchInput, this.contextViewService, { - ariaLabel: localize('SearchSettingsWidget.AriaLabel', "Search default settings"), - placeholder: localize('SearchSettingsWidget.Placeholder', "Search Default Settings") + ariaLabel: localize('SearchSettingsWidget.AriaLabel', "Search settings"), + placeholder: localize('SearchSettingsWidget.Placeholder', "Search Settings") })); this.inputBox.onDidChange(value => this._onDidChange.fire(value)); this.onkeyup(this.inputBox.inputElement, (e) => this._onKeyUp(e)); } + public showMessage(message: string, count: number): void { + this.countElement.textContent = message; + DOM.toggleClass(this.countElement, 'no-results', count === 0); + } + public focus() { this.inputBox.focus(); } - public layout(dimension: Dimension): void { - this.inputBox.width = dimension.width - 62; - } - public clear() { this.inputBox.value = ''; } + public value(): string { + return this.inputBox.value; + } + private _onKeyUp(keyboardEvent: IKeyboardEvent): void { let handled = false; switch (keyboardEvent.keyCode) { @@ -300,55 +339,6 @@ export class FloatingClickWidget extends Widget implements IOverlayWidget { } } -export class SettingsCountWidget extends Widget implements IOverlayWidget { - - private _domNode: HTMLElement; - - constructor(private editor: ICodeEditor, private total: number - ) { - super(); - } - - public render() { - this._domNode = DOM.$('.settings-count-widget'); - this.editor.addOverlayWidget(this); - } - - public show(count: number) { - if (count === this.total) { - DOM.removeClass(this._domNode, 'show'); - } else { - if (count === 0) { - this._domNode.textContent = localize('noSettings', "No settings found"); - DOM.addClass(this._domNode, 'no-results'); - } else { - this._domNode.textContent = localize('showCount', "Showing {0} of {1} Settings", count, this.total); - DOM.removeClass(this._domNode, 'no-results'); - } - DOM.addClass(this._domNode, 'show'); - } - } - - public dispose(): void { - this.editor.removeOverlayWidget(this); - super.dispose(); - } - - public getId(): string { - return 'editor.overlayWidget.settingsCountWidget'; - } - - public getDomNode(): HTMLElement { - return this._domNode; - } - - public getPosition(): IOverlayWidgetPosition { - return { - preference: null - }; - } -} - export class EditPreferenceWidget extends Widget implements IOverlayWidget { private static counter: number = 1; diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index 83cafc86be6..623dced7315 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -61,11 +61,15 @@ export interface IPreferencesService { _serviceBrand: any; defaultSettingsResource: URI; + userSettingsResource: URI; + workspaceSettingsResource: URI; defaultKeybindingsResource: URI; createDefaultPreferencesEditorModel(uri: URI): TPromise; resolvePreferencesEditorModel(uri: URI): TPromise; + openSettings(): TPromise; + switchSettings(): TPromise; openGlobalSettings(): TPromise; openWorkspaceSettings(): TPromise; openGlobalKeybindingSettings(): TPromise; diff --git a/src/vs/workbench/parts/preferences/common/preferencesModels.ts b/src/vs/workbench/parts/preferences/common/preferencesModels.ts index 57ca716dda3..a75af79fb1a 100644 --- a/src/vs/workbench/parts/preferences/common/preferencesModels.ts +++ b/src/vs/workbench/parts/preferences/common/preferencesModels.ts @@ -459,7 +459,7 @@ export class DefaultSettingsEditorModel extends AbstractSettingsModel implements return { id: 'mostCommonlyUsed', range: null, - title: nls.localize('commonlyUsed', "Most Commonly Used"), + title: nls.localize('commonlyUsed', "Commonly Used"), titleRange: null, sections: [ {