Query Editor Memory improvements (#3041)

* working through memory issues

* work in progress

* add missing value

* work in progress

* more work in progress

* various more memory fixes

* additional improvements

* fix imports

* change test that we dispose queries on close not dispose

* update tests
This commit is contained in:
Anthony Dresser
2018-11-05 16:08:41 -08:00
committed by GitHub
parent 399d6d0045
commit 5f2e17a738
22 changed files with 280 additions and 129 deletions

View File

@@ -36,7 +36,6 @@ export interface IPanelTab {
interface IInternalPanelTab extends IPanelTab { interface IInternalPanelTab extends IPanelTab {
header: HTMLElement; header: HTMLElement;
label: HTMLElement; label: HTMLElement;
dispose(): void;
} }
const defaultOptions: IPanelOptions = { const defaultOptions: IPanelOptions = {
@@ -143,8 +142,6 @@ export class TabbedPanel extends Disposable implements IThemable {
this.tabList.appendChild(tabHeaderElement); this.tabList.appendChild(tabHeaderElement);
tab.header = tabHeaderElement; tab.header = tabHeaderElement;
tab.label = tabLabel; tab.label = tabLabel;
tab.dispose = () => { };
this._register(tab);
} }
public showTab(id: PanelTabIdentifier): void { public showTab(id: PanelTabIdentifier): void {

View File

@@ -3,11 +3,14 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { IDisposableDataProvider } from 'sql/base/browser/ui/table/interfaces';
export interface IObservableCollection<T> { export interface IObservableCollection<T> {
getLength(): number; getLength(): number;
at(index: number): T; at(index: number): T;
getRange(start: number, end: number): T[]; getRange(start: number, end: number): T[];
setCollectionChangedCallback(callback: (change: CollectionChange, startIndex: number, count: number) => void): void; setCollectionChangedCallback(callback: (change: CollectionChange, startIndex: number, count: number) => void): void;
dispose(): void;
} }
export interface IGridDataRow { export interface IGridDataRow {
@@ -24,24 +27,27 @@ class LoadCancellationToken {
} }
class DataWindow<TData> { class DataWindow<TData> {
private _dataSourceLength: number;
private _data: TData[]; private _data: TData[];
private _length: number = 0; private _length: number = 0;
private _offsetFromDataSource: number = -1; private _offsetFromDataSource: number = -1;
private loadFunction: (offset: number, count: number) => Thenable<TData[]>;
private lastLoadCancellationToken: LoadCancellationToken; private lastLoadCancellationToken: LoadCancellationToken;
private loadCompleteCallback: (start: number, end: number) => void;
private placeholderItemGenerator: (index: number) => TData;
constructor(dataSourceLength: number, constructor(
loadFunction: (offset: number, count: number) => Thenable<TData[]>, private loadFunction: (offset: number, count: number) => Thenable<TData[]>,
placeholderItemGenerator: (index: number) => TData, private placeholderItemGenerator: (index: number) => TData,
loadCompleteCallback: (start: number, end: number) => void) { private loadCompleteCallback: (start: number, end: number) => void
this._dataSourceLength = dataSourceLength; ) {
this.loadFunction = loadFunction; }
this.placeholderItemGenerator = placeholderItemGenerator;
this.loadCompleteCallback = loadCompleteCallback; dispose() {
this._data = undefined;
this.loadFunction = undefined;
this.placeholderItemGenerator = undefined;
this.loadCompleteCallback = undefined;
if (this.lastLoadCancellationToken) {
this.lastLoadCancellationToken.isCancelled = true;
}
} }
getStartIndex(): number { getStartIndex(): number {
@@ -76,10 +82,9 @@ class DataWindow<TData> {
return; return;
} }
let cancellationToken = new LoadCancellationToken(); this.lastLoadCancellationToken = new LoadCancellationToken();
this.lastLoadCancellationToken = cancellationToken;
this.loadFunction(offset, length).then(data => { this.loadFunction(offset, length).then(data => {
if (!cancellationToken.isCancelled) { if (!this.lastLoadCancellationToken.isCancelled) {
this._data = data; this._data = data;
this.loadCompleteCallback(this._offsetFromDataSource, this._offsetFromDataSource + this._length); this.loadCompleteCallback(this._offsetFromDataSource, this._offsetFromDataSource + this._length);
} }
@@ -97,10 +102,12 @@ export class VirtualizedCollection<TData> implements IObservableCollection<TData
private collectionChangedCallback: (change: CollectionChange, startIndex: number, count: number) => void; private collectionChangedCallback: (change: CollectionChange, startIndex: number, count: number) => void;
constructor(windowSize: number, constructor(
windowSize: number,
length: number, length: number,
loadFn: (offset: number, count: number) => Thenable<TData[]>, loadFn: (offset: number, count: number) => Thenable<TData[]>,
private _placeHolderGenerator: (index: number) => TData) { private _placeHolderGenerator: (index: number) => TData
) {
this._windowSize = windowSize; this._windowSize = windowSize;
this._length = length; this._length = length;
@@ -110,9 +117,15 @@ export class VirtualizedCollection<TData> implements IObservableCollection<TData
} }
}; };
this._bufferWindowBefore = new DataWindow(length, loadFn, _placeHolderGenerator, loadCompleteCallback); this._bufferWindowBefore = new DataWindow(loadFn, _placeHolderGenerator, loadCompleteCallback);
this._window = new DataWindow(length, loadFn, _placeHolderGenerator, loadCompleteCallback); this._window = new DataWindow(loadFn, _placeHolderGenerator, loadCompleteCallback);
this._bufferWindowAfter = new DataWindow(length, loadFn, _placeHolderGenerator, loadCompleteCallback); this._bufferWindowAfter = new DataWindow(loadFn, _placeHolderGenerator, loadCompleteCallback);
}
dispose() {
this._bufferWindowAfter.dispose();
this._bufferWindowBefore.dispose();
this._window.dispose();
} }
setCollectionChangedCallback(callback: (change: CollectionChange, startIndex: number, count: number) => void): void { setCollectionChangedCallback(callback: (change: CollectionChange, startIndex: number, count: number) => void): void {
@@ -197,7 +210,7 @@ export class VirtualizedCollection<TData> implements IObservableCollection<TData
} }
} }
export class AsyncDataProvider<TData extends IGridDataRow> implements Slick.DataProvider<TData> { export class AsyncDataProvider<TData extends IGridDataRow> implements IDisposableDataProvider<TData> {
constructor(private dataRows: IObservableCollection<TData>) { } constructor(private dataRows: IObservableCollection<TData>) { }
@@ -212,4 +225,8 @@ export class AsyncDataProvider<TData extends IGridDataRow> implements Slick.Data
public getRange(start: number, end: number): TData[] { public getRange(start: number, end: number): TData[] {
return !this.dataRows ? undefined : this.dataRows.getRange(start, end); return !this.dataRows ? undefined : this.dataRows.getRange(start, end);
} }
dispose() {
this.dataRows.dispose();
}
} }

View File

@@ -0,0 +1,31 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IListStyles } from 'vs/base/browser/ui/list/listWidget';
import { Color } from 'vs/base/common/color';
export interface IDisposableDataProvider<T> extends Slick.DataProvider<T> {
dispose(): void;
}
export interface ITableMouseEvent {
anchor: HTMLElement | { x: number, y: number };
cell?: { row: number, cell: number };
}
export interface ITableStyles extends IListStyles {
tableHeaderBackground?: Color;
tableHeaderForeground?: Color;
}
export interface ITableSorter<T> {
sort(args: Slick.OnSortEventArgs<T>);
}
export interface ITableConfiguration<T> {
dataProvider?: IDisposableDataProvider<T> | Array<T>;
columns?: Slick.Column<T>[];
sorter?: ITableSorter<T>;
}

View File

@@ -5,28 +5,18 @@
import 'vs/css!./media/table'; import 'vs/css!./media/table';
import { TableDataView } from './tableDataView'; import { TableDataView } from './tableDataView';
import { IDisposableDataProvider, ITableSorter, ITableMouseEvent, ITableConfiguration, ITableStyles } from 'sql/base/browser/ui/table/interfaces';
import { IThemable } from 'vs/platform/theme/common/styler'; import { IThemable } from 'vs/platform/theme/common/styler';
import { IListStyles } from 'vs/base/browser/ui/list/listWidget';
import * as DOM from 'vs/base/browser/dom'; import * as DOM from 'vs/base/browser/dom';
import { Color } from 'vs/base/common/color';
import { mixin } from 'vs/base/common/objects'; import { mixin } from 'vs/base/common/objects';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';
import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
import { Widget } from 'vs/base/browser/ui/widget'; import { Widget } from 'vs/base/browser/ui/widget';
import { isArray, isBoolean } from 'vs/base/common/types'; import { isArray, isBoolean } from 'vs/base/common/types';
import { Event, Emitter } from 'vs/base/common/event'; import { Event, Emitter } from 'vs/base/common/event';
import { range } from 'vs/base/common/arrays'; import { range } from 'vs/base/common/arrays';
import { $ } from 'vs/base/browser/builder';
export interface ITableMouseEvent {
anchor: HTMLElement | { x: number, y: number };
cell?: { row: number, cell: number };
}
export interface ITableStyles extends IListStyles {
tableHeaderBackground?: Color;
tableHeaderForeground?: Color;
}
function getDefaultOptions<T>(): Slick.GridOptions<T> { function getDefaultOptions<T>(): Slick.GridOptions<T> {
return <Slick.GridOptions<T>>{ return <Slick.GridOptions<T>>{
@@ -35,23 +25,13 @@ function getDefaultOptions<T>(): Slick.GridOptions<T> {
}; };
} }
export interface ITableSorter<T> {
sort(args: Slick.OnSortEventArgs<T>);
}
export interface ITableConfiguration<T> {
dataProvider?: Slick.DataProvider<T> | Array<T>;
columns?: Slick.Column<T>[];
sorter?: ITableSorter<T>;
}
export class Table<T extends Slick.SlickData> extends Widget implements IThemable, IDisposable { export class Table<T extends Slick.SlickData> extends Widget implements IThemable, IDisposable {
private styleElement: HTMLStyleElement; private styleElement: HTMLStyleElement;
private idPrefix: string; private idPrefix: string;
private _grid: Slick.Grid<T>; private _grid: Slick.Grid<T>;
private _columns: Slick.Column<T>[]; private _columns: Slick.Column<T>[];
private _data: Slick.DataProvider<T>; private _data: IDisposableDataProvider<T>;
private _sorter: ITableSorter<T>; private _sorter: ITableSorter<T>;
private _autoscroll: boolean; private _autoscroll: boolean;
@@ -60,8 +40,6 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
private _classChangeTimeout: number; private _classChangeTimeout: number;
private _disposables: IDisposable[] = [];
private _onContextMenu = new Emitter<ITableMouseEvent>(); private _onContextMenu = new Emitter<ITableMouseEvent>();
public readonly onContextMenu: Event<ITableMouseEvent> = this._onContextMenu.event; public readonly onContextMenu: Event<ITableMouseEvent> = this._onContextMenu.event;
@@ -76,6 +54,8 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
this._data = configuration.dataProvider; this._data = configuration.dataProvider;
} }
this._register(this._data);
if (configuration && configuration.columns) { if (configuration && configuration.columns) {
this._columns = configuration.columns; this._columns = configuration.columns;
} else { } else {
@@ -117,6 +97,12 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
}); });
} }
this._register({
dispose: () => {
this._grid.destroy();
}
});
this.mapMouseEvent(this._grid.onContextMenu, this._onContextMenu); this.mapMouseEvent(this._grid.onContextMenu, this._onContextMenu);
this.mapMouseEvent(this._grid.onClick, this._onClick); this.mapMouseEvent(this._grid.onClick, this._onClick);
} }
@@ -131,7 +117,8 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
} }
public dispose() { public dispose() {
dispose(this._disposables); $(this._container).dispose();
super.dispose();
} }
public invalidateRows(rows: number[], keepEditor: boolean) { public invalidateRows(rows: number[], keepEditor: boolean) {
@@ -166,7 +153,7 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
this._grid.setData(this._data, true); this._grid.setData(this._data, true);
} }
getData(): Slick.DataProvider<T> { getData(): IDisposableDataProvider<T> {
return this._data; return this._data;
} }

