Time elapsed status item (#3006)

* added time elapsed status item

* add missing files
This commit is contained in:
Anthony Dresser
2018-10-29 15:24:08 -07:00
committed by GitHub
parent 2c0d6b93ee
commit 726eb8d0e1
6 changed files with 156 additions and 24 deletions

View File

@@ -23,9 +23,7 @@ export class ConnectionStatusbarItem implements IStatusbarItem {
constructor(
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@IEditorGroupsService private _editorGroupService: IEditorGroupsService,
@IEditorService private _editorService: EditorServiceImpl,
@ICapabilitiesService private _capabilitiesService: ICapabilitiesService,
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
) {
}

View File

@@ -69,7 +69,7 @@ export function parseTimeString(value: string): number | boolean {
* @param value The number of milliseconds to convert to a timespan string
* @returns A properly formatted timespan string.
*/
export function parseNumAsTimeString(value: number): string {
export function parseNumAsTimeString(value: number, includeFraction: boolean = true): string {
let tempVal = value;
let h = Math.floor(tempVal / msInH);
tempVal %= msInH;
@@ -85,7 +85,7 @@ export function parseNumAsTimeString(value: number): string {
let rs = hs + ':' + ms + ':' + ss;
return tempVal > 0 ? rs + '.' + mss : rs;
return tempVal > 0 && includeFraction ? rs + '.' + mss : rs;
}
export function generateUri(connection: IConnectionProfile, purpose?: 'dashboard' | 'insights' | 'connection'): string {

View File

@@ -9,7 +9,6 @@ import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle';
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorCloseEvent } from 'vs/workbench/common/editor';
import { append, $, hide, show } from 'vs/base/browser/dom';
@@ -25,7 +24,6 @@ export class RowCountStatusBarItem implements IStatusbarItem {
constructor(
@IEditorService private _editorService: EditorServiceImpl,
@IEditorGroupsService private _editorGroupService: IEditorGroupsService,
@IQueryModelService private _queryModelService: IQueryModelService
) { }
@@ -36,7 +34,7 @@ export class RowCountStatusBarItem implements IStatusbarItem {
];
this._element = append(container, $('.query-statusbar-group'));
this._flavorElement = append(this._element, $('a.editor-status-selection'));
this._flavorElement = append(this._element, $('.editor-status-selection'));
this._flavorElement.title = nls.localize('rowStatus', "Row Count");
hide(this._flavorElement);
@@ -65,11 +63,10 @@ export class RowCountStatusBarItem implements IStatusbarItem {
if (queryRunner) {
if (queryRunner.hasCompleted) {
this._displayValue(queryRunner);
} else if (queryRunner.isExecuting) {
this.dispose = queryRunner.addListener('complete', () => {
this._displayValue(queryRunner);
});
}
this.dispose = queryRunner.onQueryEnd(e => {
this._displayValue(queryRunner);
});
} else {
this.dispose = this._queryModelService.onRunQueryComplete(e => {
if (e === currentUri) {

View File

@@ -0,0 +1,107 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
import { IQueryModelService } from '../execution/queryModel';
import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { parseNumAsTimeString } from 'sql/parts/connection/common/utils';
import { IStatusbarItem } from 'vs/workbench/browser/parts/statusbar/statusbar';
import { IDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditorCloseEvent } from 'vs/workbench/common/editor';
import { append, $, hide, show } from 'vs/base/browser/dom';
import * as nls from 'vs/nls';
import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor';
import { IntervalTimer } from 'vs/base/common/async';
export class TimeElapsedStatusBarItem implements IStatusbarItem {
private _element: HTMLElement;
private _flavorElement: HTMLElement;
private dispose: IDisposable[] = [];
private intervalTimer = new IntervalTimer();
constructor(
@IEditorService private _editorService: EditorServiceImpl,
@IQueryModelService private _queryModelService: IQueryModelService
) { }
render(container: HTMLElement): IDisposable {
let disposables = [
this._editorService.onDidVisibleEditorsChange(() => this._onEditorsChanged()),
this._editorService.onDidCloseEditor(event => this._onEditorClosed(event))
];
this._element = append(container, $('.query-statusbar-group'));
this._flavorElement = append(this._element, $('.editor-status-selection'));
this._flavorElement.title = nls.localize('timeElapsed', "Time Elapsed");
hide(this._flavorElement);
this._showStatus();
return combinedDisposable(disposables);
}
private _onEditorsChanged() {
this._showStatus();
}
private _onEditorClosed(event: IEditorCloseEvent) {
hide(this._flavorElement);
}
// Show/hide query status for active editor
private _showStatus(): void {
this.intervalTimer.cancel();
hide(this._flavorElement);
dispose(this.dispose);
this._flavorElement.innerText = '';
this.dispose = [];
let activeEditor = this._editorService.activeControl;
if (activeEditor) {
let currentUri = WorkbenchUtils.getEditorUri(activeEditor.input);
if (currentUri) {
let queryRunner = this._queryModelService.getQueryRunner(currentUri);
if (queryRunner) {
if (queryRunner.hasCompleted || queryRunner.isExecuting) {
this._displayValue(queryRunner);
}
this.dispose.push(queryRunner.onQueryStart(e => {
this._displayValue(queryRunner);
}));
this.dispose.push(queryRunner.onQueryEnd(e => {
this._displayValue(queryRunner);
}));
} else {
this.dispose.push(this._queryModelService.onRunQueryStart(e => {
if (e === currentUri) {
this._displayValue(this._queryModelService.getQueryRunner(currentUri));
}
}));
this.dispose.push(this._queryModelService.onRunQueryComplete(e => {
if (e === currentUri) {
this._displayValue(this._queryModelService.getQueryRunner(currentUri));
}
}));
}
}
}
}
private _displayValue(runner: QueryRunner) {
this.intervalTimer.cancel();
if (runner.isExecuting) {
this.intervalTimer.cancelAndSet(() => {
this._flavorElement.innerText = parseNumAsTimeString(Date.now() - runner.queryStartTime.getTime(), false);
}, 1000);
this._flavorElement.innerText = parseNumAsTimeString(Date.now() - runner.queryStartTime.getTime(), false);
} else {
this._flavorElement.innerText = parseNumAsTimeString(runner.queryEndTime.getTime() - runner.queryStartTime.getTime(), false);
}
show(this._flavorElement);
}
}

View File

@@ -14,6 +14,7 @@ import { QueryInput } from 'sql/parts/query/common/queryInput';
import { QueryStatusbarItem } from 'sql/parts/query/execution/queryStatus';
import { SqlFlavorStatusbarItem } from 'sql/parts/query/common/flavorStatus';
import { RowCountStatusBarItem } from 'sql/parts/query/common/rowCountStatus';
import { TimeElapsedStatusBarItem } from 'sql/parts/query/common/timeElapsedStatus';
import * as sqlops from 'sqlops';
@@ -86,6 +87,12 @@ export class QueryModelService implements IQueryModelService {
// Register Statusbar items
(<statusbar.IStatusbarRegistry>platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor(
TimeElapsedStatusBarItem,
statusbar.StatusbarAlignment.RIGHT,
100 /* Should appear to the right of the SQL editor status */
));
(<statusbar.IStatusbarRegistry>platform.Registry.as(statusbar.Extensions.Statusbar)).registerStatusbarItem(new statusbar.StatusbarItemDescriptor(
RowCountStatusBarItem,
statusbar.StatusbarAlignment.RIGHT,
@@ -352,7 +359,7 @@ export class QueryModelService implements IQueryModelService {
public disposeQuery(ownerUri: string): void {
// Get existing query runner
let queryRunner = this.getQueryRunner(ownerUri);
let queryRunner = this.internalGetQueryRunner(ownerUri);
if (queryRunner) {
queryRunner.disposeQuery();
}
@@ -444,7 +451,7 @@ export class QueryModelService implements IQueryModelService {
public disposeEdit(ownerUri: string): Thenable<void> {
// Get existing query runner
let queryRunner = this.getQueryRunner(ownerUri);
let queryRunner = this.internalGetQueryRunner(ownerUri);
if (queryRunner) {
return queryRunner.disposeEdit(ownerUri);
}
@@ -453,7 +460,7 @@ export class QueryModelService implements IQueryModelService {
public updateCell(ownerUri: string, rowId: number, columnId: number, newValue: string): Thenable<sqlops.EditUpdateCellResult> {
// Get existing query runner
let queryRunner = this.getQueryRunner(ownerUri);
let queryRunner = this.internalGetQueryRunner(ownerUri);
if (queryRunner) {
return queryRunner.updateCell(ownerUri, rowId, columnId, newValue).then((result) => result, error => {
this._notificationService.notify({
@@ -468,7 +475,7 @@ export class QueryModelService implements IQueryModelService {
public commitEdit(ownerUri): Thenable<void> {
// Get existing query runner
let queryRunner = this.getQueryRunner(ownerUri);
let queryRunner = this.internalGetQueryRunner(ownerUri);
if (queryRunner) {
return queryRunner.commitEdit(ownerUri).then(() => { }, error => {
this._notificationService.notify({
@@ -483,7 +490,7 @@ export class QueryModelService implements IQueryModelService {
public createRow(ownerUri: string): Thenable<sqlops.EditCreateRowResult> {
// Get existing query runner
let queryRunner = this.getQueryRunner(ownerUri);
let queryRunner = this.internalGetQueryRunner(ownerUri);
if (queryRunner) {
return queryRunner.createRow(ownerUri);
}
@@ -492,7 +499,7 @@ export class QueryModelService implements IQueryModelService {
public deleteRow(ownerUri: string, rowId: number): Thenable<void> {
// Get existing query runner
let queryRunner = this.getQueryRunner(ownerUri);
let queryRunner = this.internalGetQueryRunner(ownerUri);
if (queryRunner) {
return queryRunner.deleteRow(ownerUri, rowId);
}
@@ -501,7 +508,7 @@ export class QueryModelService implements IQueryModelService {
public revertCell(ownerUri: string, rowId: number, columnId: number): Thenable<sqlops.EditRevertCellResult> {
// Get existing query runner
let queryRunner = this.getQueryRunner(ownerUri);
let queryRunner = this.internalGetQueryRunner(ownerUri);
if (queryRunner) {
return queryRunner.revertCell(ownerUri, rowId, columnId);
}
@@ -510,16 +517,25 @@ export class QueryModelService implements IQueryModelService {
public revertRow(ownerUri: string, rowId: number): Thenable<void> {
// Get existing query runner
let queryRunner = this.getQueryRunner(ownerUri);
let queryRunner = this.internalGetQueryRunner(ownerUri);
if (queryRunner) {
return queryRunner.revertRow(ownerUri, rowId);
}
return TPromise.as(null);
}
public getQueryRunner(ownerUri): QueryRunner {
let queryRunner: QueryRunner = undefined;
if (this._queryInfoMap.has(ownerUri)) {
queryRunner = this._getQueryInfo(ownerUri).queryRunner;
}
// return undefined if not found or is already executing
return queryRunner;
}
// PRIVATE METHODS //////////////////////////////////////////////////////
public getQueryRunner(ownerUri): QueryRunner {
private internalGetQueryRunner(ownerUri): QueryRunner {
let queryRunner: QueryRunner = undefined;
if (this._queryInfoMap.has(ownerUri)) {
let existingRunner = this._getQueryInfo(ownerUri).queryRunner;

View File

@@ -112,6 +112,15 @@ export default class QueryRunner {
private _onBatchEnd = new Emitter<sqlops.BatchSummary>();
public readonly onBatchEnd: Event<sqlops.BatchSummary> = this._onBatchEnd.event;
private _queryStartTime: Date;
public get queryStartTime(): Date {
return this._queryStartTime;
}
private _queryEndTime: Date;
public get queryEndTime(): Date {
return this._queryEndTime;
}
// CONSTRUCTOR /////////////////////////////////////////////////////////
constructor(
public uri: string,
@@ -185,9 +194,10 @@ export default class QueryRunner {
this._debouncedMessage.clear();
this._debouncedResultSet.clear();
this._planXml = new Deferred<string>();
let ownerUri = this.uri;
this._batchSets = [];
this._hasCompleted = false;
this._queryStartTime = undefined;
this._queryEndTime = undefined;
if (types.isObject(input) || types.isUndefinedOrNull(input)) {
// Update internal state to show that we're executing the query
this._resultLineOffset = input ? input.startLine : 0;
@@ -203,20 +213,22 @@ export default class QueryRunner {
// Send the request to execute the query
return runCurrentStatement
? this._queryManagementService.runQueryStatement(ownerUri, input.startLine, input.startColumn).then(() => this.handleSuccessRunQueryResult(), e => this.handleFailureRunQueryResult(e))
: this._queryManagementService.runQuery(ownerUri, input, runOptions).then(() => this.handleSuccessRunQueryResult(), e => this.handleFailureRunQueryResult(e));
? this._queryManagementService.runQueryStatement(this.uri, input.startLine, input.startColumn).then(() => this.handleSuccessRunQueryResult(), e => this.handleFailureRunQueryResult(e))
: this._queryManagementService.runQuery(this.uri, input, runOptions).then(() => this.handleSuccessRunQueryResult(), e => this.handleFailureRunQueryResult(e));
} else if (types.isString(input)) {
// Update internal state to show that we're executing the query
this._isExecuting = true;
this._totalElapsedMilliseconds = 0;
return this._queryManagementService.runQueryString(ownerUri, input).then(() => this.handleSuccessRunQueryResult(), e => this.handleFailureRunQueryResult(e));
return this._queryManagementService.runQueryString(this.uri, input).then(() => this.handleSuccessRunQueryResult(), e => this.handleFailureRunQueryResult(e));
} else {
return Promise.reject('Unknown input');
}
}
private handleSuccessRunQueryResult() {
// this isn't exact, but its the best we can do
this._queryStartTime = new Date();
// The query has started, so lets fire up the result pane
this._onQueryStart.fire();
this._eventEmitter.emit(EventType.START);
@@ -241,6 +253,8 @@ export default class QueryRunner {
* Handle a QueryComplete from the service layer
*/
public handleQueryComplete(result: sqlops.QueryExecuteCompleteNotificationResult): void {
// this also isn't exact but its the best we can do
this._queryEndTime = new Date();
// Store the batch sets we got back as a source of "truth"
this._isExecuting = false;