mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
fix grid resizing interation with editor (#2427)
This commit is contained in:
@@ -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 {
|
||||
|
||||
39
src/sql/base/common/event.ts
Normal file
39
src/sql/base/common/event.ts
Normal 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
|
||||
};
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -158,5 +158,8 @@ export class QueryResultsEditor extends BaseEditor {
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
if (this.resultsView) {
|
||||
this.resultsView.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,8 +107,13 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
this._panelView.pushTab(this.resultsTab);
|
||||
public dispose() {
|
||||
this._panelView.dispose();
|
||||
}
|
||||
|
||||
public get input(): QueryResultsInput {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user