mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 11:01:37 -05:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
05040425df | ||
|
|
36f7c283b8 | ||
|
|
9fe4237033 | ||
|
|
b03c0a3e2d | ||
|
|
87946996ed | ||
|
|
cc55023440 | ||
|
|
1f19dfc50d | ||
|
|
950a440350 |
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sqlops",
|
||||
"version": "0.33.4",
|
||||
"version": "0.33.5",
|
||||
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
@@ -64,7 +64,7 @@
|
||||
"reflect-metadata": "^0.1.8",
|
||||
"rxjs": "5.4.0",
|
||||
"semver": "^5.5.0",
|
||||
"slickgrid": "github:anthonydresser/SlickGrid#2.3.25",
|
||||
"slickgrid": "github:anthonydresser/SlickGrid#2.3.27",
|
||||
"spdlog": "0.7.1",
|
||||
"sudo-prompt": "8.2.0",
|
||||
"svg.js": "^2.2.5",
|
||||
|
||||
@@ -180,14 +180,16 @@ export class TabbedPanel extends Disposable implements IThemable {
|
||||
}
|
||||
|
||||
public layout(dimension: Dimension): void {
|
||||
this._currentDimensions = dimension;
|
||||
this.$parent.style('height', dimension.height + 'px');
|
||||
this.$parent.style('width', dimension.width + 'px');
|
||||
this.$header.style('width', dimension.width + 'px');
|
||||
this.$body.style('width', dimension.width + 'px');
|
||||
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
|
||||
this.$body.style('height', bodyHeight + 'px');
|
||||
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight));
|
||||
if (dimension) {
|
||||
this._currentDimensions = dimension;
|
||||
this.$parent.style('height', dimension.height + 'px');
|
||||
this.$parent.style('width', dimension.width + 'px');
|
||||
this.$header.style('width', dimension.width + 'px');
|
||||
this.$body.style('width', dimension.width + 'px');
|
||||
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
|
||||
this.$body.style('height', bodyHeight + 'px');
|
||||
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight));
|
||||
}
|
||||
}
|
||||
|
||||
private _layoutCurrentTab(dimension: Dimension): void {
|
||||
|
||||
@@ -109,6 +109,9 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
|
||||
private _onDidSashReset = new Emitter<void>();
|
||||
readonly onDidSashReset = this._onDidSashReset.event;
|
||||
|
||||
private _onScroll = new Emitter<number>();
|
||||
readonly onScroll = this._onScroll.event;
|
||||
|
||||
get length(): number {
|
||||
return this.viewItems.length;
|
||||
}
|
||||
@@ -124,6 +127,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
|
||||
debounceEvent(this.scrollable.onScroll, (l, e) => e, 25)(e => {
|
||||
this.render(e.scrollTop, e.height);
|
||||
this.relayout();
|
||||
this._onScroll.fire(e.scrollTop);
|
||||
});
|
||||
let domNode = this.scrollable.getDomNode();
|
||||
dom.addClass(this.el, 'monaco-scroll-split-view');
|
||||
@@ -330,6 +334,10 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
|
||||
this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndex);
|
||||
}
|
||||
|
||||
public setScrollPosition(position: number) {
|
||||
this.scrollable.setScrollPosition({ scrollTop: position });
|
||||
}
|
||||
|
||||
layout(size: number): void {
|
||||
const previousSize = this.size;
|
||||
this.size = size;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
|
||||
|
||||
const defaultOptions: ICellRangeSelectorOptions = {
|
||||
selectionCss: {
|
||||
'border': '2px dashed blue'
|
||||
@@ -44,6 +42,8 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
|
||||
public onCellRangeSelected = new Slick.Event<{ range: Slick.Range }>();
|
||||
|
||||
constructor(private options: ICellRangeSelectorOptions) {
|
||||
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
|
||||
|
||||
this.options = mixin(this.options, defaultOptions, false);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
|
||||
import { CellRangeSelector, ICellRangeSelector } from 'sql/base/browser/ui/table/plugins/cellRangeSelector';
|
||||
|
||||
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
|
||||
|
||||
export interface ICellSelectionModelOptions {
|
||||
cellRangeSelector?: any;
|
||||
selectActiveCell?: boolean;
|
||||
|
||||
@@ -123,6 +123,7 @@ export class ConnectionStore {
|
||||
.then(savedCred => {
|
||||
if (savedCred) {
|
||||
credentialsItem.password = savedCred.password;
|
||||
credentialsItem.options['password'] = savedCred.password;
|
||||
}
|
||||
resolve({ profile: credentialsItem, savedCred: !!savedCred });
|
||||
},
|
||||
|
||||
@@ -492,6 +492,7 @@ export class ConnectionWidget {
|
||||
this._databaseNameInputBox.enabled = false;
|
||||
this._userNameInputBox.disable();
|
||||
this._passwordInputBox.disable();
|
||||
this._connectionNameInputBox.disable();
|
||||
this._rememberPasswordCheckBox.enabled = false;
|
||||
if (this._authTypeSelectBox) {
|
||||
this._authTypeSelectBox.disable();
|
||||
@@ -503,6 +504,7 @@ export class ConnectionWidget {
|
||||
|
||||
this._serverGroupSelectBox.enable();
|
||||
this._serverNameInputBox.enable();
|
||||
this._connectionNameInputBox.enable();
|
||||
this._databaseNameInputBox.enabled = true;
|
||||
let currentAuthType: AuthenticationType = undefined;
|
||||
if (this._authTypeSelectBox) {
|
||||
|
||||
@@ -10,6 +10,7 @@ import * as TelemetryUtils from 'sql/common/telemetryUtilities';
|
||||
import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||
import { memoize, unmemoize } from 'sql/base/common/decorators';
|
||||
import { mixin } from 'sql/base/common/objects';
|
||||
import { LegendPosition, DataDirection, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
@@ -21,29 +22,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
declare var Chart: any;
|
||||
|
||||
export enum ChartType {
|
||||
Bar = 'bar',
|
||||
Doughnut = 'doughnut',
|
||||
HorizontalBar = 'horizontalBar',
|
||||
Line = 'line',
|
||||
Pie = 'pie',
|
||||
TimeSeries = 'timeSeries',
|
||||
Scatter = 'scatter'
|
||||
}
|
||||
|
||||
export enum DataDirection {
|
||||
Vertical = 'vertical',
|
||||
Horizontal = 'horizontal'
|
||||
}
|
||||
|
||||
export enum LegendPosition {
|
||||
Top = 'top',
|
||||
Bottom = 'bottom',
|
||||
Left = 'left',
|
||||
Right = 'right',
|
||||
None = 'none'
|
||||
}
|
||||
|
||||
export function customMixin(destination: any, source: any, overwrite?: boolean): any {
|
||||
if (types.isObject(source)) {
|
||||
mixin(destination, source, overwrite, customMixin);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export enum ChartType {
|
||||
Bar = 'bar',
|
||||
Doughnut = 'doughnut',
|
||||
HorizontalBar = 'horizontalBar',
|
||||
Line = 'line',
|
||||
Pie = 'pie',
|
||||
TimeSeries = 'timeSeries',
|
||||
Scatter = 'scatter'
|
||||
}
|
||||
|
||||
export enum DataDirection {
|
||||
Vertical = 'vertical',
|
||||
Horizontal = 'horizontal'
|
||||
}
|
||||
|
||||
export enum LegendPosition {
|
||||
Top = 'top',
|
||||
Bottom = 'bottom',
|
||||
Left = 'left',
|
||||
Right = 'right',
|
||||
None = 'none'
|
||||
}
|
||||
|
||||
export enum DataType {
|
||||
Number = 'number',
|
||||
Point = 'point'
|
||||
}
|
||||
@@ -3,8 +3,9 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChartInsight, ChartType, customMixin, IChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { ChartInsight, customMixin, IChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { mixin } from 'sql/base/common/objects';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import PieChart from './pieChart.component';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
export default class DoughnutChart extends PieChart {
|
||||
protected readonly chartType: ChartType = ChartType.Doughnut;
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import BarChart from './barChart.component';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
export default class HorizontalBarChart extends BarChart {
|
||||
protected readonly chartType: ChartType = ChartType.HorizontalBar;
|
||||
|
||||
@@ -3,16 +3,13 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChartType, customMixin, defaultChartConfig, IDataSet, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
import { defaultChartConfig, IDataSet, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import BarChart, { IBarChartConfig } from './barChart.component';
|
||||
import { memoize, unmemoize } from 'sql/base/common/decorators';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { clone } from 'sql/base/common/objects';
|
||||
|
||||
export enum DataType {
|
||||
Number = 'number',
|
||||
Point = 'point'
|
||||
}
|
||||
import { ChartType, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
export interface ILineConfig extends IBarChartConfig {
|
||||
dataType?: DataType;
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChartInsight, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { ChartInsight } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
export default class PieChart extends ChartInsight {
|
||||
protected readonly chartType: ChartType = ChartType.Pie;
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ChartType, defaultChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { defaultChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import LineChart, { ILineConfig } from './lineChart.component';
|
||||
import { clone } from 'sql/base/common/objects';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { clone } from 'sql/base/common/objects';
|
||||
|
||||
const defaultScatterConfig = mixin(clone(defaultChartConfig), { dataType: 'point', dataDirection: 'horizontal' }) as ILineConfig;
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { defaultChartConfig, IPointDataSet, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { defaultChartConfig, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import LineChart, { ILineConfig } from './lineChart.component';
|
||||
import { clone } from 'sql/base/common/objects';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
@@ -16,7 +16,7 @@ import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
|
||||
import { IInsightData, IInsightsView, IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
import { QueryEditor } from 'sql/parts/query/editor/queryEditor';
|
||||
import { DataType, ILineConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
|
||||
import { ILineConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
|
||||
import * as PathUtilities from 'sql/common/pathUtilities';
|
||||
import { IChartViewActionContext, CopyAction, CreateInsightAction, SaveImageAction } from 'sql/parts/grid/views/query/chartViewerActions';
|
||||
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
|
||||
@@ -24,10 +24,11 @@ import * as Constants from 'sql/parts/query/common/constants';
|
||||
import { SelectBox as AngularSelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
||||
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
|
||||
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
|
||||
import { LegendPosition, DataDirection, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
/* Insights */
|
||||
import {
|
||||
ChartInsight, DataDirection, LegendPosition
|
||||
ChartInsight
|
||||
} from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
@@ -72,6 +72,8 @@ export interface IObjectExplorerService {
|
||||
getActiveConnectionNodes(): TreeNode[];
|
||||
|
||||
getTreeNode(connectionId: string, nodePath: string): Thenable<TreeNode>;
|
||||
|
||||
refreshNodeInView(connectionId: string, nodePath: string): Thenable<TreeNode>;
|
||||
}
|
||||
|
||||
interface SessionStatus {
|
||||
@@ -476,6 +478,20 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
||||
return Object.values(this._activeObjectExplorerNodes);
|
||||
}
|
||||
|
||||
public async refreshNodeInView(connectionId: string, nodePath: string): Promise<TreeNode> {
|
||||
// Get the tree node and call refresh from the provider
|
||||
let treeNode = await this.getTreeNode(connectionId, nodePath);
|
||||
await this.refreshTreeNode(treeNode.getSession(), treeNode);
|
||||
|
||||
// Get the new tree node, refresh it in the view, and expand it if needed
|
||||
treeNode = await this.getTreeNode(connectionId, nodePath);
|
||||
await this._serverTreeView.refreshElement(treeNode);
|
||||
if (treeNode.children.length > 0) {
|
||||
await treeNode.setExpandedState(TreeItemCollapsibleState.Expanded);
|
||||
}
|
||||
return treeNode;
|
||||
}
|
||||
|
||||
private async setNodeExpandedState(treeNode: TreeNode, expandedState: TreeItemCollapsibleState): Promise<void> {
|
||||
treeNode = await this.getUpdatedTreeNode(treeNode);
|
||||
let expandNode = this.getTreeItem(treeNode);
|
||||
|
||||
@@ -138,7 +138,7 @@ export class ServerTreeView {
|
||||
|
||||
let expandGroups: boolean = self._configurationService.getValue(SERVER_GROUP_CONFIG)[SERVER_GROUP_AUTOEXPAND_CONFIG];
|
||||
if (expandGroups) {
|
||||
self._tree.expandAll(ConnectionProfileGroup.getSubgroups(root));
|
||||
self._tree.expandAll(ConnectionProfileGroup.getSubgroups(root));
|
||||
}
|
||||
|
||||
if (root && !root.hasValidConnections) {
|
||||
@@ -244,6 +244,10 @@ export class ServerTreeView {
|
||||
TreeUpdateUtils.registeredServerUpdate(this._tree, this._connectionManagementService);
|
||||
}
|
||||
|
||||
public refreshElement(element: any): Thenable<void> {
|
||||
return this._tree.refresh(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter connections based on view (recent/active)
|
||||
*/
|
||||
|
||||
@@ -10,6 +10,22 @@ import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { EditorInput } from 'vs/workbench/common/editor';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
import { GridPanelState } from 'sql/parts/query/editor/gridPanel';
|
||||
import { MessagePanelState } from 'sql/parts/query/editor/messagePanel';
|
||||
import { QueryPlanState } from 'sql/parts/queryPlan/queryPlan';
|
||||
import { ChartState } from 'sql/parts/query/editor/charting/chartView';
|
||||
|
||||
export class ResultsViewState {
|
||||
public gridPanelState: GridPanelState = new GridPanelState();
|
||||
public messagePanelState: MessagePanelState = new MessagePanelState();
|
||||
public chartState: ChartState = new ChartState();
|
||||
public queryPlanState: QueryPlanState = new QueryPlanState();
|
||||
public gridPanelSize: number;
|
||||
public messagePanelSize: number;
|
||||
public activeTab: string;
|
||||
public visibleTabs: Set<string> = new Set<string>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Input for the QueryResultsEditor. This input helps with logic for the viewing and editing of
|
||||
* data in the results grid.
|
||||
@@ -29,6 +45,8 @@ export class QueryResultsInput extends EditorInput {
|
||||
public readonly onRestoreViewStateEmitter = new Emitter<void>();
|
||||
public readonly onSaveViewStateEmitter = new Emitter<void>();
|
||||
|
||||
public readonly state = new ResultsViewState();
|
||||
|
||||
constructor(private _uri: string) {
|
||||
super();
|
||||
this._visible = false;
|
||||
|
||||
@@ -8,10 +8,9 @@
|
||||
import { localize } from 'vs/nls';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
|
||||
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
|
||||
import { DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
|
||||
import { InsightType, IInsightOptions } from './insights/interfaces';
|
||||
import { DataDirection, ChartType, LegendPosition, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
|
||||
export class ChartTab implements IPanelTab {
|
||||
public readonly title = localize('chartTabTitle', 'Chart');
|
||||
public readonly identifier = generateUuid();
|
||||
public readonly identifier = 'ChartTab';
|
||||
public readonly view: ChartView;
|
||||
|
||||
constructor(@IInstantiationService instantiationService: IInstantiationService) {
|
||||
|
||||
@@ -12,11 +12,11 @@ import { Insight } from './insights/insight';
|
||||
import QueryRunner from 'sql/parts/query/execution/queryRunner';
|
||||
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||
import { ChartOptions, IChartOption, ControlType } from './chartOptions';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
|
||||
import { IInsightOptions } from './insights/interfaces';
|
||||
import { CopyAction, SaveImageAction, CreateInsightAction, IChartActionContext } from './actions';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
import { Dimension, $, getContentHeight, getContentWidth } from 'vs/base/browser/dom';
|
||||
import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||
@@ -27,6 +27,14 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { attachSelectBoxStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
|
||||
export class ChartState {
|
||||
dataId: { batchId: number, resultId: number };
|
||||
options: IInsightOptions = {
|
||||
type: ChartType.Bar
|
||||
};
|
||||
}
|
||||
|
||||
declare class Proxy {
|
||||
constructor(object, handler);
|
||||
@@ -43,6 +51,8 @@ export class ChartView implements IPanelView {
|
||||
private _copyAction: CopyAction;
|
||||
private _saveAction: SaveImageAction;
|
||||
|
||||
private _state: ChartState;
|
||||
|
||||
private options: IInsightOptions = {
|
||||
type: ChartType.Bar
|
||||
};
|
||||
@@ -61,7 +71,7 @@ export class ChartView implements IPanelView {
|
||||
private chartingContainer: HTMLElement;
|
||||
|
||||
private optionDisposables: IDisposable[] = [];
|
||||
private optionMap: { [x: string]: HTMLElement } = {};
|
||||
private optionMap: { [x: string]: { element: HTMLElement; set: (val) => void } } = {};
|
||||
|
||||
constructor(
|
||||
@IContextViewService private _contextViewService: IContextViewService,
|
||||
@@ -95,6 +105,10 @@ export class ChartView implements IPanelView {
|
||||
}
|
||||
|
||||
let result = Reflect.set(target, key, value, receiver);
|
||||
// mirror the change in our state
|
||||
if (self.state) {
|
||||
Reflect.set(self.state.options, key, value);
|
||||
}
|
||||
|
||||
if (change) {
|
||||
self.taskbar.context = <IChartActionContext>{ options: self.options, insight: self.insight ? self.insight.insight : undefined };
|
||||
@@ -138,6 +152,7 @@ export class ChartView implements IPanelView {
|
||||
}
|
||||
|
||||
public chart(dataId: { batchId: number, resultId: number }) {
|
||||
this.state.dataId = dataId;
|
||||
this._currentData = dataId;
|
||||
this.shouldGraph();
|
||||
}
|
||||
@@ -172,7 +187,7 @@ export class ChartView implements IPanelView {
|
||||
});
|
||||
}
|
||||
}
|
||||
// if we have the necessary information but the information isn't avaiable yet,
|
||||
// if we have the necessary information but the information isn't available yet,
|
||||
// we should be smart and retrying when the information might be available
|
||||
}
|
||||
}
|
||||
@@ -180,7 +195,9 @@ export class ChartView implements IPanelView {
|
||||
private buildOptions() {
|
||||
dispose(this.optionDisposables);
|
||||
this.optionDisposables = [];
|
||||
this.optionMap = {};
|
||||
this.optionMap = {
|
||||
'type': this.optionMap['type']
|
||||
};
|
||||
new Builder(this.typeControls).clearChildren();
|
||||
|
||||
this.updateActionbar();
|
||||
@@ -200,9 +217,9 @@ export class ChartView implements IPanelView {
|
||||
let option = ChartOptions[this.options.type].find(e => e.configEntry === key);
|
||||
if (option && option.if) {
|
||||
if (option.if(this.options)) {
|
||||
new Builder(this.optionMap[key]).show();
|
||||
new Builder(this.optionMap[key].element).show();
|
||||
} else {
|
||||
new Builder(this.optionMap[key]).hide();
|
||||
new Builder(this.optionMap[key].element).hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,6 +228,7 @@ export class ChartView implements IPanelView {
|
||||
|
||||
private updateActionbar() {
|
||||
if (this.insight && this.insight.isCopyable) {
|
||||
this.taskbar.context = { insight: this.insight.insight, options: this.options };
|
||||
this.taskbar.setContent([
|
||||
{ action: this._createInsightAction },
|
||||
{ action: this._copyAction },
|
||||
@@ -226,57 +244,104 @@ export class ChartView implements IPanelView {
|
||||
label.innerText = option.label;
|
||||
let optionContainer = $('div.option-container');
|
||||
optionContainer.appendChild(label);
|
||||
let setFunc: (val) => void;
|
||||
let value = this.state ? this.state.options[option.configEntry] || option.default : option.default;
|
||||
switch (option.type) {
|
||||
case ControlType.checkbox:
|
||||
let checkbox = new Checkbox(optionContainer, {
|
||||
label: '',
|
||||
ariaLabel: option.label,
|
||||
checked: option.default,
|
||||
checked: value,
|
||||
onChange: () => {
|
||||
if (this.options[option.configEntry] !== checkbox.checked) {
|
||||
this.options[option.configEntry] = checkbox.checked;
|
||||
this.insight.options = this.options;
|
||||
if (this.insight) {
|
||||
this.insight.options = this.options;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
setFunc = (val: boolean) => {
|
||||
checkbox.checked = val;
|
||||
};
|
||||
break;
|
||||
case ControlType.combo:
|
||||
let dropdown = new SelectBox(option.displayableOptions || option.options, 0, this._contextViewService);
|
||||
dropdown.select(option.options.indexOf(option.default));
|
||||
dropdown.select(option.options.indexOf(value));
|
||||
dropdown.render(optionContainer);
|
||||
dropdown.onDidSelect(e => {
|
||||
if (this.options[option.configEntry] !== option.options[e.index]) {
|
||||
this.options[option.configEntry] = option.options[e.index];
|
||||
this.insight.options = this.options;
|
||||
if (this.insight) {
|
||||
this.insight.options = this.options;
|
||||
}
|
||||
}
|
||||
});
|
||||
setFunc = (val: string) => {
|
||||
if (!isUndefinedOrNull(val)) {
|
||||
dropdown.select(option.options.indexOf(val));
|
||||
}
|
||||
};
|
||||
this.optionDisposables.push(attachSelectBoxStyler(dropdown, this._themeService));
|
||||
break;
|
||||
case ControlType.input:
|
||||
let input = new InputBox(optionContainer, this._contextViewService);
|
||||
input.value = option.default || '';
|
||||
input.value = value || '';
|
||||
input.onDidChange(e => {
|
||||
if (this.options[option.configEntry] !== e) {
|
||||
this.options[option.configEntry] = e;
|
||||
this.insight.options = this.options;
|
||||
if (this.insight) {
|
||||
this.insight.options = this.options;
|
||||
}
|
||||
}
|
||||
});
|
||||
setFunc = (val: string) => {
|
||||
if (!isUndefinedOrNull(val)) {
|
||||
input.value = val;
|
||||
}
|
||||
};
|
||||
this.optionDisposables.push(attachInputBoxStyler(input, this._themeService));
|
||||
break;
|
||||
case ControlType.numberInput:
|
||||
let numberInput = new InputBox(optionContainer, this._contextViewService, { type: 'number' });
|
||||
numberInput.value = option.default || '';
|
||||
numberInput.value = value || '';
|
||||
numberInput.onDidChange(e => {
|
||||
if (this.options[option.configEntry] !== Number(e)) {
|
||||
this.options[option.configEntry] = Number(e);
|
||||
this.insight.options = this.options;
|
||||
if (this.insight) {
|
||||
this.insight.options = this.options;
|
||||
}
|
||||
}
|
||||
});
|
||||
setFunc = (val: string) => {
|
||||
if (!isUndefinedOrNull(val)) {
|
||||
input.value = val;
|
||||
}
|
||||
};
|
||||
this.optionDisposables.push(attachInputBoxStyler(numberInput, this._themeService));
|
||||
break;
|
||||
}
|
||||
this.optionMap[option.configEntry] = optionContainer;
|
||||
this.optionMap[option.configEntry] = { element: optionContainer, set: setFunc };
|
||||
container.appendChild(optionContainer);
|
||||
this.options[option.configEntry] = option.default;
|
||||
this.options[option.configEntry] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public set state(val: ChartState) {
|
||||
this._state = val;
|
||||
if (this.state.options) {
|
||||
for (let key in this.state.options) {
|
||||
if (this.state.options.hasOwnProperty(key)) {
|
||||
this.options[key] = this.state.options[key];
|
||||
this.optionMap[key].set(this.state.options[key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (this.state.dataId) {
|
||||
this.chart(this.state.dataId);
|
||||
}
|
||||
}
|
||||
|
||||
public get state(): ChartState {
|
||||
return this._state;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ import * as colors from 'vs/platform/theme/common/colorRegistry';
|
||||
import { editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry';
|
||||
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||
import { IInsightOptions, IInsight } from './interfaces';
|
||||
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
const noneLineGraphs = [ChartType.Doughnut, ChartType.Pie];
|
||||
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
'use strict';
|
||||
|
||||
import { Graph } from './graphInsight';
|
||||
import { ChartType, DataDirection } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||
|
||||
import { Builder } from 'vs/base/browser/builder';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { DataDirection, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
import { ImageInsight } from './imageInsight';
|
||||
import { TableInsight } from './tableInsight';
|
||||
import { IInsightOptions, IInsight, InsightType, IInsightCtor } from './interfaces';
|
||||
import { CountInsight } from './countInsight';
|
||||
|
||||
import { Builder } from 'vs/base/browser/builder';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
|
||||
const defaultOptions: IInsightOptions = {
|
||||
|
||||
@@ -4,11 +4,10 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
'use strict';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
|
||||
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
|
||||
import { ChartType, LegendPosition, DataDirection } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
|
||||
import { Dimension } from 'vs/base/browser/dom';
|
||||
import { DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
|
||||
import { DataDirection, ChartType, LegendPosition, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
|
||||
|
||||
export interface IInsightOptions {
|
||||
type: InsightType | ChartType;
|
||||
|
||||
@@ -55,6 +55,11 @@ const ACTIONBAR_HEIGHT = 100;
|
||||
// this handles min size if rows is greater than the min grid visible rows
|
||||
const MIN_GRID_HEIGHT = (MIN_GRID_HEIGHT_ROWS * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT;
|
||||
|
||||
export class GridPanelState {
|
||||
public tableStates: GridTableState[] = [];
|
||||
public scrollPosition: number;
|
||||
public collapsed = false;
|
||||
}
|
||||
|
||||
export interface IGridTableState {
|
||||
canBeMaximized: boolean;
|
||||
@@ -73,11 +78,12 @@ export class GridTableState {
|
||||
|
||||
private _canBeMaximized: boolean;
|
||||
|
||||
constructor(state?: IGridTableState) {
|
||||
if (state) {
|
||||
this._maximized = state.maximized;
|
||||
this._canBeMaximized = state.canBeMaximized;
|
||||
}
|
||||
/* The top row of the current scroll */
|
||||
public scrollPosition = 0;
|
||||
public selection: Slick.Range[];
|
||||
public activeCell: Slick.Cell;
|
||||
|
||||
constructor(public readonly resultId: number, public readonly batchId: number) {
|
||||
}
|
||||
|
||||
public get canBeMaximized(): boolean {
|
||||
@@ -103,10 +109,6 @@ export class GridTableState {
|
||||
this._maximized = val;
|
||||
this._onMaximizedChange.fire(val);
|
||||
}
|
||||
|
||||
public clone(): GridTableState {
|
||||
return new GridTableState({ canBeMaximized: this.canBeMaximized, maximized: this.maximized });
|
||||
}
|
||||
}
|
||||
|
||||
export class GridPanel extends ViewletPanel {
|
||||
@@ -120,6 +122,7 @@ export class GridPanel extends ViewletPanel {
|
||||
private runner: QueryRunner;
|
||||
|
||||
private maximizedGrid: GridTable<any>;
|
||||
private _state: GridPanelState;
|
||||
|
||||
constructor(
|
||||
options: IViewletPanelOptions,
|
||||
@@ -131,6 +134,16 @@ export class GridPanel extends ViewletPanel {
|
||||
) {
|
||||
super(options, keybindingService, contextMenuService, configurationService);
|
||||
this.splitView = new ScrollableSplitView(this.container, { enableResizing: false });
|
||||
this.splitView.onScroll(e => {
|
||||
if (this.state) {
|
||||
this.state.scrollPosition = e;
|
||||
}
|
||||
});
|
||||
this.onDidChange(e => {
|
||||
if (this.state) {
|
||||
this.state.collapsed = !this.isExpanded();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement): void {
|
||||
@@ -168,6 +181,10 @@ export class GridPanel extends ViewletPanel {
|
||||
this.maximumBodySize = this.tables.reduce((p, c) => {
|
||||
return p + c.maximumSize;
|
||||
}, 0);
|
||||
|
||||
if (this.state && this.state.scrollPosition) {
|
||||
this.splitView.setScrollPosition(this.state.scrollPosition);
|
||||
}
|
||||
}
|
||||
|
||||
private addResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) {
|
||||
@@ -181,8 +198,18 @@ export class GridPanel extends ViewletPanel {
|
||||
let tables: GridTable<any>[] = [];
|
||||
|
||||
for (let set of resultsToAdd) {
|
||||
let tableState = new GridTableState();
|
||||
let table = this.instantiationService.createInstance(GridTable, this.runner, tableState, set);
|
||||
let tableState: GridTableState;
|
||||
if (this._state) {
|
||||
tableState = this.state.tableStates.find(e => e.batchId === set.batchId && e.resultId === set.id);
|
||||
}
|
||||
if (!tableState) {
|
||||
tableState = new GridTableState(set.id, set.batchId);
|
||||
if (this._state) {
|
||||
this._state.tableStates.push(tableState);
|
||||
}
|
||||
}
|
||||
let table = this.instantiationService.createInstance(GridTable, this.runner, set);
|
||||
table.state = tableState;
|
||||
tableState.onMaximizedChange(e => {
|
||||
if (e) {
|
||||
this.maximizeTable(table.id);
|
||||
@@ -241,6 +268,24 @@ export class GridPanel extends ViewletPanel {
|
||||
this.splitView.addViews(this.tables, this.tables.map(i => i.minimumSize));
|
||||
}
|
||||
}
|
||||
|
||||
public set state(val: GridPanelState) {
|
||||
this._state = val;
|
||||
this.tables.map(t => {
|
||||
let state = this.state.tableStates.find(s => s.batchId === t.resultSet.batchId && s.resultId === t.resultSet.id);
|
||||
if (!state) {
|
||||
this.state.tableStates.push(t.state);
|
||||
}
|
||||
if (state) {
|
||||
t.state = state;
|
||||
}
|
||||
});
|
||||
this.setExpanded(!this.state.collapsed);
|
||||
}
|
||||
|
||||
public get state(): GridPanelState {
|
||||
return this._state;
|
||||
}
|
||||
}
|
||||
|
||||
class GridTable<T> extends Disposable implements IView {
|
||||
@@ -259,13 +304,14 @@ class GridTable<T> extends Disposable implements IView {
|
||||
public id = generateUuid();
|
||||
readonly element: HTMLElement = this.container;
|
||||
|
||||
private _state: GridTableState;
|
||||
|
||||
// this handles if the row count is small, like 4-5 rows
|
||||
private readonly maxSize = ((this.resultSet.rowCount) * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT;
|
||||
|
||||
constructor(
|
||||
private runner: QueryRunner,
|
||||
public state: GridTableState,
|
||||
private resultSet: sqlops.ResultSetSummary,
|
||||
public readonly resultSet: sqlops.ResultSetSummary,
|
||||
@IContextMenuService private contextMenuService: IContextMenuService,
|
||||
@IInstantiationService private instantiationService: IInstantiationService,
|
||||
@IEditorService private editorService: IEditorService,
|
||||
@@ -357,10 +403,52 @@ class GridTable<T> extends Disposable implements IView {
|
||||
});
|
||||
this.actionBar.push(actions, { icon: true, label: false });
|
||||
|
||||
this.selectionModel.onSelectedRangesChanged.subscribe(e => {
|
||||
if (this.state) {
|
||||
this.state.selection = this.selectionModel.getSelectedRanges();
|
||||
}
|
||||
});
|
||||
|
||||
this.table.grid.onScroll.subscribe(e => {
|
||||
if (this.state) {
|
||||
this.state.scrollPosition = this.table.grid.getViewport().top;
|
||||
}
|
||||
});
|
||||
|
||||
this.table.grid.onActiveCellChanged.subscribe(e => {
|
||||
if (this.state) {
|
||||
this.state.activeCell = this.table.grid.getActiveCell();
|
||||
}
|
||||
});
|
||||
|
||||
this.setupState();
|
||||
}
|
||||
|
||||
private setupState() {
|
||||
// change actionbar on maximize change
|
||||
this.state.onMaximizedChange(this.rebuildActionBar, this);
|
||||
|
||||
this.state.onCanBeMaximizedChange(this.rebuildActionBar, this);
|
||||
|
||||
if (this.state.scrollPosition) {
|
||||
this.table.grid.scrollRowToTop(this.state.scrollPosition);
|
||||
}
|
||||
|
||||
if (this.state.selection) {
|
||||
this.selectionModel.setSelectedRanges(this.state.selection);
|
||||
}
|
||||
|
||||
if (this.state.activeCell) {
|
||||
this.table.setActiveCell(this.state.activeCell.row, this.state.activeCell.cell);
|
||||
}
|
||||
}
|
||||
|
||||
public get state(): GridTableState {
|
||||
return this._state;
|
||||
}
|
||||
|
||||
public set state(val: GridTableState) {
|
||||
this._state = val;
|
||||
}
|
||||
|
||||
private onTableClick(event: ITableMouseEvent) {
|
||||
|
||||
5
src/sql/parts/query/editor/media/queryActions.css
Normal file
5
src/sql/parts/query/editor/media/queryActions.css
Normal file
@@ -0,0 +1,5 @@
|
||||
.monaco-select-box {
|
||||
cursor: pointer;
|
||||
min-width: 150px;
|
||||
padding: 2px;
|
||||
}
|
||||
@@ -59,6 +59,11 @@ const TemplateIds = {
|
||||
ERROR: 'error'
|
||||
};
|
||||
|
||||
export class MessagePanelState {
|
||||
public scrollPosition: number;
|
||||
public collapsed = false;
|
||||
}
|
||||
|
||||
export class MessagePanel extends ViewletPanel {
|
||||
private ds = new MessageDataSource();
|
||||
private renderer = new MessageRenderer();
|
||||
@@ -67,6 +72,7 @@ export class MessagePanel extends ViewletPanel {
|
||||
private container = $('div message-tree').getHTMLElement();
|
||||
|
||||
private queryRunnerDisposables: IDisposable[] = [];
|
||||
private _state: MessagePanelState;
|
||||
|
||||
private tree: ITree;
|
||||
|
||||
@@ -86,6 +92,16 @@ export class MessagePanel extends ViewletPanel {
|
||||
renderer: this.renderer,
|
||||
controller: this.controller
|
||||
}, { keyboardSupport: false });
|
||||
this.tree.onDidScroll(e => {
|
||||
if (this.state) {
|
||||
this.state.scrollPosition = this.tree.getScrollPosition();
|
||||
}
|
||||
});
|
||||
this.onDidChange(e => {
|
||||
if (this.state) {
|
||||
this.state.collapsed = !this.isExpanded();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected renderBody(container: HTMLElement): void {
|
||||
@@ -99,8 +115,12 @@ export class MessagePanel extends ViewletPanel {
|
||||
protected layoutBody(size: number): void {
|
||||
const previousScrollPosition = this.tree.getScrollPosition();
|
||||
this.tree.layout(size);
|
||||
if (previousScrollPosition === 1) {
|
||||
this.tree.setScrollPosition(1);
|
||||
if (this.state && this.state.scrollPosition) {
|
||||
this.tree.setScrollPosition(this.state.scrollPosition);
|
||||
} else {
|
||||
if (previousScrollPosition === 1) {
|
||||
this.tree.setScrollPosition(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,12 +144,19 @@ export class MessagePanel extends ViewletPanel {
|
||||
if (hasError) {
|
||||
this.setExpanded(true);
|
||||
}
|
||||
const previousScrollPosition = this.tree.getScrollPosition();
|
||||
this.tree.refresh(this.model).then(() => {
|
||||
if (previousScrollPosition === 1) {
|
||||
if (this.state.scrollPosition) {
|
||||
this.tree.refresh(this.model).then(() => {
|
||||
this.tree.setScrollPosition(1);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
const previousScrollPosition = this.tree.getScrollPosition();
|
||||
this.tree.refresh(this.model).then(() => {
|
||||
if (previousScrollPosition === 1) {
|
||||
this.tree.setScrollPosition(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.maximumBodySize = this.model.messages.length * 22;
|
||||
}
|
||||
|
||||
private reset() {
|
||||
@@ -137,6 +164,17 @@ export class MessagePanel extends ViewletPanel {
|
||||
this.model.totalExecuteMessage = undefined;
|
||||
this.tree.refresh(this.model);
|
||||
}
|
||||
|
||||
public set state(val: MessagePanelState) {
|
||||
this._state = val;
|
||||
if (this.state.scrollPosition) {
|
||||
this.tree.setScrollPosition(this.state.scrollPosition);
|
||||
}
|
||||
this.setExpanded(!this.state.collapsed);
|
||||
}
|
||||
public get state(): MessagePanelState {
|
||||
return this._state;
|
||||
}
|
||||
}
|
||||
|
||||
class MessageDataSource implements IDataSource {
|
||||
|
||||
@@ -43,7 +43,6 @@ import {
|
||||
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
|
||||
import { IEditorDescriptorService } from 'sql/parts/query/editor/editorDescriptorService';
|
||||
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
|
||||
import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
@@ -500,7 +499,7 @@ export class QueryEditor extends BaseEditor {
|
||||
public get listDatabasesActionItem(): ListDatabasesActionItem {
|
||||
if (!this._listDatabasesActionItem) {
|
||||
this._listDatabasesActionItem = this._instantiationService.createInstance(ListDatabasesActionItem, this, this._listDatabasesAction);
|
||||
this._register(attachEditableDropdownStyler(this._listDatabasesActionItem, this.themeService));
|
||||
this._register(this._listDatabasesActionItem.attachStyler(this.themeService));
|
||||
}
|
||||
return this._listDatabasesActionItem;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput';
|
||||
import { QueryResultsInput, ResultsViewState } from 'sql/parts/query/common/queryResultsInput';
|
||||
import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/panel';
|
||||
import { IQueryModelService } from '../execution/queryModel';
|
||||
import QueryRunner from 'sql/parts/query/execution/queryRunner';
|
||||
@@ -14,11 +14,10 @@ import { ChartTab } from './charting/chartTab';
|
||||
import { QueryPlanTab } from 'sql/parts/queryPlan/queryPlan';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import * as UUID from 'vs/base/common/uuid';
|
||||
import { PanelViewlet } from 'vs/workbench/browser/parts/views/panelViewlet';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { once } from 'vs/base/common/event';
|
||||
import { once, anyEvent } from 'vs/base/common/event';
|
||||
|
||||
class ResultsView implements IPanelView {
|
||||
private panelViewlet: PanelViewlet;
|
||||
@@ -26,61 +25,72 @@ class ResultsView implements IPanelView {
|
||||
private messagePanel: MessagePanel;
|
||||
private container = document.createElement('div');
|
||||
private currentDimension: DOM.Dimension;
|
||||
private isGridRendered = false;
|
||||
private needsGridResize = false;
|
||||
private lastGridHeight: number;
|
||||
private _state: ResultsViewState;
|
||||
|
||||
constructor(instantiationService: IInstantiationService) {
|
||||
this.panelViewlet = instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
|
||||
this.gridPanel = instantiationService.createInstance(GridPanel, { title: nls.localize('gridPanel', 'Results'), id: 'gridPanel' });
|
||||
this.messagePanel = instantiationService.createInstance(MessagePanel, { title: nls.localize('messagePanel', 'Messages'), minimumBodySize: 0, id: 'messagePanel' });
|
||||
constructor(private instantiationService: IInstantiationService) {
|
||||
|
||||
this.panelViewlet = this.instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
|
||||
this.gridPanel = this.instantiationService.createInstance(GridPanel, { title: nls.localize('gridPanel', 'Results'), id: 'gridPanel' });
|
||||
this.messagePanel = this.instantiationService.createInstance(MessagePanel, { title: nls.localize('messagePanel', 'Messages'), minimumBodySize: 0, id: 'messagePanel' });
|
||||
this.gridPanel.render();
|
||||
this.messagePanel.render();
|
||||
this.panelViewlet.create(this.container).then(() => {
|
||||
this.gridPanel.setVisible(false);
|
||||
this.panelViewlet.addPanels([
|
||||
{ panel: this.messagePanel, size: this.messagePanel.minimumSize, index: 1 }
|
||||
]);
|
||||
});
|
||||
this.gridPanel.onDidChange(e => {
|
||||
anyEvent(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(e => {
|
||||
let size = this.gridPanel.maximumBodySize;
|
||||
if (this.isGridRendered) {
|
||||
if (size < 1) {
|
||||
this.lastGridHeight = this.panelViewlet.getPanelSize(this.gridPanel);
|
||||
this.panelViewlet.removePanels([this.gridPanel]);
|
||||
// tell the panel is has been removed.
|
||||
this.gridPanel.layout(0);
|
||||
this.isGridRendered = false;
|
||||
}
|
||||
} else {
|
||||
if (this.currentDimension) {
|
||||
this.needsGridResize = false;
|
||||
if (size > 0) {
|
||||
this.panelViewlet.addPanels([
|
||||
{ panel: this.gridPanel, index: 0, size: this.lastGridHeight || Math.round(this.currentDimension.height * .7) }
|
||||
]);
|
||||
this.isGridRendered = true;
|
||||
}
|
||||
if (size < 1 && this.gridPanel.isVisible()) {
|
||||
this.gridPanel.setVisible(false);
|
||||
this.panelViewlet.removePanels([this.gridPanel]);
|
||||
this.gridPanel.layout(0);
|
||||
} else if (size > 0 && !this.gridPanel.isVisible()) {
|
||||
this.gridPanel.setVisible(true);
|
||||
let panelSize: number;
|
||||
if (this.state && this.state.gridPanelSize) {
|
||||
panelSize = this.state.gridPanelSize;
|
||||
} else if (this.currentDimension) {
|
||||
panelSize = Math.round(this.currentDimension.height * .7);
|
||||
} else {
|
||||
this.panelViewlet.addPanels([
|
||||
{ panel: this.gridPanel, index: 0, size: this.lastGridHeight || 200 }
|
||||
]);
|
||||
this.isGridRendered = true;
|
||||
panelSize = 200;
|
||||
this.needsGridResize = true;
|
||||
}
|
||||
this.panelViewlet.addPanels([{ panel: this.gridPanel, index: 0, size: panelSize }]);
|
||||
}
|
||||
});
|
||||
let gridResizeList = this.gridPanel.onDidChange(e => {
|
||||
if (this.currentDimension) {
|
||||
this.needsGridResize = false;
|
||||
this.panelViewlet.resizePanel(this.gridPanel, Math.round(this.currentDimension.height * .7));
|
||||
let resizeList = anyEvent(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(() => {
|
||||
let panelSize: number;
|
||||
if (this.state && this.state.gridPanelSize) {
|
||||
panelSize = this.state.gridPanelSize;
|
||||
} else if (this.currentDimension) {
|
||||
panelSize = Math.round(this.currentDimension.height * .7);
|
||||
} else {
|
||||
panelSize = 200;
|
||||
this.needsGridResize = true;
|
||||
}
|
||||
});
|
||||
if (this.state.messagePanelSize) {
|
||||
this.panelViewlet.resizePanel(this.gridPanel, this.state.messagePanelSize);
|
||||
}
|
||||
this.panelViewlet.resizePanel(this.gridPanel, panelSize);
|
||||
})
|
||||
// once the user changes the sash we should stop trying to resize the grid
|
||||
once(this.panelViewlet.onDidSashChange)(e => {
|
||||
this.needsGridResize = false;
|
||||
gridResizeList.dispose();
|
||||
resizeList.dispose();
|
||||
});
|
||||
|
||||
this.panelViewlet.onDidSashChange(e => {
|
||||
if (this.state) {
|
||||
if (this.gridPanel.isExpanded()) {
|
||||
this.state.gridPanelSize = this.panelViewlet.getPanelSize(this.gridPanel);
|
||||
}
|
||||
if (this.messagePanel.isExpanded()) {
|
||||
this.state.messagePanelSize = this.panelViewlet.getPanelSize(this.messagePanel);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -96,7 +106,7 @@ class ResultsView implements IPanelView {
|
||||
}
|
||||
this.currentDimension = dimension;
|
||||
if (this.needsGridResize) {
|
||||
this.panelViewlet.resizePanel(this.gridPanel, Math.round(this.currentDimension.height * .7));
|
||||
this.panelViewlet.resizePanel(this.gridPanel, this.state.gridPanelSize || Math.round(this.currentDimension.height * .7));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,11 +122,21 @@ class ResultsView implements IPanelView {
|
||||
public hideResultHeader() {
|
||||
this.gridPanel.headerVisible = false;
|
||||
}
|
||||
|
||||
public set state(val: ResultsViewState) {
|
||||
this._state = val;
|
||||
this.gridPanel.state = val.gridPanelState;
|
||||
this.messagePanel.state = val.messagePanelState;
|
||||
}
|
||||
|
||||
public get state(): ResultsViewState {
|
||||
return this._state;
|
||||
}
|
||||
}
|
||||
|
||||
class ResultsTab implements IPanelTab {
|
||||
public readonly title = nls.localize('resultsTabTitle', 'Results');
|
||||
public readonly identifier = UUID.generateUuid();
|
||||
public readonly identifier = 'resultsTab';
|
||||
public readonly view: ResultsView;
|
||||
|
||||
constructor(instantiationService: IInstantiationService) {
|
||||
@@ -144,6 +164,12 @@ export class QueryResultsView {
|
||||
this.chartTab = new ChartTab(instantiationService);
|
||||
this._panelView = new TabbedPanel(container, { showHeaderWhenSingleView: false });
|
||||
this.qpTab = new QueryPlanTab();
|
||||
this._panelView.pushTab(this.resultsTab);
|
||||
this._panelView.onTabChange(e => {
|
||||
if (this.input) {
|
||||
this.input.state.activeTab = e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public style() {
|
||||
@@ -151,11 +177,24 @@ export class QueryResultsView {
|
||||
|
||||
public set input(input: QueryResultsInput) {
|
||||
this._input = input;
|
||||
this.resultsTab.view.state = this.input.state;
|
||||
this.qpTab.view.state = this.input.state.queryPlanState;
|
||||
this.chartTab.view.state = this.input.state.chartState;
|
||||
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);
|
||||
if (this.input.state.visibleTabs.has(this.chartTab.identifier)) {
|
||||
if (!this._panelView.contains(this.chartTab)) {
|
||||
this._panelView.pushTab(this.chartTab);
|
||||
}
|
||||
}
|
||||
if (this.input.state.visibleTabs.has(this.qpTab.identifier)) {
|
||||
if (!this._panelView.contains(this.qpTab)) {
|
||||
this._panelView.pushTab(this.qpTab);
|
||||
}
|
||||
}
|
||||
if (this.input.state.activeTab) {
|
||||
this._panelView.showTab(this.input.state.activeTab);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +211,7 @@ export class QueryResultsView {
|
||||
}
|
||||
|
||||
public chartData(dataId: { resultId: number, batchId: number }): void {
|
||||
this.input.state.visibleTabs.add(this.chartTab.identifier);
|
||||
if (!this._panelView.contains(this.chartTab)) {
|
||||
this._panelView.pushTab(this.chartTab);
|
||||
}
|
||||
@@ -181,6 +221,7 @@ export class QueryResultsView {
|
||||
}
|
||||
|
||||
public showPlan(xml: string) {
|
||||
this.input.state.visibleTabs.add(this.qpTab.identifier);
|
||||
if (!this._panelView.contains(this.qpTab)) {
|
||||
this._panelView.pushTab(this.qpTab);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!sql/parts/query/editor/media/queryActions';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Builder, $ } from 'vs/base/browser/builder';
|
||||
import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown';
|
||||
@@ -12,6 +13,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { attachEditableDropdownStyler, attachSelectBoxStyler } from 'sql/common/theme/styler';
|
||||
|
||||
import { ISelectionData } from 'sqlops';
|
||||
import {
|
||||
@@ -25,6 +27,8 @@ import { QueryEditor } from 'sql/parts/query/editor/queryEditor';
|
||||
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
/**
|
||||
* Action class that query-based Actions will extend. This base class automatically handles activating and
|
||||
@@ -431,6 +435,9 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
|
||||
private _isConnected: boolean;
|
||||
private $databaseListDropdown: Builder;
|
||||
private _dropdown: Dropdown;
|
||||
private _databaseSelectBox: SelectBox;
|
||||
private _isInAccessibilityMode: boolean;
|
||||
private readonly _selectDatabaseString: string = nls.localize("selectDatabase", "Select Database");
|
||||
|
||||
// CONSTRUCTOR /////////////////////////////////////////////////////////
|
||||
constructor(
|
||||
@@ -439,23 +446,33 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
@IContextViewService contextViewProvider: IContextViewService,
|
||||
@IThemeService themeService: IThemeService
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService private readonly _configurationService: IConfigurationService
|
||||
) {
|
||||
super();
|
||||
this._toDispose = [];
|
||||
this.$databaseListDropdown = $('.databaseListDropdown');
|
||||
let selectString = nls.localize("selectDatabase", "Select Database");
|
||||
this._dropdown = new Dropdown(this.$databaseListDropdown.getHTMLElement(), contextViewProvider, themeService, {
|
||||
strictSelection: true,
|
||||
placeholder: selectString,
|
||||
ariaLabel: selectString,
|
||||
actionLabel: nls.localize('listDatabases.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
|
||||
});
|
||||
this._dropdown.onValueChange(s => this.databaseSelected(s));
|
||||
this._isInAccessibilityMode = this._configurationService.getValue('editor.accessibilitySupport') === 'on';
|
||||
|
||||
if (this._isInAccessibilityMode) {
|
||||
this._databaseSelectBox = new SelectBox([this._selectDatabaseString], this._selectDatabaseString, contextViewProvider, undefined, { ariaLabel: this._selectDatabaseString });
|
||||
this._databaseSelectBox.render(this.$databaseListDropdown.getHTMLElement());
|
||||
this._databaseSelectBox.onDidSelect(e => { this.databaseSelected(e.selected); });
|
||||
this._databaseSelectBox.disable();
|
||||
|
||||
} else {
|
||||
this._dropdown = new Dropdown(this.$databaseListDropdown.getHTMLElement(), contextViewProvider, themeService, {
|
||||
strictSelection: true,
|
||||
placeholder: this._selectDatabaseString,
|
||||
ariaLabel: this._selectDatabaseString,
|
||||
actionLabel: nls.localize('listDatabases.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
|
||||
});
|
||||
this._dropdown.onValueChange(s => this.databaseSelected(s));
|
||||
this._toDispose.push(this._dropdown.onFocus(() => { self.onDropdownFocus(); }));
|
||||
}
|
||||
|
||||
// Register event handlers
|
||||
let self = this;
|
||||
this._toDispose.push(this._dropdown.onFocus(() => { self.onDropdownFocus(); }));
|
||||
this._toDispose.push(this._connectionManagementService.onConnectionChanged(params => { self.onConnectionChanged(params); }));
|
||||
}
|
||||
|
||||
@@ -465,7 +482,12 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
|
||||
}
|
||||
|
||||
public style(styles) {
|
||||
this._dropdown.style(styles);
|
||||
if (this._isInAccessibilityMode) {
|
||||
this._databaseSelectBox.style(styles);
|
||||
}
|
||||
else {
|
||||
this._dropdown.style(styles);
|
||||
}
|
||||
}
|
||||
|
||||
public setActionContext(context: any): void {
|
||||
@@ -477,11 +499,27 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
this._dropdown.focus();
|
||||
if (this._isInAccessibilityMode) {
|
||||
this._databaseSelectBox.focus();
|
||||
} else {
|
||||
this._dropdown.focus();
|
||||
}
|
||||
}
|
||||
|
||||
public blur(): void {
|
||||
this._dropdown.blur();
|
||||
if (this._isInAccessibilityMode) {
|
||||
this._databaseSelectBox.blur();
|
||||
} else {
|
||||
this._dropdown.blur();
|
||||
}
|
||||
}
|
||||
|
||||
public attachStyler(themeService: IThemeService): IDisposable {
|
||||
if (this._isInAccessibilityMode) {
|
||||
return attachSelectBoxStyler(this, themeService);
|
||||
} else {
|
||||
return attachEditableDropdownStyler(this, themeService);
|
||||
}
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
@@ -496,9 +534,15 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
|
||||
|
||||
public onDisconnect(): void {
|
||||
this._isConnected = false;
|
||||
this._dropdown.enabled = false;
|
||||
this._currentDatabaseName = undefined;
|
||||
this._dropdown.value = '';
|
||||
|
||||
if (this._isInAccessibilityMode) {
|
||||
this._databaseSelectBox.disable();
|
||||
this._databaseSelectBox.setOptions([this._selectDatabaseString]);
|
||||
} else {
|
||||
this._dropdown.enabled = false;
|
||||
this._dropdown.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
// PRIVATE HELPERS /////////////////////////////////////////////////////
|
||||
@@ -515,22 +559,22 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
|
||||
|
||||
this._connectionManagementService.changeDatabase(this._editor.uri, dbName)
|
||||
.then(
|
||||
result => {
|
||||
if (!result) {
|
||||
result => {
|
||||
if (!result) {
|
||||
this.resetDatabaseName();
|
||||
this._notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: nls.localize('changeDatabase.failed', "Failed to change database")
|
||||
});
|
||||
}
|
||||
},
|
||||
error => {
|
||||
this.resetDatabaseName();
|
||||
this._notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: nls.localize('changeDatabase.failed', "Failed to change database")
|
||||
message: nls.localize('changeDatabase.failedWithError', "Failed to change database {0}", error)
|
||||
});
|
||||
}
|
||||
},
|
||||
error => {
|
||||
this.resetDatabaseName();
|
||||
this._notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: nls.localize('changeDatabase.failedWithError', "Failed to change database {0}", error)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private getCurrentDatabaseName() {
|
||||
@@ -545,7 +589,11 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
|
||||
}
|
||||
|
||||
private resetDatabaseName() {
|
||||
this._dropdown.value = this.getCurrentDatabaseName();
|
||||
if (this._isInAccessibilityMode) {
|
||||
this._databaseSelectBox.selectWithOptionName(this.getCurrentDatabaseName());
|
||||
} else {
|
||||
this._dropdown.value = this.getCurrentDatabaseName();
|
||||
}
|
||||
}
|
||||
|
||||
private onConnectionChanged(connParams: IConnectionParams): void {
|
||||
@@ -579,9 +627,26 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
|
||||
|
||||
private updateConnection(databaseName: string) {
|
||||
this._isConnected = true;
|
||||
this._dropdown.enabled = true;
|
||||
this._currentDatabaseName = databaseName;
|
||||
this._dropdown.value = databaseName;
|
||||
|
||||
if (this._isInAccessibilityMode) {
|
||||
this._databaseSelectBox.enable();
|
||||
let self = this;
|
||||
let uri = self._editor.connectedUri;
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
self._connectionManagementService.listDatabases(uri)
|
||||
.then(result => {
|
||||
if (result && result.databaseNames) {
|
||||
this._databaseSelectBox.setOptions(result.databaseNames);
|
||||
}
|
||||
this._databaseSelectBox.selectWithOptionName(databaseName);
|
||||
});
|
||||
} else {
|
||||
this._dropdown.enabled = true;
|
||||
this._dropdown.value = databaseName;
|
||||
}
|
||||
}
|
||||
|
||||
// TESTING PROPERTIES //////////////////////////////////////////////////
|
||||
|
||||
@@ -13,9 +13,13 @@ import { localize } from 'vs/nls';
|
||||
import * as UUID from 'vs/base/common/uuid';
|
||||
import { Builder } from 'vs/base/browser/builder';
|
||||
|
||||
export class QueryPlanState {
|
||||
xml: string;
|
||||
}
|
||||
|
||||
export class QueryPlanTab implements IPanelTab {
|
||||
public readonly title = localize('queryPlanTitle', 'Query Plan');
|
||||
public readonly identifier = UUID.generateUuid();
|
||||
public readonly identifier = 'QueryPlanTab';
|
||||
public readonly view: QueryPlanView;
|
||||
|
||||
constructor() {
|
||||
@@ -27,6 +31,7 @@ export class QueryPlanView implements IPanelView {
|
||||
private qp: QueryPlan;
|
||||
private xml: string;
|
||||
private container = document.createElement('div');
|
||||
private _state: QueryPlanState;
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
if (!this.qp) {
|
||||
@@ -47,6 +52,20 @@ export class QueryPlanView implements IPanelView {
|
||||
} else {
|
||||
this.xml = xml;
|
||||
}
|
||||
if (this.state) {
|
||||
this.state.xml = xml;
|
||||
}
|
||||
}
|
||||
|
||||
public set state(val: QueryPlanState) {
|
||||
this._state = val;
|
||||
if (this.state.xml) {
|
||||
this.showPlan(this.state.xml);
|
||||
}
|
||||
}
|
||||
|
||||
public get state(): QueryPlanState {
|
||||
return this._state;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
5
src/sql/sqlops.d.ts
vendored
5
src/sql/sqlops.d.ts
vendored
@@ -185,6 +185,11 @@ declare module 'sqlops' {
|
||||
* Get the parent node. Returns undefined if there is none.
|
||||
*/
|
||||
getParent(): Thenable<ObjectExplorerNode>;
|
||||
|
||||
/**
|
||||
* Refresh the node, expanding it if it has children
|
||||
*/
|
||||
refresh(): Thenable<void>;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ExtHostObjectExplorerShape, SqlMainContext, MainThreadObjectExplorerSha
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
export class ExtHostObjectExplorer implements ExtHostObjectExplorerShape {
|
||||
export class ExtHostObjectExplorer implements ExtHostObjectExplorerShape {
|
||||
|
||||
private _proxy: MainThreadObjectExplorerShape;
|
||||
|
||||
@@ -44,7 +44,7 @@ class ExtHostObjectExplorerNode implements sqlops.objectexplorer.ObjectExplorerN
|
||||
public errorMessage: string;
|
||||
|
||||
constructor(nodeInfo: sqlops.NodeInfo, connectionId: string, private _proxy: MainThreadObjectExplorerShape) {
|
||||
Object.entries(nodeInfo).forEach(([key, value]) => this[key] = value);
|
||||
this.getDetailsFromInfo(nodeInfo);
|
||||
this.connectionId = connectionId;
|
||||
}
|
||||
|
||||
@@ -71,4 +71,12 @@ class ExtHostObjectExplorerNode implements sqlops.objectexplorer.ObjectExplorerN
|
||||
}
|
||||
return this._proxy.$getNode(this.connectionId, this.nodePath.slice(0, parentPathEndIndex)).then(nodeInfo => nodeInfo ? new ExtHostObjectExplorerNode(nodeInfo, this.connectionId, this._proxy) : undefined);
|
||||
}
|
||||
|
||||
refresh(): Thenable<void> {
|
||||
return this._proxy.$refresh(this.connectionId, this.nodePath).then(nodeInfo => this.getDetailsFromInfo(nodeInfo));
|
||||
}
|
||||
|
||||
private getDetailsFromInfo(nodeInfo: sqlops.NodeInfo): void {
|
||||
Object.entries(nodeInfo).forEach(([key, value]) => this[key] = value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
|
||||
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { TreeItemCollapsibleState } from 'sql/parts/objectExplorer/common/treeNode';
|
||||
|
||||
@extHostNamedCustomer(SqlMainContext.MainThreadObjectExplorer)
|
||||
export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape {
|
||||
@@ -50,7 +51,7 @@ export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape {
|
||||
public $getActiveConnectionNodes(): Thenable<NodeInfoWithConnection[]> {
|
||||
let connectionNodes = this._objectExplorerService.getActiveConnectionNodes();
|
||||
return Promise.resolve(connectionNodes.map(node => {
|
||||
return {connectionId: node.connection.id, nodeInfo: node.toNodeInfo()};
|
||||
return { connectionId: node.connection.id, nodeInfo: node.toNodeInfo() };
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -73,4 +74,8 @@ export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape {
|
||||
public $findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames: string[]): Thenable<sqlops.NodeInfo[]> {
|
||||
return this._objectExplorerService.findNodes(connectionId, type, schema, name, database, parentObjectNames);
|
||||
}
|
||||
|
||||
public $refresh(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo> {
|
||||
return this._objectExplorerService.refreshNodeInView(connectionId, nodePath).then(node => node.toNodeInfo());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ export abstract class ExtHostAccountManagementShape {
|
||||
|
||||
export abstract class ExtHostConnectionManagementShape {
|
||||
$onConnectionOpened(handleId: string, connection: sqlops.connection.Connection): void { throw ni; }
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class ExtHostDataProtocolShape {
|
||||
|
||||
@@ -663,6 +663,7 @@ export interface MainThreadObjectExplorerShape extends IDisposable {
|
||||
$getChildren(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo[]>;
|
||||
$isExpanded(connectionId: string, nodePath: string): Thenable<boolean>;
|
||||
$findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames: string[]): Thenable<sqlops.NodeInfo[]>;
|
||||
$refresh(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo>;
|
||||
}
|
||||
|
||||
export interface ExtHostModelViewDialogShape {
|
||||
|
||||
@@ -301,7 +301,8 @@ suite('SQL Object Explorer Service tests', () => {
|
||||
reveal: element => Promise.resolve() as Thenable<void>,
|
||||
setSelected: (element, selected, clearOtherSelections) => undefined,
|
||||
isExpanded: element => undefined,
|
||||
onSelectionOrFocusChange: Event.None
|
||||
onSelectionOrFocusChange: Event.None,
|
||||
refreshElement: (element) => Promise.resolve() as Thenable<void>
|
||||
} as ServerTreeView);
|
||||
});
|
||||
|
||||
@@ -738,4 +739,22 @@ suite('SQL Object Explorer Service tests', () => {
|
||||
}, err => done(err));
|
||||
});
|
||||
});
|
||||
|
||||
test('refreshInView refreshes the node, expands it, and returns the refreshed node', async () => {
|
||||
// Set up the session and tree view
|
||||
await objectExplorerService.createNewSession('MSSQL', connection);
|
||||
objectExplorerService.onSessionCreated(1, objectExplorerSession);
|
||||
serverTreeView.setup(x => x.refreshElement(TypeMoq.It.isAny())).returns(() => Promise.resolve());
|
||||
objectExplorerService.registerServerTreeView(serverTreeView.object);
|
||||
|
||||
// Refresh the node
|
||||
let nodePath = objectExplorerSession.rootNode.nodePath;
|
||||
let refreshedNode = await objectExplorerService.refreshNodeInView(connection.id, nodePath);
|
||||
|
||||
// Verify that it was refreshed, expanded, and the refreshed detailed were returned
|
||||
sqlOEProvider.verify(x => x.refreshNode(TypeMoq.It.is(refreshNode => refreshNode.nodePath === nodePath)), TypeMoq.Times.once());
|
||||
refreshedNode.children.forEach((childNode, index) => {
|
||||
assert.equal(childNode.nodePath, objectExplorerExpandInfoRefresh.nodes[index].nodePath);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -28,6 +28,7 @@ import { ConnectionManagementService } from 'sql/parts/connection/common/connect
|
||||
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
|
||||
|
||||
import { TestThemeService } from 'sqltest/stubs/themeTestService';
|
||||
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
|
||||
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import * as assert from 'assert';
|
||||
@@ -40,6 +41,7 @@ suite('SQL QueryAction Tests', () => {
|
||||
let editor: TypeMoq.Mock<QueryEditor>;
|
||||
let calledRunQueryOnInput: boolean = undefined;
|
||||
let testQueryInput: TypeMoq.Mock<QueryInput>;
|
||||
let configurationService: TypeMoq.Mock<ConfigurationService>;
|
||||
|
||||
setup(() => {
|
||||
// Setup a reusable mock QueryInput
|
||||
@@ -56,6 +58,13 @@ suite('SQL QueryAction Tests', () => {
|
||||
editor.setup(x => x.getSelection()).returns(() => undefined);
|
||||
editor.setup(x => x.getSelection(false)).returns(() => undefined);
|
||||
editor.setup(x => x.isSelectionEmpty()).returns(() => false);
|
||||
configurationService = TypeMoq.Mock.ofInstance({
|
||||
getValue: () => undefined,
|
||||
onDidChangeConfiguration: () => undefined
|
||||
} as any);
|
||||
configurationService.setup(x => x.getValue(TypeMoq.It.isAny())).returns(() => {
|
||||
return {};
|
||||
});
|
||||
});
|
||||
|
||||
test('setClass sets child CSS class correctly', (done) => {
|
||||
@@ -463,7 +472,7 @@ suite('SQL QueryAction Tests', () => {
|
||||
});
|
||||
|
||||
// If I query without having initialized anything, state should be clear
|
||||
listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, undefined, undefined);
|
||||
listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, undefined, undefined, configurationService.object);
|
||||
|
||||
assert.equal(listItem.isEnabled(), false, 'do not expect dropdown enabled unless connected');
|
||||
assert.equal(listItem.currentDatabaseName, undefined, 'do not expect dropdown to have entries unless connected');
|
||||
@@ -498,7 +507,7 @@ suite('SQL QueryAction Tests', () => {
|
||||
cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName });
|
||||
|
||||
// ... Create a database dropdown that has been connected
|
||||
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null);
|
||||
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
|
||||
listItem.onConnected();
|
||||
|
||||
// If: I raise a connection changed event
|
||||
@@ -522,7 +531,7 @@ suite('SQL QueryAction Tests', () => {
|
||||
cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName });
|
||||
|
||||
// ... Create a database dropdown that has been connected
|
||||
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null);
|
||||
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
|
||||
listItem.onConnected();
|
||||
|
||||
// If: I raise a connection changed event for the 'wrong' URI
|
||||
@@ -549,7 +558,7 @@ suite('SQL QueryAction Tests', () => {
|
||||
cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event);
|
||||
|
||||
// ... Create a database dropdown
|
||||
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null);
|
||||
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
|
||||
|
||||
// If: I raise a connection changed event
|
||||
let eventParams = <IConnectionParams>{
|
||||
|
||||
@@ -93,7 +93,7 @@ suite('SQL QueryEditor Tests', () => {
|
||||
instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((classDef, editor, action) => {
|
||||
if (classDef.ID) {
|
||||
if (classDef.ID === 'listDatabaseQueryActionItem') {
|
||||
return new ListDatabasesActionItem(editor, action, connectionManagementService.object, undefined, undefined, undefined);
|
||||
return new ListDatabasesActionItem(editor, action, connectionManagementService.object, undefined, undefined, undefined, configurationService.object);
|
||||
}
|
||||
}
|
||||
// Default
|
||||
@@ -344,7 +344,7 @@ suite('SQL QueryEditor Tests', () => {
|
||||
queryActionInstantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
|
||||
.returns((definition, editor, action, selectBox) => {
|
||||
if (definition.ID === 'listDatabaseQueryActionItem') {
|
||||
let item = new ListDatabasesActionItem(editor, action, queryConnectionService.object, undefined, undefined, undefined);
|
||||
let item = new ListDatabasesActionItem(editor, action, queryConnectionService.object, undefined, undefined, undefined,configurationService.object);
|
||||
return item;
|
||||
}
|
||||
// Default
|
||||
|
||||
@@ -61,7 +61,7 @@ function main() {
|
||||
'bootstrap': `../${ out }/bootstrap`
|
||||
},
|
||||
catchError: true,
|
||||
// {{SQL CARBON EDIT}}
|
||||
// {{SQL CARBON EDIT}}
|
||||
nodeModules: [
|
||||
'@angular/common',
|
||||
'@angular/core',
|
||||
@@ -70,6 +70,7 @@ function main() {
|
||||
'@angular/platform-browser-dynamic',
|
||||
'@angular/router',
|
||||
'angular2-grid',
|
||||
'ng2-charts/ng2-charts',
|
||||
'rxjs/add/observable/of',
|
||||
'rxjs/Observable',
|
||||
'rxjs/Subject',
|
||||
|
||||
@@ -6111,9 +6111,9 @@ slice-ansi@0.0.4:
|
||||
version "0.0.4"
|
||||
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
|
||||
|
||||
"slickgrid@github:anthonydresser/SlickGrid#2.3.25":
|
||||
version "2.3.25"
|
||||
resolved "https://codeload.github.com/anthonydresser/SlickGrid/tar.gz/5fa498a3df7ed671958bedeac1863c41352c7825"
|
||||
"slickgrid@github:anthonydresser/SlickGrid#2.3.27":
|
||||
version "2.3.27"
|
||||
resolved "https://codeload.github.com/anthonydresser/SlickGrid/tar.gz/712a59307942c8fdb18708b6d1a501880a74db8d"
|
||||
dependencies:
|
||||
jquery ">=1.8.0"
|
||||
jquery-ui ">=1.8.0"
|
||||
|
||||
Reference in New Issue
Block a user