Files
azuredatastudio/src/sql/parts/query/editor/queryResultsView.ts
2018-10-04 11:48:41 -07:00

261 lines
8.6 KiB
TypeScript

/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import { QueryResultsInput, ResultsViewState } from 'sql/parts/query/common/queryResultsInput';
import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/panel';
import { IQueryModelService } from '../execution/queryModel';
import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { MessagePanel } from './messagePanel';
import { GridPanel } from './gridPanel';
import { ChartTab } from './charting/chartTab';
import { QueryPlanTab } from 'sql/parts/queryPlan/queryPlan';
import * as nls from 'vs/nls';
import { PanelViewlet } from 'vs/workbench/browser/parts/views/panelViewlet';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import * as DOM from 'vs/base/browser/dom';
import { once, anyEvent } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
class ResultsView implements IPanelView {
private panelViewlet: PanelViewlet;
private gridPanel: GridPanel;
private messagePanel: MessagePanel;
private container = document.createElement('div');
private currentDimension: DOM.Dimension;
private needsGridResize = false;
private _state: ResultsViewState;
constructor(private instantiationService: IInstantiationService) {
this.panelViewlet = this.instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
this.gridPanel = this.instantiationService.createInstance(GridPanel, { title: nls.localize('gridPanel', 'Results'), id: 'gridPanel' });
this.messagePanel = this.instantiationService.createInstance(MessagePanel, { title: nls.localize('messagePanel', 'Messages'), minimumBodySize: 0, id: 'messagePanel' });
this.gridPanel.render();
this.messagePanel.render();
this.panelViewlet.create(this.container).then(() => {
this.gridPanel.setVisible(false);
this.panelViewlet.addPanels([
{ panel: this.messagePanel, size: this.messagePanel.minimumSize, index: 1 }
]);
});
anyEvent(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(e => {
let size = this.gridPanel.maximumBodySize;
if (size < 1 && this.gridPanel.isVisible()) {
this.gridPanel.setVisible(false);
this.panelViewlet.removePanels([this.gridPanel]);
this.gridPanel.layout(0);
} else if (size > 0 && !this.gridPanel.isVisible()) {
this.gridPanel.setVisible(true);
let panelSize: number;
if (this.state && this.state.gridPanelSize) {
panelSize = this.state.gridPanelSize;
} else if (this.currentDimension) {
panelSize = Math.round(this.currentDimension.height * .7);
} else {
panelSize = 200;
this.needsGridResize = true;
}
this.panelViewlet.addPanels([{ panel: this.gridPanel, index: 0, size: panelSize }]);
}
});
let resizeList = anyEvent(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(() => {
let panelSize: number;
if (this.state && this.state.gridPanelSize) {
panelSize = this.state.gridPanelSize;
} else if (this.currentDimension) {
panelSize = Math.round(this.currentDimension.height * .7);
} else {
panelSize = 200;
this.needsGridResize = true;
}
if (this.state.messagePanelSize) {
this.panelViewlet.resizePanel(this.gridPanel, this.state.messagePanelSize);
}
this.panelViewlet.resizePanel(this.gridPanel, panelSize);
})
// once the user changes the sash we should stop trying to resize the grid
once(this.panelViewlet.onDidSashChange)(e => {
this.needsGridResize = false;
resizeList.dispose();
});
this.panelViewlet.onDidSashChange(e => {
if (this.state) {
if (this.gridPanel.isExpanded()) {
this.state.gridPanelSize = this.panelViewlet.getPanelSize(this.gridPanel);
}
if (this.messagePanel.isExpanded()) {
this.state.messagePanelSize = this.panelViewlet.getPanelSize(this.messagePanel);
}
}
});
}
render(container: HTMLElement): void {
container.appendChild(this.container);
}
layout(dimension: DOM.Dimension): void {
this.panelViewlet.layout(dimension);
// the grid won't be resize if the height has not changed so we need to do it manually
if (this.currentDimension && dimension.height === this.currentDimension.height) {
this.gridPanel.layout(dimension.height);
}
this.currentDimension = dimension;
if (this.needsGridResize) {
this.panelViewlet.resizePanel(this.gridPanel, this.state.gridPanelSize || Math.round(this.currentDimension.height * .7));
}
}
remove(): void {
this.container.remove();
}
public set queryRunner(runner: QueryRunner) {
this.gridPanel.queryRunner = runner;
this.messagePanel.queryRunner = runner;
}
public hideResultHeader() {
this.gridPanel.headerVisible = false;
}
public set state(val: ResultsViewState) {
this._state = val;
this.gridPanel.state = val.gridPanelState;
this.messagePanel.state = val.messagePanelState;
}
public get state(): ResultsViewState {
return this._state;
}
}
class ResultsTab implements IPanelTab {
public readonly title = nls.localize('resultsTabTitle', 'Results');
public readonly identifier = 'resultsTab';
public readonly view: ResultsView;
constructor(instantiationService: IInstantiationService) {
this.view = new ResultsView(instantiationService);
}
public set queryRunner(runner: QueryRunner) {
this.view.queryRunner = runner;
}
}
export class QueryResultsView {
private _panelView: TabbedPanel;
private _input: QueryResultsInput;
private resultsTab: ResultsTab;
private chartTab: ChartTab;
private qpTab: QueryPlanTab;
private runnerDisposables: IDisposable[];
constructor(
container: HTMLElement,
@IInstantiationService instantiationService: IInstantiationService,
@IQueryModelService private queryModelService: IQueryModelService
) {
this.resultsTab = new ResultsTab(instantiationService);
this.chartTab = new ChartTab(instantiationService);
this._panelView = new TabbedPanel(container, { showHeaderWhenSingleView: false });
this.qpTab = new QueryPlanTab();
this._panelView.pushTab(this.resultsTab);
this._panelView.onTabChange(e => {
if (this.input) {
this.input.state.activeTab = e;
}
});
}
public style() {
}
public set input(input: QueryResultsInput) {
this._input = input;
dispose(this.runnerDisposables);
this.runnerDisposables = [];
this.resultsTab.view.state = this.input.state;
this.qpTab.view.state = this.input.state.queryPlanState;
this.chartTab.view.state = this.input.state.chartState;
let queryRunner = this.queryModelService._getQueryInfo(input.uri).queryRunner;
this.resultsTab.queryRunner = queryRunner;
this.chartTab.queryRunner = queryRunner;
this.runnerDisposables.push(queryRunner.onQueryStart(e => {
this.hideChart();
this.hidePlan();
this.input.state.visibleTabs = new Set();
this.input.state.activeTab = this.resultsTab.identifier;
}));
if (this.input.state.visibleTabs.has(this.chartTab.identifier)) {
if (!this._panelView.contains(this.chartTab)) {
this._panelView.pushTab(this.chartTab);
}
}
if (this.input.state.visibleTabs.has(this.qpTab.identifier)) {
if (!this._panelView.contains(this.qpTab)) {
this._panelView.pushTab(this.qpTab);
}
} else if (queryRunner.isQueryPlan) {
let disp = queryRunner.onResultSet(() => {
this.showPlan(queryRunner.planXml);
disp.dispose();
});
}
if (this.input.state.activeTab) {
this._panelView.showTab(this.input.state.activeTab);
}
}
public dispose() {
this._panelView.dispose();
}
public get input(): QueryResultsInput {
return this._input;
}
public layout(dimension: DOM.Dimension) {
this._panelView.layout(dimension);
}
public chartData(dataId: { resultId: number, batchId: number }): void {
this.input.state.visibleTabs.add(this.chartTab.identifier);
if (!this._panelView.contains(this.chartTab)) {
this._panelView.pushTab(this.chartTab);
}
this._panelView.showTab(this.chartTab.identifier);
this.chartTab.chart(dataId);
}
public hideChart() {
if (this._panelView.contains(this.chartTab)) {
this._panelView.removeTab(this.chartTab.identifier);
}
}
public showPlan(xml: string) {
this.input.state.visibleTabs.add(this.qpTab.identifier);
if (!this._panelView.contains(this.qpTab)) {
this._panelView.pushTab(this.qpTab);
}
this._panelView.showTab(this.qpTab.identifier);
this.qpTab.view.showPlan(xml);
}
public hidePlan() {
if (this._panelView.contains(this.qpTab)) {
this._panelView.removeTab(this.qpTab.identifier);
}
}
}