diff --git a/src/sql/parts/editData/common/editDataInput.ts b/src/sql/parts/editData/common/editDataInput.ts index 386e22cf88..ae307c332b 100644 --- a/src/sql/parts/editData/common/editDataInput.ts +++ b/src/sql/parts/editData/common/editDataInput.ts @@ -16,6 +16,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import Severity from 'vs/base/common/severity'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput'; +import { IEditorViewState } from 'vs/editor/common/editorCommon'; /** * Input for the EditDataEditor. @@ -37,6 +38,8 @@ export class EditDataInput extends EditorInput implements IConnectableInput { private _css: HTMLStyleElement; private _useQueryFilter: boolean; + public savedViewState: IEditorViewState; + constructor( private _uri: URI, private _schemaName, diff --git a/src/sql/parts/editData/common/editDataResultsInput.ts b/src/sql/parts/editData/common/editDataResultsInput.ts index 7d2e2597ea..817fb6400d 100644 --- a/src/sql/parts/editData/common/editDataResultsInput.ts +++ b/src/sql/parts/editData/common/editDataResultsInput.ts @@ -8,6 +8,7 @@ import { localize } from 'vs/nls'; import { TPromise } from 'vs/base/common/winjs.base'; import { EditorInput } from 'vs/workbench/common/editor'; +import { Emitter } from 'vs/base/common/event'; /** * Input for the EditDataResultsEditor. This input helps with logic for the viewing and editing of @@ -25,6 +26,9 @@ export class EditDataResultsInput extends EditorInput { private _editorContainer: HTMLElement; public css: HTMLStyleElement; + public readonly onRestoreViewStateEmitter = new Emitter(); + public readonly onSaveViewStateEmitter = new Emitter(); + constructor(private _uri: string) { super(); this._visible = false; diff --git a/src/sql/parts/editData/editor/editDataEditor.ts b/src/sql/parts/editData/editor/editDataEditor.ts index 1fe38fc8d1..48e24f1963 100644 --- a/src/sql/parts/editData/editor/editDataEditor.ts +++ b/src/sql/parts/editData/editor/editDataEditor.ts @@ -13,7 +13,7 @@ import { Builder } from 'vs/base/browser/builder'; import { EditorOptions, EditorInput } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { Position, IEditorControl, IEditor } from 'vs/platform/editor/common/editor'; +import { Position, IEditorControl, IEditor, IEditorInput } from 'vs/platform/editor/common/editor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -43,6 +43,8 @@ import { IFlexibleSash, VerticalFlexibleSash, HorizontalFlexibleSash } from 'sql import { Orientation } from 'vs/base/browser/ui/sash/sash'; import { EditDataResultsEditor } from 'sql/parts/editData/editor/editDataResultsEditor'; import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput'; +import { IEditorViewState } from 'vs/editor/common/editorCommon'; +import { Emitter } from 'vs/base/common/event'; /** * Editor that hosts an action bar and a resultSetInput for an edit data session @@ -96,6 +98,14 @@ export class EditDataEditor extends BaseEditor { if (contextKeyService) { this._queryEditorVisible = queryContext.QueryEditorVisibleContext.bindTo(contextKeyService); } + + if (_editorGroupService) { + _editorGroupService.onEditorOpening(e => { + if (this.isVisible() && (e.input !== this.input || e.position !== this.position)) { + this.saveEditorViewState(); + } + }); + } } // PUBLIC METHODS //////////////////////////////////////////////////////////// @@ -594,7 +604,15 @@ export class EditDataEditor extends BaseEditor { // Run all three steps synchronously return createEditors() .then(onEditorsCreated) - .then(doLayout); + .then(doLayout) + .then(() => { + if (newInput.results) { + newInput.results.onRestoreViewStateEmitter.fire(); + } + if (newInput.savedViewState) { + this._sqlEditor.getControl().restoreViewState(newInput.savedViewState); + } + }); } private _setSashDimension(): void { @@ -631,7 +649,6 @@ export class EditDataEditor extends BaseEditor { * has been opened with the same editor, or we are opening the editor for the first time). */ private _updateInput(oldInput: EditDataInput, newInput: EditDataInput, options?: EditorOptions): TPromise { - if (this._sqlEditor) { this._sqlEditor.clearInput(); } @@ -727,4 +744,16 @@ export class EditDataEditor extends BaseEditor { public queryPaneEnabled(): boolean { return this.editDataInput.queryPaneEnabled; } + + private saveEditorViewState(): void { + let editDataInput = this.input as EditDataInput; + if (editDataInput) { + if (this._sqlEditor) { + editDataInput.savedViewState = this._sqlEditor.getControl().saveViewState(); + } + if (editDataInput.results) { + editDataInput.results.onSaveViewStateEmitter.fire(); + } + } + } } diff --git a/src/sql/parts/editData/editor/editDataResultsEditor.ts b/src/sql/parts/editData/editor/editDataResultsEditor.ts index 571e75fdbd..ebe28e1c52 100644 --- a/src/sql/parts/editData/editor/editDataResultsEditor.ts +++ b/src/sql/parts/editData/editor/editDataResultsEditor.ts @@ -23,6 +23,7 @@ import { IEditDataComponentParams } from 'sql/services/bootstrap/bootstrapParams import { EditDataModule } from 'sql/parts/grid/views/editData/editData.module'; import { EDITDATA_SELECTOR } from 'sql/parts/grid/views/editData/editData.component'; import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput'; +import { Event } from 'vs/base/common/event'; export class EditDataResultsEditor extends BaseEditor { @@ -109,7 +110,11 @@ export class EditDataResultsEditor extends BaseEditor { // Otherwise many components will be left around and be subscribed // to events from the backing data service const parent = input.container; - let params: IEditDataComponentParams = { dataService: dataService }; + let params: IEditDataComponentParams = { + dataService: dataService, + onSaveViewState: input.onSaveViewStateEmitter.event, + onRestoreViewState: input.onRestoreViewStateEmitter.event + }; bootstrapAngular(this._instantiationService, EditDataModule, parent, diff --git a/src/sql/parts/grid/views/editData/editData.component.ts b/src/sql/parts/grid/views/editData/editData.component.ts index 1f93368140..a04b7ab652 100644 --- a/src/sql/parts/grid/views/editData/editData.component.ts +++ b/src/sql/parts/grid/views/editData/editData.component.ts @@ -13,7 +13,7 @@ import 'vs/css!sql/parts/grid/media/slickGrid'; import 'vs/css!./media/editData'; import { ElementRef, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject, forwardRef, EventEmitter } from '@angular/core'; -import { IGridDataRow, VirtualizedCollection } from 'angular2-slickgrid'; +import { IGridDataRow, VirtualizedCollection, ISlickRange } from 'angular2-slickgrid'; import { IGridDataSet } from 'sql/parts/grid/common/interfaces'; import * as Services from 'sql/parts/grid/services/sharedServices'; @@ -81,6 +81,12 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On public overrideCellFn: (rowNumber, columnId, value?, data?) => string; public loadDataFunction: (offset: number, count: number) => Promise; + private savedViewState: { + gridSelections: ISlickRange[]; + scrollTop; + scrollLeft; + }; + constructor( @Inject(forwardRef(() => ElementRef)) el: ElementRef, @Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef, @@ -98,6 +104,8 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On this._el.nativeElement.className = 'slickgridContainer'; this.dataService = params.dataService; this.actionProvider = this.instantiationService.createInstance(EditDataGridActionProvider, this.dataService, this.onGridSelectAll(), this.onDeleteRow(), this.onRevertRow()); + params.onRestoreViewState(() => this.restoreViewState()); + params.onSaveViewState(() => this.saveViewState()); } /** @@ -578,4 +586,25 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On ? (this._defaultNumShowingRows + 1) * this._rowHeight + 10 : this.getMaxHeight(rowCount); } + + private saveViewState(): void { + let gridSelections = this.slickgrids.toArray()[0].getSelectedRanges(); + let viewport = ((this.slickgrids.toArray()[0] as any)._grid.getCanvasNode() as HTMLElement).parentElement; + + this.savedViewState = { + gridSelections, + scrollTop: viewport.scrollTop, + scrollLeft: viewport.scrollLeft + }; + } + + private restoreViewState(): void { + if (this.savedViewState) { + this.slickgrids.toArray()[0].selection = this.savedViewState.gridSelections; + let viewport = ((this.slickgrids.toArray()[0] as any)._grid.getCanvasNode() as HTMLElement).parentElement; + viewport.scrollLeft = this.savedViewState.scrollLeft; + viewport.scrollTop = this.savedViewState.scrollTop; + this.savedViewState = undefined; + } + } } diff --git a/src/sql/parts/profiler/editor/controller/profilerTableEditor.ts b/src/sql/parts/profiler/editor/controller/profilerTableEditor.ts index fafdbd86bc..f4b5dfec38 100644 --- a/src/sql/parts/profiler/editor/controller/profilerTableEditor.ts +++ b/src/sql/parts/profiler/editor/controller/profilerTableEditor.ts @@ -27,6 +27,12 @@ import { Event, Emitter } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Dimension } from 'vs/base/browser/dom'; import { textFormatter } from 'sql/parts/grid/services/sharedServices'; +import { IEditorInput } from 'vs/platform/editor/common/editor'; + +export interface ProfilerTableViewState { + scrollTop: number; + scrollLeft: number; +} export class ProfilerTableEditor extends BaseEditor implements IProfilerController, ITableController { @@ -219,4 +225,18 @@ export class ProfilerTableEditor extends BaseEditor implements IProfilerControll this._findState.changeMatchInfo(0, 0, undefined); } } + + public saveViewState(): ProfilerTableViewState { + let viewElement = this._profilerTable.grid.getCanvasNode().parentElement; + return { + scrollTop: viewElement.scrollTop, + scrollLeft: viewElement.scrollLeft + }; + } + + public restoreViewState(state: ProfilerTableViewState): void { + let viewElement = this._profilerTable.grid.getCanvasNode().parentElement; + viewElement.scrollTop = state.scrollTop; + viewElement.scrollLeft = state.scrollLeft; + } } diff --git a/src/sql/parts/profiler/editor/profilerEditor.ts b/src/sql/parts/profiler/editor/profilerEditor.ts index 8238f2c5eb..216d1cc475 100644 --- a/src/sql/parts/profiler/editor/profilerEditor.ts +++ b/src/sql/parts/profiler/editor/profilerEditor.ts @@ -12,7 +12,7 @@ import { IProfilerService, IProfilerViewTemplate } from 'sql/parts/profiler/serv import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar'; import { attachTableStyler } from 'sql/common/theme/styler'; import { IProfilerStateChangedEvent } from './profilerState'; -import { ProfilerTableEditor } from './controller/profilerTableEditor'; +import { ProfilerTableEditor, ProfilerTableViewState } from './controller/profilerTableEditor'; import * as Actions from 'sql/parts/profiler/contrib/profilerActions'; import { CONTEXT_PROFILER_EDITOR, PROFILER_TABLE_COMMAND_SEARCH } from './interfaces'; import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox'; @@ -45,6 +45,7 @@ import { CommonFindController, FindStartFocusAction } from 'vs/editor/contrib/fi import * as types from 'vs/base/common/types'; import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; import { DARK, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; +import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; class BasicView extends View { private _previousSize: number; @@ -131,6 +132,7 @@ export class ProfilerEditor extends BaseEditor { private _createAction: Actions.ProfilerCreate; private _collapsedPanelAction: Actions.ProfilerCollapsablePanelAction; + private _savedTableViewStates = new Map(); constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -140,10 +142,19 @@ export class ProfilerEditor extends BaseEditor { @IModelService private _modelService: IModelService, @IProfilerService private _profilerService: IProfilerService, @IContextKeyService private _contextKeyService: IContextKeyService, - @IContextViewService private _contextViewService: IContextViewService + @IContextViewService private _contextViewService: IContextViewService, + @IEditorGroupService private _editorGroupService: IEditorGroupService ) { super(ProfilerEditor.ID, telemetryService, themeService); this._profilerEditorContextKey = CONTEXT_PROFILER_EDITOR.bindTo(this._contextKeyService); + + if (_editorGroupService) { + _editorGroupService.onEditorOpening(e => { + if (this.isVisible() && (e.input !== this.input || e.position !== this.position)) { + this.saveEditorViewState(); + } + }); + } } protected createEditor(parent: HTMLElement): void { @@ -366,8 +377,13 @@ export class ProfilerEditor extends BaseEditor { } public setInput(input: ProfilerInput, options?: EditorOptions): TPromise { + let savedViewState = this._savedTableViewStates.get(input); + this._profilerEditorContextKey.set(true); if (input instanceof ProfilerInput && input.matches(this.input)) { + if (savedViewState) { + this._profilerTableEditor.restoreViewState(savedViewState); + } return TPromise.as(null); } @@ -398,6 +414,9 @@ export class ProfilerEditor extends BaseEditor { this._profilerTableEditor.updateState(); this._splitView.layout(); this._profilerTableEditor.focus(); + if (savedViewState) { + this._profilerTableEditor.restoreViewState(savedViewState); + } }); } @@ -498,6 +517,12 @@ export class ProfilerEditor extends BaseEditor { this._body.style.height = (dimension.height - (28 + 4)) + 'px'; this._splitView.layout(dimension.height - (28 + 4)); } + + private saveEditorViewState(): void { + if (this.input && this._profilerTableEditor) { + this._savedTableViewStates.set(this.input, this._profilerTableEditor.saveViewState()); + } + } } abstract class SettingsCommand extends Command { diff --git a/src/sql/parts/query/common/queryInput.ts b/src/sql/parts/query/common/queryInput.ts index 3483e87f36..420f138439 100644 --- a/src/sql/parts/query/common/queryInput.ts +++ b/src/sql/parts/query/common/queryInput.ts @@ -11,6 +11,7 @@ import URI from 'vs/base/common/uri'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { EditorInput, EditorModel, ConfirmResult, EncodingMode, IEncodingSupport } from 'vs/workbench/common/editor'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IEditorViewState } from 'vs/editor/common/editorCommon'; import { IConnectionManagementService, IConnectableInput, INewConnectionParams, RunQueryOnConnectionMode } from 'sql/parts/connection/common/connectionManagement'; import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput'; @@ -56,6 +57,8 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec private _toDispose: IDisposable[]; private _currentEventCallbacks: IDisposable[]; + public savedViewState: IEditorViewState; + constructor( private _description: string, private _sql: UntitledEditorInput, diff --git a/src/sql/parts/query/common/queryResultsInput.ts b/src/sql/parts/query/common/queryResultsInput.ts index 2a0cb23586..4873c63e83 100644 --- a/src/sql/parts/query/common/queryResultsInput.ts +++ b/src/sql/parts/query/common/queryResultsInput.ts @@ -8,6 +8,7 @@ import { localize } from 'vs/nls'; import { TPromise } from 'vs/base/common/winjs.base'; import { EditorInput } from 'vs/workbench/common/editor'; +import { Emitter } from 'vs/base/common/event'; /** * Input for the QueryResultsEditor. This input helps with logic for the viewing and editing of @@ -25,6 +26,9 @@ export class QueryResultsInput extends EditorInput { private _editorContainer: HTMLElement; public css: HTMLStyleElement; + public readonly onRestoreViewStateEmitter = new Emitter(); + public readonly onSaveViewStateEmitter = new Emitter(); + constructor(private _uri: string) { super(); this._visible = false; diff --git a/src/sql/parts/query/editor/queryEditor.ts b/src/sql/parts/query/editor/queryEditor.ts index f7c71b1fa9..d9c6724c24 100644 --- a/src/sql/parts/query/editor/queryEditor.ts +++ b/src/sql/parts/query/editor/queryEditor.ts @@ -89,9 +89,6 @@ export class QueryEditor extends BaseEditor { private _estimatedQueryPlanAction: EstimatedQueryPlanAction; private _actualQueryPlanAction: ActualQueryPlanAction; - private _savedViewStates = new Map(); - private _resultViewStateChangeEmitters = new Map; onRestoreViewState: Emitter }>(); - constructor( @ITelemetryService _telemetryService: ITelemetryService, @IThemeService themeService: IThemeService, @@ -111,6 +108,14 @@ export class QueryEditor extends BaseEditor { if (contextKeyService) { this.queryEditorVisible = queryContext.QueryEditorVisibleContext.bindTo(contextKeyService); } + + if (_editorGroupService) { + _editorGroupService.onEditorOpening(e => { + if (this.isVisible() && (e.input !== this.input || e.position !== this.position)) { + this.saveEditorViewState(); + } + }); + } } // PROPERTIES ////////////////////////////////////////////////////////// @@ -500,19 +505,11 @@ export class QueryEditor extends BaseEditor { * Handles setting input for this editor. */ private _updateInput(oldInput: QueryInput, newInput: QueryInput, options?: EditorOptions): TPromise { - if (this._sqlEditor) { - let sqlEditorViewState = this._sqlEditor.getControl().saveViewState(); - this._savedViewStates.set(this._sqlEditor.input, sqlEditorViewState); this._sqlEditor.clearInput(); } if (oldInput) { - let resultViewStateChangeEmitters = this._resultViewStateChangeEmitters.get(oldInput.results); - if (resultViewStateChangeEmitters) { - resultViewStateChangeEmitters.onSaveViewState.fire(); - } - this._disposeEditors(); } @@ -587,11 +584,11 @@ export class QueryEditor extends BaseEditor { .then(onEditorsCreated) .then(doLayout) .then(() => { - if (this._resultViewStateChangeEmitters.has(newInput.results)) { - this._resultViewStateChangeEmitters.get(newInput.results).onRestoreViewState.fire(); + if (newInput.results) { + newInput.results.onRestoreViewStateEmitter.fire(); } - if (this._savedViewStates.has(newInput.sql)) { - this._sqlEditor.getControl().restoreViewState(this._savedViewStates.get(newInput.sql)); + if (newInput.savedViewState) { + this._sqlEditor.getControl().restoreViewState(newInput.savedViewState); } }); } @@ -624,14 +621,6 @@ export class QueryEditor extends BaseEditor { */ private _onResultsEditorCreated(resultsEditor: QueryResultsEditor, resultsInput: QueryResultsInput, options: EditorOptions): TPromise { this._resultsEditor = resultsEditor; - if (!this._resultViewStateChangeEmitters.has(resultsInput)) { - this._resultViewStateChangeEmitters.set(resultsInput, { - onRestoreViewState: new Emitter(), - onSaveViewState: new Emitter() - }); - } - let emitters = this._resultViewStateChangeEmitters.get(resultsInput); - this._resultsEditor.setViewStateChangeEvents(emitters.onRestoreViewState.event, emitters.onSaveViewState.event); return this._resultsEditor.setInput(resultsInput, options); } @@ -891,6 +880,18 @@ export class QueryEditor extends BaseEditor { editor.focus(); } + private saveEditorViewState(): void { + let queryInput = this.input as QueryInput; + if (queryInput) { + if (this._sqlEditor) { + queryInput.savedViewState = this._sqlEditor.getControl().saveViewState(); + } + if (queryInput.results) { + queryInput.results.onSaveViewStateEmitter.fire(); + } + } + } + // TESTING PROPERTIES //////////////////////////////////////////////////////////// public get resultsEditor(): QueryResultsEditor { diff --git a/src/sql/parts/query/editor/queryResultsEditor.ts b/src/sql/parts/query/editor/queryResultsEditor.ts index 6c9af8ce54..5d4d9d228e 100644 --- a/src/sql/parts/query/editor/queryResultsEditor.ts +++ b/src/sql/parts/query/editor/queryResultsEditor.ts @@ -96,8 +96,6 @@ export class QueryResultsEditor extends BaseEditor { public static AngularSelectorString: string = 'slickgrid-container.slickgridContainer'; protected _rawOptions: BareResultsGridInfo; protected _input: QueryResultsInput; - private _restoreViewStateEvent: Event; - private _saveViewStateEvent: Event; constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -152,11 +150,6 @@ export class QueryResultsEditor extends BaseEditor { return TPromise.wrap(null); } - public setViewStateChangeEvents(onRestoreViewStateEvent: Event, onSaveViewStateEvent: Event) { - this._restoreViewStateEvent = onRestoreViewStateEvent; - this._saveViewStateEvent = onSaveViewStateEvent; - } - /** * Load the angular components and record for this input that we have done so */ @@ -179,8 +172,8 @@ export class QueryResultsEditor extends BaseEditor { // to events from the backing data service let params: IQueryComponentParams = { dataService: dataService, - onSaveViewState: this._saveViewStateEvent, - onRestoreViewState: this._restoreViewStateEvent + onSaveViewState: this.input.onSaveViewStateEmitter.event, + onRestoreViewState: this.input.onRestoreViewStateEmitter.event }; bootstrapAngular(this._instantiationService, QueryOutputModule, diff --git a/src/sql/services/bootstrap/bootstrapParams.ts b/src/sql/services/bootstrap/bootstrapParams.ts index 4c4f25b6fa..4a208931b7 100644 --- a/src/sql/services/bootstrap/bootstrapParams.ts +++ b/src/sql/services/bootstrap/bootstrapParams.ts @@ -18,6 +18,8 @@ export interface IQueryComponentParams extends IBootstrapParams { export interface IEditDataComponentParams extends IBootstrapParams { dataService: DataService; + onSaveViewState: Event; + onRestoreViewState: Event; } export interface IDefaultComponentParams extends IBootstrapParams {