selected cell summary for status bar (#14358)

This commit is contained in:
Alan Ren
2021-02-19 11:29:04 -08:00
committed by GitHub
parent f528ffea9b
commit 83da03a728
7 changed files with 105 additions and 17 deletions

View File

@@ -36,6 +36,7 @@ import { ChartView } from 'sql/workbench/contrib/charts/browser/chartView';
import { ToggleableAction } from 'sql/workbench/contrib/notebook/browser/notebookActions';
import { IInsightOptions } from 'sql/workbench/common/editor/query/chartState';
import { NotebookChangeType } from 'sql/workbench/services/notebook/common/contracts';
import { IQueryModelService } from 'sql/workbench/services/query/common/queryModel';
import { ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
import { values } from 'vs/base/common/collections';
import { URI } from 'vs/base/common/uri';
@@ -198,9 +199,10 @@ class DataResourceTable extends GridTableBase<any> {
@IInstantiationService protected instantiationService: IInstantiationService,
@IEditorService editorService: IEditorService,
@IUntitledTextEditorService untitledEditorService: IUntitledTextEditorService,
@IConfigurationService configurationService: IConfigurationService
@IConfigurationService configurationService: IConfigurationService,
@IQueryModelService queryModelService: IQueryModelService
) {
super(state, createResultSet(source), { actionOrientation: ActionsOrientation.HORIZONTAL }, contextMenuService, instantiationService, editorService, untitledEditorService, configurationService);
super(state, createResultSet(source), { actionOrientation: ActionsOrientation.HORIZONTAL }, contextMenuService, instantiationService, editorService, untitledEditorService, configurationService, queryModelService);
this._gridDataProvider = this.instantiationService.createInstance(DataResourceDataProvider, source, this.resultSet, this.cellModel);
this._chart = this.instantiationService.createInstance(ChartView, false);

View File

@@ -48,6 +48,7 @@ import { Progress } from 'vs/platform/progress/common/progress';
import { ScrollableView, IView } from 'sql/base/browser/ui/scrollableView/scrollableView';
import { IQueryEditorConfiguration } from 'sql/platform/query/common/query';
import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
import { IQueryModelService } from 'sql/workbench/services/query/common/queryModel';
const ROW_HEIGHT = 29;
const HEADER_HEIGHT = 26;
@@ -373,7 +374,8 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
@IInstantiationService protected readonly instantiationService: IInstantiationService,
@IEditorService private readonly editorService: IEditorService,
@IUntitledTextEditorService private readonly untitledEditorService: IUntitledTextEditorService,
@IConfigurationService private readonly configurationService: IConfigurationService
@IConfigurationService private readonly configurationService: IConfigurationService,
@IQueryModelService private readonly queryModelService: IQueryModelService
) {
super();
let config = this.configurationService.getValue<{ rowHeight: number }>('resultsGrid');
@@ -513,10 +515,11 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
});
this.rebuildActionBar();
this.selectionModel.onSelectedRangesChanged.subscribe(e => {
this.selectionModel.onSelectedRangesChanged.subscribe(async e => {
if (this.state) {
this.state.selection = this.selectionModel.getSelectedRanges();
}
await this.notifyTableSelectionChanged();
});
this.table.grid.onScroll.subscribe((e, data) => {
@@ -590,6 +593,20 @@ export abstract class GridTableBase<T> extends Disposable implements IView {
this.scrolled = false;
}
private async notifyTableSelectionChanged() {
const selectedValues = [];
for (const range of this.state.selection) {
const subset = await this.gridDataProvider.getRowData(range.fromRow, range.toRow - range.fromRow + 1);
subset.rows.forEach(row => {
// start with range.fromCell -1 because we have row number column which is not available in the actual data
for (let i = range.fromCell - 1; i < range.toCell; i++) {
selectedValues.push(row[i]?.displayValue);
}
});
}
this.queryModelService.notifyCellSelectionChanged(selectedValues);
}
private onTableClick(event: ITableMouseEvent) {
// account for not having the number column
let column = this.resultSet.columnInfo[event.cell.cell - 1];
@@ -769,9 +786,10 @@ class GridTable<T> extends GridTableBase<T> {
@IContextKeyService private contextKeyService: IContextKeyService,
@IEditorService editorService: IEditorService,
@IUntitledTextEditorService untitledEditorService: IUntitledTextEditorService,
@IConfigurationService configurationService: IConfigurationService
@IConfigurationService configurationService: IConfigurationService,
@IQueryModelService queryModelService: IQueryModelService
) {
super(state, resultSet, undefined, contextMenuService, instantiationService, editorService, untitledEditorService, configurationService);
super(state, resultSet, undefined, contextMenuService, instantiationService, editorService, untitledEditorService, configurationService, queryModelService);
this._gridDataProvider = this.instantiationService.createInstance(QueryGridDataProvider, this._runner, resultSet.batchId, resultSet.id);
}

View File

@@ -27,7 +27,7 @@ import { localize } from 'vs/nls';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { TimeElapsedStatusBarContributions, RowCountStatusBarContributions, QueryStatusStatusBarContributions } from 'sql/workbench/contrib/query/browser/statusBarItems';
import { TimeElapsedStatusBarContributions, RowCountStatusBarContributions, QueryStatusStatusBarContributions, QueryResultSelectionSummaryStatusBarContribution } from 'sql/workbench/contrib/query/browser/statusBarItems';
import { SqlFlavorStatusbarItem, ChangeFlavorAction } from 'sql/workbench/contrib/query/browser/flavorStatus';
import { IEditorInputFactoryRegistry, Extensions as EditorInputFactoryExtensions } from 'vs/workbench/common/editor';
import { FileQueryEditorInput } from 'sql/workbench/contrib/query/common/fileQueryEditorInput';
@@ -479,3 +479,4 @@ workbenchRegistry.registerWorkbenchContribution(TimeElapsedStatusBarContribution
workbenchRegistry.registerWorkbenchContribution(RowCountStatusBarContributions, LifecyclePhase.Restored);
workbenchRegistry.registerWorkbenchContribution(QueryStatusStatusBarContributions, LifecyclePhase.Restored);
workbenchRegistry.registerWorkbenchContribution(SqlFlavorStatusbarItem, LifecyclePhase.Restored);
workbenchRegistry.registerWorkbenchContribution(QueryResultSelectionSummaryStatusBarContribution, LifecyclePhase.Restored);

View File

@@ -3,18 +3,17 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IQueryModelService } from 'sql/workbench/services/query/common/queryModel';
import { IntervalTimer } from 'vs/base/common/async';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { localize } from 'vs/nls';
import QueryRunner from 'sql/workbench/services/query/common/queryRunner';
import { parseNumAsTimeString } from 'sql/platform/connection/common/utils';
import { Event } from 'vs/base/common/event';
import { QueryEditorInput } from 'sql/workbench/common/editor/query/queryEditorInput';
import { IStatusbarService, IStatusbarEntryAccessor, StatusbarAlignment } from 'vs/workbench/services/statusbar/common/statusbar';
import { IQueryModelService } from 'sql/workbench/services/query/common/queryModel';
import QueryRunner from 'sql/workbench/services/query/common/queryRunner';
import { IntervalTimer } from 'vs/base/common/async';
import { Event } from 'vs/base/common/event';
import { Disposable, DisposableStore } from 'vs/base/common/lifecycle';
import { localize } from 'vs/nls';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/common/statusbar';
export class TimeElapsedStatusBarContributions extends Disposable implements IWorkbenchContribution {
private static readonly ID = 'status.query.timeElapsed';
@@ -253,3 +252,54 @@ export class QueryStatusStatusBarContributions extends Disposable implements IWo
this.statusbarService.updateEntryVisibility(QueryStatusStatusBarContributions.ID, true);
}
}
export class QueryResultSelectionSummaryStatusBarContribution extends Disposable implements IWorkbenchContribution {
private static readonly ID = 'status.query.selection-summary';
private statusItem: IStatusbarEntryAccessor;
constructor(
@IStatusbarService private readonly statusbarService: IStatusbarService,
@IEditorService private editorService: IEditorService,
@IQueryModelService queryModelService: IQueryModelService
) {
super();
this.statusItem = this._register(
this.statusbarService.addEntry({
text: '',
ariaLabel: ''
},
QueryResultSelectionSummaryStatusBarContribution.ID,
localize('status.query.selection-summary', "Selection Summary"),
StatusbarAlignment.RIGHT, 100)
);
this._register(editorService.onDidActiveEditorChange(() => { this.hide(); }, this));
this._register(queryModelService.onRunQueryStart(() => { this.hide(); }));
this._register(queryModelService.onCellSelectionChanged((data: string[]) => {
this.onCellSelectionChanged(data);
}));
}
private hide(): void {
this.statusbarService.updateEntryVisibility(QueryResultSelectionSummaryStatusBarContribution.ID, false);
}
private show(): void {
this.statusbarService.updateEntryVisibility(QueryResultSelectionSummaryStatusBarContribution.ID, true);
}
private onCellSelectionChanged(data: string[]): void {
const numericValues = data?.filter(value => !isNaN(parseFloat(value))).map(value => parseFloat(value));
if (numericValues?.length < 2 || !(this.editorService.activeEditor instanceof QueryEditorInput)) {
this.hide();
return;
}
const sum = numericValues.reduce((previous, current, idx, array) => previous + current);
const summaryText = localize('status.query.summaryText', "Average: {0} Count: {1} Sum: {2}", sum / numericValues.length, data.length, sum);
this.statusItem.update({
text: summaryText,
ariaLabel: summaryText
});
this.show();
}
}

View File

@@ -48,6 +48,9 @@ export interface IQueryEvent {
export interface IQueryModelService {
_serviceBrand: undefined;
onCellSelectionChanged: Event<string[]>;
notifyCellSelectionChanged(selectedValues: string[]): void;
getQueryRunner(uri: string): QueryRunner | undefined;
getQueryRows(uri: string, rowStart: number, numberOfRows: number, batchId: number, resultId: number): Promise<ResultSetSubset | undefined>;

View File

@@ -62,6 +62,7 @@ export class QueryModelService implements IQueryModelService {
private _onRunQueryComplete: Emitter<string>;
private _onQueryEvent: Emitter<IQueryEvent>;
private _onEditSessionReady: Emitter<azdata.EditSessionReadyParams>;
private _onCellSelectionChangedEmitter = new Emitter<string[]>();
// EVENTS /////////////////////////////////////////////////////////////
public get onRunQueryStart(): Event<string> { return this._onRunQueryStart.event; }
@@ -69,6 +70,7 @@ export class QueryModelService implements IQueryModelService {
public get onRunQueryComplete(): Event<string> { return this._onRunQueryComplete.event; }
public get onQueryEvent(): Event<IQueryEvent> { return this._onQueryEvent.event; }
public get onEditSessionReady(): Event<azdata.EditSessionReadyParams> { return this._onEditSessionReady.event; }
public get onCellSelectionChanged(): Event<string[]> { return this._onCellSelectionChangedEmitter.event; }
// CONSTRUCTOR /////////////////////////////////////////////////////////
constructor(
@@ -96,6 +98,14 @@ export class QueryModelService implements IQueryModelService {
return dataService;
}
/**
* Notify the event subscribers about the new selected cell values
* @param selectedValues current selected cell values
*/
public notifyCellSelectionChanged(selectedValues: string[]): void {
this._onCellSelectionChangedEmitter.fire(selectedValues);
}
/**
* Force all grids to re-render. This is needed to re-render the grids when switching
* between different URIs.

View File

@@ -14,6 +14,10 @@ import { IRange } from 'vs/editor/common/core/range';
export class TestQueryModelService implements IQueryModelService {
_serviceBrand: any;
onRunQueryUpdate: Event<string>;
onCellSelectionChanged: Event<string[]>;
notifyCellSelectionChanged(selectedValues: string[]): void {
throw new Error('Method not implemented.');
}
getQueryRunner(uri: string): QueryRunner {
throw new Error('Method not implemented.');
}