mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-19 17:22:48 -05:00
selected cell summary for status bar (#14358)
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user