View File

@@ -9,6 +9,8 @@ import { Event, Emitter } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
import * as types from 'vs/base/common/types'; import * as types from 'vs/base/common/types';
import { IDisposableDataProvider } from 'sql/base/browser/ui/table/interfaces';
export interface IFindPosition { export interface IFindPosition {
col: number; col: number;
row: number; row: number;
@@ -20,7 +22,7 @@ function defaultSort<T>(args: Slick.OnSortEventArgs<T>, data: Array<T>): Array<T
return data.sort((a, b) => (a[field] === b[field] ? 0 : (a[field] > b[field] ? 1 : -1)) * sign); return data.sort((a, b) => (a[field] === b[field] ? 0 : (a[field] > b[field] ? 1 : -1)) * sign);
} }
export class TableDataView<T extends Slick.SlickData> implements Slick.DataProvider<T> { export class TableDataView<T extends Slick.SlickData> implements IDisposableDataProvider<T> {
private _data: Array<T>; private _data: Array<T>;
private _findArray: Array<IFindPosition>; private _findArray: Array<IFindPosition>;
private _findObs: Observable<IFindPosition>; private _findObs: Observable<IFindPosition>;
@@ -154,4 +156,10 @@ export class TableDataView<T extends Slick.SlickData> implements Slick.DataProvi
get findCount(): number { get findCount(): number {
return types.isUndefinedOrNull(this._findArray) ? 0 : this._findArray.length; return types.isUndefinedOrNull(this._findArray) ? 0 : this._findArray.length;
} }
dispose() {
this._data = undefined;
this._findArray = undefined;
this._findObs = undefined;
}
} }

