fix grid resizing interation with editor (#2427)

This commit is contained in:
Anthony Dresser
2018-09-05 17:32:58 -07:00
committed by GitHub
parent 534bbe9b9a
commit f147d799e0
7 changed files with 109 additions and 51 deletions

View File

@@ -36,6 +36,7 @@ export interface IPanelTab {
interface IInternalPanelTab extends IPanelTab {
header: Builder;
label: Builder;
dispose(): void;
}
const defaultOptions: IPanelOptions = {
@@ -133,6 +134,11 @@ export class TabbedPanel extends Disposable implements IThemable {
this.$tabList.append(tabHeaderElement);
tab.header = tabHeaderElement;
tab.label = tabLabel;
tab.dispose = () => {
tab.header.dispose();
tab.label.dispose();
};
this._register(tab);
}
public showTab(id: PanelTabIdentifier): void {

View File

@@ -0,0 +1,39 @@
/*---------------------------------------------------------------------------------------------
* 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 { Emitter, Event } from 'vs/base/common/event';
/**
* Implementation of vs/base/common/event/echo that is clearable
* Similar to `buffer` but it buffers indefinitely and repeats
* the buffered events to every new listener.
*/
export function echo<T>(event: Event<T>, nextTick = false, buffer: T[] = []): { clear: () => void; event: Event<T> } {
buffer = buffer.slice();
event(e => {
buffer.push(e);
emitter.fire(e);
});
const flush = (listener: (e: T) => any, thisArgs?: any) => buffer.forEach(e => listener.call(thisArgs, e));
const clear = () => buffer = [];
const emitter = new Emitter<T>({
onListenerDidAdd(emitter, listener: (e: T) => any, thisArgs?: any) {
if (nextTick) {
setTimeout(() => flush(listener, thisArgs));
} else {
flush(listener, thisArgs);
}
}
});
return {
event: emitter.event,
clear
};
}

View File

@@ -97,6 +97,7 @@ export class GridPanel extends ViewletPanel {
private tables: GridTable<any>[] = [];
private tableDisposable: IDisposable[] = [];
private queryRunnerDisposables: IDisposable[] = [];
private currentHeight: number;
private runner: QueryRunner;
@@ -123,6 +124,11 @@ export class GridPanel extends ViewletPanel {
protected layoutBody(size: number): void {
this.splitView.layout(size);
// if the size hasn't change it won't layout our table so we have to do it manually
if (size === this.currentHeight) {
this.tables.map(e => e.layout());
}
this.currentHeight = size;
}
public set queryRunner(runner: QueryRunner) {
@@ -229,6 +235,7 @@ class GridTable<T> extends Disposable implements IView {
private container = document.createElement('div');
private selectionModel = new CellSelectionModel();
private styles: ITableStyles;
private currentHeight: number;
private columns: Slick.Column<T>[];
@@ -349,10 +356,15 @@ class GridTable<T> extends Disposable implements IView {
}
}
public layout(size: number): void {
public layout(size?: number): void {
if (!this.table) {
this.build();
}
if (!size) {
size = this.currentHeight;
} else {
this.currentHeight = size;
}
this.table.layout(
new Dimension(
getContentWidth(this.container) - ACTIONBAR_WIDTH,

View File

@@ -109,24 +109,14 @@ export class MessagePanel extends ViewletPanel {
this.queryRunnerDisposables = [];
this.reset();
this.queryRunnerDisposables.push(runner.onQueryStart(() => this.reset()));
this.queryRunnerDisposables.push(runner.onBatchStart(e => this.onBatchStart(e)));
this.queryRunnerDisposables.push(runner.onMessage(e => this.onMessage(e)));
this.queryRunnerDisposables.push(runner.onQueryEnd(e => this.onQueryEnd(e)));
}
private onMessage(message: IResultMessage | IResultMessage[]) {
if (isArray(message)) {
this.model.messages.push(...message.map(c => {
return <IMessagePanelMessage>{
isError: c.isError,
message: c.message
};
}));
this.model.messages.push(...message);
} else {
this.model.messages.push({
message: message.message,
isError: message.isError
});
this.model.messages.push(message);
}
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.refresh(this.model).then(() => {
@@ -136,34 +126,6 @@ export class MessagePanel extends ViewletPanel {
});
}
private onBatchStart(batch: BatchSummary) {
this.model.messages.push({
message: localize('query.message.startQuery', 'Started executing query at Line {0}', batch.selection.startLine),
time: new Date(batch.executionStart).toLocaleTimeString(),
selection: batch.selection,
isError: false
});
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.refresh(this.model).then(() => {
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
}
});
}
private onQueryEnd(elapsedTime: string) {
this.model.totalExecuteMessage = {
message: localize('query.message.executionTime', 'Total execution time: {0}', elapsedTime),
isError: false
};
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.refresh(this.model).then(() => {
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
}
});
}
private reset() {
this.model.messages = [];
this.model.totalExecuteMessage = undefined;

View File

@@ -158,5 +158,8 @@ export class QueryResultsEditor extends BaseEditor {
public dispose(): void {
super.dispose();
if (this.resultsView) {
this.resultsView.dispose();
}
}
}

View File

@@ -24,6 +24,7 @@ class ResultsView implements IPanelView {
private gridPanel: GridPanel;
private messagePanel: MessagePanel;
private container = document.createElement('div');
private currentDimension: DOM.Dimension;
constructor(instantiationService: IInstantiationService) {
this.panelViewlet = instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
@@ -45,6 +46,11 @@ class ResultsView implements IPanelView {
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;
}
remove(): void {
@@ -101,9 +107,14 @@ export class QueryResultsView {
let queryRunner = this.queryModelService._getQueryInfo(input.uri).queryRunner;
this.resultsTab.queryRunner = queryRunner;
this.chartTab.queryRunner = queryRunner;
if (!this._panelView.contains(this.resultsTab)) {
this._panelView.pushTab(this.resultsTab);
}
}
public dispose() {
this._panelView.dispose();
}
public get input(): QueryResultsInput {
return this._input;

View File

@@ -12,6 +12,7 @@ import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
import * as Utils from 'sql/parts/connection/common/utils';
import { SaveFormat } from 'sql/parts/grid/common/interfaces';
import { echo } from 'sql/base/common/event';
import Severity from 'vs/base/common/severity';
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
@@ -21,7 +22,7 @@ import * as types from 'vs/base/common/types';
import { EventEmitter } from 'sql/base/common/eventEmitter';
import { IDisposable } from 'vs/base/common/lifecycle';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { Emitter, echo, debounceEvent, Event } from 'vs/base/common/event';
import { Emitter, debounceEvent, Event } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ResultSerializer } from 'sql/parts/query/common/resultSerializer';
@@ -51,6 +52,10 @@ export interface IEventType {
editSessionReady: IEditSessionReadyEvent;
}
export interface IGridMessage extends sqlops.IResultMessage {
selection: sqlops.ISelectionData;
}
/*
* 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.
@@ -68,36 +73,38 @@ export default class QueryRunner {
public get isQueryPlan(): boolean { return this._isQueryPlan; }
private _onMessage = new Emitter<sqlops.IResultMessage>();
public readonly onMessage = debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(echo(this._onMessage.event), (l, e) => {
private _echoedMessages = echo(debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(this._onMessage.event, (l, e) => {
// on first run
if (types.isUndefinedOrNull(l)) {
return [e];
} else {
return l.concat(e);
}
});
}));
public readonly onMessage = this._echoedMessages.event;
private _onResultSet = new Emitter<sqlops.ResultSetSummary>();
public readonly onResultSet = debounceEvent<sqlops.ResultSetSummary, sqlops.ResultSetSummary[]>(echo(this._onResultSet.event), (l, e) => {
private _echoedResultSet = echo(debounceEvent<sqlops.ResultSetSummary, sqlops.ResultSetSummary[]>(this._onResultSet.event, (l, e) => {
// on first run
if (types.isUndefinedOrNull(l)) {
return [e];
} else {
return l.concat(e);
}
});
}));
public readonly onResultSet = this._echoedResultSet.event;
private _onQueryStart = new Emitter<void>();
public readonly onQueryStart: Event<void> = echo(this._onQueryStart.event);
public readonly onQueryStart: Event<void> = this._onQueryStart.event;
private _onQueryEnd = new Emitter<string>();
public readonly onQueryEnd: Event<string> = echo(this._onQueryEnd.event);
public readonly onQueryEnd: Event<string> = this._onQueryEnd.event;
private _onBatchStart = new Emitter<sqlops.BatchSummary>();
public readonly onBatchStart: Event<sqlops.BatchSummary> = echo(this._onBatchStart.event);
public readonly onBatchStart: Event<sqlops.BatchSummary> = this._onBatchStart.event;
private _onBatchEnd = new Emitter<sqlops.BatchSummary>();
public readonly onBatchEnd: Event<sqlops.BatchSummary> = echo(this._onBatchEnd.event);
public readonly onBatchEnd: Event<sqlops.BatchSummary> = this._onBatchEnd.event;
// CONSTRUCTOR /////////////////////////////////////////////////////////
constructor(
@@ -164,6 +171,8 @@ export default class QueryRunner {
private doRunQuery(input: string, runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): Thenable<void>;
private doRunQuery(input: sqlops.ISelectionData, runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): Thenable<void>;
private doRunQuery(input, runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): Thenable<void> {
this._echoedMessages.clear();
this._echoedResultSet.clear();
let ownerUri = this.uri;
this._batchSets = [];
this._hasCompleted = false;
@@ -235,7 +244,15 @@ export default class QueryRunner {
this._eventEmitter.emit(EventType.COMPLETE, Utils.parseNumAsTimeString(this._totalElapsedMilliseconds));
// We're done with this query so shut down any waiting mechanisms
let message = {
message: nls.localize('query.message.executionTime', 'Total execution time: {0}', this._totalElapsedMilliseconds),
isError: false,
time: undefined
};
this._onQueryEnd.fire(Utils.parseNumAsTimeString(this._totalElapsedMilliseconds));
this._onMessage.fire(message);
}
/**
@@ -255,7 +272,15 @@ export default class QueryRunner {
// Store the batch
this.batchSets[batch.id] = batch;
let message = {
message: nls.localize('query.message.startQuery', 'Started executing query at Line {0}', batch.selection.startLine),
time: new Date(batch.executionStart).toLocaleTimeString(),
selection: batch.selection,
isError: false
};
this._eventEmitter.emit(EventType.BATCH_START, batch);
this._onMessage.fire(message);
this._onBatchStart.fire(batch);
}