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 {
|
interface IInternalPanelTab extends IPanelTab {
|
||||||
header: Builder;
|
header: Builder;
|
||||||
label: Builder;
|
label: Builder;
|
||||||
|
dispose(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultOptions: IPanelOptions = {
|
const defaultOptions: IPanelOptions = {
|
||||||
@@ -133,6 +134,11 @@ export class TabbedPanel extends Disposable implements IThemable {
|
|||||||
this.$tabList.append(tabHeaderElement);
|
this.$tabList.append(tabHeaderElement);
|
||||||
tab.header = tabHeaderElement;
|
tab.header = tabHeaderElement;
|
||||||
tab.label = tabLabel;
|
tab.label = tabLabel;
|
||||||
|
tab.dispose = () => {
|
||||||
|
tab.header.dispose();
|
||||||
|
tab.label.dispose();
|
||||||
|
};
|
||||||
|
this._register(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
public showTab(id: PanelTabIdentifier): void {
|
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 tables: GridTable<any>[] = [];
|
||||||
private tableDisposable: IDisposable[] = [];
|
private tableDisposable: IDisposable[] = [];
|
||||||
private queryRunnerDisposables: IDisposable[] = [];
|
private queryRunnerDisposables: IDisposable[] = [];
|
||||||
|
private currentHeight: number;
|
||||||
|
|
||||||
private runner: QueryRunner;
|
private runner: QueryRunner;
|
||||||
|
|
||||||
@@ -123,6 +124,11 @@ export class GridPanel extends ViewletPanel {
|
|||||||
|
|
||||||
protected layoutBody(size: number): void {
|
protected layoutBody(size: number): void {
|
||||||
this.splitView.layout(size);
|
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) {
|
public set queryRunner(runner: QueryRunner) {
|
||||||
@@ -229,6 +235,7 @@ class GridTable<T> extends Disposable implements IView {
|
|||||||
private container = document.createElement('div');
|
private container = document.createElement('div');
|
||||||
private selectionModel = new CellSelectionModel();
|
private selectionModel = new CellSelectionModel();
|
||||||
private styles: ITableStyles;
|
private styles: ITableStyles;
|
||||||
|
private currentHeight: number;
|
||||||
|
|
||||||
private columns: Slick.Column<T>[];
|
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) {
|
if (!this.table) {
|
||||||
this.build();
|
this.build();
|
||||||
}
|
}
|
||||||
|
if (!size) {
|
||||||
|
size = this.currentHeight;
|
||||||
|
} else {
|
||||||
|
this.currentHeight = size;
|
||||||
|
}
|
||||||
this.table.layout(
|
this.table.layout(
|
||||||
new Dimension(
|
new Dimension(
|
||||||
getContentWidth(this.container) - ACTIONBAR_WIDTH,
|
getContentWidth(this.container) - ACTIONBAR_WIDTH,
|
||||||
|
|||||||
@@ -109,24 +109,14 @@ export class MessagePanel extends ViewletPanel {
|
|||||||
this.queryRunnerDisposables = [];
|
this.queryRunnerDisposables = [];
|
||||||
this.reset();
|
this.reset();
|
||||||
this.queryRunnerDisposables.push(runner.onQueryStart(() => 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.onMessage(e => this.onMessage(e)));
|
||||||
this.queryRunnerDisposables.push(runner.onQueryEnd(e => this.onQueryEnd(e)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onMessage(message: IResultMessage | IResultMessage[]) {
|
private onMessage(message: IResultMessage | IResultMessage[]) {
|
||||||
if (isArray(message)) {
|
if (isArray(message)) {
|
||||||
this.model.messages.push(...message.map(c => {
|
this.model.messages.push(...message);
|
||||||
return <IMessagePanelMessage>{
|
|
||||||
isError: c.isError,
|
|
||||||
message: c.message
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
} else {
|
} else {
|
||||||
this.model.messages.push({
|
this.model.messages.push(message);
|
||||||
message: message.message,
|
|
||||||
isError: message.isError
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
const previousScrollPosition = this.tree.getScrollPosition();
|
const previousScrollPosition = this.tree.getScrollPosition();
|
||||||
this.tree.refresh(this.model).then(() => {
|
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() {
|
private reset() {
|
||||||
this.model.messages = [];
|
this.model.messages = [];
|
||||||
this.model.totalExecuteMessage = undefined;
|
this.model.totalExecuteMessage = undefined;
|
||||||
|
|||||||
@@ -158,5 +158,8 @@ export class QueryResultsEditor extends BaseEditor {
|
|||||||
|
|
||||||
public dispose(): void {
|
public dispose(): void {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
|
if (this.resultsView) {
|
||||||
|
this.resultsView.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ class ResultsView implements IPanelView {
|
|||||||
private gridPanel: GridPanel;
|
private gridPanel: GridPanel;
|
||||||
private messagePanel: MessagePanel;
|
private messagePanel: MessagePanel;
|
||||||
private container = document.createElement('div');
|
private container = document.createElement('div');
|
||||||
|
private currentDimension: DOM.Dimension;
|
||||||
|
|
||||||
constructor(instantiationService: IInstantiationService) {
|
constructor(instantiationService: IInstantiationService) {
|
||||||
this.panelViewlet = instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
|
this.panelViewlet = instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
|
||||||
@@ -45,6 +46,11 @@ class ResultsView implements IPanelView {
|
|||||||
|
|
||||||
layout(dimension: DOM.Dimension): void {
|
layout(dimension: DOM.Dimension): void {
|
||||||
this.panelViewlet.layout(dimension);
|
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 {
|
remove(): void {
|
||||||
@@ -101,8 +107,13 @@ export class QueryResultsView {
|
|||||||
let queryRunner = this.queryModelService._getQueryInfo(input.uri).queryRunner;
|
let queryRunner = this.queryModelService._getQueryInfo(input.uri).queryRunner;
|
||||||
this.resultsTab.queryRunner = queryRunner;
|
this.resultsTab.queryRunner = queryRunner;
|
||||||
this.chartTab.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 {
|
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 { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
|
||||||
import * as Utils from 'sql/parts/connection/common/utils';
|
import * as Utils from 'sql/parts/connection/common/utils';
|
||||||
import { SaveFormat } from 'sql/parts/grid/common/interfaces';
|
import { SaveFormat } from 'sql/parts/grid/common/interfaces';
|
||||||
|
import { echo } from 'sql/base/common/event';
|
||||||
|
|
||||||
import Severity from 'vs/base/common/severity';
|
import Severity from 'vs/base/common/severity';
|
||||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
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 { EventEmitter } from 'sql/base/common/eventEmitter';
|
||||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
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 { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
import { ResultSerializer } from 'sql/parts/query/common/resultSerializer';
|
import { ResultSerializer } from 'sql/parts/query/common/resultSerializer';
|
||||||
|
|
||||||
@@ -51,6 +52,10 @@ export interface IEventType {
|
|||||||
editSessionReady: IEditSessionReadyEvent;
|
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,
|
* 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.
|
||||||
@@ -68,36 +73,38 @@ export default class QueryRunner {
|
|||||||
public get isQueryPlan(): boolean { return this._isQueryPlan; }
|
public get isQueryPlan(): boolean { return this._isQueryPlan; }
|
||||||
|
|
||||||
private _onMessage = new Emitter<sqlops.IResultMessage>();
|
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
|
// on first run
|
||||||
if (types.isUndefinedOrNull(l)) {
|
if (types.isUndefinedOrNull(l)) {
|
||||||
return [e];
|
return [e];
|
||||||
} else {
|
} else {
|
||||||
return l.concat(e);
|
return l.concat(e);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
public readonly onMessage = this._echoedMessages.event;
|
||||||
|
|
||||||
private _onResultSet = new Emitter<sqlops.ResultSetSummary>();
|
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
|
// on first run
|
||||||
if (types.isUndefinedOrNull(l)) {
|
if (types.isUndefinedOrNull(l)) {
|
||||||
return [e];
|
return [e];
|
||||||
} else {
|
} else {
|
||||||
return l.concat(e);
|
return l.concat(e);
|
||||||
}
|
}
|
||||||
});
|
}));
|
||||||
|
public readonly onResultSet = this._echoedResultSet.event;
|
||||||
|
|
||||||
private _onQueryStart = new Emitter<void>();
|
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>();
|
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>();
|
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>();
|
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 /////////////////////////////////////////////////////////
|
||||||
constructor(
|
constructor(
|
||||||
@@ -164,6 +171,8 @@ export default class QueryRunner {
|
|||||||
private doRunQuery(input: string, runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): Thenable<void>;
|
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: sqlops.ISelectionData, runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): Thenable<void>;
|
||||||
private doRunQuery(input, 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;
|
let ownerUri = this.uri;
|
||||||
this._batchSets = [];
|
this._batchSets = [];
|
||||||
this._hasCompleted = false;
|
this._hasCompleted = false;
|
||||||
@@ -235,7 +244,15 @@ export default class QueryRunner {
|
|||||||
|
|
||||||
this._eventEmitter.emit(EventType.COMPLETE, Utils.parseNumAsTimeString(this._totalElapsedMilliseconds));
|
this._eventEmitter.emit(EventType.COMPLETE, Utils.parseNumAsTimeString(this._totalElapsedMilliseconds));
|
||||||
// We're done with this query so shut down any waiting mechanisms
|
// 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._onQueryEnd.fire(Utils.parseNumAsTimeString(this._totalElapsedMilliseconds));
|
||||||
|
this._onMessage.fire(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -255,7 +272,15 @@ export default class QueryRunner {
|
|||||||
|
|
||||||
// Store the batch
|
// Store the batch
|
||||||
this.batchSets[batch.id] = 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._eventEmitter.emit(EventType.BATCH_START, batch);
|
||||||
|
this._onMessage.fire(message);
|
||||||
this._onBatchStart.fire(batch);
|
this._onBatchStart.fire(batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user