More Layering (#9139)

* move handling generated files to the serilization classes

* remove unneeded methods

* add more folders to strictire compile, add more strict compile options

* update ci

* wip

* add more layering and fix issues

* add more strictness

* remove unnecessary assertion

* add missing checks

* fix indentation

* wip

* remove jsdoc

* fix layering

* fix compile

* fix compile errors

* wip

* wip

* finish layering

* fix css

* more layering

* rip

* reworking results serializer

* move some files around

* move capabilities to platform wip

* implement capabilities register provider

* fix capabilities service

* fix usage of the regist4ry

* add contribution

* wip

* wip

* wip

* remove no longer good parts

* fix strict-nulls

* fix issues with startup

* another try

* fix startup

* fix imports

* fix tests

* fix tests

* fix more tests

* fix tests

* fix more tests

* fix broken test

* fix tabbing

* fix naming

* wip

* finished layering

* fix imports

* fix valid layers

* fix layers
This commit is contained in:
Anthony Dresser
2020-02-15 01:54:23 -06:00
committed by GitHub
parent 873c6a39fe
commit 506c6a5e5f
338 changed files with 815 additions and 724 deletions

View File

@@ -0,0 +1,107 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
export interface IProfilerStateChangedEvent {
isConnected?: boolean;
isRunning?: boolean;
isPaused?: boolean;
isStopped?: boolean;
autoscroll?: boolean;
isPanelCollapsed?: boolean;
}
export interface INewProfilerState {
isConnected?: boolean;
isRunning?: boolean;
isPaused?: boolean;
isStopped?: boolean;
autoscroll?: boolean;
isPanelCollapsed?: boolean;
}
export class ProfilerState implements IDisposable {
private _isConnected?: boolean;
private _isRunning?: boolean;
private _isPaused?: boolean;
private _isStopped?: boolean;
private _autoscroll?: boolean;
private _isPanelCollapsed = true;
public get isConnected(): boolean | undefined { return this._isConnected; }
public get isRunning(): boolean | undefined { return this._isRunning; }
public get isPaused(): boolean | undefined { return this._isPaused; }
public get isStopped(): boolean | undefined { return this._isStopped; }
public get autoscroll(): boolean | undefined { return this._autoscroll; }
public get isPanelCollapsed(): boolean { return this._isPanelCollapsed; }
private readonly _onProfilerStateChange = new Emitter<IProfilerStateChangedEvent>();
public readonly onProfilerStateChange = this._onProfilerStateChange.event;
public dispose(): void {
}
public change(newState: INewProfilerState): void {
let changeEvent: IProfilerStateChangedEvent = {
isConnected: false,
isRunning: false,
isPaused: false,
isStopped: false,
autoscroll: false,
isPanelCollapsed: false
};
let somethingChanged = false;
if (typeof newState.isConnected !== 'undefined') {
if (this._isConnected !== newState.isConnected) {
this._isConnected = newState.isConnected;
changeEvent.isConnected = true;
somethingChanged = true;
}
}
if (typeof newState.isRunning !== 'undefined') {
if (this._isRunning !== newState.isRunning) {
this._isRunning = newState.isRunning;
changeEvent.isRunning = true;
somethingChanged = true;
}
}
if (typeof newState.isPaused !== 'undefined') {
if (this._isPaused !== newState.isPaused) {
this._isPaused = newState.isPaused;
changeEvent.isPaused = true;
somethingChanged = true;
}
}
if (typeof newState.isStopped !== 'undefined') {
if (this._isStopped !== newState.isStopped) {
this._isStopped = newState.isStopped;
changeEvent.isStopped = true;
somethingChanged = true;
}
}
if (typeof newState.autoscroll !== 'undefined') {
if (this._autoscroll !== newState.autoscroll) {
this._autoscroll = newState.autoscroll;
changeEvent.autoscroll = true;
somethingChanged = true;
}
}
if (typeof newState.isPanelCollapsed !== 'undefined') {
if (this._isPanelCollapsed !== newState.isPanelCollapsed) {
this._isPanelCollapsed = newState.isPanelCollapsed;
changeEvent.isPanelCollapsed = true;
somethingChanged = true;
}
}
if (somethingChanged) {
this._onProfilerStateChange.fire(changeEvent);
}
}
}

View File

@@ -0,0 +1,67 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export enum InsightType {
Image = 'image',
Table = 'table',
Count = 'count'
}
export enum ChartType {
Bar = 'bar',
Doughnut = 'doughnut',
HorizontalBar = 'horizontalBar',
Line = 'line',
Pie = 'pie',
TimeSeries = 'timeSeries',
Scatter = 'scatter'
}
export enum LegendPosition {
Top = 'top',
Bottom = 'bottom',
Left = 'left',
Right = 'right',
None = 'none'
}
export enum DataType {
Number = 'number',
Point = 'point'
}
export enum DataDirection {
Vertical = 'vertical',
Horizontal = 'horizontal'
}
export interface IInsightOptions {
type: InsightType | ChartType;
dataDirection?: DataDirection;
dataType?: DataType;
labelFirstColumn?: boolean;
columnsAsLabels?: boolean;
legendPosition?: LegendPosition;
yAxisLabel?: string;
yAxisMin?: number;
yAxisMax?: number;
xAxisLabel?: string;
xAxisMin?: number;
xAxisMax?: number;
encoding?: string;
imageFormat?: string;
}
export class ChartState {
dataId?: {
batchId: number;
resultId: number;
};
options: IInsightOptions = {
type: ChartType.Bar
};
dispose() {
}
}

View File

@@ -0,0 +1,64 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { dispose, Disposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
export class GridPanelState {
public tableStates: GridTableState[] = [];
public scrollPosition?: number;
dispose() {
dispose(this.tableStates);
}
}
export class GridTableState extends Disposable {
private _maximized?: boolean;
private _onMaximizedChange = this._register(new Emitter<boolean>());
public onMaximizedChange: Event<boolean> = this._onMaximizedChange.event;
private _onCanBeMaximizedChange = this._register(new Emitter<boolean>());
public onCanBeMaximizedChange: Event<boolean> = this._onCanBeMaximizedChange.event;
private _canBeMaximized?: boolean;
/* The top row of the current scroll */
public scrollPositionY = 0;
public scrollPositionX = 0;
public columnSizes?: number[] = undefined;
public selection?: Slick.Range[];
public activeCell?: Slick.Cell;
constructor(public readonly resultId: number, public readonly batchId: number) {
super();
}
public get canBeMaximized(): boolean | undefined {
return this._canBeMaximized;
}
public set canBeMaximized(val: boolean | undefined) {
if (!val || val === this._canBeMaximized) {
return;
}
this._canBeMaximized = val;
this._onCanBeMaximizedChange.fire(val);
}
public get maximized(): boolean | undefined {
return this._maximized;
}
public set maximized(val: boolean | undefined) {
if (!val || val === this._maximized) {
return;
}
this._maximized = val;
this._onMaximizedChange.fire(val);
}
}

View File

@@ -0,0 +1,12 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export class MessagePanelState {
public scrollPosition?: number;
dispose() {
}
}

View File

@@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export class QueryModelViewState {
public componentId?: string;
public dispose() {
}
}

View File

@@ -0,0 +1,321 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
import { URI } from 'vs/base/common/uri';
import { EditorInput, GroupIdentifier, IRevertOptions, ISaveOptions, IEditorInput } from 'vs/workbench/common/editor';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IConnectionManagementService, IConnectableInput, INewConnectionParams, RunQueryOnConnectionMode } from 'sql/platform/connection/common/connectionManagement';
import { QueryResultsInput } from 'sql/workbench/common/editor/query/queryResultsInput';
import { IQueryModelService } from 'sql/workbench/services/query/common/queryModel';
import { ISelectionData, ExecutionPlanOptions } from 'azdata';
import { startsWith } from 'vs/base/common/strings';
const MAX_SIZE = 13;
function trimTitle(title: string): string {
const length = title.length;
const diff = length - MAX_SIZE;
if (diff <= 0) {
return title;
} else {
const start = (length / 2) - (diff / 2);
return title.slice(0, start) + '...' + title.slice(start + diff, length);
}
}
export interface IQueryEditorStateChange {
connectedChange?: boolean;
resultsVisibleChange?: boolean;
executingChange?: boolean;
connectingChange?: boolean;
sqlCmdModeChanged?: boolean;
}
export class QueryEditorState extends Disposable {
private _connected = false;
private _isSqlCmdMode = false;
private _resultsVisible = false;
private _executing = false;
private _connecting = false;
private _onChange = this._register(new Emitter<IQueryEditorStateChange>());
public onChange = this._onChange.event;
public set connected(val: boolean) {
if (val !== this._connected) {
this._connected = val;
this._onChange.fire({ connectedChange: true });
}
}
public get connected(): boolean {
return this._connected;
}
public set connecting(val: boolean) {
if (val !== this._connecting) {
this._connecting = val;
this._onChange.fire({ connectingChange: true });
}
}
public get connecting(): boolean {
return this._connecting;
}
public set resultsVisible(val: boolean) {
if (val !== this._resultsVisible) {
this._resultsVisible = val;
this._onChange.fire({ resultsVisibleChange: true });
}
}
public get resultsVisible(): boolean {
return this._resultsVisible;
}
public set executing(val: boolean) {
if (val !== this._executing) {
this._executing = val;
this._onChange.fire({ executingChange: true });
}
}
public get executing(): boolean {
return this._executing;
}
public set isSqlCmdMode(val: boolean) {
if (val !== this._isSqlCmdMode) {
this._isSqlCmdMode = val;
this._onChange.fire({ sqlCmdModeChanged: true });
}
}
public get isSqlCmdMode(): boolean {
return this._isSqlCmdMode;
}
}
/**
* Input for the QueryEditor. This input is simply a wrapper around a QueryResultsInput for the QueryResultsEditor
* and a UntitledEditorInput for the SQL File Editor.
*/
export abstract class QueryEditorInput extends EditorInput implements IConnectableInput, IDisposable {
public static SCHEMA: string = 'sql';
private _state = this._register(new QueryEditorState());
public get state(): QueryEditorState { return this._state; }
constructor(
private _description: string,
protected _text: EditorInput,
protected _results: QueryResultsInput,
@IConnectionManagementService private readonly connectionManagementService: IConnectionManagementService,
@IQueryModelService private readonly queryModelService: IQueryModelService,
@IConfigurationService private readonly configurationService: IConfigurationService
) {
super();
this._register(this._text);
this._register(this._results);
this._text.onDidChangeDirty(() => this._onDidChangeDirty.fire());
this._register(
this.queryModelService.onRunQueryStart(uri => {
if (this.uri === uri) {
this.onRunQuery();
}
})
);
this._register(
this.queryModelService.onRunQueryComplete(uri => {
if (this.uri === uri) {
this.onQueryComplete();
}
})
);
this._register(this.connectionManagementService.onDisconnect(result => {
if (result.connectionUri === this.uri) {
this.onDisconnect();
}
}));
this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectedKeys.indexOf('sql.showConnectionInfoInTitle') > -1) {
this._onDidChangeLabel.fire();
}
}));
this.connectionManagementService.ensureDefaultLanguageFlavor(this.uri);
this.onDisconnect();
this.onQueryComplete();
}
// Getters for private properties
public get uri(): string { return this.getResource()!.toString(true); }
public get text(): EditorInput { return this._text; }
public get results(): QueryResultsInput { return this._results; }
// Description is shown beside the tab name in the combobox of open editors
public getDescription(): string { return this._description; }
public supportsSplitEditor(): boolean { return false; }
public revert(group: GroupIdentifier, options?: IRevertOptions): Promise<boolean> {
return this._text.revert(group, options);
}
public isReadonly(): boolean {
return false;
}
public matches(otherInput: any): boolean {
// we want to be able to match against our underlying input as well, bascially we are our underlying input
if (otherInput instanceof QueryEditorInput) {
return this._text.matches(otherInput._text);
} else {
return this._text.matches(otherInput);
}
}
// Forwarding resource functions to the inline sql file editor
public isDirty(): boolean { return this._text.isDirty(); }
public getResource(): URI | undefined { return this._text.getResource(); }
public matchInputInstanceType(inputType: any): boolean {
return (this._text instanceof inputType);
}
public getName(longForm?: boolean): string {
if (this.configurationService.getValue('sql.showConnectionInfoInTitle')) {
let profile = this.connectionManagementService.getConnectionProfile(this.uri);
let title = '';
if (this._description && this._description !== '') {
title = this._description + ' ';
}
if (profile) {
if (profile.userName) {
title += `${profile.serverName}.${profile.databaseName} (${profile.userName})`;
} else {
title += `${profile.serverName}.${profile.databaseName} (${profile.authenticationType})`;
}
} else {
title += localize('disconnected', "disconnected");
}
return this._text.getName() + (longForm ? (' - ' + title) : ` - ${trimTitle(title)}`);
} else {
return this._text.getName();
}
}
save(group: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> {
return this.text.save(group, options);
}
saveAs(group: GroupIdentifier, options?: ISaveOptions): Promise<IEditorInput | undefined> {
return this.text.saveAs(group, options);
}
// Called to get the tooltip of the tab
public getTitle(): string {
return this.getName(true);
}
// State update funtions
public runQuery(selection?: ISelectionData, executePlanOptions?: ExecutionPlanOptions): void {
this.queryModelService.runQuery(this.uri, selection, executePlanOptions);
this.state.executing = true;
}
public runQueryStatement(selection?: ISelectionData): void {
this.queryModelService.runQueryStatement(this.uri, selection);
this.state.executing = true;
}
public runQueryString(text: string): void {
this.queryModelService.runQueryString(this.uri, text);
this.state.executing = true;
}
public onConnectStart(): void {
this.state.connecting = true;
this.state.connected = false;
}
public onConnectReject(): void {
this.state.connecting = false;
this.state.connected = false;
}
public onConnectCanceled(): void {
// If we're currently connecting and then cancel, set connected state to false
// Otherwise, keep connected state as it was
if (this.state.connecting) {
this.state.connected = false;
}
this.state.connecting = false;
}
public onConnectSuccess(params?: INewConnectionParams): void {
this.state.connected = true;
this.state.connecting = false;
let isRunningQuery = this.queryModelService.isRunningQuery(this.uri);
if (!isRunningQuery && params && params.runQueryOnCompletion) {
let selection: ISelectionData | undefined = params ? params.querySelection : undefined;
if (params.runQueryOnCompletion === RunQueryOnConnectionMode.executeCurrentQuery) {
this.runQueryStatement(selection);
} else if (params.runQueryOnCompletion === RunQueryOnConnectionMode.executeQuery) {
this.runQuery(selection);
} else if (params.runQueryOnCompletion === RunQueryOnConnectionMode.estimatedQueryPlan) {
this.runQuery(selection, { displayEstimatedQueryPlan: true });
} else if (params.runQueryOnCompletion === RunQueryOnConnectionMode.actualQueryPlan) {
this.runQuery(selection, { displayActualQueryPlan: true });
}
}
this._onDidChangeLabel.fire();
}
public onDisconnect(): void {
this.state.connected = false;
this._onDidChangeLabel.fire();
}
public onRunQuery(): void {
this.state.executing = true;
this.state.resultsVisible = true;
}
public onQueryComplete(): void {
this.state.executing = false;
}
/**
* Get the color that should be displayed
*/
public get tabColor(): string {
return this.connectionManagementService.getTabColorForUri(this.uri);
}
public dispose() {
this.queryModelService.disposeQuery(this.uri);
this.connectionManagementService.disconnectEditor(this, true);
super.dispose();
}
public get isSharedSession(): boolean {
return !!(this.uri && startsWith(this.uri, 'vsls:'));
}
}