View File

@@ -32,9 +32,6 @@ export interface IQueryEditorService {
// Creates new edit data session // Creates new edit data session
newEditDataEditor(schemaName: string, tableName: string, queryString: string): Promise<IConnectableInput>; newEditDataEditor(schemaName: string, tableName: string, queryString: string): Promise<IConnectableInput>;
// Clears any QueryEditor data for the given URI held by this service
onQueryInputClosed(uri: string): void;
/** /**
* Handles updating of SQL files on a save as event. These need special consideration * Handles updating of SQL files on a save as event. These need special consideration
* due to query results and other information being tied to the URI of the file * due to query results and other information being tied to the URI of the file

View File

@@ -116,11 +116,11 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec
} }
if (this._configurationService) { if (this._configurationService) {
this._configurationService.onDidChangeConfiguration(e => { this._toDispose.push(this._configurationService.onDidChangeConfiguration(e => {
if (e.affectedKeys.includes('sql.showConnectionInfoInTitle')) { if (e.affectedKeys.includes('sql.showConnectionInfoInTitle')) {
this._onDidChangeLabel.fire(); this._onDidChangeLabel.fire();
} }
}); }));
} }
this.onDisconnect(); this.onDisconnect();
@@ -196,17 +196,17 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec
// State update funtions // State update funtions
public runQuery(selection: ISelectionData, executePlanOptions?: ExecutionPlanOptions): void { public runQuery(selection: ISelectionData, executePlanOptions?: ExecutionPlanOptions): void {
this._queryModelService.runQuery(this.uri, selection, this.uri, this, executePlanOptions); this._queryModelService.runQuery(this.uri, selection, this, executePlanOptions);
this.showQueryResultsEditor(); this.showQueryResultsEditor();
} }
public runQueryStatement(selection: ISelectionData): void { public runQueryStatement(selection: ISelectionData): void {
this._queryModelService.runQueryStatement(this.uri, selection, this.uri, this); this._queryModelService.runQueryStatement(this.uri, selection, this);
this.showQueryResultsEditor(); this.showQueryResultsEditor();
} }
public runQueryString(text: string): void { public runQueryString(text: string): void {
this._queryModelService.runQueryString(this.uri, text, this.uri, this); this._queryModelService.runQueryString(this.uri, text, this);
this.showQueryResultsEditor(); this.showQueryResultsEditor();
} }
@@ -276,7 +276,6 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec
// Clean up functions // Clean up functions
public dispose(): void { public dispose(): void {
this._queryModelService.disposeQuery(this.uri);
this._sql.dispose(); this._sql.dispose();
this._results.dispose(); this._results.dispose();
this._toDispose = dispose(this._toDispose); this._toDispose = dispose(this._toDispose);
@@ -285,7 +284,7 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec
} }
public close(): void { public close(): void {
this._queryEditorService.onQueryInputClosed(this.uri); this._queryModelService.disposeQuery(this.uri);
this._connectionManagementService.disconnectEditor(this, true); this._connectionManagementService.disconnectEditor(this, true);
this._sql.close(); this._sql.close();

View File

@@ -215,6 +215,7 @@ export class QueryManagementService implements IQueryManagementService {
}); });
} }
public disposeQuery(ownerUri: string): Thenable<void> { public disposeQuery(ownerUri: string): Thenable<void> {
this._queryRunners.delete(ownerUri);
return this._runAction(ownerUri, (runner) => { return this._runAction(ownerUri, (runner) => {
return runner.disposeQuery(ownerUri); return runner.disposeQuery(ownerUri);
}); });

View File

@@ -29,6 +29,13 @@ export class ResultsViewState {
constructor(@IConfigurationService private configurationService: IConfigurationService) { constructor(@IConfigurationService private configurationService: IConfigurationService) {
} }
dispose() {
this.gridPanelState.dispose();
this.messagePanelState.dispose();
this.chartState.dispose();
this.queryPlanState.dispose();
}
} }
/** /**
@@ -50,7 +57,11 @@ export class QueryResultsInput extends EditorInput {
public readonly onRestoreViewStateEmitter = new Emitter<void>(); public readonly onRestoreViewStateEmitter = new Emitter<void>();
public readonly onSaveViewStateEmitter = new Emitter<void>(); public readonly onSaveViewStateEmitter = new Emitter<void>();
public readonly state = new ResultsViewState(this.configurationService); private _state = new ResultsViewState(this.configurationService);
public get state(): ResultsViewState {
return this._state;
}
constructor(private _uri: string, constructor(private _uri: string,
@IConfigurationService private configurationService: IConfigurationService @IConfigurationService private configurationService: IConfigurationService
@@ -60,6 +71,12 @@ export class QueryResultsInput extends EditorInput {
this._hasBootstrapped = false; this._hasBootstrapped = false;
} }
close() {
this.state.dispose();
this._state = undefined;
super.close();
}
getTypeId(): string { getTypeId(): string {
return QueryResultsInput.ID; return QueryResultsInput.ID;
} }

View File

@@ -33,4 +33,8 @@ export class ChartTab implements IPanelTab {
public dispose() { public dispose() {
this.view.dispose(); this.view.dispose();
} }
public clear() {
this.view.clear();
}
} }

View File

@@ -35,6 +35,10 @@ export class ChartState {
options: IInsightOptions = { options: IInsightOptions = {
type: ChartType.Bar type: ChartType.Bar
}; };
dispose() {
}
} }
declare class Proxy { declare class Proxy {
@@ -134,6 +138,15 @@ export class ChartView extends Disposable implements IPanelView {
this.buildOptions(); this.buildOptions();
} }
public clear() {
}
public dispose() {
dispose(this.optionDisposables);
super.dispose();
}
render(container: HTMLElement): void { render(container: HTMLElement): void {
if (!this.container) { if (!this.container) {
this.container = $('div.chart-parent-container'); this.container = $('div.chart-parent-container');

View File

@@ -7,7 +7,7 @@
import { attachTableStyler } from 'sql/common/theme/styler'; import { attachTableStyler } from 'sql/common/theme/styler';
import QueryRunner from 'sql/parts/query/execution/queryRunner'; import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { VirtualizedCollection, AsyncDataProvider } from 'sql/base/browser/ui/table/asyncDataView'; import { VirtualizedCollection, AsyncDataProvider } from 'sql/base/browser/ui/table/asyncDataView';
import { Table, ITableStyles, ITableMouseEvent } from 'sql/base/browser/ui/table/table'; import { Table } from 'sql/base/browser/ui/table/table';
import { ScrollableSplitView } from 'sql/base/browser/ui/scrollableSplitview/scrollableSplitview'; import { ScrollableSplitView } from 'sql/base/browser/ui/scrollableSplitview/scrollableSplitview';
import { MouseWheelSupport } from 'sql/base/browser/ui/table/plugins/mousewheelTableScroll.plugin'; import { MouseWheelSupport } from 'sql/base/browser/ui/table/plugins/mousewheelTableScroll.plugin';
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin'; import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
@@ -19,6 +19,7 @@ import { escape } from 'sql/base/common/strings';
import { hyperLinkFormatter, textFormatter } from 'sql/parts/grid/services/sharedServices'; import { hyperLinkFormatter, textFormatter } from 'sql/parts/grid/services/sharedServices';
import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin'; import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin';
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin'; import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
import { ITableStyles, ITableMouseEvent } from 'sql/base/browser/ui/table/interfaces';
import * as sqlops from 'sqlops'; import * as sqlops from 'sqlops';
import * as pretty from 'pretty-data'; import * as pretty from 'pretty-data';
@@ -61,6 +62,10 @@ export class GridPanelState {
public tableStates: GridTableState[] = []; public tableStates: GridTableState[] = [];
public scrollPosition: number; public scrollPosition: number;
public collapsed = false; public collapsed = false;
dispose() {
dispose(this.tableStates);
}
} }
export interface IGridTableState { export interface IGridTableState {
@@ -68,14 +73,14 @@ export interface IGridTableState {
maximized: boolean; maximized: boolean;
} }
export class GridTableState { export class GridTableState extends Disposable {
private _maximized: boolean; private _maximized: boolean;
private _onMaximizedChange = new Emitter<boolean>(); private _onMaximizedChange = this._register(new Emitter<boolean>());
public onMaximizedChange: Event<boolean> = this._onMaximizedChange.event; public onMaximizedChange: Event<boolean> = this._onMaximizedChange.event;
private _onCanBeMaximizedChange = new Emitter<boolean>(); private _onCanBeMaximizedChange = this._register(new Emitter<boolean>());
public onCanBeMaximizedChange: Event<boolean> = this._onCanBeMaximizedChange.event; public onCanBeMaximizedChange: Event<boolean> = this._onCanBeMaximizedChange.event;
private _canBeMaximized: boolean; private _canBeMaximized: boolean;
@@ -86,6 +91,7 @@ export class GridTableState {
public activeCell: Slick.Cell; public activeCell: Slick.Cell;
constructor(public readonly resultId: number, public readonly batchId: number) { constructor(public readonly resultId: number, public readonly batchId: number) {
super();
} }
public get canBeMaximized(): boolean { public get canBeMaximized(): boolean {
@@ -217,13 +223,13 @@ export class GridPanel extends ViewletPanel {
} }
let table = this.instantiationService.createInstance(GridTable, this.runner, set); let table = this.instantiationService.createInstance(GridTable, this.runner, set);
table.state = tableState; table.state = tableState;
tableState.onMaximizedChange(e => { this.tableDisposable.push(tableState.onMaximizedChange(e => {
if (e) { if (e) {
this.maximizeTable(table.id); this.maximizeTable(table.id);
} else { } else {
this.minimizeTables(); this.minimizeTables();
} }
}); }));
this.tableDisposable.push(attachTableStyler(table, this.themeService)); this.tableDisposable.push(attachTableStyler(table, this.themeService));
tables.push(table); tables.push(table);
@@ -238,11 +244,17 @@ export class GridPanel extends ViewletPanel {
this.tables = this.tables.concat(tables); this.tables = this.tables.concat(tables);
} }
public clear() {
this.reset();
}
private reset() { private reset() {
for (let i = this.splitView.length - 1; i >= 0; i--) { for (let i = this.splitView.length - 1; i >= 0; i--) {
this.splitView.removeView(i); this.splitView.removeView(i);
} }
dispose(this.tables); dispose(this.tables);
dispose(this.tableDisposable);
this.tableDisposable = [];
this.tables = []; this.tables = [];
this.maximizedGrid = undefined; this.maximizedGrid = undefined;
@@ -293,6 +305,15 @@ export class GridPanel extends ViewletPanel {
public get state(): GridPanelState { public get state(): GridPanelState {
return this._state; return this._state;
} }
public dispose() {
dispose(this.queryRunnerDisposables);
dispose(this.tableDisposable);
dispose(this.tables);
this.tableDisposable = undefined;
this.tables = undefined;
super.dispose();
}
} }
class GridTable<T> extends Disposable implements IView { class GridTable<T> extends Disposable implements IView {
@@ -444,9 +465,9 @@ class GridTable<T> extends Disposable implements IView {
private setupState() { private setupState() {
// change actionbar on maximize change // change actionbar on maximize change
this.state.onMaximizedChange(this.rebuildActionBar, this); this._register(this.state.onMaximizedChange(this.rebuildActionBar, this));
this.state.onCanBeMaximizedChange(this.rebuildActionBar, this); this._register(this.state.onCanBeMaximizedChange(this.rebuildActionBar, this));
if (this.state.scrollPosition) { if (this.state.scrollPosition) {
// most of the time this won't do anything // most of the time this won't do anything
@@ -656,6 +677,8 @@ class GridTable<T> extends Disposable implements IView {
public dispose() { public dispose() {
$(this.container).destroy(); $(this.container).destroy();
this.table.dispose();
this.actionBar.dispose();
super.dispose(); super.dispose();
} }
} }

View File

@@ -7,6 +7,7 @@
import 'vs/css!./media/messagePanel'; import 'vs/css!./media/messagePanel';
import { IMessagesActionContext, SelectAllMessagesAction, CopyMessagesAction } from './actions'; import { IMessagesActionContext, SelectAllMessagesAction, CopyMessagesAction } from './actions';
import QueryRunner from 'sql/parts/query/execution/queryRunner'; import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { QueryInput } from 'sql/parts/query/common/queryInput';
import { IResultMessage, ISelectionData } from 'sqlops'; import { IResultMessage, ISelectionData } from 'sqlops';
@@ -28,8 +29,6 @@ import { $ } from 'vs/base/browser/builder';
import { isArray, isUndefinedOrNull } from 'vs/base/common/types'; import { isArray, isUndefinedOrNull } from 'vs/base/common/types';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditor } from 'vs/editor/common/editorCommon';
import { QueryInput } from 'sql/parts/query/common/queryInput';
import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ScrollbarVisibility } from 'vs/base/common/scrollable';
export interface IResultMessageIntern extends IResultMessage { export interface IResultMessageIntern extends IResultMessage {
@@ -71,6 +70,10 @@ export class MessagePanelState {
this.collapsed = !messagesOpenedSettings; this.collapsed = !messagesOpenedSettings;
} }
} }
dispose() {
}
} }
export class MessagePanel extends ViewletPanel { export class MessagePanel extends ViewletPanel {
@@ -102,6 +105,7 @@ export class MessagePanel extends ViewletPanel {
renderer: this.renderer, renderer: this.renderer,
controller: this.controller controller: this.controller
}, { keyboardSupport: false, horizontalScrollMode: ScrollbarVisibility.Auto }); }, { keyboardSupport: false, horizontalScrollMode: ScrollbarVisibility.Auto });
this.disposables.push(this.tree);
this.tree.onDidScroll(e => { this.tree.onDidScroll(e => {
if (this.state) { if (this.state) {
this.state.scrollPosition = this.tree.getScrollPosition(); this.state.scrollPosition = this.tree.getScrollPosition();
@@ -117,7 +121,7 @@ export class MessagePanel extends ViewletPanel {
protected renderBody(container: HTMLElement): void { protected renderBody(container: HTMLElement): void {
this.container.style.width = '100%'; this.container.style.width = '100%';
this.container.style.height = '100%'; this.container.style.height = '100%';
attachListStyler(this.tree, this.themeService); this.disposables.push(attachListStyler(this.tree, this.themeService));
container.appendChild(this.container); container.appendChild(this.container);
this.tree.setInput(this.model); this.tree.setInput(this.model);
} }
@@ -193,9 +197,19 @@ export class MessagePanel extends ViewletPanel {
} }
this.setExpanded(!this.state.collapsed); this.setExpanded(!this.state.collapsed);
} }
public get state(): MessagePanelState { public get state(): MessagePanelState {
return this._state; return this._state;
} }
public clear() {
this.reset();
}
public dispose() {
dispose(this.queryRunnerDisposables);
super.dispose();
}
} }
class MessageDataSource implements IDataSource { class MessageDataSource implements IDataSource {

View File

@@ -478,11 +478,11 @@ export class QueryEditor extends BaseEditor {
this.setTaskbarContent(); this.setTaskbarContent();
this._configurationService.onDidChangeConfiguration(e => { this._toDispose.push(this._configurationService.onDidChangeConfiguration(e => {
if (e.affectedKeys.includes('workbench.enablePreviewFeatures')) { if (e.affectedKeys.includes('workbench.enablePreviewFeatures')) {
this.setTaskbarContent(); this.setTaskbarContent();
} }
}); }));
} }
private setTaskbarContent(): void { private setTaskbarContent(): void {

View File

@@ -90,7 +90,6 @@ export class QueryResultsEditor extends BaseEditor {
public static ID: string = 'workbench.editor.queryResultsEditor'; public static ID: string = 'workbench.editor.queryResultsEditor';
public static AngularSelectorString: string = 'slickgrid-container.slickgridContainer'; public static AngularSelectorString: string = 'slickgrid-container.slickgridContainer';
protected _rawOptions: BareResultsGridInfo; protected _rawOptions: BareResultsGridInfo;
protected _input: QueryResultsInput;
private resultsView: QueryResultsView; private resultsView: QueryResultsView;
private styleSheet = DOM.createStyleSheet(); private styleSheet = DOM.createStyleSheet();
@@ -104,17 +103,17 @@ export class QueryResultsEditor extends BaseEditor {
) { ) {
super(QueryResultsEditor.ID, telemetryService, themeService); super(QueryResultsEditor.ID, telemetryService, themeService);
this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel()); this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel());
this._configurationService.onDidChangeConfiguration(e => { this._register(this._configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('resultsGrid')) { if (e.affectsConfiguration('resultsGrid')) {
this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel()); this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel());
this.applySettings(); this.applySettings();
} }
}); }));
this.applySettings(); this.applySettings();
} }
public get input(): QueryResultsInput { public get input(): QueryResultsInput {
return this._input; return this._input as QueryResultsInput;
} }
private applySettings() { private applySettings() {
@@ -133,10 +132,16 @@ export class QueryResultsEditor extends BaseEditor {
this.styleSheet.remove(); this.styleSheet.remove();
parent.appendChild(this.styleSheet); parent.appendChild(this.styleSheet);
if (!this.resultsView) { if (!this.resultsView) {
this.resultsView = new QueryResultsView(parent, this._instantiationService, this._queryModelService); this.resultsView = this._register(new QueryResultsView(parent, this._instantiationService, this._queryModelService));
} }
} }
dispose() {
this.styleSheet.remove();
this.styleSheet = undefined;
super.dispose();
}
layout(dimension: DOM.Dimension): void { layout(dimension: DOM.Dimension): void {
this.resultsView.layout(dimension); this.resultsView.layout(dimension);
} }
@@ -147,6 +152,11 @@ export class QueryResultsEditor extends BaseEditor {
return TPromise.wrap<void>(null); return TPromise.wrap<void>(null);
} }
clearInput() {
this.resultsView.clearInput();
super.clearInput();
}
public chart(dataId: { batchId: number, resultId: number }) { public chart(dataId: { batchId: number, resultId: number }) {
this.resultsView.chartData(dataId); this.resultsView.chartData(dataId);
} }
@@ -154,11 +164,4 @@ export class QueryResultsEditor extends BaseEditor {
public showQueryPlan(xml: string) { public showQueryPlan(xml: string) {
this.resultsView.showPlan(xml); this.resultsView.showPlan(xml);
} }
public dispose(): void {
super.dispose();
if (this.resultsView) {
this.resultsView.dispose();
}
}
} }

View File

@@ -111,6 +111,15 @@ class ResultsView extends Disposable implements IPanelView {
} }
} }
dispose() {
super.dispose();
}
public clear() {
this.gridPanel.clear();
this.messagePanel.clear();
}
remove(): void { remove(): void {
this.container.remove(); this.container.remove();
} }
@@ -151,6 +160,10 @@ class ResultsTab implements IPanelTab {
public dispose() { public dispose() {
dispose(this.view); dispose(this.view);
} }
public clear() {
this.view.clear();
}
} }
export class QueryResultsView extends Disposable { export class QueryResultsView extends Disposable {
@@ -221,8 +234,11 @@ export class QueryResultsView extends Disposable {
} }
} }
public dispose() { clearInput() {
this._panelView.dispose(); this._input = undefined;
this.resultsTab.clear();
this.qpTab.clear();
this.chartTab.clear();
} }
public get input(): QueryResultsInput { public get input(): QueryResultsInput {
@@ -264,4 +280,8 @@ export class QueryResultsView extends Disposable {
this._panelView.removeTab(this.qpTab.identifier); this._panelView.removeTab(this.qpTab.identifier);
} }
} }
public dispose() {
super.dispose();
}
} }

View File

@@ -35,9 +35,9 @@ export interface IQueryModelService {
getConfig(): Promise<{ [key: string]: any }>; getConfig(): Promise<{ [key: string]: any }>;
getShortcuts(): Promise<any>; getShortcuts(): Promise<any>;
getQueryRows(uri: string, rowStart: number, numberOfRows: number, batchId: number, resultId: number): Thenable<ResultSetSubset>; getQueryRows(uri: string, rowStart: number, numberOfRows: number, batchId: number, resultId: number): Thenable<ResultSetSubset>;
runQuery(uri: string, selection: ISelectionData, title: string, queryInput: QueryInput, runOptions?: ExecutionPlanOptions): void; runQuery(uri: string, selection: ISelectionData, queryInput: QueryInput, runOptions?: ExecutionPlanOptions): void;
runQueryStatement(uri: string, selection: ISelectionData, title: string, queryInput: QueryInput): void; runQueryStatement(uri: string, selection: ISelectionData, queryInput: QueryInput): void;
runQueryString(uri: string, selection: string, title: string, queryInput: QueryInput); runQueryString(uri: string, selection: string, queryInput: QueryInput);
cancelQuery(input: QueryRunner | string): void; cancelQuery(input: QueryRunner | string): void;
disposeQuery(uri: string): void; disposeQuery(uri: string): void;
isRunningQuery(uri: string): boolean; isRunningQuery(uri: string): boolean;

View File

@@ -209,32 +209,28 @@ export class QueryModelService implements IQueryModelService {
/** /**
* Run a query for the given URI with the given text selection * Run a query for the given URI with the given text selection
*/ */
public runQuery(uri: string, selection: sqlops.ISelectionData, public runQuery(uri: string, selection: sqlops.ISelectionData, queryInput: QueryInput, runOptions?: sqlops.ExecutionPlanOptions): void {
title: string, queryInput: QueryInput, runOptions?: sqlops.ExecutionPlanOptions): void { this.doRunQuery(uri, selection, queryInput, false, runOptions);
this.doRunQuery(uri, selection, title, queryInput, false, runOptions);
} }
/** /**
* Run the current SQL statement for the given URI * Run the current SQL statement for the given URI
*/ */
public runQueryStatement(uri: string, selection: sqlops.ISelectionData, public runQueryStatement(uri: string, selection: sqlops.ISelectionData, queryInput: QueryInput): void {
title: string, queryInput: QueryInput): void { this.doRunQuery(uri, selection, queryInput, true);
this.doRunQuery(uri, selection, title, queryInput, true);
} }
/** /**
* Run the current SQL statement for the given URI * Run the current SQL statement for the given URI
*/ */
public runQueryString(uri: string, selection: string, public runQueryString(uri: string, selection: string, queryInput: QueryInput): void {
title: string, queryInput: QueryInput): void { this.doRunQuery(uri, selection, queryInput, true);
this.doRunQuery(uri, selection, title, queryInput, true);
} }
/** /**
* Run Query implementation * Run Query implementation
*/ */
private doRunQuery(uri: string, selection: sqlops.ISelectionData | string, private doRunQuery(uri: string, selection: sqlops.ISelectionData | string, queryInput: QueryInput,
title: string, queryInput: QueryInput,
runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): void { runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): void {
// Reuse existing query runner if it exists // Reuse existing query runner if it exists
let queryRunner: QueryRunner; let queryRunner: QueryRunner;
@@ -256,7 +252,7 @@ export class QueryModelService implements IQueryModelService {
} else { } else {
// We do not have a query runner for this editor, so create a new one // We do not have a query runner for this editor, so create a new one
// and map it to the results uri // and map it to the results uri
info = this.initQueryRunner(uri, title); info = this.initQueryRunner(uri);
queryRunner = info.queryRunner; queryRunner = info.queryRunner;
} }
@@ -277,8 +273,8 @@ export class QueryModelService implements IQueryModelService {
} }
} }
private initQueryRunner(uri: string, title: string): QueryInfo { private initQueryRunner(uri: string): QueryInfo {
let queryRunner = this._instantiationService.createInstance(QueryRunner, uri, title); let queryRunner = this._instantiationService.createInstance(QueryRunner, uri);
let info = new QueryInfo(); let info = new QueryInfo();
queryRunner.addListener(QREvents.RESULT_SET, e => { queryRunner.addListener(QREvents.RESULT_SET, e => {
this._fireQueryEvent(uri, 'resultSet', e); this._fireQueryEvent(uri, 'resultSet', e);
@@ -363,6 +359,10 @@ export class QueryModelService implements IQueryModelService {
if (queryRunner) { if (queryRunner) {
queryRunner.disposeQuery(); queryRunner.disposeQuery();
} }
// remove our info map
if (this._queryInfoMap.has(ownerUri)) {
this._queryInfoMap.delete(ownerUri);
}
} }
// EDIT DATA METHODS ///////////////////////////////////////////////////// // EDIT DATA METHODS /////////////////////////////////////////////////////
@@ -386,7 +386,7 @@ export class QueryModelService implements IQueryModelService {
// We do not have a query runner for this editor, so create a new one // We do not have a query runner for this editor, so create a new one
// and map it to the results uri // and map it to the results uri
queryRunner = this._instantiationService.createInstance(QueryRunner, ownerUri, ownerUri); queryRunner = this._instantiationService.createInstance(QueryRunner, ownerUri);
queryRunner.addListener(QREvents.RESULT_SET, resultSet => { queryRunner.addListener(QREvents.RESULT_SET, resultSet => {
this._fireQueryEvent(ownerUri, 'resultSet', resultSet); this._fireQueryEvent(ownerUri, 'resultSet', resultSet);
}); });

View File

@@ -20,7 +20,7 @@ import * as nls from 'vs/nls';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import * as types from 'vs/base/common/types'; import * as types from 'vs/base/common/types';
import { EventEmitter } from 'sql/base/common/eventEmitter'; import { EventEmitter } from 'sql/base/common/eventEmitter';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotificationService } from 'vs/platform/notification/common/notification';
import { Emitter, Event } from 'vs/base/common/event'; import { Emitter, Event } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -62,7 +62,7 @@ export interface IGridMessage extends sqlops.IResultMessage {
* Query Runner class which handles running a query, reports the results to the content manager, * Query Runner class which handles running a query, reports the results to the content manager,
* and handles getting more rows from the service layer and disposing when the content is closed. * and handles getting more rows from the service layer and disposing when the content is closed.
*/ */
export default class QueryRunner { export default class QueryRunner extends Disposable {
// MEMBER VARIABLES //////////////////////////////////////////////////// // MEMBER VARIABLES ////////////////////////////////////////////////////
private _resultLineOffset: number; private _resultLineOffset: number;
private _totalElapsedMilliseconds: number = 0; private _totalElapsedMilliseconds: number = 0;
@@ -76,7 +76,7 @@ export default class QueryRunner {
private _planXml = new Deferred<string>(); private _planXml = new Deferred<string>();
public get planXml(): Thenable<string> { return this._planXml.promise; } public get planXml(): Thenable<string> { return this._planXml.promise; }
private _onMessage = new Emitter<sqlops.IResultMessage>(); private _onMessage = this._register(new Emitter<sqlops.IResultMessage>());
private _debouncedMessage = debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(this._onMessage.event, (l, e) => { private _debouncedMessage = debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(this._onMessage.event, (l, e) => {
// on first run // on first run
if (types.isUndefinedOrNull(l)) { if (types.isUndefinedOrNull(l)) {
@@ -88,7 +88,7 @@ export default class QueryRunner {
private _echoedMessages = echo(this._debouncedMessage.event); private _echoedMessages = echo(this._debouncedMessage.event);
public readonly onMessage = this._echoedMessages.event; public readonly onMessage = this._echoedMessages.event;
private _onResultSet = new Emitter<sqlops.ResultSetSummary>(); private _onResultSet = this._register(new Emitter<sqlops.ResultSetSummary>());
private _debouncedResultSet = debounceEvent<sqlops.ResultSetSummary, sqlops.ResultSetSummary[]>(this._onResultSet.event, (l, e) => { private _debouncedResultSet = debounceEvent<sqlops.ResultSetSummary, sqlops.ResultSetSummary[]>(this._onResultSet.event, (l, e) => {
// on first run // on first run
if (types.isUndefinedOrNull(l)) { if (types.isUndefinedOrNull(l)) {
@@ -100,16 +100,16 @@ export default class QueryRunner {
private _echoedResultSet = echo(this._debouncedResultSet.event); private _echoedResultSet = echo(this._debouncedResultSet.event);
public readonly onResultSet = this._echoedResultSet.event; public readonly onResultSet = this._echoedResultSet.event;
private _onQueryStart = new Emitter<void>(); private _onQueryStart = this._register(new Emitter<void>());
public readonly onQueryStart: Event<void> = this._onQueryStart.event; public readonly onQueryStart: Event<void> = this._onQueryStart.event;
private _onQueryEnd = new Emitter<string>(); private _onQueryEnd = this._register(new Emitter<string>());
public readonly onQueryEnd: Event<string> = this._onQueryEnd.event; public readonly onQueryEnd: Event<string> = this._onQueryEnd.event;
private _onBatchStart = new Emitter<sqlops.BatchSummary>(); private _onBatchStart = this._register(new Emitter<sqlops.BatchSummary>());
public readonly onBatchStart: Event<sqlops.BatchSummary> = this._onBatchStart.event; public readonly onBatchStart: Event<sqlops.BatchSummary> = this._onBatchStart.event;
private _onBatchEnd = new Emitter<sqlops.BatchSummary>(); private _onBatchEnd = this._register(new Emitter<sqlops.BatchSummary>());
public readonly onBatchEnd: Event<sqlops.BatchSummary> = this._onBatchEnd.event; public readonly onBatchEnd: Event<sqlops.BatchSummary> = this._onBatchEnd.event;
private _queryStartTime: Date; private _queryStartTime: Date;
@@ -124,13 +124,14 @@ export default class QueryRunner {
// CONSTRUCTOR ///////////////////////////////////////////////////////// // CONSTRUCTOR /////////////////////////////////////////////////////////
constructor( constructor(
public uri: string, public uri: string,
public title: string,
@IQueryManagementService private _queryManagementService: IQueryManagementService, @IQueryManagementService private _queryManagementService: IQueryManagementService,
@INotificationService private _notificationService: INotificationService, @INotificationService private _notificationService: INotificationService,
@IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService, @IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService,
@IClipboardService private _clipboardService: IClipboardService, @IClipboardService private _clipboardService: IClipboardService,
@IInstantiationService private instantiationService: IInstantiationService @IInstantiationService private instantiationService: IInstantiationService
) { } ) {
super();
}
get isExecuting(): boolean { get isExecuting(): boolean {
return this._isExecuting; return this._isExecuting;
@@ -504,10 +505,16 @@ export default class QueryRunner {
/** /**
* Disposes the Query from the service client * Disposes the Query from the service client
* @returns A promise that will be rejected if a problem occured
*/ */
public disposeQuery(): void { public disposeQuery(): void {
this._queryManagementService.disposeQuery(this.uri); this._queryManagementService.disposeQuery(this.uri).then(() => {
this.dispose();
});
}
public dispose() {
this._batchSets = undefined;
super.dispose();
} }
get totalElapsedMilliseconds(): number { get totalElapsedMilliseconds(): number {

View File

@@ -152,12 +152,6 @@ export class QueryEditorService implements IQueryEditorService {
}); });
} }
/**
* Clears any QueryEditor data for the given URI held by this service
*/
public onQueryInputClosed(uri: string): void {
}
onSaveAsCompleted(oldResource: URI, newResource: URI): void { onSaveAsCompleted(oldResource: URI, newResource: URI): void {
let oldResourceString: string = oldResource.toString(); let oldResourceString: string = oldResource.toString();

View File

@@ -15,6 +15,9 @@ import { dispose, Disposable } from 'vs/base/common/lifecycle';
export class QueryPlanState { export class QueryPlanState {
xml: string; xml: string;
dispose() {
}
} }
export class QueryPlanTab implements IPanelTab { export class QueryPlanTab implements IPanelTab {
@@ -29,6 +32,10 @@ export class QueryPlanTab implements IPanelTab {
public dispose() { public dispose() {
dispose(this.view); dispose(this.view);
} }
public clear() {
this.view.clear();
}
} }
export class QueryPlanView implements IPanelView { export class QueryPlanView implements IPanelView {
@@ -59,6 +66,12 @@ export class QueryPlanView implements IPanelView {
this.container.style.height = dimension.height + 'px'; this.container.style.height = dimension.height + 'px';
} }
public clear() {
if (this.qp) {
this.qp.xml = undefined;
}
}
public showPlan(xml: string) { public showPlan(xml: string) {
if (this.qp) { if (this.qp) {
this.qp.xml = xml; this.qp.xml = xml;

View File

@@ -141,6 +141,8 @@ suite('SQL QueryEditor Tests', () => {
connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, memento.object, undefined); connectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, memento.object, undefined);
connectionManagementService.callBase = true; connectionManagementService.callBase = true;
connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAny())).returns(() => false); connectionManagementService.setup(x => x.isConnected(TypeMoq.It.isAny())).returns(() => false);
connectionManagementService.setup(x => x.disconnectEditor(TypeMoq.It.isAny())).returns(() => void 0);
connectionManagementService.setup(x => x.ensureDefaultLanguageFlavor(TypeMoq.It.isAnyString())).returns(() => void 0);
// Create a QueryModelService // Create a QueryModelService
queryModelService = new QueryModelService(instantiationService.object, notificationService.object); queryModelService = new QueryModelService(instantiationService.object, notificationService.object);
@@ -328,6 +330,9 @@ suite('SQL QueryEditor Tests', () => {
queryConnectionService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, memento.object, undefined); queryConnectionService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Loose, memento.object, undefined);
queryConnectionService.callBase = true; queryConnectionService.callBase = true;
queryConnectionService.setup(x => x.disconnectEditor(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => void 0);
queryConnectionService.setup(x => x.ensureDefaultLanguageFlavor(TypeMoq.It.isAnyString())).returns(() => void 0);
// Mock InstantiationService to give us the actions // Mock InstantiationService to give us the actions
queryActionInstantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); queryActionInstantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose);
@@ -354,12 +359,13 @@ suite('SQL QueryEditor Tests', () => {
let fileInput = new UntitledEditorInput(URI.parse('testUri'), false, '', '', '', instantiationService.object, undefined, undefined, undefined); let fileInput = new UntitledEditorInput(URI.parse('testUri'), false, '', '', '', instantiationService.object, undefined, undefined, undefined);
queryModelService = TypeMoq.Mock.ofType(QueryModelService, TypeMoq.MockBehavior.Loose, undefined, undefined); queryModelService = TypeMoq.Mock.ofType(QueryModelService, TypeMoq.MockBehavior.Loose, undefined, undefined);
queryModelService.callBase = true; queryModelService.callBase = true;
queryModelService.setup(x => x.disposeQuery(TypeMoq.It.isAny())).returns(() => void 0);
queryInput = new QueryInput( queryInput = new QueryInput(
'', '',
fileInput, fileInput,
undefined, undefined,
undefined, undefined,
undefined, connectionManagementService.object,
queryModelService.object, queryModelService.object,
undefined, undefined,
undefined undefined
@@ -395,7 +401,7 @@ suite('SQL QueryEditor Tests', () => {
test('Test that we attempt to dispose query when the queryInput is disposed', (done) => { test('Test that we attempt to dispose query when the queryInput is disposed', (done) => {
let queryResultsInput = new QueryResultsInput('testUri', configurationService.object); let queryResultsInput = new QueryResultsInput('testUri', configurationService.object);
queryInput['_results'] = queryResultsInput; queryInput['_results'] = queryResultsInput;
queryInput.dispose(); queryInput.close();
queryModelService.verify(x => x.disposeQuery(TypeMoq.It.isAnyString()), TypeMoq.Times.once()); queryModelService.verify(x => x.disposeQuery(TypeMoq.It.isAnyString()), TypeMoq.Times.once());
done(); done();
}); });