mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-02 17:23:40 -05:00
Layering of everything else but query (#5085)
* layer profiler and edit data * relayering everything but query * fix css import * readd qp * fix script src * fix hygiene
This commit is contained in:
268
src/sql/workbench/parts/editData/browser/editDataActions.ts
Normal file
268
src/sql/workbench/parts/editData/browser/editDataActions.ts
Normal file
@@ -0,0 +1,268 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Action, IActionItem, IActionRunner } from 'vs/base/common/actions';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
|
||||
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
|
||||
import { EventEmitter } from 'sql/base/common/eventEmitter';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { EditDataEditor } from 'sql/workbench/parts/editData/browser/editDataEditor';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
const $ = dom.$;
|
||||
|
||||
/**
|
||||
* Action class that edit data based actions will extend
|
||||
*/
|
||||
export abstract class EditDataAction extends Action {
|
||||
|
||||
private _classes: string[];
|
||||
|
||||
constructor(protected editor: EditDataEditor, id: string, enabledClass: string,
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService) {
|
||||
super(id);
|
||||
this.enabled = true;
|
||||
this.setClass(enabledClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is executed when the button is clicked.
|
||||
*/
|
||||
public abstract run(): Promise<void>;
|
||||
|
||||
protected setClass(enabledClass: string): void {
|
||||
this._classes = [];
|
||||
|
||||
if (enabledClass) {
|
||||
this._classes.push(enabledClass);
|
||||
}
|
||||
this.class = this._classes.join(' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the URI of the given editor if it is not undefined and is connected.
|
||||
*/
|
||||
public isConnected(editor: EditDataEditor): boolean {
|
||||
if (!editor || !editor.uri) {
|
||||
return false;
|
||||
}
|
||||
return this._connectionManagementService.isConnected(editor.uri);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action class that refreshes the table for an edit data session
|
||||
*/
|
||||
export class RefreshTableAction extends EditDataAction {
|
||||
private static EnabledClass = 'start';
|
||||
public static ID = 'refreshTableAction';
|
||||
|
||||
constructor(editor: EditDataEditor,
|
||||
@IQueryModelService private _queryModelService: IQueryModelService,
|
||||
@IConnectionManagementService _connectionManagementService: IConnectionManagementService,
|
||||
@INotificationService private _notificationService: INotificationService,
|
||||
) {
|
||||
super(editor, RefreshTableAction.ID, RefreshTableAction.EnabledClass, _connectionManagementService);
|
||||
this.label = nls.localize('editData.run', 'Run');
|
||||
}
|
||||
|
||||
public run(): Promise<void> {
|
||||
if (this.isConnected(this.editor)) {
|
||||
let input = this.editor.editDataInput;
|
||||
|
||||
let rowLimit: number = undefined;
|
||||
let queryString: string = undefined;
|
||||
if (input.queryPaneEnabled) {
|
||||
queryString = input.queryString = this.editor.getEditorText();
|
||||
} else {
|
||||
rowLimit = input.rowLimit;
|
||||
}
|
||||
|
||||
this._queryModelService.disposeEdit(input.uri).then((result) => {
|
||||
this._queryModelService.initializeEdit(input.uri, input.schemaName, input.tableName, input.objectType, rowLimit, queryString);
|
||||
input.showResultsEditor();
|
||||
}, error => {
|
||||
this._notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: nls.localize('disposeEditFailure', 'Dispose Edit Failed With Error: ') + error
|
||||
});
|
||||
});
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action class that cancels the refresh data trigger in an edit data session
|
||||
*/
|
||||
export class StopRefreshTableAction extends EditDataAction {
|
||||
|
||||
private static EnabledClass = 'stop';
|
||||
public static ID = 'stopRefreshAction';
|
||||
|
||||
constructor(editor: EditDataEditor,
|
||||
@IQueryModelService private _queryModelService: IQueryModelService,
|
||||
@IConnectionManagementService _connectionManagementService: IConnectionManagementService
|
||||
) {
|
||||
super(editor, StopRefreshTableAction.ID, StopRefreshTableAction.EnabledClass, _connectionManagementService);
|
||||
this.enabled = false;
|
||||
this.label = nls.localize('editData.stop', 'Stop');
|
||||
}
|
||||
|
||||
public run(): Promise<void> {
|
||||
let input = this.editor.editDataInput;
|
||||
this._queryModelService.disposeEdit(input.uri);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action class that is tied with ChangeMaxRowsActionItem
|
||||
*/
|
||||
export class ChangeMaxRowsAction extends EditDataAction {
|
||||
|
||||
private static EnabledClass = '';
|
||||
public static ID = 'changeMaxRowsAction';
|
||||
|
||||
constructor(editor: EditDataEditor,
|
||||
@IQueryModelService private _queryModelService: IQueryModelService,
|
||||
@IConnectionManagementService _connectionManagementService: IConnectionManagementService
|
||||
) {
|
||||
super(editor, ChangeMaxRowsAction.ID, undefined, _connectionManagementService);
|
||||
this.enabled = false;
|
||||
this.class = ChangeMaxRowsAction.EnabledClass;
|
||||
}
|
||||
|
||||
public run(): Promise<void> {
|
||||
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Action item that handles the dropdown (combobox) that lists the avaliable number of row selections
|
||||
* for an edit data session
|
||||
*/
|
||||
export class ChangeMaxRowsActionItem extends EventEmitter implements IActionItem {
|
||||
|
||||
public actionRunner: IActionRunner;
|
||||
public defaultRowCount: number;
|
||||
private container: HTMLElement;
|
||||
private start: HTMLElement;
|
||||
private selectBox: SelectBox;
|
||||
private toDispose: IDisposable[];
|
||||
private context: any;
|
||||
private _options: string[];
|
||||
private _currentOptionsIndex: number;
|
||||
|
||||
constructor(
|
||||
private _editor: EditDataEditor,
|
||||
@IContextViewService contextViewService: IContextViewService,
|
||||
@IThemeService private _themeService: IThemeService) {
|
||||
super();
|
||||
this._options = ['200', '1000', '10000'];
|
||||
this._currentOptionsIndex = 0;
|
||||
this.toDispose = [];
|
||||
this.selectBox = new SelectBox(this._options, this._options[this._currentOptionsIndex], contextViewService);
|
||||
this._registerListeners();
|
||||
this._refreshOptions();
|
||||
this.defaultRowCount = Number(this._options[this._currentOptionsIndex]);
|
||||
|
||||
this.toDispose.push(attachSelectBoxStyler(this.selectBox, _themeService));
|
||||
}
|
||||
|
||||
public render(container: HTMLElement): void {
|
||||
this.container = container;
|
||||
this.selectBox.render(dom.append(container, $('.configuration.listDatabasesSelectBox')));
|
||||
}
|
||||
|
||||
public setActionContext(context: any): void {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public isEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
public enable(): void {
|
||||
this.selectBox.enable();
|
||||
}
|
||||
|
||||
public disable(): void {
|
||||
this.selectBox.disable();
|
||||
}
|
||||
|
||||
public set setCurrentOptionIndex(selection: number) {
|
||||
this._currentOptionsIndex = this._options.findIndex(x => x === selection.toString());
|
||||
this._refreshOptions();
|
||||
}
|
||||
|
||||
public focus(): void {
|
||||
this.start.focus();
|
||||
}
|
||||
|
||||
public blur(): void {
|
||||
this.container.blur();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this.toDispose = dispose(this.toDispose);
|
||||
}
|
||||
|
||||
private _refreshOptions(databaseIndex?: number): void {
|
||||
this.selectBox.setOptions(this._options, this._currentOptionsIndex);
|
||||
}
|
||||
|
||||
private _registerListeners(): void {
|
||||
this.toDispose.push(this.selectBox.onDidSelect(selection => {
|
||||
this._currentOptionsIndex = this._options.findIndex(x => x === selection.selected);
|
||||
this._editor.editDataInput.onRowDropDownSet(Number(selection.selected));
|
||||
}));
|
||||
this.toDispose.push(attachSelectBoxStyler(this.selectBox, this._themeService));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Action class that is tied with toggling the Query editor
|
||||
*/
|
||||
export class ShowQueryPaneAction extends EditDataAction {
|
||||
|
||||
private static EnabledClass = 'filterLabel';
|
||||
public static ID = 'showQueryPaneAction';
|
||||
private readonly showSqlLabel = nls.localize('editData.showSql', 'Show SQL Pane');
|
||||
private readonly closeSqlLabel = nls.localize('editData.closeSql', 'Close SQL Pane');
|
||||
|
||||
constructor(editor: EditDataEditor,
|
||||
@IQueryModelService private _queryModelService: IQueryModelService,
|
||||
@IConnectionManagementService _connectionManagementService: IConnectionManagementService
|
||||
) {
|
||||
super(editor, ShowQueryPaneAction.ID, ShowQueryPaneAction.EnabledClass, _connectionManagementService);
|
||||
this.label = this.showSqlLabel;
|
||||
}
|
||||
|
||||
public set queryPaneEnabled(value: boolean) {
|
||||
this.updateLabel(value);
|
||||
}
|
||||
|
||||
private updateLabel(queryPaneEnabled: boolean): void {
|
||||
if (queryPaneEnabled) {
|
||||
this.label = this.closeSqlLabel;
|
||||
} else {
|
||||
this.label = this.showSqlLabel;
|
||||
}
|
||||
}
|
||||
|
||||
public run(): Promise<void> {
|
||||
this.editor.toggleQueryPane();
|
||||
this.updateLabel(this.editor.queryPaneEnabled());
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
}
|
||||
737
src/sql/workbench/parts/editData/browser/editDataEditor.ts
Normal file
737
src/sql/workbench/parts/editData/browser/editDataEditor.ts
Normal file
@@ -0,0 +1,737 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!sql/parts/query/editor/media/queryEditor';
|
||||
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import * as nls from 'vs/nls';
|
||||
|
||||
import { EditorOptions, EditorInput, IEditorControl, IEditor } from 'vs/workbench/common/editor';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
import { EditDataInput } from 'sql/workbench/parts/editData/common/editDataInput';
|
||||
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import * as queryContext from 'sql/parts/query/common/queryContext';
|
||||
import { Taskbar, ITaskbarContent } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
|
||||
import { IEditorDescriptorService } from 'sql/workbench/services/queryEditor/common/editorDescriptorService';
|
||||
import {
|
||||
RefreshTableAction, StopRefreshTableAction, ChangeMaxRowsAction, ChangeMaxRowsActionItem, ShowQueryPaneAction
|
||||
} from 'sql/workbench/parts/editData/browser/editDataActions';
|
||||
import { TextResourceEditor } from 'vs/workbench/browser/parts/editor/textResourceEditor';
|
||||
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
|
||||
import { IFlexibleSash, HorizontalFlexibleSash } from 'sql/parts/query/views/flexibleSash';
|
||||
import { EditDataResultsEditor } from 'sql/workbench/parts/editData/browser/editDataResultsEditor';
|
||||
import { EditDataResultsInput } from 'sql/workbench/parts/editData/common/editDataResultsInput';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
|
||||
/**
|
||||
* Editor that hosts an action bar and a resultSetInput for an edit data session
|
||||
*/
|
||||
export class EditDataEditor extends BaseEditor {
|
||||
|
||||
public static ID: string = 'workbench.editor.editDataEditor';
|
||||
|
||||
// The minimum width/height of the editors hosted in the QueryEditor
|
||||
private readonly _minEditorSize: number = 220;
|
||||
|
||||
private _sash: IFlexibleSash;
|
||||
private _dimension: DOM.Dimension;
|
||||
|
||||
private _resultsEditor: EditDataResultsEditor;
|
||||
private _resultsEditorContainer: HTMLElement;
|
||||
|
||||
private _sqlEditor: TextResourceEditor;
|
||||
private _sqlEditorContainer: HTMLElement;
|
||||
|
||||
private _taskbar: Taskbar;
|
||||
private _taskbarContainer: HTMLElement;
|
||||
private _changeMaxRowsActionItem: ChangeMaxRowsActionItem;
|
||||
private _stopRefreshTableAction: StopRefreshTableAction;
|
||||
private _refreshTableAction: RefreshTableAction;
|
||||
private _changeMaxRowsAction: ChangeMaxRowsAction;
|
||||
private _showQueryPaneAction: ShowQueryPaneAction;
|
||||
private _spinnerElement: HTMLElement;
|
||||
private _initialized: boolean = false;
|
||||
|
||||
private _queryEditorVisible: IContextKey<boolean>;
|
||||
private hideQueryResultsView = false;
|
||||
|
||||
constructor(
|
||||
@ITelemetryService _telemetryService: ITelemetryService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IEditorService private _editorService: IEditorService,
|
||||
@IQueryModelService private _queryModelService: IQueryModelService,
|
||||
@IEditorDescriptorService private _editorDescriptorService: IEditorDescriptorService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(EditDataEditor.ID, _telemetryService, themeService, storageService);
|
||||
|
||||
if (contextKeyService) {
|
||||
this._queryEditorVisible = queryContext.QueryEditorVisibleContext.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
if (_editorService) {
|
||||
_editorService.overrideOpenEditor((editor, options, group) => {
|
||||
if (this.isVisible() && (editor !== this.input || group !== this.group)) {
|
||||
this.saveEditorViewState();
|
||||
}
|
||||
return {};
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// PUBLIC METHODS ////////////////////////////////////////////////////////////
|
||||
|
||||
// Getters and Setters
|
||||
public get editDataInput(): EditDataInput { return <EditDataInput>this.input; }
|
||||
public get tableName(): string { return this.editDataInput.tableName; }
|
||||
public get uri(): string { return this.input ? this.editDataInput.uri.toString() : undefined; }
|
||||
public set resultsEditorVisibility(isVisible: boolean) {
|
||||
let input: EditDataInput = <EditDataInput>this.input;
|
||||
input.results.visible = isVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to indicate to the editor that the input should be cleared and resources associated with the
|
||||
* input should be freed.
|
||||
*/
|
||||
public clearInput(): void {
|
||||
if (this._resultsEditor) {
|
||||
this._resultsEditor.clearInput();
|
||||
}
|
||||
if (this._sqlEditor) {
|
||||
this._sqlEditor.clearInput();
|
||||
}
|
||||
this._disposeEditors();
|
||||
super.clearInput();
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
this.editDataInput.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to create the editor in the parent element.
|
||||
*/
|
||||
public createEditor(parent: HTMLElement): void {
|
||||
const parentElement = parent;
|
||||
DOM.addClass(parentElement, 'side-by-side-editor');
|
||||
this._createTaskbar(parentElement);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._disposeEditors();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets focus on this editor. Specifically, it sets the focus on the hosted text editor.
|
||||
*/
|
||||
public focus(): void {
|
||||
if (this._sqlEditor) {
|
||||
this._sqlEditor.focus();
|
||||
}
|
||||
}
|
||||
|
||||
public getControl(): IEditorControl {
|
||||
if (this._sqlEditor) {
|
||||
return this._sqlEditor.getControl();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public getEditorText(): string {
|
||||
if (this._sqlEditor && this._sqlEditor.getControl()) {
|
||||
let control = this._sqlEditor.getControl();
|
||||
let codeEditor: ICodeEditor = <ICodeEditor>control;
|
||||
|
||||
if (codeEditor) {
|
||||
let value = codeEditor.getModel().getValue();
|
||||
if (value !== undefined && value.length > 0) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the spinner element to show that something was happening, hidden by default
|
||||
*/
|
||||
public hideSpinner(): void {
|
||||
this._spinnerElement.style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the internal variable keeping track of the editor's size, and re-calculates the sash position.
|
||||
* To be called when the container of this editor changes size.
|
||||
*/
|
||||
public layout(dimension: DOM.Dimension): void {
|
||||
this._dimension = dimension;
|
||||
|
||||
if (this._sash) {
|
||||
this._setSashDimension();
|
||||
this._sash.layout();
|
||||
}
|
||||
|
||||
this._doLayout();
|
||||
this._resizeGridContents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets this editor and the sub-editors to visible.
|
||||
*/
|
||||
public setEditorVisible(visible: boolean, group: IEditorGroup): void {
|
||||
if (this._resultsEditor) {
|
||||
this._resultsEditor.setVisible(visible, group);
|
||||
}
|
||||
if (this._sqlEditor) {
|
||||
this._sqlEditor.setVisible(visible, group);
|
||||
}
|
||||
|
||||
super.setEditorVisible(visible, group);
|
||||
|
||||
// Note: must update after calling super.setEditorVisible so that the accurate count is handled
|
||||
this._updateQueryEditorVisible(visible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the input data for this editor.
|
||||
*/
|
||||
public setInput(newInput: EditDataInput, options?: EditorOptions): Promise<void> {
|
||||
let oldInput = <EditDataInput>this.input;
|
||||
if (!newInput.setup) {
|
||||
this._initialized = false;
|
||||
this._register(newInput.updateTaskbarEvent((owner) => this._updateTaskbar(owner)));
|
||||
this._register(newInput.editorInitializingEvent((initializing) => this._onEditorInitializingChanged(initializing)));
|
||||
this._register(newInput.showResultsEditorEvent(() => this._showResultsEditor()));
|
||||
newInput.onRowDropDownSet(this._changeMaxRowsActionItem.defaultRowCount);
|
||||
newInput.setupComplete();
|
||||
}
|
||||
|
||||
return super.setInput(newInput, options, CancellationToken.None)
|
||||
.then(() => this._updateInput(oldInput, newInput, options));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the spinner element that shows something is happening, hidden by default
|
||||
*/
|
||||
public showSpinner(): void {
|
||||
setTimeout(() => {
|
||||
if (!this._initialized) {
|
||||
this._spinnerElement.style.visibility = 'visible';
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
|
||||
public toggleResultsEditorVisibility(): void {
|
||||
let input = <EditDataInput>this.input;
|
||||
let hideResults = this.hideQueryResultsView;
|
||||
this.hideQueryResultsView = !this.hideQueryResultsView;
|
||||
if (!input.results) {
|
||||
return;
|
||||
}
|
||||
this.resultsEditorVisibility = hideResults;
|
||||
this._doLayout();
|
||||
}
|
||||
|
||||
// PRIVATE METHODS ////////////////////////////////////////////////////////////
|
||||
private _createEditor(editorInput: EditorInput, container: HTMLElement): Promise<BaseEditor> {
|
||||
const descriptor = this._editorDescriptorService.getEditor(editorInput);
|
||||
if (!descriptor) {
|
||||
return Promise.reject(new Error(strings.format('Can not find a registered editor for the input {0}', editorInput)));
|
||||
}
|
||||
|
||||
let editor = descriptor.instantiate(this._instantiationService);
|
||||
editor.create(container);
|
||||
editor.setVisible(this.isVisible(), editor.group);
|
||||
return Promise.resolve(editor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the HTML for the EditDataResultsEditor to the EditDataEditor. If the HTML has not yet been
|
||||
* created, it creates it and appends it. If it has already been created, it locates it and
|
||||
* appends it.
|
||||
*/
|
||||
private _createResultsEditorContainer() {
|
||||
this._createSash();
|
||||
|
||||
const parentElement = this.getContainer();
|
||||
let input = <EditDataInput>this.input;
|
||||
|
||||
if (!input.results.container) {
|
||||
this._resultsEditorContainer = DOM.append(parentElement, DOM.$('.editDataContainer-horizontal'));
|
||||
this._resultsEditorContainer.style.position = 'absolute';
|
||||
|
||||
input.results.container = this._resultsEditorContainer;
|
||||
} else {
|
||||
this._resultsEditorContainer = DOM.append(parentElement, input.results.container);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the sash with the requested orientation and registers sash callbacks
|
||||
*/
|
||||
private _createSash(): void {
|
||||
if (!this._sash) {
|
||||
let parentElement: HTMLElement = this.getContainer();
|
||||
|
||||
this._sash = this._register(new HorizontalFlexibleSash(parentElement, this._minEditorSize));
|
||||
this._setSashDimension();
|
||||
|
||||
this._register(this._sash.onPositionChange(position => this._doLayout()));
|
||||
}
|
||||
|
||||
this._sash.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends the HTML for the SQL editor. Creates new HTML every time.
|
||||
*/
|
||||
private _createSqlEditorContainer() {
|
||||
const parentElement = this.getContainer();
|
||||
this._sqlEditorContainer = DOM.append(parentElement, DOM.$('.details-editor-container'));
|
||||
this._sqlEditorContainer.style.position = 'absolute';
|
||||
}
|
||||
|
||||
private _createTaskbar(parentElement: HTMLElement): void {
|
||||
// Create QueryTaskbar
|
||||
this._taskbarContainer = DOM.append(parentElement, DOM.$('div'));
|
||||
this._taskbar = new Taskbar(this._taskbarContainer, {
|
||||
actionItemProvider: (action: Action) => this._getChangeMaxRowsAction(action)
|
||||
});
|
||||
|
||||
// Create Actions for the toolbar
|
||||
this._refreshTableAction = this._instantiationService.createInstance(RefreshTableAction, this);
|
||||
this._stopRefreshTableAction = this._instantiationService.createInstance(StopRefreshTableAction, this);
|
||||
this._changeMaxRowsAction = this._instantiationService.createInstance(ChangeMaxRowsAction, this);
|
||||
this._showQueryPaneAction = this._instantiationService.createInstance(ShowQueryPaneAction, this);
|
||||
|
||||
// Create HTML Elements for the taskbar
|
||||
let separator = Taskbar.createTaskbarSeparator();
|
||||
let textSeparator = Taskbar.createTaskbarText(nls.localize('maxRowTaskbar', 'Max Rows:'));
|
||||
|
||||
this._spinnerElement = Taskbar.createTaskbarSpinner();
|
||||
|
||||
// Set the content in the order we desire
|
||||
let content: ITaskbarContent[] = [
|
||||
{ action: this._refreshTableAction },
|
||||
{ action: this._stopRefreshTableAction },
|
||||
{ element: separator },
|
||||
{ element: textSeparator },
|
||||
{ action: this._changeMaxRowsAction },
|
||||
{ action: this._showQueryPaneAction },
|
||||
{ element: this._spinnerElement }
|
||||
];
|
||||
this._taskbar.setContent(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the IActionItem for the list of row number drop down
|
||||
*/
|
||||
private _getChangeMaxRowsAction(action: Action): IActionItem {
|
||||
let actionID = ChangeMaxRowsAction.ID;
|
||||
if (action.id === actionID) {
|
||||
if (!this._changeMaxRowsActionItem) {
|
||||
this._changeMaxRowsActionItem = this._instantiationService.createInstance(ChangeMaxRowsActionItem, this);
|
||||
}
|
||||
return this._changeMaxRowsActionItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private _disposeEditors(): void {
|
||||
if (this._sqlEditor) {
|
||||
this._sqlEditor.dispose();
|
||||
this._sqlEditor = null;
|
||||
}
|
||||
if (this._resultsEditor) {
|
||||
this._resultsEditor.dispose();
|
||||
this._resultsEditor = null;
|
||||
}
|
||||
|
||||
let thisEditorParent: HTMLElement = this.getContainer();
|
||||
|
||||
if (this._sqlEditorContainer) {
|
||||
let sqlEditorParent: HTMLElement = this._sqlEditorContainer.parentElement;
|
||||
if (sqlEditorParent && sqlEditorParent === thisEditorParent) {
|
||||
this._sqlEditorContainer.parentElement.removeChild(this._sqlEditorContainer);
|
||||
}
|
||||
this._sqlEditorContainer = null;
|
||||
}
|
||||
|
||||
if (this._resultsEditorContainer) {
|
||||
let resultsEditorParent: HTMLElement = this._resultsEditorContainer.parentElement;
|
||||
if (resultsEditorParent && resultsEditorParent === thisEditorParent) {
|
||||
this._resultsEditorContainer.parentElement.removeChild(this._resultsEditorContainer);
|
||||
}
|
||||
this._resultsEditorContainer = null;
|
||||
this.hideQueryResultsView = false;
|
||||
}
|
||||
}
|
||||
|
||||
private _doLayout(skipResizeGridContent: boolean = false): void {
|
||||
if (!this._isResultsEditorVisible() && this._sqlEditor) {
|
||||
this._doLayoutSql();
|
||||
return;
|
||||
}
|
||||
if (!this._sqlEditor || !this._resultsEditor || !this._dimension || !this._sash) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._doLayoutHorizontal();
|
||||
|
||||
if (!skipResizeGridContent) {
|
||||
this._resizeGridContents();
|
||||
}
|
||||
}
|
||||
|
||||
private _doLayoutHorizontal(): void {
|
||||
let splitPointTop: number = this._sash.getSplitPoint();
|
||||
let parent: ClientRect = this.getContainer().getBoundingClientRect();
|
||||
|
||||
let sqlEditorHeight: number;
|
||||
let sqlEditorTop: number;
|
||||
let resultsEditorHeight: number;
|
||||
let resultsEditorTop: number;
|
||||
|
||||
let editorTopOffset = parent.top + this._getTaskBarHeight();
|
||||
|
||||
this._resultsEditorContainer.hidden = false;
|
||||
|
||||
let titleBar = document.getElementById('workbench.parts.titlebar');
|
||||
if (this.queryPaneEnabled()) {
|
||||
this._sqlEditorContainer.hidden = false;
|
||||
|
||||
sqlEditorTop = editorTopOffset;
|
||||
sqlEditorHeight = splitPointTop - sqlEditorTop;
|
||||
|
||||
resultsEditorTop = splitPointTop;
|
||||
resultsEditorHeight = parent.bottom - resultsEditorTop;
|
||||
|
||||
if (titleBar) {
|
||||
sqlEditorHeight += DOM.getContentHeight(titleBar);
|
||||
}
|
||||
} else {
|
||||
this._sqlEditorContainer.hidden = true;
|
||||
|
||||
sqlEditorTop = editorTopOffset;
|
||||
sqlEditorHeight = 0;
|
||||
|
||||
resultsEditorTop = editorTopOffset;
|
||||
resultsEditorHeight = parent.bottom - resultsEditorTop;
|
||||
|
||||
if (titleBar) {
|
||||
resultsEditorHeight += DOM.getContentHeight(titleBar);
|
||||
}
|
||||
}
|
||||
|
||||
this._sqlEditorContainer.style.height = `${sqlEditorHeight}px`;
|
||||
this._sqlEditorContainer.style.width = `${this._dimension.width}px`;
|
||||
this._sqlEditorContainer.style.top = `${sqlEditorTop}px`;
|
||||
|
||||
this._resultsEditorContainer.style.height = `${resultsEditorHeight}px`;
|
||||
this._resultsEditorContainer.style.width = `${this._dimension.width}px`;
|
||||
this._resultsEditorContainer.style.top = `${resultsEditorTop}px`;
|
||||
|
||||
this._sqlEditor.layout(new DOM.Dimension(this._dimension.width, sqlEditorHeight));
|
||||
this._resultsEditor.layout(new DOM.Dimension(this._dimension.width, resultsEditorHeight));
|
||||
}
|
||||
|
||||
private _doLayoutSql() {
|
||||
if (this._resultsEditorContainer) {
|
||||
this._resultsEditorContainer.style.width = '0px';
|
||||
this._resultsEditorContainer.style.height = '0px';
|
||||
this._resultsEditorContainer.style.left = '0px';
|
||||
this._resultsEditorContainer.hidden = true;
|
||||
}
|
||||
|
||||
if (this._dimension) {
|
||||
let sqlEditorHeight: number;
|
||||
|
||||
if (this.queryPaneEnabled()) {
|
||||
this._sqlEditorContainer.hidden = false;
|
||||
sqlEditorHeight = this._dimension.height - this._getTaskBarHeight();
|
||||
} else {
|
||||
this._sqlEditorContainer.hidden = true;
|
||||
sqlEditorHeight = 0;
|
||||
}
|
||||
|
||||
this._sqlEditorContainer.style.height = `${sqlEditorHeight}px`;
|
||||
this._sqlEditorContainer.style.width = `${this._dimension.width}px`;
|
||||
|
||||
this._sqlEditor.layout(new DOM.Dimension(this._dimension.width, sqlEditorHeight));
|
||||
}
|
||||
}
|
||||
|
||||
private _getTaskBarHeight(): number {
|
||||
let taskBarElement = this._taskbar.getContainer();
|
||||
return DOM.getContentHeight(taskBarElement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the results table for the current edit data session is visible
|
||||
* Public for testing only.
|
||||
*/
|
||||
private _isResultsEditorVisible(): boolean {
|
||||
let input: EditDataInput = <EditDataInput>this.input;
|
||||
|
||||
if (!input) {
|
||||
return false;
|
||||
}
|
||||
return input.results.visible;
|
||||
}
|
||||
|
||||
private _onEditorInitializingChanged(initializing: boolean): void {
|
||||
if (initializing) {
|
||||
this.showSpinner();
|
||||
} else {
|
||||
this._initialized = true;
|
||||
this.hideSpinner();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets input for the results editor after it has been created.
|
||||
*/
|
||||
private _onResultsEditorCreated(resultsEditor: EditDataResultsEditor, resultsInput: EditDataResultsInput, options: EditorOptions): Promise<void> {
|
||||
this._resultsEditor = resultsEditor;
|
||||
return this._resultsEditor.setInput(resultsInput, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets input for the SQL editor after it has been created.
|
||||
*/
|
||||
private _onSqlEditorCreated(sqlEditor: TextResourceEditor, sqlInput: UntitledEditorInput, options: EditorOptions): Thenable<void> {
|
||||
this._sqlEditor = sqlEditor;
|
||||
return this._sqlEditor.setInput(sqlInput, options, CancellationToken.None);
|
||||
}
|
||||
|
||||
private _resizeGridContents(): void {
|
||||
if (this._isResultsEditorVisible()) {
|
||||
let queryInput: EditDataInput = <EditDataInput>this.input;
|
||||
let uri: string = queryInput.uri;
|
||||
if (uri) {
|
||||
this._queryModelService.resizeResultsets(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles setting input and creating editors when this QueryEditor is either:
|
||||
* - Opened for the first time
|
||||
* - Opened with a new EditDataInput
|
||||
*/
|
||||
private _setNewInput(newInput: EditDataInput, options?: EditorOptions): Promise<any> {
|
||||
|
||||
// Promises that will ensure proper ordering of editor creation logic
|
||||
let createEditors: () => Promise<any>;
|
||||
let onEditorsCreated: (result) => Promise<any>;
|
||||
|
||||
// If both editors exist, create joined promises - one for each editor
|
||||
if (this._isResultsEditorVisible()) {
|
||||
createEditors = () => {
|
||||
return Promise.all([
|
||||
this._createEditor(<EditDataResultsInput>newInput.results, this._resultsEditorContainer),
|
||||
this._createEditor(<UntitledEditorInput>newInput.sql, this._sqlEditorContainer)
|
||||
]);
|
||||
};
|
||||
onEditorsCreated = (result: IEditor[]) => {
|
||||
return Promise.all([
|
||||
this._onResultsEditorCreated(<EditDataResultsEditor>result[0], newInput.results, options),
|
||||
this._onSqlEditorCreated(<TextResourceEditor>result[1], newInput.sql, options)
|
||||
]);
|
||||
};
|
||||
|
||||
// If only the sql editor exists, create a promise and wait for the sql editor to be created
|
||||
} else {
|
||||
createEditors = () => {
|
||||
return this._createEditor(<UntitledEditorInput>newInput.sql, this._sqlEditorContainer);
|
||||
};
|
||||
onEditorsCreated = (result: TextResourceEditor) => {
|
||||
return Promise.all([
|
||||
this._onSqlEditorCreated(result, newInput.sql, options)
|
||||
]);
|
||||
};
|
||||
}
|
||||
|
||||
// Create a promise to re render the layout after the editor creation logic
|
||||
let doLayout: () => Promise<any> = () => {
|
||||
this._doLayout();
|
||||
return Promise.resolve(undefined);
|
||||
};
|
||||
|
||||
// Run all three steps synchronously
|
||||
return createEditors()
|
||||
.then(onEditorsCreated)
|
||||
.then(doLayout)
|
||||
.then(() => {
|
||||
if (newInput.results) {
|
||||
newInput.results.onRestoreViewStateEmitter.fire();
|
||||
}
|
||||
if (newInput.savedViewState) {
|
||||
this._sqlEditor.getControl().restoreViewState(newInput.savedViewState);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _setSashDimension(): void {
|
||||
if (!this._dimension) {
|
||||
return;
|
||||
}
|
||||
this._sash.setDimenesion(this._dimension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes visible the results table for the current edit data session
|
||||
*/
|
||||
private _showResultsEditor(): void {
|
||||
if (this._isResultsEditorVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//this._editorGroupService.pinEditor(this.position, this.input);
|
||||
|
||||
let input = <EditDataInput>this.input;
|
||||
this._createResultsEditorContainer();
|
||||
|
||||
this._createEditor(<EditDataResultsInput>input.results, this._resultsEditorContainer)
|
||||
.then(result => {
|
||||
this._onResultsEditorCreated(<EditDataResultsEditor>result, input.results, this.options);
|
||||
this.resultsEditorVisibility = true;
|
||||
this.hideQueryResultsView = false;
|
||||
this._doLayout(true);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles setting input for this editor. If this new input does not match the old input (e.g. a new file
|
||||
* has been opened with the same editor, or we are opening the editor for the first time).
|
||||
*/
|
||||
private _updateInput(oldInput: EditDataInput, newInput: EditDataInput, options?: EditorOptions): Promise<void> {
|
||||
if (this._sqlEditor) {
|
||||
this._sqlEditor.clearInput();
|
||||
}
|
||||
|
||||
if (oldInput) {
|
||||
this._disposeEditors();
|
||||
}
|
||||
|
||||
this._createSqlEditorContainer();
|
||||
if (this._isResultsEditorVisible()) {
|
||||
this._createResultsEditorContainer();
|
||||
|
||||
let uri: string = newInput.uri;
|
||||
if (uri) {
|
||||
this._queryModelService.refreshResultsets(uri);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._sash) {
|
||||
if (this._isResultsEditorVisible()) {
|
||||
this._sash.show();
|
||||
} else {
|
||||
this._sash.hide();
|
||||
}
|
||||
}
|
||||
|
||||
this._updateTaskbar(newInput);
|
||||
return this._setNewInput(newInput, options);
|
||||
}
|
||||
|
||||
private _updateQueryEditorVisible(currentEditorIsVisible: boolean): void {
|
||||
if (this._queryEditorVisible) {
|
||||
let visible = currentEditorIsVisible;
|
||||
if (!currentEditorIsVisible) {
|
||||
// Current editor is closing but still tracked as visible. Check if any other editor is visible
|
||||
const candidates = [...this._editorService.visibleControls].filter(e => {
|
||||
if (e && e.getId) {
|
||||
return e.getId() === EditDataEditor.ID;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
// Note: require 2 or more candidates since current is closing but still
|
||||
// counted as visible
|
||||
visible = candidates.length > 1;
|
||||
}
|
||||
this._queryEditorVisible.set(visible);
|
||||
}
|
||||
}
|
||||
|
||||
private _updateTaskbar(owner: EditDataInput): void {
|
||||
// Update the taskbar if the owner of this call is being presented
|
||||
if (owner.matches(this.editDataInput)) {
|
||||
this._refreshTableAction.enabled = owner.refreshButtonEnabled;
|
||||
this._stopRefreshTableAction.enabled = owner.stopButtonEnabled;
|
||||
this._changeMaxRowsActionItem.setCurrentOptionIndex = owner.rowLimit;
|
||||
this._showQueryPaneAction.queryPaneEnabled = owner.queryPaneEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the run method of this editor's RunQueryAction
|
||||
*/
|
||||
public runQuery(): void {
|
||||
this._refreshTableAction.run();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the run method of this editor's CancelQueryAction
|
||||
*/
|
||||
public cancelQuery(): void {
|
||||
this._stopRefreshTableAction.run();
|
||||
}
|
||||
|
||||
public toggleQueryPane(): void {
|
||||
this.editDataInput.queryPaneEnabled = !this.queryPaneEnabled();
|
||||
if (this.queryPaneEnabled()) {
|
||||
this._showQueryEditor();
|
||||
} else {
|
||||
this._hideQueryEditor();
|
||||
}
|
||||
this._doLayout(false);
|
||||
}
|
||||
|
||||
private _showQueryEditor(): void {
|
||||
this._sqlEditorContainer.hidden = false;
|
||||
this._changeMaxRowsActionItem.disable();
|
||||
}
|
||||
private _hideQueryEditor(): void {
|
||||
this._sqlEditorContainer.hidden = true;
|
||||
this._changeMaxRowsActionItem.enable();
|
||||
}
|
||||
|
||||
public queryPaneEnabled(): boolean {
|
||||
return this.editDataInput.queryPaneEnabled;
|
||||
}
|
||||
|
||||
private saveEditorViewState(): void {
|
||||
let editDataInput = this.input as EditDataInput;
|
||||
if (editDataInput) {
|
||||
if (this._sqlEditor) {
|
||||
editDataInput.savedViewState = this._sqlEditor.getControl().saveViewState();
|
||||
}
|
||||
if (editDataInput.results) {
|
||||
editDataInput.results.onSaveViewStateEmitter.fire();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { getZoomLevel } from 'vs/base/browser/browser';
|
||||
import { Configuration } from 'vs/editor/browser/config/configuration';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import * as types from 'vs/base/common/types';
|
||||
|
||||
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
|
||||
import { bootstrapAngular } from 'sql/platform/bootstrap/node/bootstrapService';
|
||||
import { BareResultsGridInfo } from 'sql/parts/query/editor/queryResultsEditor';
|
||||
import { IEditDataComponentParams } from 'sql/platform/bootstrap/node/bootstrapParams';
|
||||
import { EditDataModule } from 'sql/workbench/parts/grid/views/editData/editData.module';
|
||||
import { EDITDATA_SELECTOR } from 'sql/workbench/parts/grid/views/editData/editData.component';
|
||||
import { EditDataResultsInput } from 'sql/workbench/parts/editData/common/editDataResultsInput';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
|
||||
export class EditDataResultsEditor extends BaseEditor {
|
||||
|
||||
public static ID: string = 'workbench.editor.editDataResultsEditor';
|
||||
public static AngularSelectorString: string = 'slickgrid-container.slickgridContainer';
|
||||
protected _input: EditDataResultsInput;
|
||||
protected _rawOptions: BareResultsGridInfo;
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IQueryModelService private _queryModelService: IQueryModelService,
|
||||
@IConfigurationService private _configurationService: IConfigurationService,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IStorageService storageService: IStorageService
|
||||
) {
|
||||
super(EditDataResultsEditor.ID, telemetryService, themeService, storageService);
|
||||
this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel());
|
||||
this._configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('resultsGrid')) {
|
||||
this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel());
|
||||
this._applySettings();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public get input(): EditDataResultsInput {
|
||||
return this._input;
|
||||
}
|
||||
|
||||
public createEditor(parent: HTMLElement): void {
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public layout(dimension: DOM.Dimension): void {
|
||||
}
|
||||
|
||||
public setInput(input: EditDataResultsInput, options: EditorOptions): Promise<void> {
|
||||
super.setInput(input, options, CancellationToken.None);
|
||||
this._applySettings();
|
||||
if (!input.hasBootstrapped) {
|
||||
this._bootstrapAngular();
|
||||
}
|
||||
return Promise.resolve<void>(null);
|
||||
}
|
||||
|
||||
private _applySettings() {
|
||||
if (this.input && this.input.container) {
|
||||
Configuration.applyFontInfoSlow(this.getContainer(), this._rawOptions);
|
||||
if (!this.input.css) {
|
||||
this.input.css = DOM.createStyleSheet(this.input.container);
|
||||
}
|
||||
let cssRuleText = '';
|
||||
if (types.isNumber(this._rawOptions.cellPadding)) {
|
||||
cssRuleText = this._rawOptions.cellPadding + 'px';
|
||||
} else {
|
||||
cssRuleText = this._rawOptions.cellPadding.join('px ') + 'px;';
|
||||
}
|
||||
let content = `.grid .slick-cell { padding: ${cssRuleText}; }`;
|
||||
this.input.css.innerHTML = content;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the angular components and record for this input that we have done so
|
||||
*/
|
||||
private _bootstrapAngular(): void {
|
||||
let input = <EditDataResultsInput>this.input;
|
||||
let uri = input.uri;
|
||||
|
||||
// Pass the correct DataService to the new angular component
|
||||
let dataService = this._queryModelService.getDataService(uri);
|
||||
if (!dataService) {
|
||||
throw new Error('DataService not found for URI: ' + uri);
|
||||
}
|
||||
|
||||
// Mark that we have bootstrapped
|
||||
input.setBootstrappedTrue();
|
||||
|
||||
// Get the bootstrap params and perform the bootstrap
|
||||
// Note: pass in input so on disposal this is cleaned up.
|
||||
// Otherwise many components will be left around and be subscribed
|
||||
// to events from the backing data service
|
||||
const parent = input.container;
|
||||
let params: IEditDataComponentParams = {
|
||||
dataService: dataService,
|
||||
onSaveViewState: input.onSaveViewStateEmitter.event,
|
||||
onRestoreViewState: input.onRestoreViewStateEmitter.event
|
||||
};
|
||||
bootstrapAngular(this._instantiationService,
|
||||
EditDataModule,
|
||||
parent,
|
||||
EDITDATA_SELECTOR,
|
||||
params,
|
||||
input);
|
||||
}
|
||||
}
|
||||
242
src/sql/workbench/parts/editData/common/editDataInput.ts
Normal file
242
src/sql/workbench/parts/editData/common/editDataInput.ts
Normal file
@@ -0,0 +1,242 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { EditorInput, EditorModel, ConfirmResult, EncodingMode } from 'vs/workbench/common/editor';
|
||||
import { IConnectionManagementService, IConnectableInput, INewConnectionParams } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { IQueryModelService } from 'sql/platform/query/common/queryModel';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { EditSessionReadyParams } from 'azdata';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as nls from 'vs/nls';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
|
||||
import { EditDataResultsInput } from 'sql/workbench/parts/editData/common/editDataResultsInput';
|
||||
import { IEditorViewState } from 'vs/editor/common/editorCommon';
|
||||
|
||||
/**
|
||||
* Input for the EditDataEditor.
|
||||
*/
|
||||
export class EditDataInput extends EditorInput implements IConnectableInput {
|
||||
public static ID: string = 'workbench.editorinputs.editDataInput';
|
||||
private _hasBootstrapped: boolean;
|
||||
private _editorContainer: HTMLElement;
|
||||
private _updateTaskbar: Emitter<EditDataInput>;
|
||||
private _editorInitializing: Emitter<boolean>;
|
||||
private _showResultsEditor: Emitter<EditDataInput>;
|
||||
private _refreshButtonEnabled: boolean;
|
||||
private _stopButtonEnabled: boolean;
|
||||
private _setup: boolean;
|
||||
private _rowLimit: number;
|
||||
private _objectType: string;
|
||||
private _css: HTMLStyleElement;
|
||||
private _useQueryFilter: boolean;
|
||||
|
||||
public savedViewState: IEditorViewState;
|
||||
|
||||
constructor(
|
||||
private _uri: URI,
|
||||
private _schemaName,
|
||||
private _tableName,
|
||||
private _sql: UntitledEditorInput,
|
||||
private _queryString: string,
|
||||
private _results: EditDataResultsInput,
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
@IQueryModelService private _queryModelService: IQueryModelService,
|
||||
@INotificationService private notificationService: INotificationService
|
||||
) {
|
||||
super();
|
||||
this._hasBootstrapped = false;
|
||||
this._updateTaskbar = new Emitter<EditDataInput>();
|
||||
this._showResultsEditor = new Emitter<EditDataInput>();
|
||||
this._editorInitializing = new Emitter<boolean>();
|
||||
this._setup = false;
|
||||
this._stopButtonEnabled = false;
|
||||
this._refreshButtonEnabled = false;
|
||||
this._toDispose = [];
|
||||
this._useQueryFilter = false;
|
||||
|
||||
// re-emit sql editor events through this editor if it exists
|
||||
if (this._sql) {
|
||||
this._toDispose.push(this._sql.onDidChangeDirty(() => this._onDidChangeDirty.fire()));
|
||||
this._sql.disableSaving();
|
||||
}
|
||||
this.disableSaving();
|
||||
|
||||
//TODO determine is this is a table or a view
|
||||
this._objectType = 'TABLE';
|
||||
|
||||
// Attach to event callbacks
|
||||
if (this._queryModelService) {
|
||||
let self = this;
|
||||
|
||||
// Register callbacks for the Actions
|
||||
this._toDispose.push(
|
||||
this._queryModelService.onRunQueryStart(uri => {
|
||||
if (self.uri === uri) {
|
||||
self.initEditStart();
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
this._toDispose.push(
|
||||
this._queryModelService.onEditSessionReady((result) => {
|
||||
if (self.uri === result.ownerUri) {
|
||||
self.initEditEnd(result);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Getters/Setters
|
||||
public get tableName(): string { return this._tableName; }
|
||||
public get schemaName(): string { return this._schemaName; }
|
||||
public get uri(): string { return this._uri.toString(); }
|
||||
public get sql(): UntitledEditorInput { return this._sql; }
|
||||
public get results(): EditDataResultsInput { return this._results; }
|
||||
public getResultsInputResource(): string { return this._results.uri; }
|
||||
public get updateTaskbarEvent(): Event<EditDataInput> { return this._updateTaskbar.event; }
|
||||
public get editorInitializingEvent(): Event<boolean> { return this._editorInitializing.event; }
|
||||
public get showResultsEditorEvent(): Event<EditDataInput> { return this._showResultsEditor.event; }
|
||||
public get stopButtonEnabled(): boolean { return this._stopButtonEnabled; }
|
||||
public get refreshButtonEnabled(): boolean { return this._refreshButtonEnabled; }
|
||||
public get container(): HTMLElement { return this._editorContainer; }
|
||||
public get hasBootstrapped(): boolean { return this._hasBootstrapped; }
|
||||
public get setup(): boolean { return this._setup; }
|
||||
public get rowLimit(): number { return this._rowLimit; }
|
||||
public get objectType(): string { return this._objectType; }
|
||||
public showResultsEditor(): void { this._showResultsEditor.fire(undefined); }
|
||||
public isDirty(): boolean { return false; }
|
||||
public save(): Promise<boolean> { return Promise.resolve(false); }
|
||||
public confirmSave(): Promise<ConfirmResult> { return Promise.resolve(ConfirmResult.DONT_SAVE); }
|
||||
public getTypeId(): string { return EditDataInput.ID; }
|
||||
public setBootstrappedTrue(): void { this._hasBootstrapped = true; }
|
||||
public getResource(): URI { return this._uri; }
|
||||
public supportsSplitEditor(): boolean { return false; }
|
||||
public setupComplete() { this._setup = true; }
|
||||
public get queryString(): string {
|
||||
return this._queryString;
|
||||
}
|
||||
public set queryString(queryString: string) {
|
||||
this._queryString = queryString;
|
||||
}
|
||||
public get css(): HTMLStyleElement {
|
||||
return this._css;
|
||||
}
|
||||
public set css(css: HTMLStyleElement) {
|
||||
this._css = css;
|
||||
}
|
||||
public get queryPaneEnabled(): boolean {
|
||||
return this._useQueryFilter;
|
||||
}
|
||||
public set queryPaneEnabled(useQueryFilter: boolean) {
|
||||
this._useQueryFilter = useQueryFilter;
|
||||
}
|
||||
|
||||
// State Update Callbacks
|
||||
public initEditStart(): void {
|
||||
this._editorInitializing.fire(true);
|
||||
this._refreshButtonEnabled = false;
|
||||
this._stopButtonEnabled = true;
|
||||
this._updateTaskbar.fire(this);
|
||||
}
|
||||
|
||||
public initEditEnd(result: EditSessionReadyParams): void {
|
||||
this._refreshButtonEnabled = true;
|
||||
this._stopButtonEnabled = false;
|
||||
if (!result.success) {
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: result.message
|
||||
});
|
||||
}
|
||||
this._editorInitializing.fire(false);
|
||||
this._updateTaskbar.fire(this);
|
||||
}
|
||||
|
||||
public onConnectStart(): void {
|
||||
// TODO: Indicate connection started
|
||||
}
|
||||
|
||||
public onConnectReject(error?: string): void {
|
||||
if (error) {
|
||||
|
||||
this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: nls.localize('connectionFailure', 'Edit Data Session Failed To Connect')
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public onConnectCanceled(): void {
|
||||
}
|
||||
|
||||
public onConnectSuccess(params?: INewConnectionParams): void {
|
||||
let rowLimit: number = undefined;
|
||||
let queryString: string = undefined;
|
||||
if (this._useQueryFilter) {
|
||||
queryString = this._queryString;
|
||||
} else {
|
||||
rowLimit = this._rowLimit;
|
||||
}
|
||||
|
||||
this._queryModelService.initializeEdit(this.uri, this.schemaName, this.tableName, this._objectType, rowLimit, queryString);
|
||||
this.showResultsEditor();
|
||||
this._onDidChangeLabel.fire();
|
||||
}
|
||||
|
||||
public onDisconnect(): void {
|
||||
// TODO: deal with disconnections
|
||||
}
|
||||
|
||||
public onRowDropDownSet(rows: number) {
|
||||
this._rowLimit = rows;
|
||||
}
|
||||
|
||||
// Boiler Plate Functions
|
||||
public matches(otherInput: any): boolean {
|
||||
if (otherInput instanceof EditDataInput) {
|
||||
return this._sql.matches(otherInput.sql);
|
||||
}
|
||||
|
||||
return this._sql.matches(otherInput);
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._queryModelService.disposeQuery(this.uri);
|
||||
this._sql.dispose();
|
||||
this._results.dispose();
|
||||
this._toDispose = dispose(this._toDispose);
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
public close(): void {
|
||||
// Dispose our edit session then disconnect our input
|
||||
this._queryModelService.disposeEdit(this.uri).then(() => {
|
||||
return this._connectionManagementService.disconnectEditor(this, true);
|
||||
}).then(() => {
|
||||
this.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
public get tabColor(): string {
|
||||
return this._connectionManagementService.getTabColorForUri(this.uri);
|
||||
}
|
||||
|
||||
public get onDidModelChangeContent(): Event<void> { return this._sql.onDidModelChangeContent; }
|
||||
public get onDidModelChangeEncoding(): Event<void> { return this._sql.onDidModelChangeEncoding; }
|
||||
public resolve(refresh?: boolean): Promise<EditorModel> { return this._sql.resolve(); }
|
||||
public getEncoding(): string { return this._sql.getEncoding(); }
|
||||
public suggestFileName(): string { return this._sql.suggestFileName(); }
|
||||
public getName(): string { return this._sql.getName(); }
|
||||
public get hasAssociatedFilePath(): boolean { return this._sql.hasAssociatedFilePath; }
|
||||
|
||||
public setEncoding(encoding: string, mode: EncodingMode /* ignored, we only have Encode */): void {
|
||||
this._sql.setEncoding(encoding, mode);
|
||||
}
|
||||
}
|
||||
105
src/sql/workbench/parts/editData/common/editDataResultsInput.ts
Normal file
105
src/sql/workbench/parts/editData/common/editDataResultsInput.ts
Normal file
@@ -0,0 +1,105 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { EditorInput } from 'vs/workbench/common/editor';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
|
||||
/**
|
||||
* Input for the EditDataResultsEditor. This input helps with logic for the viewing and editing of
|
||||
* data in the results grid.
|
||||
*/
|
||||
export class EditDataResultsInput extends EditorInput {
|
||||
|
||||
// Tracks if the editor that holds this input should be visible (i.e. true if a query has been run)
|
||||
private _visible: boolean;
|
||||
|
||||
// Tracks if the editor has holds this input has has bootstrapped angular yet
|
||||
private _hasBootstrapped: boolean;
|
||||
|
||||
// Holds the HTML content for the editor when the editor discards this input and loads another
|
||||
private _editorContainer: HTMLElement;
|
||||
public css: HTMLStyleElement;
|
||||
|
||||
public readonly onRestoreViewStateEmitter = new Emitter<void>();
|
||||
public readonly onSaveViewStateEmitter = new Emitter<void>();
|
||||
|
||||
constructor(private _uri: string) {
|
||||
super();
|
||||
this._visible = false;
|
||||
this._hasBootstrapped = false;
|
||||
}
|
||||
|
||||
getTypeId(): string {
|
||||
return EditDataResultsInput.ID;
|
||||
}
|
||||
|
||||
matches(other: any): boolean {
|
||||
if (other instanceof EditDataResultsInput) {
|
||||
return (other._uri === this._uri);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
resolve(refresh?: boolean): Promise<any> {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
supportsSplitEditor(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
public setBootstrappedTrue(): void {
|
||||
this._hasBootstrapped = true;
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
this._disposeContainer();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private _disposeContainer() {
|
||||
if (!this._editorContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
let parentContainer = this._editorContainer.parentNode;
|
||||
if (parentContainer) {
|
||||
parentContainer.removeChild(this._editorContainer);
|
||||
this._editorContainer = null;
|
||||
}
|
||||
}
|
||||
|
||||
//// Properties
|
||||
|
||||
static get ID() {
|
||||
return 'workbench.editorinputs.editDataResultsInput';
|
||||
}
|
||||
|
||||
set container(container: HTMLElement) {
|
||||
this._disposeContainer();
|
||||
this._editorContainer = container;
|
||||
}
|
||||
|
||||
get container(): HTMLElement {
|
||||
return this._editorContainer;
|
||||
}
|
||||
|
||||
get hasBootstrapped(): boolean {
|
||||
return this._hasBootstrapped;
|
||||
}
|
||||
|
||||
get visible(): boolean {
|
||||
return this._visible;
|
||||
}
|
||||
|
||||
set visible(visible: boolean) {
|
||||
this._visible = visible;
|
||||
}
|
||||
|
||||
get uri(): string {
|
||||
return this._uri;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user