View File

@@ -0,0 +1,11 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export class QueryPlanState {
xml?: string;
dispose() {
}
}

View File

@@ -0,0 +1,92 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from 'vs/nls';
import { EditorInput } from 'vs/workbench/common/editor';
import { TopOperationsState } from 'sql/workbench/common/editor/query/topOperationsState';
import { ChartState } from 'sql/workbench/common/editor/query/chartState';
import { QueryPlanState } from 'sql/workbench/common/editor/query/queryPlanState';
import { MessagePanelState } from 'sql/workbench/common/editor/query/messagePanelState';
import { GridPanelState } from 'sql/workbench/common/editor/query/gridPanelState';
import { QueryModelViewState } from 'sql/workbench/common/editor/query/modelViewState';
export class ResultsViewState {
public readonly gridPanelState: GridPanelState = new GridPanelState();
public readonly messagePanelState: MessagePanelState = new MessagePanelState();
public readonly chartState: ChartState = new ChartState();
public readonly queryPlanState: QueryPlanState = new QueryPlanState();
public readonly topOperationsState = new TopOperationsState();
public readonly dynamicModelViewTabsState: Map<string, QueryModelViewState> = new Map<string, QueryModelViewState>();
public activeTab?: string;
public readonly visibleTabs: Set<string> = new Set<string>();
dispose() {
this.gridPanelState.dispose();
this.messagePanelState.dispose();
this.chartState.dispose();
this.queryPlanState.dispose();
this.dynamicModelViewTabsState.forEach((state: QueryModelViewState, identifier: string) => {
state.dispose();
});
this.dynamicModelViewTabsState.clear();
}
}
/**
* Input for the QueryResultsEditor. This input helps with logic for the viewing and editing of
* data in the results grid.
*/
export class QueryResultsInput extends EditorInput {
private _state?= new ResultsViewState();
public get state(): ResultsViewState | undefined {
return this._state;
}
constructor(private _uri: string) {
super();
}
getTypeId(): string {
return QueryResultsInput.ID;
}
getName(): string {
return localize('extensionsInputName', "Extension");
}
matches(other: any): boolean {
if (other instanceof QueryResultsInput) {
return (other._uri === this._uri);
}
return false;
}
resolve(refresh?: boolean): Promise<any> {
return Promise.resolve(null);
}
supportsSplitEditor(): boolean {
return false;
}
public dispose(): void {
super.dispose();
}
//// Properties
static get ID() {
return 'workbench.query.queryResultsInput';
}
get uri(): string {
return this._uri;
}
}

View File

@@ -0,0 +1,11 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export class TopOperationsState {
xml?: string;
dispose() {
}
}

View File

@@ -0,0 +1,68 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { QueryEditorInput } from 'sql/workbench/common/editor/query/queryEditorInput';
import { QueryResultsInput } from 'sql/workbench/common/editor/query/queryResultsInput';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { IQueryModelService } from 'sql/workbench/services/query/common/queryModel';
import { IEncodingSupport, EncodingMode } from 'vs/workbench/common/editor';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput';
import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel';
export class UntitledQueryEditorInput extends QueryEditorInput implements IEncodingSupport {
public static readonly ID = 'workbench.editorInput.untitledQueryInput';
constructor(
description: string,
text: UntitledTextEditorInput,
results: QueryResultsInput,
@IConnectionManagementService connectionManagementService: IConnectionManagementService,
@IQueryModelService queryModelService: IQueryModelService,
@IConfigurationService configurationService: IConfigurationService
) {
super(description, text, results, connectionManagementService, queryModelService, configurationService);
}
public resolve(): Promise<IUntitledTextEditorModel & IResolvedTextEditorModel> {
return this.text.resolve();
}
public get text(): UntitledTextEditorInput {
return this._text as UntitledTextEditorInput;
}
public get hasAssociatedFilePath(): boolean {
return this.text.model.hasAssociatedFilePath;
}
public setMode(mode: string): void {
this.text.setMode(mode);
}
public getMode(): string | undefined {
return this.text.getMode();
}
public getTypeId(): string {
return UntitledQueryEditorInput.ID;
}
public getEncoding(): string | undefined {
return this.text.getEncoding();
}
public setEncoding(encoding: string, mode: EncodingMode): void {
this.text.setEncoding(encoding, mode);
}
isUntitled(): boolean {
// Subclasses need to explicitly opt-in to being untitled.
return true;
}
}