mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-20 11:01:37 -05:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c92b88bfaf | ||
|
|
10875f26dc | ||
|
|
d62e809c18 | ||
|
|
d85bf4f6dd | ||
|
|
801e201cc3 | ||
|
|
e18e0da0c1 | ||
|
|
d046b0a412 | ||
|
|
72084b8fc1 | ||
|
|
2639b2bd2c | ||
|
|
82aa493dfd | ||
|
|
6c3c7c40b5 | ||
|
|
5616751c04 | ||
|
|
7d898ca34d | ||
|
|
e26556b21a | ||
|
|
89e6d363e2 | ||
|
|
c559ac7be9 | ||
|
|
b3fbe47f0a | ||
|
|
c73af4c480 | ||
|
|
8887fe1eac | ||
|
|
ed861a6c96 | ||
|
|
e63bb6a8ec | ||
|
|
8ec09d25ce | ||
|
|
a9a01ae479 | ||
|
|
31a3864789 | ||
|
|
a5c537197c | ||
|
|
4ea13bdbc0 | ||
|
|
b06ddf2dc7 | ||
|
|
2c45ac9df3 | ||
|
|
7735f68502 | ||
|
|
ffb0f5a1c7 | ||
|
|
80c7f9e855 | ||
|
|
709ef4e39f | ||
|
|
4ceb869420 | ||
|
|
432a209184 | ||
|
|
8444271c58 | ||
|
|
2bc97c23d4 |
2
.yarnrc
2
.yarnrc
@@ -1,3 +1,3 @@
|
||||
disturl "https://atom.io/download/electron"
|
||||
target "2.0.7"
|
||||
target "2.0.8"
|
||||
runtime "electron"
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
const localize = nls.loadMessageBundle();
|
||||
import { ExtensionContext, workspace, window, Disposable, commands, Uri, OutputChannel } from 'vscode';
|
||||
import { ExtensionContext, workspace, window, Disposable, commands, OutputChannel } from 'vscode';
|
||||
import { findGit, Git, IGit } from './git';
|
||||
import { Model } from './model';
|
||||
import { CommandCenter } from './commands';
|
||||
@@ -98,28 +98,29 @@ export async function activate(context: ExtensionContext): Promise<API> {
|
||||
throw err;
|
||||
}
|
||||
|
||||
const config = workspace.getConfiguration('git');
|
||||
const shouldIgnore = config.get<boolean>('ignoreMissingGitWarning') === true;
|
||||
// {{SQL CARBON EDIT}} turn-off Git missing prompt
|
||||
//const config = workspace.getConfiguration('git');
|
||||
//const shouldIgnore = config.get<boolean>('ignoreMissingGitWarning') === true;
|
||||
|
||||
if (!shouldIgnore) {
|
||||
console.warn(err.message);
|
||||
outputChannel.appendLine(err.message);
|
||||
outputChannel.show();
|
||||
// if (!shouldIgnore) {
|
||||
// console.warn(err.message);
|
||||
// outputChannel.appendLine(err.message);
|
||||
// outputChannel.show();
|
||||
|
||||
const download = localize('downloadgit', "Download Git");
|
||||
const neverShowAgain = localize('neverShowAgain', "Don't Show Again");
|
||||
const choice = await window.showWarningMessage(
|
||||
localize('notfound', "Git not found. Install it or configure it using the 'git.path' setting."),
|
||||
download,
|
||||
neverShowAgain
|
||||
);
|
||||
// const download = localize('downloadgit', "Download Git");
|
||||
// const neverShowAgain = localize('neverShowAgain', "Don't Show Again");
|
||||
// const choice = await window.showWarningMessage(
|
||||
// localize('notfound', "Git not found. Install it or configure it using the 'git.path' setting."),
|
||||
// download,
|
||||
// neverShowAgain
|
||||
// );
|
||||
|
||||
if (choice === download) {
|
||||
commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/'));
|
||||
} else if (choice === neverShowAgain) {
|
||||
await config.update('ignoreMissingGitWarning', true, true);
|
||||
}
|
||||
}
|
||||
// if (choice === download) {
|
||||
// commands.executeCommand('vscode.open', Uri.parse('https://git-scm.com/'));
|
||||
// } else if (choice === neverShowAgain) {
|
||||
// await config.update('ignoreMissingGitWarning', true, true);
|
||||
// }
|
||||
// }
|
||||
|
||||
return new NoopAPIImpl();
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "sqlops",
|
||||
"version": "0.33.2",
|
||||
"version": "0.33.4",
|
||||
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
|
||||
@@ -518,7 +518,8 @@ export default class MainController implements vscode.Disposable {
|
||||
let runButton = view.modelBuilder.button()
|
||||
.withProperties({
|
||||
label: 'Run',
|
||||
iconPath: runIcon
|
||||
iconPath: runIcon,
|
||||
title: 'Run title'
|
||||
}).component();
|
||||
|
||||
let monitorLightPath = vscode.Uri.file(path.join(__dirname, '..', 'media', 'monitor.svg'));
|
||||
@@ -530,7 +531,8 @@ export default class MainController implements vscode.Disposable {
|
||||
let monitorButton = view.modelBuilder.button()
|
||||
.withProperties({
|
||||
label: 'Monitor',
|
||||
iconPath: monitorIcon
|
||||
iconPath: monitorIcon,
|
||||
title: 'Monitor title'
|
||||
}).component();
|
||||
let toolbarModel = view.modelBuilder.toolbarContainer()
|
||||
.withToolbarItems([{
|
||||
|
||||
@@ -23,6 +23,18 @@ export class Button extends vsButton {
|
||||
this.$el.style('outline-color', this.buttonFocusOutline ? this.buttonFocusOutline.toString() : null);
|
||||
this.$el.style('outline-width', '1px');
|
||||
});
|
||||
|
||||
this.$el.on(DOM.EventType.MOUSE_DOWN, (e) => {
|
||||
const mouseEvent = e as MouseEvent;
|
||||
if (!this.$el.hasClass('disabled') && mouseEvent.button === 0) {
|
||||
this.$el.addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
this.$el.on([DOM.EventType.MOUSE_UP], (e) => {
|
||||
DOM.EventHelper.stop(e);
|
||||
this.$el.removeClass('active');
|
||||
});
|
||||
}
|
||||
|
||||
public style(styles: IButtonStyles): void {
|
||||
|
||||
@@ -217,7 +217,7 @@ export abstract class Modal extends Disposable implements IThemable {
|
||||
if (this._modalOptions.isAngular === false && this._modalOptions.hasErrors) {
|
||||
let builder = errorMessagesInFooter ? this._leftFooter : body;
|
||||
builder.div({ class: 'dialogErrorMessage', id: 'dialogErrorMessage' }, (errorMessageContainer) => {
|
||||
errorMessageContainer.div({ class: 'icon error' }, (iconContainer) => {
|
||||
errorMessageContainer.div({ class: 'sql icon error' }, (iconContainer) => {
|
||||
this._errorIconElement = iconContainer.getHTMLElement();
|
||||
this._errorIconElement.style.visibility = 'hidden';
|
||||
});
|
||||
|
||||
@@ -331,8 +331,11 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
|
||||
}
|
||||
|
||||
layout(size: number): void {
|
||||
const previousSize = Math.max(this.size, this.contentSize);
|
||||
const previousSize = this.size;
|
||||
this.size = size;
|
||||
this.contentSize = 0;
|
||||
this.lastRenderHeight = undefined;
|
||||
this.lastRenderTop = undefined;
|
||||
this.resize(this.viewItems.length - 1, size - previousSize);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
|
||||
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
|
||||
import { range } from 'vs/base/common/arrays';
|
||||
|
||||
/**
|
||||
* Implements the various additional navigation keybindings we want out of slickgrid
|
||||
@@ -50,6 +51,14 @@ export class AdditionalKeyBindings<T> implements Slick.Plugin<T> {
|
||||
}
|
||||
} else if (event.equals(KeyCode.End | KeyMod.CtrlCmd)) {
|
||||
this.grid.setActiveCell(this.grid.getDataLength() - 1, this.grid.getColumns().length - 1);
|
||||
} else if (event.equals(KeyCode.KEY_A | KeyMod.CtrlCmd)) {
|
||||
// check if we can set the rows directly on the selectionModel, its cleaner
|
||||
let selectionModel = this.grid.getSelectionModel();
|
||||
if (selectionModel) {
|
||||
selectionModel.setSelectedRanges([new Slick.Range(0, 0, this.grid.getDataLength() - 1, this.grid.getColumns().length - 1)]);
|
||||
} else {
|
||||
this.grid.setSelectedRows(range(this.grid.getDataLength()));
|
||||
}
|
||||
} else {
|
||||
handled = false;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
|
||||
|
||||
this.canvas.classList.add(this.options.dragClass);
|
||||
|
||||
this.grid.focus();
|
||||
this.grid.setActiveCell(cell.row, cell.cell);
|
||||
|
||||
let start = this.grid.getCellFromPoint(
|
||||
dd.startX - $(this.canvas).offset().left,
|
||||
@@ -128,8 +128,11 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
|
||||
this.canvas.classList.remove(this.options.dragClass);
|
||||
this.dragging = false;
|
||||
e.stopImmediatePropagation();
|
||||
|
||||
this.decorator.hide();
|
||||
// if this happens to fast there is a chance we don't have the necessary information to actually do proper selection
|
||||
if (!dd || !dd.range || !dd.range.start || !dd.range.end) {
|
||||
return;
|
||||
}
|
||||
this.onCellRangeSelected.notify({
|
||||
range: new Slick.Range(
|
||||
dd.range.start.row,
|
||||
|
||||
@@ -38,8 +38,8 @@ export class CopyKeybind<T> implements Slick.Plugin<T> {
|
||||
let selectionModel = this.grid.getSelectionModel();
|
||||
let ranges: Slick.Range[];
|
||||
// check to see if we can get the range from the model directly
|
||||
if (selectionModel && (<any>selectionModel).getSelectedRanges) {
|
||||
ranges = (<any>selectionModel).getSelectedRanges();
|
||||
if (selectionModel) {
|
||||
ranges = selectionModel.getSelectedRanges();
|
||||
} else {
|
||||
let selectedRows = this.grid.getSelectedRows();
|
||||
let startColumn = 0;
|
||||
|
||||
@@ -36,23 +36,31 @@ export class RowNumberColumn<T> implements Slick.Plugin<T> {
|
||||
private handleClick(e: MouseEvent, args: Slick.OnClickEventArgs<T>): void {
|
||||
if (this.grid.getColumns()[args.cell].id === 'rowNumber') {
|
||||
this.grid.setActiveCell(args.row, 1);
|
||||
this.grid.setSelectedRows([args.row]);
|
||||
if (this.grid.getSelectionModel()) {
|
||||
this.grid.setSelectedRows([args.row]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleHeaderClick(e: MouseEvent, args: Slick.OnHeaderClickEventArgs<T>): void {
|
||||
if (args.column.id === 'rowNumber') {
|
||||
this.grid.setActiveCell(0, 1);
|
||||
this.grid.setSelectedRows(range(this.grid.getDataLength()));
|
||||
if (this.grid.getSelectionModel()) {
|
||||
this.grid.setSelectedRows(range(this.grid.getDataLength()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public getColumnDefinition(): Slick.Column<T> {
|
||||
// that smallest we can make it is 22 due to padding and margins in the cells
|
||||
let columnWidth = Math.max(this.options.numberOfRows.toString().length * sizePerDigit, 22);
|
||||
return {
|
||||
id: 'rowNumber',
|
||||
name: '',
|
||||
field: 'rowNumber',
|
||||
width: this.options.numberOfRows.toString().length * sizePerDigit,
|
||||
width: columnWidth,
|
||||
minWidth: columnWidth,
|
||||
maxWidth: columnWidth,
|
||||
resizable: false,
|
||||
cssClass: this.options.cssClass,
|
||||
focusable: false,
|
||||
|
||||
@@ -166,6 +166,10 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
|
||||
this._grid.setData(this._data, true);
|
||||
}
|
||||
|
||||
getData(): Slick.DataProvider<T> {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
get columns(): Slick.Column<T>[] {
|
||||
return this._grid.getColumns();
|
||||
}
|
||||
@@ -244,7 +248,6 @@ export class Table<T extends Slick.SlickData> extends Widget implements IThemabl
|
||||
}
|
||||
}
|
||||
this.resizeCanvas();
|
||||
this.autosizeColumns();
|
||||
}
|
||||
|
||||
autosizeColumns() {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
'use strict';
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
/**
|
||||
* Implementation of vs/base/common/event/echo that is clearable
|
||||
@@ -37,3 +38,52 @@ export function echo<T>(event: Event<T>, nextTick = false, buffer: T[] = []): {
|
||||
clear
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Implementation of vs/base/common/event/debounceEvent that is clearable
|
||||
*/
|
||||
export function debounceEvent<T>(event: Event<T>, merger: (last: T, event: T) => T, delay?: number, leading?: boolean): { clear: () => void; event: Event<T> };
|
||||
export function debounceEvent<I, O>(event: Event<I>, merger: (last: O, event: I) => O, delay?: number, leading?: boolean): { clear: () => void; event: Event<O> };
|
||||
export function debounceEvent<I, O>(event: Event<I>, merger: (last: O, event: I) => O, delay: number = 100, leading = false): { clear: () => void; event: Event<O> } {
|
||||
|
||||
let subscription: IDisposable;
|
||||
let output: O = undefined;
|
||||
let handle: any = undefined;
|
||||
let numDebouncedCalls = 0;
|
||||
|
||||
const clear = () => output = undefined;
|
||||
|
||||
const emitter = new Emitter<O>({
|
||||
onFirstListenerAdd() {
|
||||
subscription = event(cur => {
|
||||
numDebouncedCalls++;
|
||||
output = merger(output, cur);
|
||||
|
||||
if (leading && !handle) {
|
||||
emitter.fire(output);
|
||||
}
|
||||
|
||||
clearTimeout(handle);
|
||||
handle = setTimeout(() => {
|
||||
let _output = output;
|
||||
output = undefined;
|
||||
handle = undefined;
|
||||
if (!leading || numDebouncedCalls > 1) {
|
||||
emitter.fire(_output);
|
||||
}
|
||||
|
||||
numDebouncedCalls = 0;
|
||||
}, delay);
|
||||
});
|
||||
},
|
||||
onLastListenerRemove() {
|
||||
subscription.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
event: emitter.event,
|
||||
clear
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -63,21 +63,21 @@
|
||||
background: url("globalerror.svg") center center no-repeat;
|
||||
}
|
||||
|
||||
.vs .icon.error,
|
||||
.vs-dark .icon.error,
|
||||
.hc-black .icon.error {
|
||||
.vs .sql.icon.error,
|
||||
.vs-dark .sql.icon.error,
|
||||
.hc-black .sql.icon.error {
|
||||
content: url("status_error.svg");
|
||||
}
|
||||
|
||||
.vs .icon.warning,
|
||||
.vs-dark .icon.warning,
|
||||
.hc-black .icon.warning {
|
||||
.vs .sql.icon.warning,
|
||||
.vs-dark .sql.icon.warning,
|
||||
.hc-black .sql.icon.warning {
|
||||
content: url("status_warning.svg");
|
||||
}
|
||||
|
||||
.vs .icon.info,
|
||||
.vs-dark .icon.info,
|
||||
.hc-black .icon.info {
|
||||
.vs .sql.icon.info,
|
||||
.vs-dark .sql.icon.info,
|
||||
.hc-black .sql.icon.info {
|
||||
content: url("status_info.svg");
|
||||
}
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ export class AccountPicker extends Disposable {
|
||||
|
||||
// Create refresh account action
|
||||
this._refreshContainer = DOM.append(this._rootElement, DOM.$('div.refresh-container'));
|
||||
DOM.append(this._refreshContainer, DOM.$('div.icon warning'));
|
||||
DOM.append(this._refreshContainer, DOM.$('div.sql icon warning'));
|
||||
let actionBar = new ActionBar(this._refreshContainer, { animated: false });
|
||||
this._refreshAccountAction = this._instantiationService.createInstance(RefreshAccountAction);
|
||||
actionBar.push(this._refreshAccountAction, { icon: false, label: true });
|
||||
|
||||
@@ -156,11 +156,6 @@ export class ConnectionWidget {
|
||||
}
|
||||
|
||||
private fillInConnectionForm(): void {
|
||||
// Connection name
|
||||
let connectionNameOption = this._optionsMaps[ConnectionOptionSpecialType.connectionName];
|
||||
let connectionNameBuilder = DialogHelper.appendRow(this._tableContainer, connectionNameOption.displayName, 'connection-label', 'connection-input');
|
||||
this._connectionNameInputBox = new InputBox(connectionNameBuilder.getHTMLElement(), this._contextViewService, { ariaLabel: connectionNameOption.displayName });
|
||||
|
||||
// Server name
|
||||
let serverNameOption = this._optionsMaps[ConnectionOptionSpecialType.serverName];
|
||||
let serverNameBuilder = DialogHelper.appendRow(this._tableContainer, serverNameOption.displayName, 'connection-label', 'connection-input');
|
||||
@@ -222,6 +217,11 @@ export class ConnectionWidget {
|
||||
let serverGroupBuilder = DialogHelper.appendRow(this._tableContainer, this._serverGroupDisplayString, 'connection-label', 'connection-input');
|
||||
DialogHelper.appendInputSelectBox(serverGroupBuilder, this._serverGroupSelectBox);
|
||||
|
||||
// Connection name
|
||||
let connectionNameOption = this._optionsMaps[ConnectionOptionSpecialType.connectionName];
|
||||
let connectionNameBuilder = DialogHelper.appendRow(this._tableContainer, connectionNameOption.displayName, 'connection-label', 'connection-input');
|
||||
this._connectionNameInputBox = new InputBox(connectionNameBuilder.getHTMLElement(), this._contextViewService, { ariaLabel: connectionNameOption.displayName });
|
||||
|
||||
let AdvancedLabel = localize('advanced', 'Advanced...');
|
||||
this._advancedButton = this.createAdvancedButton(this._tableContainer, AdvancedLabel);
|
||||
}
|
||||
@@ -398,7 +398,7 @@ export class ConnectionWidget {
|
||||
|
||||
public focusOnOpen(): void {
|
||||
this._handleClipboard();
|
||||
this._connectionNameInputBox.focus();
|
||||
this._serverNameInputBox.focus();
|
||||
this.focusPasswordIfNeeded();
|
||||
this.clearValidationMessages();
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ export class TasksWidget extends DashboardWidget implements IDashboardWidget, On
|
||||
}).filter(i => !!i);
|
||||
}
|
||||
|
||||
this._tasks = tasks.map(i => TaskRegistry.getCommandActionById(i)).filter(v => !!v);
|
||||
this._tasks = tasks.map(i => MenuRegistry.getCommand(i)).filter(v => !!v);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<div class="option check" #encryptCheckContainer>
|
||||
</div>
|
||||
<div class="option" #encryptWarningContainer>
|
||||
<div class="icon warning">
|
||||
<div class="sql icon warning">
|
||||
</div>
|
||||
<div class="warning-message">
|
||||
{{localizedStrings.NO_ENCRYPTOR_WARNING}}
|
||||
|
||||
@@ -25,7 +25,7 @@ classes should alter those!
|
||||
overflow: hidden;
|
||||
-o-text-overflow: ellipsis;
|
||||
text-overflow: ellipsis;
|
||||
height: 16px;
|
||||
height: 28px;
|
||||
line-height: 16px;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
@@ -35,7 +35,7 @@ classes should alter those!
|
||||
border-bottom: 2px solid #bbb;
|
||||
float: left;
|
||||
background-color: #eee;
|
||||
box-sizing: content-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.slick-headerrow-column.ui-state-default, .slick-footerrow-column.ui-state-default {
|
||||
|
||||
@@ -13,11 +13,11 @@ import * as sqlops from 'sqlops';
|
||||
import { ComponentWithIconBase } from 'sql/parts/modelComponents/componentWithIconBase';
|
||||
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||
import { attachButtonStyler } from 'sql/common/theme/styler';
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
|
||||
import { SIDE_BAR_BACKGROUND, SIDE_BAR_TITLE_FOREGROUND } from 'vs/workbench/common/theme';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { focusBorder, foreground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
|
||||
@@ -101,6 +101,7 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
|
||||
super.setProperties(properties);
|
||||
this._button.enabled = this.enabled;
|
||||
this._button.label = this.label;
|
||||
this._button.title = this.title;
|
||||
if (this.width) {
|
||||
this._button.setWidth(this.convertSize(this.width.toString()));
|
||||
}
|
||||
@@ -165,4 +166,13 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
|
||||
private setFileProperties(properties: sqlops.ButtonProperties, isFile: boolean): void {
|
||||
properties.isFile = isFile;
|
||||
}
|
||||
|
||||
private get title(): string {
|
||||
return this.getPropertyOrDefault<sqlops.ButtonProperties, string>((props) => props.title, '');
|
||||
}
|
||||
|
||||
private set title(newValue: string) {
|
||||
this.setPropertyFromUI<sqlops.ButtonProperties, string>((properties, title) => { properties.title = title; }, newValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ export class ItemDescriptor<T> {
|
||||
constructor(public descriptor: IComponentDescriptor, public config: T) { }
|
||||
}
|
||||
|
||||
const ids = new IdGenerator('model-view-component-icon-');
|
||||
|
||||
export abstract class ComponentWithIconBase extends ComponentBase {
|
||||
|
||||
protected _iconClass: string;
|
||||
@@ -42,7 +44,6 @@ export abstract class ComponentWithIconBase extends ComponentBase {
|
||||
if (this.iconPath && this.iconPath !== this._iconPath) {
|
||||
this._iconPath = this.iconPath;
|
||||
if (!this._iconClass) {
|
||||
const ids = new IdGenerator('model-view-component-icon-' + Math.round(Math.random() * 1000));
|
||||
this._iconClass = ids.nextId();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import DivContainer from './divContainer.component';
|
||||
import FlexContainer from './flexContainer.component';
|
||||
import FormContainer from './formContainer.component';
|
||||
import ToolbarContainer from './toolbarContainer.component';
|
||||
@@ -25,6 +25,9 @@ import EditorComponent from './editor.component';
|
||||
import { registerComponentType } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
||||
import { ModelComponentTypes } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
export const DIV_CONTAINER = 'div-container';
|
||||
registerComponentType(DIV_CONTAINER, ModelComponentTypes.DivContainer, DivContainer);
|
||||
|
||||
export const FLEX_CONTAINER = 'flex-container';
|
||||
registerComponentType(FLEX_CONTAINER, ModelComponentTypes.FlexContainer, FlexContainer);
|
||||
|
||||
|
||||
120
src/sql/parts/modelComponents/divContainer.component.ts
Normal file
120
src/sql/parts/modelComponents/divContainer.component.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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!./divContainer';
|
||||
|
||||
import {
|
||||
Component, Input, Inject, ChangeDetectorRef, forwardRef, ComponentFactoryResolver,
|
||||
ViewChild, ViewChildren, ElementRef, Injector, OnDestroy, QueryList,
|
||||
} from '@angular/core';
|
||||
|
||||
import { IComponent, IComponentDescriptor, IModelStore } from 'sql/parts/modelComponents/interfaces';
|
||||
import * as sqlops from 'sqlops';
|
||||
|
||||
import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service';
|
||||
import { ContainerBase } from 'sql/parts/modelComponents/componentBase';
|
||||
import { ModelComponentWrapper } from 'sql/parts/modelComponents/modelComponentWrapper.component';
|
||||
|
||||
import types = require('vs/base/common/types');
|
||||
|
||||
class DivItem {
|
||||
constructor(public descriptor: IComponentDescriptor, public config: sqlops.DivItemLayout) { }
|
||||
}
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<div #divContainer *ngIf="items" class="divContainer" [style.height]="height" [style.width]="width">
|
||||
<div *ngFor="let item of items" [style.order]="getItemOrder(item)" [ngStyle]="getItemStyles(item)">
|
||||
<model-component-wrapper [descriptor]="item.descriptor" [modelStore]="modelStore">
|
||||
</model-component-wrapper>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
export default class DivContainer extends ContainerBase<sqlops.DivItemLayout> implements IComponent, OnDestroy {
|
||||
@Input() descriptor: IComponentDescriptor;
|
||||
@Input() modelStore: IModelStore;
|
||||
@ViewChild('divContainer', { read: ElementRef }) divContainer;
|
||||
private _height: string;
|
||||
private _width: string;
|
||||
private _overflowY: string;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => ElementRef)) el: ElementRef
|
||||
) {
|
||||
super(changeRef, el);
|
||||
this._overflowY = ''; // default
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.baseInit();
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.baseDestroy();
|
||||
}
|
||||
|
||||
|
||||
/// IComponent implementation
|
||||
|
||||
public setLayout(layout: sqlops.DivLayout): void {
|
||||
this._height = this.convertSize(layout.height);
|
||||
this._width = this.convertSize(layout.width);
|
||||
this.layout();
|
||||
}
|
||||
|
||||
public setProperties(properties: { [key: string]: any; }): void {
|
||||
super.setProperties(properties);
|
||||
if (this.overflowY !== this._overflowY) {
|
||||
this.updateOverflowY();
|
||||
}
|
||||
this.updateScroll();
|
||||
}
|
||||
|
||||
private updateOverflowY() {
|
||||
this._overflowY = this.overflowY;
|
||||
if (this._overflowY) {
|
||||
let element = <HTMLElement> this.divContainer.nativeElement;
|
||||
element.style.overflowY = this._overflowY;
|
||||
}
|
||||
}
|
||||
|
||||
private updateScroll() {
|
||||
let element = <HTMLElement> this.divContainer.nativeElement;
|
||||
element.scrollTop = element.scrollTop - this.yOffsetChange;
|
||||
element.dispatchEvent(new Event('scroll'));
|
||||
}
|
||||
|
||||
// CSS-bound properties
|
||||
public get height(): string {
|
||||
return this._height;
|
||||
}
|
||||
|
||||
public get width(): string {
|
||||
return this._width;
|
||||
}
|
||||
|
||||
// CSS-bound properties
|
||||
public get overflowY(): string {
|
||||
return this.getPropertyOrDefault<sqlops.DivContainerProperties, any>((props) => props.overflowY, '');
|
||||
}
|
||||
public set overflowY(newValue: string) {
|
||||
this.setPropertyFromUI<sqlops.DivContainerProperties, any>((properties, newValue) => { properties.overflowY = newValue; }, newValue);
|
||||
}
|
||||
|
||||
public get yOffsetChange(): number {
|
||||
return this.getPropertyOrDefault<sqlops.DivContainerProperties, any>((props) => props.yOffsetChange, 0);
|
||||
}
|
||||
public set yOffsetChange(newValue: number) {
|
||||
this.setPropertyFromUI<sqlops.DivContainerProperties, any>((properties, newValue) => { properties.yOffsetChange = newValue; }, newValue);
|
||||
}
|
||||
|
||||
private getItemOrder(item: DivItem): number {
|
||||
return item.config ? item.config.order : 0;
|
||||
}
|
||||
private getItemStyles(item: DivItem): { [key: string]: string } {
|
||||
return item.config && item.config.CSSStyles ? item.config.CSSStyles : {};
|
||||
}
|
||||
}
|
||||
5
src/sql/parts/modelComponents/divContainer.css
Normal file
5
src/sql/parts/modelComponents/divContainer.css
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
.divContainer {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -3,16 +3,50 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IEditorModel } from 'vs/platform/editor/common/editor';
|
||||
import { EditorInput } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, EditorModel, ConfirmResult } from 'vs/workbench/common/editor';
|
||||
import * as DOM from 'vs/base/browser/dom';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
|
||||
|
||||
import { DialogPane } from 'sql/platform/dialog/dialogPane';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
export type ModeViewSaveHandler = (handle: number) => Thenable<boolean>;
|
||||
|
||||
export class ModelViewInputModel extends EditorModel {
|
||||
private dirty: boolean;
|
||||
private readonly _onDidChangeDirty: Emitter<void> = this._register(new Emitter<void>());
|
||||
get onDidChangeDirty(): Event<void> { return this._onDidChangeDirty.event; }
|
||||
|
||||
constructor(public readonly modelViewId, private readonly handle: number, private saveHandler?: ModeViewSaveHandler) {
|
||||
super();
|
||||
this.dirty = false;
|
||||
}
|
||||
|
||||
get isDirty(): boolean {
|
||||
return this.dirty;
|
||||
}
|
||||
|
||||
public setDirty(dirty: boolean): void {
|
||||
if (this.dirty === dirty) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.dirty = dirty;
|
||||
this._onDidChangeDirty.fire();
|
||||
}
|
||||
|
||||
save(): TPromise<boolean> {
|
||||
if (this.saveHandler) {
|
||||
return TPromise.wrap(this.saveHandler(this.handle));
|
||||
}
|
||||
return TPromise.wrap(true);
|
||||
}
|
||||
}
|
||||
export class ModelViewInput extends EditorInput {
|
||||
|
||||
public static ID: string = 'workbench.editorinputs.ModelViewEditorInput';
|
||||
@@ -20,14 +54,15 @@ export class ModelViewInput extends EditorInput {
|
||||
private _dialogPaneContainer: HTMLElement;
|
||||
private _dialogPane: DialogPane;
|
||||
|
||||
constructor(private _title: string, private _modelViewId: string,
|
||||
constructor(private _title: string, private _model: ModelViewInputModel,
|
||||
private _options: sqlops.ModelViewEditorOptions,
|
||||
@IInstantiationService private _instantiationService: IInstantiationService,
|
||||
@IPartService private readonly _partService: IPartService
|
||||
) {
|
||||
super();
|
||||
this._model.onDidChangeDirty(() => this._onDidChangeDirty.fire());
|
||||
this._container = document.createElement('div');
|
||||
this._container.id = `modelView-${_modelViewId}`;
|
||||
this._container.id = `modelView-${_model.modelViewId}`;
|
||||
this._partService.getContainer(Parts.EDITOR_PART).appendChild(this._container);
|
||||
|
||||
}
|
||||
@@ -37,7 +72,7 @@ export class ModelViewInput extends EditorInput {
|
||||
}
|
||||
|
||||
public get modelViewId(): string {
|
||||
return this._modelViewId;
|
||||
return this._model.modelViewId;
|
||||
}
|
||||
|
||||
public getTypeId(): string {
|
||||
@@ -85,6 +120,31 @@ export class ModelViewInput extends EditorInput {
|
||||
return this._options;
|
||||
}
|
||||
|
||||
/**
|
||||
* An editor that is dirty will be asked to be saved once it closes.
|
||||
*/
|
||||
isDirty(): boolean {
|
||||
return this._model.isDirty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should bring up a proper dialog for the user if the editor is dirty and return the result.
|
||||
*/
|
||||
confirmSave(): TPromise<ConfirmResult> {
|
||||
// TODO #2530 support save on close / confirm save. This is significantly more work
|
||||
// as we need to either integrate with textFileService (seems like this isn't viable)
|
||||
// or register our own complimentary service that handles the lifecycle operations such
|
||||
// as close all, auto save etc.
|
||||
return TPromise.wrap(ConfirmResult.DONT_SAVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the editor if it is dirty. Subclasses return a promise with a boolean indicating the success of the operation.
|
||||
*/
|
||||
save(): TPromise<boolean> {
|
||||
return this._model.save();
|
||||
}
|
||||
|
||||
public dispose(): void {
|
||||
if (this._dialogPane) {
|
||||
this._dialogPane.dispose();
|
||||
@@ -93,6 +153,9 @@ export class ModelViewInput extends EditorInput {
|
||||
this._container.remove();
|
||||
this._container = undefined;
|
||||
}
|
||||
if (this._model) {
|
||||
this._model.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ export class ToolbarItem {
|
||||
template: `
|
||||
<div #container *ngIf="items" [class]="toolbarClass" >
|
||||
<ng-container *ngFor="let item of items">
|
||||
<div class="modelview-toolbar-item" [title]="getItemTitle(item)" [style.paddingTop]="paddingTop" tabindex="0">
|
||||
<div class="modelview-toolbar-item" [style.paddingTop]="paddingTop">
|
||||
<div *ngIf="shouldShowTitle(item)" class="modelview-toolbar-title" >
|
||||
{{getItemTitle(item)}}
|
||||
</div>
|
||||
|
||||
@@ -62,4 +62,12 @@
|
||||
margin-right: 0.3em;
|
||||
background-position: 50% 50%;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.modelview-toolbar-container .modelview-toolbar-component modelview-button .monaco-text-button.active {
|
||||
-ms-transform: scale(1.272019649, 1.272019649); /* 1.272019649 = √φ */
|
||||
-webkit-transform: scale(1.272019649, 1.272019649);
|
||||
-moz-transform: scale(1.272019649, 1.272019649);
|
||||
-o-transform: scale(1.272019649, 1.272019649);
|
||||
transform: scale(1.272019649, 1.272019649);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ const viewletDescriptor = new ViewletDescriptor(
|
||||
VIEWLET_ID,
|
||||
'Servers',
|
||||
'connectionViewlet',
|
||||
-100
|
||||
0
|
||||
);
|
||||
|
||||
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor);
|
||||
|
||||
@@ -11,13 +11,14 @@ import { localize } from 'vs/nls';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
|
||||
import QueryRunner from 'sql/parts/query/execution/queryRunner';
|
||||
import { SaveFormat } from 'sql/parts/grid/common/interfaces';
|
||||
import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { GridTableState } from 'sql/parts/query/editor/gridPanel';
|
||||
import { QueryEditor } from './queryEditor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
||||
|
||||
export interface IGridActionContext {
|
||||
cell: { row: number; cell: number; };
|
||||
@@ -26,6 +27,7 @@ export interface IGridActionContext {
|
||||
batchId: number;
|
||||
resultId: number;
|
||||
table: Table<any>;
|
||||
selectionModel: CellSelectionModel<any>;
|
||||
tableState: GridTableState;
|
||||
}
|
||||
|
||||
@@ -113,7 +115,7 @@ export class SelectAllGridAction extends Action {
|
||||
}
|
||||
|
||||
public run(context: IGridActionContext): TPromise<boolean> {
|
||||
context.table.setSelectedRows(true);
|
||||
context.selectionModel.setSelectedRanges([new Slick.Range(0, 0, context.table.getData().getLength() - 1, context.table.columns.length - 1)]);
|
||||
return TPromise.as(true);
|
||||
}
|
||||
}
|
||||
@@ -167,13 +169,13 @@ export class MaximizeTableAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class MinimizeTableAction extends Action {
|
||||
public static ID = 'grid.minimize';
|
||||
public static LABEL = localize('minimize', 'Minimize');
|
||||
export class RestoreTableAction extends Action {
|
||||
public static ID = 'grid.restore';
|
||||
public static LABEL = localize('restore', 'Restore');
|
||||
public static ICON = 'exitFullScreen';
|
||||
|
||||
constructor() {
|
||||
super(MinimizeTableAction.ID, MinimizeTableAction.LABEL, MinimizeTableAction.ICON);
|
||||
super(RestoreTableAction.ID, RestoreTableAction.LABEL, RestoreTableAction.ICON);
|
||||
}
|
||||
|
||||
public run(context: IGridActionContext): TPromise<boolean> {
|
||||
|
||||
@@ -12,11 +12,13 @@ import { ScrollableSplitView } from 'sql/base/browser/ui/scrollableSplitview/scr
|
||||
import { MouseWheelSupport } from 'sql/base/browser/ui/table/plugins/mousewheelTableScroll.plugin';
|
||||
import { AutoColumnSize } from 'sql/base/browser/ui/table/plugins/autoSizeColumns.plugin';
|
||||
import { SaveFormat } from 'sql/parts/grid/common/interfaces';
|
||||
import { IGridActionContext, SaveResultAction, CopyResultAction, SelectAllGridAction, MaximizeTableAction, MinimizeTableAction, ChartDataAction, ShowQueryPlanAction } from 'sql/parts/query/editor/actions';
|
||||
import { IGridActionContext, SaveResultAction, CopyResultAction, SelectAllGridAction, MaximizeTableAction, RestoreTableAction, ChartDataAction, ShowQueryPlanAction } from 'sql/parts/query/editor/actions';
|
||||
import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin';
|
||||
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
|
||||
import { escape } from 'sql/base/common/strings';
|
||||
import { hyperLinkFormatter, textFormatter } from 'sql/parts/grid/services/sharedServices';
|
||||
import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin';
|
||||
import { AdditionalKeyBindings } from 'sql/base/browser/ui/table/plugins/additionalKeyBindings.plugin';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
|
||||
@@ -38,20 +40,20 @@ import { Dimension, getContentWidth } from 'vs/base/browser/dom';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { CopyKeybind } from 'sql/base/browser/ui/table/plugins/copyKeybind.plugin';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
|
||||
const ROW_HEIGHT = 29;
|
||||
const HEADER_HEIGHT = 26;
|
||||
const MIN_GRID_HEIGHT_ROWS = 8;
|
||||
const ESTIMATED_SCROLL_BAR_HEIGHT = 10;
|
||||
const BOTTOM_PADDING = 5;
|
||||
const BOTTOM_PADDING = 15;
|
||||
const ACTIONBAR_WIDTH = 26;
|
||||
|
||||
// minimum height needed to show the full actionbar
|
||||
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 + BOTTOM_PADDING;
|
||||
const MIN_GRID_HEIGHT = (MIN_GRID_HEIGHT_ROWS * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT;
|
||||
|
||||
|
||||
export interface IGridTableState {
|
||||
@@ -66,15 +68,30 @@ export class GridTableState {
|
||||
private _onMaximizedChange = new Emitter<boolean>();
|
||||
public onMaximizedChange: Event<boolean> = this._onMaximizedChange.event;
|
||||
|
||||
public canBeMaximized: boolean;
|
||||
private _onCanBeMaximizedChange = new Emitter<boolean>();
|
||||
public onCanBeMaximizedChange: Event<boolean> = this._onCanBeMaximizedChange.event;
|
||||
|
||||
private _canBeMaximized: boolean;
|
||||
|
||||
constructor(state?: IGridTableState) {
|
||||
if (state) {
|
||||
this._maximized = state.maximized;
|
||||
this.canBeMaximized = state.canBeMaximized;
|
||||
this._canBeMaximized = state.canBeMaximized;
|
||||
}
|
||||
}
|
||||
|
||||
public get canBeMaximized(): boolean {
|
||||
return this._canBeMaximized;
|
||||
}
|
||||
|
||||
public set canBeMaximized(val: boolean) {
|
||||
if (val === this._canBeMaximized) {
|
||||
return;
|
||||
}
|
||||
this._canBeMaximized = val;
|
||||
this._onCanBeMaximizedChange.fire(val);
|
||||
}
|
||||
|
||||
public get maximized(): boolean {
|
||||
return this._maximized;
|
||||
}
|
||||
@@ -243,7 +260,7 @@ class GridTable<T> extends Disposable implements IView {
|
||||
readonly element: HTMLElement = this.container;
|
||||
|
||||
// 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 + BOTTOM_PADDING;
|
||||
private readonly maxSize = ((this.resultSet.rowCount) * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT;
|
||||
|
||||
constructor(
|
||||
private runner: QueryRunner,
|
||||
@@ -257,7 +274,7 @@ class GridTable<T> extends Disposable implements IView {
|
||||
super();
|
||||
this.container.style.width = '100%';
|
||||
this.container.style.height = '100%';
|
||||
this.container.style.marginBottom = BOTTOM_PADDING + 'px';
|
||||
// this.container.style.marginBottom = BOTTOM_PADDING + 'px';
|
||||
this.container.className = 'grid-panel';
|
||||
|
||||
this.columns = this.resultSet.columnInfo.map((c, i) => {
|
||||
@@ -294,15 +311,7 @@ class GridTable<T> extends Disposable implements IView {
|
||||
let numberColumn = new RowNumberColumn({ numberOfRows: this.resultSet.rowCount });
|
||||
let copyHandler = new CopyKeybind();
|
||||
copyHandler.onCopy(e => {
|
||||
new CopyResultAction(CopyResultAction.COPY_ID, CopyResultAction.COPY_LABEL, false).run({
|
||||
selection: e,
|
||||
batchId: this.resultSet.batchId,
|
||||
resultId: this.resultSet.id,
|
||||
cell: this.table.grid.getActiveCell(),
|
||||
runner: this.runner,
|
||||
table: this.table,
|
||||
tableState: this.state
|
||||
});
|
||||
new CopyResultAction(CopyResultAction.COPY_ID, CopyResultAction.COPY_LABEL, false).run(this.generateContext());
|
||||
});
|
||||
this.columns.unshift(numberColumn.getColumnDefinition());
|
||||
let tableOptions: Slick.GridOptions<T> = {
|
||||
@@ -317,6 +326,7 @@ class GridTable<T> extends Disposable implements IView {
|
||||
this.table.registerPlugin(new AutoColumnSize());
|
||||
this.table.registerPlugin(copyHandler);
|
||||
this.table.registerPlugin(numberColumn);
|
||||
this.table.registerPlugin(new AdditionalKeyBindings());
|
||||
this._register(this.table.onContextMenu(this.contextMenu, this));
|
||||
this._register(this.table.onClick(this.onTableClick, this));
|
||||
|
||||
@@ -324,22 +334,7 @@ class GridTable<T> extends Disposable implements IView {
|
||||
this.table.style(this.styles);
|
||||
}
|
||||
|
||||
let actions = [];
|
||||
|
||||
if (this.state.canBeMaximized) {
|
||||
if (this.state.maximized) {
|
||||
actions.splice(1, 0, new MinimizeTableAction());
|
||||
} else {
|
||||
actions.splice(1, 0, new MaximizeTableAction());
|
||||
}
|
||||
}
|
||||
|
||||
actions.push(
|
||||
new SaveResultAction(SaveResultAction.SAVECSV_ID, SaveResultAction.SAVECSV_LABEL, SaveResultAction.SAVECSV_ICON, SaveFormat.CSV),
|
||||
new SaveResultAction(SaveResultAction.SAVEEXCEL_ID, SaveResultAction.SAVEEXCEL_LABEL, SaveResultAction.SAVEEXCEL_ICON, SaveFormat.EXCEL),
|
||||
new SaveResultAction(SaveResultAction.SAVEJSON_ID, SaveResultAction.SAVEJSON_LABEL, SaveResultAction.SAVEJSON_ICON, SaveFormat.JSON),
|
||||
this.instantiationService.createInstance(ChartDataAction)
|
||||
);
|
||||
let actions = this.getCurrentActions();
|
||||
|
||||
let actionBarContainer = document.createElement('div');
|
||||
actionBarContainer.style.width = ACTIONBAR_WIDTH + 'px';
|
||||
@@ -356,7 +351,16 @@ class GridTable<T> extends Disposable implements IView {
|
||||
tableState: this.state
|
||||
}
|
||||
});
|
||||
// update context before we run an action
|
||||
this.selectionModel.onSelectedRangesChanged.subscribe(e => {
|
||||
this.actionBar.context = this.generateContext();
|
||||
});
|
||||
this.actionBar.push(actions, { icon: true, label: false });
|
||||
|
||||
// change actionbar on maximize change
|
||||
this.state.onMaximizedChange(this.rebuildActionBar, this);
|
||||
|
||||
this.state.onCanBeMaximizedChange(this.rebuildActionBar, this);
|
||||
}
|
||||
|
||||
private onTableClick(event: ITableMouseEvent) {
|
||||
@@ -372,6 +376,48 @@ class GridTable<T> extends Disposable implements IView {
|
||||
}
|
||||
}
|
||||
|
||||
private generateContext(cell?: Slick.Cell): IGridActionContext {
|
||||
const selection = this.selectionModel.getSelectedRanges();
|
||||
return <IGridActionContext>{
|
||||
cell,
|
||||
selection,
|
||||
runner: this.runner,
|
||||
batchId: this.resultSet.batchId,
|
||||
resultId: this.resultSet.id,
|
||||
table: this.table,
|
||||
tableState: this.state,
|
||||
selectionModel: this.selectionModel
|
||||
};
|
||||
}
|
||||
|
||||
private rebuildActionBar() {
|
||||
let actions = this.getCurrentActions();
|
||||
this.actionBar.clear();
|
||||
this.actionBar.push(actions, { icon: true, label: false });
|
||||
}
|
||||
|
||||
private getCurrentActions(): IAction[] {
|
||||
|
||||
let actions = [];
|
||||
|
||||
if (this.state.canBeMaximized) {
|
||||
if (this.state.maximized) {
|
||||
actions.splice(1, 0, new RestoreTableAction());
|
||||
} else {
|
||||
actions.splice(1, 0, new MaximizeTableAction());
|
||||
}
|
||||
}
|
||||
|
||||
actions.push(
|
||||
new SaveResultAction(SaveResultAction.SAVECSV_ID, SaveResultAction.SAVECSV_LABEL, SaveResultAction.SAVECSV_ICON, SaveFormat.CSV),
|
||||
new SaveResultAction(SaveResultAction.SAVEEXCEL_ID, SaveResultAction.SAVEEXCEL_LABEL, SaveResultAction.SAVEEXCEL_ICON, SaveFormat.EXCEL),
|
||||
new SaveResultAction(SaveResultAction.SAVEJSON_ID, SaveResultAction.SAVEJSON_LABEL, SaveResultAction.SAVEJSON_ICON, SaveFormat.JSON),
|
||||
this.instantiationService.createInstance(ChartDataAction)
|
||||
);
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
public layout(size?: number): void {
|
||||
if (!this.table) {
|
||||
this.build();
|
||||
@@ -384,18 +430,18 @@ class GridTable<T> extends Disposable implements IView {
|
||||
this.table.layout(
|
||||
new Dimension(
|
||||
getContentWidth(this.container) - ACTIONBAR_WIDTH,
|
||||
size - BOTTOM_PADDING
|
||||
size
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public get minimumSize(): number {
|
||||
// clamp between ensuring we can show the actionbar, while also making sure we don't take too much space
|
||||
return Math.max(Math.min(this.maxSize, MIN_GRID_HEIGHT), ACTIONBAR_HEIGHT);
|
||||
return Math.max(Math.min(this.maxSize, MIN_GRID_HEIGHT), ACTIONBAR_HEIGHT + BOTTOM_PADDING);
|
||||
}
|
||||
|
||||
public get maximumSize(): number {
|
||||
return Math.max(this.maxSize, ACTIONBAR_HEIGHT);
|
||||
return Math.max(this.maxSize, ACTIONBAR_HEIGHT + BOTTOM_PADDING);
|
||||
}
|
||||
|
||||
private loadData(offset: number, count: number): Thenable<T[]> {
|
||||
@@ -419,7 +465,6 @@ class GridTable<T> extends Disposable implements IView {
|
||||
}
|
||||
|
||||
private contextMenu(e: ITableMouseEvent): void {
|
||||
const selection = this.selectionModel.getSelectedRanges();
|
||||
const { cell } = e;
|
||||
this.contextMenuService.showContextMenu({
|
||||
getAnchor: () => e.anchor,
|
||||
@@ -437,7 +482,7 @@ class GridTable<T> extends Disposable implements IView {
|
||||
|
||||
if (this.state.canBeMaximized) {
|
||||
if (this.state.maximized) {
|
||||
actions.splice(1, 0, new MinimizeTableAction());
|
||||
actions.splice(1, 0, new RestoreTableAction());
|
||||
} else {
|
||||
actions.splice(1, 0, new MaximizeTableAction());
|
||||
}
|
||||
@@ -446,15 +491,7 @@ class GridTable<T> extends Disposable implements IView {
|
||||
return TPromise.as(actions);
|
||||
},
|
||||
getActionsContext: () => {
|
||||
return <IGridActionContext>{
|
||||
cell,
|
||||
selection,
|
||||
runner: this.runner,
|
||||
batchId: this.resultSet.batchId,
|
||||
resultId: this.resultSet.id,
|
||||
table: this.table,
|
||||
tableState: this.state
|
||||
};
|
||||
return this.generateContext(cell);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -13,18 +13,28 @@
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.monaco-workbench .message-tree .monaco-tree .monaco-tree-rows>.monaco-tree-row {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.message-tree .time-stamp {
|
||||
width: 100px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.message-tree .message,
|
||||
.message-tree .batch-start {
|
||||
.message-tree .batch-start,
|
||||
.message-tree .error-message {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.message-tree .batch-start {
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.message-tree .error-message {
|
||||
color: red;
|
||||
}
|
||||
|
||||
.message-tree .batch-start:hover {
|
||||
|
||||
@@ -8,7 +8,7 @@ import 'vs/css!./media/messagePanel';
|
||||
import { IMessagesActionContext, SelectAllMessagesAction, CopyMessagesAction } from './actions';
|
||||
import QueryRunner from 'sql/parts/query/execution/queryRunner';
|
||||
|
||||
import { IResultMessage, BatchSummary, ISelectionData } from 'sqlops';
|
||||
import { IResultMessage, ISelectionData } from 'sqlops';
|
||||
|
||||
import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet';
|
||||
import { IDataSource, ITree, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree';
|
||||
@@ -27,7 +27,6 @@ import { IMouseEvent } from 'vs/base/browser/mouseEvent';
|
||||
import { $ } from 'vs/base/browser/builder';
|
||||
import { isArray } from 'vs/base/common/types';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { localize } from 'vs/nls';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditor } from 'vs/editor/common/editorCommon';
|
||||
|
||||
@@ -56,7 +55,8 @@ interface IBatchTemplate extends IMessageTemplate {
|
||||
const TemplateIds = {
|
||||
MESSAGE: 'message',
|
||||
BATCH: 'batch',
|
||||
MODEL: 'model'
|
||||
MODEL: 'model',
|
||||
ERROR: 'error'
|
||||
};
|
||||
|
||||
export class MessagePanel extends ViewletPanel {
|
||||
@@ -113,11 +113,17 @@ export class MessagePanel extends ViewletPanel {
|
||||
}
|
||||
|
||||
private onMessage(message: IResultMessage | IResultMessage[]) {
|
||||
let hasError = false;
|
||||
if (isArray(message)) {
|
||||
hasError = message.find(e => e.isError) ? true : false;
|
||||
this.model.messages.push(...message);
|
||||
} else {
|
||||
hasError = message.isError;
|
||||
this.model.messages.push(message);
|
||||
}
|
||||
if (hasError) {
|
||||
this.setExpanded(true);
|
||||
}
|
||||
const previousScrollPosition = this.tree.getScrollPosition();
|
||||
this.tree.refresh(this.model).then(() => {
|
||||
if (previousScrollPosition === 1) {
|
||||
@@ -176,6 +182,8 @@ class MessageRenderer implements IRenderer {
|
||||
return TemplateIds.MODEL;
|
||||
} else if (element.selection) {
|
||||
return TemplateIds.BATCH;
|
||||
} else if (element.isError) {
|
||||
return TemplateIds.ERROR;
|
||||
} else {
|
||||
return TemplateIds.MESSAGE;
|
||||
}
|
||||
@@ -191,15 +199,19 @@ class MessageRenderer implements IRenderer {
|
||||
const timeStamp = $('div.time-stamp').appendTo(container).getHTMLElement();
|
||||
const message = $('div.batch-start').appendTo(container).getHTMLElement();
|
||||
return { message, timeStamp };
|
||||
} else if (templateId === TemplateIds.ERROR) {
|
||||
$('div.time-stamp').appendTo(container);
|
||||
const message = $('div.error-message').appendTo(container).getHTMLElement();
|
||||
return { message };
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
renderElement(tree: ITree, element: IResultMessage, templateId: string, templateData: IMessageTemplate | IBatchTemplate): void {
|
||||
if (templateId === TemplateIds.MESSAGE) {
|
||||
if (templateId === TemplateIds.MESSAGE || templateId === TemplateIds.ERROR) {
|
||||
let data: IMessageTemplate = templateData;
|
||||
data.message.innerText = element.message;
|
||||
data.message.innerText = element.message.replace(/(\r\n|\n|\r)/g, ' ');
|
||||
} else if (templateId === TemplateIds.BATCH) {
|
||||
let data = templateData as IBatchTemplate;
|
||||
data.timeStamp.innerText = element.time;
|
||||
|
||||
@@ -93,6 +93,7 @@ export class QueryResultsEditor extends BaseEditor {
|
||||
protected _input: QueryResultsInput;
|
||||
|
||||
private resultsView: QueryResultsView;
|
||||
private styleSheet = DOM.createStyleSheet();
|
||||
|
||||
constructor(
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@@ -103,12 +104,13 @@ export class QueryResultsEditor extends BaseEditor {
|
||||
) {
|
||||
super(QueryResultsEditor.ID, telemetryService, themeService);
|
||||
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();
|
||||
// }
|
||||
// });
|
||||
this._configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('resultsGrid')) {
|
||||
this._rawOptions = BareResultsGridInfo.createFromRawSettings(this._configurationService.getValue('resultsGrid'), getZoomLevel());
|
||||
this.applySettings();
|
||||
}
|
||||
});
|
||||
this.applySettings();
|
||||
}
|
||||
|
||||
public get input(): QueryResultsInput {
|
||||
@@ -116,23 +118,20 @@ export class QueryResultsEditor extends BaseEditor {
|
||||
}
|
||||
|
||||
private applySettings() {
|
||||
if (this.input && this.input.container) {
|
||||
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}; }`;
|
||||
content += `.grid { ${getBareResultsGridInfoStyles(this._rawOptions)} }`;
|
||||
this.input.css.innerHTML = content;
|
||||
let cssRuleText = '';
|
||||
if (types.isNumber(this._rawOptions.cellPadding)) {
|
||||
cssRuleText = this._rawOptions.cellPadding + 'px';
|
||||
} else {
|
||||
cssRuleText = this._rawOptions.cellPadding.join('px ') + 'px;';
|
||||
}
|
||||
let content = `.grid-panel .monaco-table .slick-cell { padding: ${cssRuleText} }`;
|
||||
content += `.grid-panel .monaco-table { ${getBareResultsGridInfoStyles(this._rawOptions)} }`;
|
||||
this.styleSheet.innerHTML = content;
|
||||
}
|
||||
|
||||
createEditor(parent: HTMLElement): void {
|
||||
this.styleSheet.remove();
|
||||
parent.appendChild(this.styleSheet);
|
||||
if (!this.resultsView) {
|
||||
this.resultsView = new QueryResultsView(parent, this._instantiationService, this._queryModelService);
|
||||
}
|
||||
|
||||
@@ -26,24 +26,60 @@ 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;
|
||||
|
||||
constructor(instantiationService: IInstantiationService) {
|
||||
this.panelViewlet = instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
|
||||
this.gridPanel = instantiationService.createInstance(GridPanel, { title: nls.localize('gridPanel', 'Results') });
|
||||
this.messagePanel = instantiationService.createInstance(MessagePanel, { title: nls.localize('messagePanel', 'Messages') });
|
||||
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' });
|
||||
this.gridPanel.render();
|
||||
this.messagePanel.render();
|
||||
this.panelViewlet.create(this.container).then(() => {
|
||||
this.panelViewlet.addPanels([
|
||||
{ panel: this.gridPanel, size: 1000, index: 0 },
|
||||
{ panel: this.messagePanel, size: this.messagePanel.minimumSize, index: 1 }
|
||||
]);
|
||||
});
|
||||
this.gridPanel.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;
|
||||
}
|
||||
} else {
|
||||
this.panelViewlet.addPanels([
|
||||
{ panel: this.gridPanel, index: 0, size: this.lastGridHeight || 200 }
|
||||
]);
|
||||
this.isGridRendered = true;
|
||||
this.needsGridResize = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
let gridResizeList = this.gridPanel.onDidChange(e => {
|
||||
this.panelViewlet.resizePanel(this.gridPanel, this.gridPanel.maximumSize);
|
||||
if (this.currentDimension) {
|
||||
this.needsGridResize = false;
|
||||
this.panelViewlet.resizePanel(this.gridPanel, Math.round(this.currentDimension.height * .7));
|
||||
} else {
|
||||
this.needsGridResize = true;
|
||||
}
|
||||
});
|
||||
// once the user changes the sash we should stop trying to resize the grid
|
||||
once(this.panelViewlet.onDidSashChange)(e => {
|
||||
this.needsGridResize = false;
|
||||
gridResizeList.dispose();
|
||||
});
|
||||
}
|
||||
@@ -59,6 +95,9 @@ class ResultsView implements IPanelView {
|
||||
this.gridPanel.layout(dimension.height);
|
||||
}
|
||||
this.currentDimension = dimension;
|
||||
if (this.needsGridResize) {
|
||||
this.panelViewlet.resizePanel(this.gridPanel, Math.round(this.currentDimension.height * .7));
|
||||
}
|
||||
}
|
||||
|
||||
remove(): void {
|
||||
|
||||
@@ -12,7 +12,7 @@ import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
|
||||
import { IQueryManagementService } from 'sql/parts/query/common/queryManagement';
|
||||
import * as Utils from 'sql/parts/connection/common/utils';
|
||||
import { SaveFormat } from 'sql/parts/grid/common/interfaces';
|
||||
import { echo } from 'sql/base/common/event';
|
||||
import { echo, debounceEvent } from 'sql/base/common/event';
|
||||
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IWorkspaceConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
|
||||
@@ -22,9 +22,10 @@ import * as types from 'vs/base/common/types';
|
||||
import { EventEmitter } from 'sql/base/common/eventEmitter';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { Emitter, debounceEvent, Event } from 'vs/base/common/event';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ResultSerializer } from 'sql/parts/query/common/resultSerializer';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
|
||||
export interface IEditSessionReadyEvent {
|
||||
ownerUri: string;
|
||||
@@ -73,25 +74,27 @@ export default class QueryRunner {
|
||||
public get isQueryPlan(): boolean { return this._isQueryPlan; }
|
||||
|
||||
private _onMessage = new Emitter<sqlops.IResultMessage>();
|
||||
private _echoedMessages = echo(debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(this._onMessage.event, (l, e) => {
|
||||
private _debouncedMessage = debounceEvent<sqlops.IResultMessage, sqlops.IResultMessage[]>(this._onMessage.event, (l, e) => {
|
||||
// on first run
|
||||
if (types.isUndefinedOrNull(l)) {
|
||||
return [e];
|
||||
} else {
|
||||
return l.concat(e);
|
||||
}
|
||||
}));
|
||||
});
|
||||
private _echoedMessages = echo(this._debouncedMessage.event);
|
||||
public readonly onMessage = this._echoedMessages.event;
|
||||
|
||||
private _onResultSet = new Emitter<sqlops.ResultSetSummary>();
|
||||
private _echoedResultSet = echo(debounceEvent<sqlops.ResultSetSummary, sqlops.ResultSetSummary[]>(this._onResultSet.event, (l, e) => {
|
||||
private _debouncedResultSet = debounceEvent<sqlops.ResultSetSummary, sqlops.ResultSetSummary[]>(this._onResultSet.event, (l, e) => {
|
||||
// on first run
|
||||
if (types.isUndefinedOrNull(l)) {
|
||||
return [e];
|
||||
} else {
|
||||
return l.concat(e);
|
||||
}
|
||||
}));
|
||||
});
|
||||
private _echoedResultSet = echo(this._debouncedResultSet.event);
|
||||
public readonly onResultSet = this._echoedResultSet.event;
|
||||
|
||||
private _onQueryStart = new Emitter<void>();
|
||||
@@ -171,8 +174,13 @@ export default class QueryRunner {
|
||||
private doRunQuery(input: string, runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): Thenable<void>;
|
||||
private doRunQuery(input: sqlops.ISelectionData, runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): Thenable<void>;
|
||||
private doRunQuery(input, runCurrentStatement: boolean, runOptions?: sqlops.ExecutionPlanOptions): Thenable<void> {
|
||||
if (this.isExecuting) {
|
||||
return TPromise.as(undefined);
|
||||
}
|
||||
this._echoedMessages.clear();
|
||||
this._echoedResultSet.clear();
|
||||
this._debouncedMessage.clear();
|
||||
this._debouncedResultSet.clear();
|
||||
let ownerUri = this.uri;
|
||||
this._batchSets = [];
|
||||
this._hasCompleted = false;
|
||||
|
||||
@@ -93,7 +93,7 @@ const viewletDescriptor = new ViewletDescriptor(
|
||||
VIEWLET_ID,
|
||||
'Task History',
|
||||
'taskHistoryViewlet',
|
||||
-90
|
||||
1
|
||||
);
|
||||
|
||||
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor);
|
||||
|
||||
@@ -48,9 +48,7 @@ export abstract class Task {
|
||||
id: this.id,
|
||||
handler: (accessor, profile, args) => this.runTask(accessor, profile, args),
|
||||
description: this._description,
|
||||
iconClass: this._iconClass,
|
||||
iconPath: this.opts.iconPath,
|
||||
title: this.title
|
||||
iconClass: this._iconClass
|
||||
};
|
||||
}
|
||||
|
||||
@@ -62,10 +60,8 @@ export abstract class Task {
|
||||
};
|
||||
}
|
||||
|
||||
public registerTask(showInCommandPalette: boolean = true): IDisposable {
|
||||
if (showInCommandPalette) {
|
||||
MenuRegistry.addCommand(this.toCommandAction());
|
||||
}
|
||||
public registerTask(): IDisposable {
|
||||
MenuRegistry.addCommand(this.toCommandAction());
|
||||
return TaskRegistry.registerTask(this.toITask());
|
||||
}
|
||||
|
||||
@@ -100,8 +96,6 @@ export interface ITask {
|
||||
precondition?: ContextKeyExpr;
|
||||
description?: ITaskHandlerDescription;
|
||||
iconClass?: string;
|
||||
iconPath?: { dark: string; light?: string; };
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface ITaskRegistry {
|
||||
@@ -110,7 +104,6 @@ export interface ITaskRegistry {
|
||||
getTasks(): string[];
|
||||
getOrCreateTaskIconClassName(item: ICommandAction): string;
|
||||
onTaskRegistered: Event<string>;
|
||||
getCommandActionById(id: string): ICommandAction;
|
||||
}
|
||||
|
||||
const ids = new IdGenerator('task-icon-');
|
||||
@@ -121,7 +114,6 @@ export const TaskRegistry: ITaskRegistry = new class implements ITaskRegistry {
|
||||
private _onTaskRegistered = new Emitter<string>();
|
||||
public readonly onTaskRegistered: Event<string> = this._onTaskRegistered.event;
|
||||
private taskIdToIconClassNameMap: Map<string /* task id */, string /* CSS rule */> = new Map<string, string>();
|
||||
private taskIdToCommandActionMap: Map<string, ICommandAction> = new Map<string, ICommandAction>();
|
||||
|
||||
registerTask(idOrTask: string | ITask, handler?: ITaskHandler): IDisposable {
|
||||
let disposable: IDisposable;
|
||||
@@ -133,16 +125,6 @@ export const TaskRegistry: ITaskRegistry = new class implements ITaskRegistry {
|
||||
if (idOrTask.iconClass) {
|
||||
this.taskIdToIconClassNameMap.set(idOrTask.id, idOrTask.iconClass);
|
||||
}
|
||||
if (idOrTask.iconPath && idOrTask.title) {
|
||||
this.taskIdToCommandActionMap.set(idOrTask.id, {
|
||||
iconLocation: {
|
||||
dark: URI.parse(idOrTask.iconPath.dark),
|
||||
light: URI.parse(idOrTask.iconPath.light),
|
||||
},
|
||||
id: idOrTask.id,
|
||||
title: idOrTask.title
|
||||
});
|
||||
}
|
||||
disposable = CommandsRegistry.registerCommand(idOrTask);
|
||||
id = idOrTask.id;
|
||||
}
|
||||
@@ -177,8 +159,4 @@ export const TaskRegistry: ITaskRegistry = new class implements ITaskRegistry {
|
||||
getTasks(): string[] {
|
||||
return this._tasks.slice(0);
|
||||
}
|
||||
|
||||
getCommandActionById(taskId: string): ICommandAction {
|
||||
return this.taskIdToCommandActionMap.get(taskId);
|
||||
}
|
||||
};
|
||||
|
||||
76
src/sql/sqlops.proposed.d.ts
vendored
76
src/sql/sqlops.proposed.d.ts
vendored
@@ -17,6 +17,7 @@ declare module 'sqlops' {
|
||||
*/
|
||||
export interface ModelBuilder {
|
||||
navContainer(): ContainerBuilder<NavContainer, any, any>;
|
||||
divContainer(): DivBuilder;
|
||||
flexContainer(): FlexBuilder;
|
||||
card(): ComponentBuilder<CardComponent>;
|
||||
inputBox(): ComponentBuilder<InputBoxComponent>;
|
||||
@@ -72,6 +73,10 @@ declare module 'sqlops' {
|
||||
|
||||
}
|
||||
|
||||
export interface DivBuilder extends ContainerBuilder<DivContainer, DivLayout, DivItemLayout> {
|
||||
|
||||
}
|
||||
|
||||
export interface GroupBuilder extends ContainerBuilder<GroupContainer, GroupLayout, GroupItemLayout> {
|
||||
}
|
||||
|
||||
@@ -346,6 +351,33 @@ declare module 'sqlops' {
|
||||
export interface GroupItemLayout {
|
||||
}
|
||||
|
||||
export interface DivLayout {
|
||||
/**
|
||||
* Container Height
|
||||
*/
|
||||
height?: number | string;
|
||||
|
||||
/**
|
||||
* Container Width
|
||||
*/
|
||||
width?: number | string;
|
||||
}
|
||||
|
||||
export interface DivItemLayout {
|
||||
/**
|
||||
* Matches the order CSS property and its available values.
|
||||
*/
|
||||
order?: number;
|
||||
|
||||
/**
|
||||
* Matches the CSS style key and its available values.
|
||||
*/
|
||||
CSSStyles?: { [key: string]: string };
|
||||
}
|
||||
|
||||
export interface DivContainer extends Container<DivLayout, DivItemLayout>, DivContainerProperties {
|
||||
}
|
||||
|
||||
export interface FlexContainer extends Container<FlexLayout, FlexItemLayout> {
|
||||
}
|
||||
|
||||
@@ -561,12 +593,26 @@ declare module 'sqlops' {
|
||||
label?: string;
|
||||
isFile?: boolean;
|
||||
fileContent?: string;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export interface LoadingComponentProperties {
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
export interface DivContainerProperties extends ComponentProperties {
|
||||
/**
|
||||
* Matches the overflow-y CSS property and its available values.
|
||||
*/
|
||||
overflowY?: string;
|
||||
|
||||
/**
|
||||
* Setting the scroll based on the y offset
|
||||
* This is used when its child component is webview
|
||||
*/
|
||||
yOffsetChange?: number;
|
||||
}
|
||||
|
||||
export interface CardComponent extends Component, CardProperties {
|
||||
onDidActionClick: vscode.Event<ActionDescriptor>;
|
||||
onCardSelectedChanged: vscode.Event<any>;
|
||||
@@ -662,8 +708,22 @@ declare module 'sqlops' {
|
||||
}
|
||||
|
||||
export interface ButtonComponent extends Component, ButtonProperties {
|
||||
/**
|
||||
* The label for the button
|
||||
*/
|
||||
label: string;
|
||||
/**
|
||||
* The title for the button. This title will show when it hovers
|
||||
*/
|
||||
title: string;
|
||||
/**
|
||||
* Icon Path for the button.
|
||||
*/
|
||||
iconPath: string | vscode.Uri | { light: string | vscode.Uri; dark: string | vscode.Uri };
|
||||
|
||||
/**
|
||||
* An event called when the button is clicked
|
||||
*/
|
||||
onDidClick: vscode.Event<any>;
|
||||
}
|
||||
|
||||
@@ -1100,11 +1160,22 @@ declare module 'sqlops' {
|
||||
export function createModelViewEditor(title: string, options?: ModelViewEditorOptions): ModelViewEditor;
|
||||
|
||||
export interface ModelViewEditor extends window.modelviewdialog.ModelViewPanel {
|
||||
/**
|
||||
* `true` if there are unpersisted changes.
|
||||
* This is editable to support extensions updating the dirty status.
|
||||
*/
|
||||
isDirty: boolean;
|
||||
|
||||
/**
|
||||
* Opens the editor
|
||||
*/
|
||||
openEditor(position?: vscode.ViewColumn): Thenable<void>;
|
||||
|
||||
/**
|
||||
* Registers a save handler for this editor. This will be called if [supportsSave](#ModelViewEditorOptions.supportsSave)
|
||||
* is set to true and the editor is marked as dirty
|
||||
*/
|
||||
registerSaveHandler(handler: () => Thenable<boolean>);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1113,6 +1184,11 @@ declare module 'sqlops' {
|
||||
* Should the model view editor's context be kept around even when the editor is no longer visible? It is false by default
|
||||
*/
|
||||
readonly retainContextWhenHidden?: boolean;
|
||||
|
||||
/**
|
||||
* Does this model view editor support save?
|
||||
*/
|
||||
readonly supportsSave?: boolean;
|
||||
}
|
||||
|
||||
export enum DataProviderType {
|
||||
|
||||
@@ -131,6 +131,7 @@ export enum FrequencyRelativeIntervals {
|
||||
|
||||
export enum ModelComponentTypes {
|
||||
NavContainer,
|
||||
DivContainer,
|
||||
FlexContainer,
|
||||
Card,
|
||||
InputBox,
|
||||
|
||||
@@ -90,14 +90,16 @@ export class ExtHostAccountManagement extends ExtHostAccountManagementShape {
|
||||
}
|
||||
|
||||
public $getSecurityToken(account: sqlops.Account): Thenable<{}> {
|
||||
for (const handle in this._accounts) {
|
||||
const providerHandle = parseInt(handle);
|
||||
if (this._accounts[handle].findIndex((acct) => acct.key.accountId === account.key.accountId) !== -1) {
|
||||
return this._withProvider(providerHandle, (provider: sqlops.AccountProvider) => provider.getSecurityToken(account));
|
||||
return this.$getAllAccounts().then(() => {
|
||||
for (const handle in this._accounts) {
|
||||
const providerHandle = parseInt(handle);
|
||||
if (this._accounts[handle].findIndex((acct) => acct.key.accountId === account.key.accountId) !== -1) {
|
||||
return this._withProvider(providerHandle, (provider: sqlops.AccountProvider) => provider.getSecurityToken(account));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Account ${account.key.accountId} not found.`);
|
||||
throw new Error(`Account ${account.key.accountId} not found.`);
|
||||
});
|
||||
}
|
||||
|
||||
public get onDidChangeAccounts(): Event<sqlops.DidChangeAccountsParams> {
|
||||
|
||||
@@ -32,14 +32,21 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
|
||||
|
||||
navContainer(): sqlops.ContainerBuilder<sqlops.NavContainer, any, any> {
|
||||
let id = this.getNextComponentId();
|
||||
let container: ContainerBuilderImpl<sqlops.NavContainer, any, any> = new ContainerBuilderImpl(this._proxy, this._handle, ModelComponentTypes.NavContainer, id);
|
||||
let container: GenericContainerBuilder<sqlops.NavContainer, any, any> = new GenericContainerBuilder(this._proxy, this._handle, ModelComponentTypes.NavContainer, id);
|
||||
this._componentBuilders.set(id, container);
|
||||
return container;
|
||||
}
|
||||
|
||||
divContainer(): sqlops.DivBuilder {
|
||||
let id = this.getNextComponentId();
|
||||
let container = new DivContainerBuilder(this._proxy, this._handle, ModelComponentTypes.DivContainer, id);
|
||||
this._componentBuilders.set(id, container);
|
||||
return container;
|
||||
}
|
||||
|
||||
flexContainer(): sqlops.FlexBuilder {
|
||||
let id = this.getNextComponentId();
|
||||
let container: ContainerBuilderImpl<sqlops.FlexContainer, any, any> = new ContainerBuilderImpl<sqlops.FlexContainer, sqlops.FlexLayout, sqlops.FlexItemLayout>(this._proxy, this._handle, ModelComponentTypes.FlexContainer, id);
|
||||
let container: GenericContainerBuilder<sqlops.FlexContainer, any, any> = new GenericContainerBuilder<sqlops.FlexContainer, sqlops.FlexLayout, sqlops.FlexItemLayout>(this._proxy, this._handle, ModelComponentTypes.FlexContainer, id);
|
||||
this._componentBuilders.set(id, container);
|
||||
return container;
|
||||
}
|
||||
@@ -60,7 +67,7 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
|
||||
|
||||
groupContainer(): sqlops.GroupBuilder {
|
||||
let id = this.getNextComponentId();
|
||||
let container: ContainerBuilderImpl<sqlops.GroupContainer, any, any> = new ContainerBuilderImpl<sqlops.GroupContainer, sqlops.GroupLayout, sqlops.GroupItemLayout>(this._proxy, this._handle, ModelComponentTypes.Group, id);
|
||||
let container: GenericContainerBuilder<sqlops.GroupContainer, any, any> = new GenericContainerBuilder<sqlops.GroupContainer, sqlops.GroupLayout, sqlops.GroupItemLayout>(this._proxy, this._handle, ModelComponentTypes.Group, id);
|
||||
this._componentBuilders.set(id, container);
|
||||
return container;
|
||||
}
|
||||
@@ -241,17 +248,9 @@ class ComponentBuilderImpl<T extends sqlops.Component> implements sqlops.Compone
|
||||
}
|
||||
}
|
||||
|
||||
class GenericComponentBuilder<T extends sqlops.Component> extends ComponentBuilderImpl<T> {
|
||||
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
|
||||
super(new ComponentWrapper(proxy, handle, type, id));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
class ContainerBuilderImpl<T extends sqlops.Component, TLayout, TItemLayout> extends ComponentBuilderImpl<T> implements sqlops.ContainerBuilder<T, TLayout, TItemLayout> {
|
||||
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
|
||||
super(new ComponentWrapper(proxy, handle, type, id));
|
||||
constructor(componentWrapper: ComponentWrapper) {
|
||||
super(componentWrapper);
|
||||
}
|
||||
|
||||
withLayout(layout: TLayout): sqlops.ContainerBuilder<T, TLayout, TItemLayout> {
|
||||
@@ -268,7 +267,19 @@ class ContainerBuilderImpl<T extends sqlops.Component, TLayout, TItemLayout> ext
|
||||
}
|
||||
}
|
||||
|
||||
class FormContainerBuilder extends ContainerBuilderImpl<sqlops.FormContainer, sqlops.FormLayout, sqlops.FormItemLayout> implements sqlops.FormBuilder {
|
||||
class GenericContainerBuilder<T extends sqlops.Component, TLayout, TItemLayout> extends ContainerBuilderImpl<T, TLayout, TItemLayout> {
|
||||
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
|
||||
super(new ComponentWrapper(proxy, handle, type, id));
|
||||
}
|
||||
}
|
||||
|
||||
class DivContainerBuilder extends ContainerBuilderImpl<sqlops.DivContainer, sqlops.DivLayout, sqlops.DivItemLayout> {
|
||||
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string) {
|
||||
super(new DivContainerWrapper(proxy, handle, type, id));
|
||||
}
|
||||
}
|
||||
|
||||
class FormContainerBuilder extends GenericContainerBuilder<sqlops.FormContainer, sqlops.FormLayout, sqlops.FormItemLayout> implements sqlops.FormBuilder {
|
||||
constructor(proxy: MainThreadModelViewShape, handle: number, type: ModelComponentTypes, id: string, private _builder: ModelBuilderImpl) {
|
||||
super(proxy, handle, type, id);
|
||||
}
|
||||
@@ -376,7 +387,7 @@ class FormContainerBuilder extends ContainerBuilderImpl<sqlops.FormContainer, sq
|
||||
}
|
||||
}
|
||||
|
||||
class ToolbarContainerBuilder extends ContainerBuilderImpl<sqlops.ToolbarContainer, sqlops.ToolbarLayout, any> implements sqlops.ToolbarBuilder {
|
||||
class ToolbarContainerBuilder extends GenericContainerBuilder<sqlops.ToolbarContainer, sqlops.ToolbarLayout, any> implements sqlops.ToolbarBuilder {
|
||||
withToolbarItems(components: sqlops.ToolbarComponent[]): sqlops.ContainerBuilder<sqlops.ToolbarContainer, any, any> {
|
||||
this._component.itemConfigs = components.map(item => {
|
||||
return this.convertToItemConfig(item);
|
||||
@@ -844,9 +855,6 @@ class WebViewWrapper extends ComponentWrapper implements sqlops.WebViewComponent
|
||||
public set options(o: vscode.WebviewOptions) {
|
||||
this.setProperty('options', o);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
class EditorWrapper extends ComponentWrapper implements sqlops.EditorComponent {
|
||||
@@ -1103,6 +1111,13 @@ class ButtonWrapper extends ComponentWrapper implements sqlops.ButtonComponent {
|
||||
this.setProperty('iconPath', v);
|
||||
}
|
||||
|
||||
public get title(): string {
|
||||
return this.properties['title'];
|
||||
}
|
||||
public set title(v: string) {
|
||||
this.setProperty('title', v);
|
||||
}
|
||||
|
||||
public get onDidClick(): vscode.Event<any> {
|
||||
let emitter = this._emitterMap.get(ComponentEventType.onDidClick);
|
||||
return emitter && emitter.event;
|
||||
@@ -1155,6 +1170,24 @@ class FileBrowserTreeComponentWrapper extends ComponentWrapper implements sqlops
|
||||
}
|
||||
}
|
||||
|
||||
class DivContainerWrapper extends ComponentWrapper implements sqlops.DivContainer {
|
||||
public get overflowY(): string {
|
||||
return this.properties['overflowY'];
|
||||
}
|
||||
|
||||
public set overflowY(value: string) {
|
||||
this.setProperty('overflowY', value);
|
||||
}
|
||||
|
||||
public get yOffsetChange(): number {
|
||||
return this.properties['yOffsetChange'];
|
||||
}
|
||||
|
||||
public set yOffsetChange(value: number) {
|
||||
this.setProperty('yOffsetChange', value);
|
||||
}
|
||||
}
|
||||
|
||||
class TreeComponentWrapper<T> extends ComponentWrapper implements sqlops.TreeComponent<T> {
|
||||
|
||||
constructor(
|
||||
|
||||
@@ -75,6 +75,9 @@ class ModelViewPanelImpl implements sqlops.window.modelviewdialog.ModelViewPanel
|
||||
}
|
||||
|
||||
class ModelViewEditorImpl extends ModelViewPanelImpl implements sqlops.workspace.ModelViewEditor {
|
||||
private _isDirty: boolean;
|
||||
private _saveHandler: () => Thenable<boolean>;
|
||||
|
||||
constructor(
|
||||
extHostModelViewDialog: ExtHostModelViewDialog,
|
||||
extHostModelView: ExtHostModelViewShape,
|
||||
@@ -84,10 +87,32 @@ class ModelViewEditorImpl extends ModelViewPanelImpl implements sqlops.workspace
|
||||
private _options: sqlops.ModelViewEditorOptions
|
||||
) {
|
||||
super('modelViewEditor', extHostModelViewDialog, extHostModelView, extensionLocation);
|
||||
this._isDirty = false;
|
||||
}
|
||||
|
||||
public openEditor(position?: vscode.ViewColumn): Thenable<void> {
|
||||
return this._proxy.$openEditor(this._modelViewId, this._title, this._options, position);
|
||||
return this._proxy.$openEditor(this.handle, this._modelViewId, this._title, this._options, position);
|
||||
}
|
||||
|
||||
public get isDirty(): boolean {
|
||||
return this._isDirty;
|
||||
}
|
||||
|
||||
public set isDirty(value: boolean) {
|
||||
this._isDirty = value;
|
||||
this._proxy.$setDirty(this.handle, value);
|
||||
}
|
||||
|
||||
registerSaveHandler(handler: () => Thenable<boolean>) {
|
||||
this._saveHandler = handler;
|
||||
}
|
||||
|
||||
public handleSave(): Thenable<boolean> {
|
||||
if (this._saveHandler) {
|
||||
return Promise.resolve(this._saveHandler());
|
||||
} else {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,6 +495,11 @@ export class ExtHostModelViewDialog implements ExtHostModelViewDialogShape {
|
||||
return dialog.validateClose();
|
||||
}
|
||||
|
||||
public $handleSave(handle: number): Thenable<boolean> {
|
||||
let editor = this._objectsByHandle.get(handle) as ModelViewEditorImpl;
|
||||
return editor.handleSave();
|
||||
}
|
||||
|
||||
public openDialog(dialog: sqlops.window.modelviewdialog.Dialog): void {
|
||||
let handle = this.getHandle(dialog);
|
||||
this.updateDialogContent(dialog);
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { IEditor } from 'vs/workbench/common/editor';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
|
||||
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -14,7 +15,7 @@ import { MainThreadModelViewDialogShape, SqlMainContext, ExtHostModelViewDialogS
|
||||
import { Dialog, DialogTab, DialogButton, WizardPage, Wizard } from 'sql/platform/dialog/dialogTypes';
|
||||
import { CustomDialogService } from 'sql/platform/dialog/customDialogService';
|
||||
import { IModelViewDialogDetails, IModelViewTabDetails, IModelViewButtonDetails, IModelViewWizardPageDetails, IModelViewWizardDetails } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { ModelViewInput } from 'sql/parts/modelComponents/modelEditor/modelViewInput';
|
||||
import { ModelViewInput, ModelViewInputModel, ModeViewSaveHandler } from 'sql/parts/modelComponents/modelEditor/modelViewInput';
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as sqlops from 'sqlops';
|
||||
@@ -28,6 +29,7 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
||||
private readonly _wizardPages = new Map<number, WizardPage>();
|
||||
private readonly _wizardPageHandles = new Map<WizardPage, number>();
|
||||
private readonly _wizards = new Map<number, Wizard>();
|
||||
private readonly _editorInputModels = new Map<number, ModelViewInputModel>();
|
||||
private _dialogService: CustomDialogService;
|
||||
|
||||
constructor(
|
||||
@@ -43,15 +45,18 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
public $openEditor(modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable<void> {
|
||||
public $openEditor(handle: number, modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable<void> {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
let input = this._instatiationService.createInstance(ModelViewInput, title, modelViewId, options);
|
||||
let saveHandler: ModeViewSaveHandler = options && options.supportsSave ? (h) => this.handleSave(h) : undefined;
|
||||
let model = new ModelViewInputModel(modelViewId, handle, saveHandler);
|
||||
let input = this._instatiationService.createInstance(ModelViewInput, title, model, options);
|
||||
let editorOptions = {
|
||||
preserveFocus: true,
|
||||
pinned: true
|
||||
};
|
||||
|
||||
this._editorService.openEditor(input, editorOptions, position as any).then(() => {
|
||||
this._editorService.openEditor(input, editorOptions, position as any).then((editor) => {
|
||||
this._editorInputModels.set(handle, model);
|
||||
resolve();
|
||||
}, error => {
|
||||
reject(error);
|
||||
@@ -59,6 +64,10 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
||||
});
|
||||
}
|
||||
|
||||
private handleSave(handle: number): Thenable<boolean> {
|
||||
return this._proxy.$handleSave(handle);
|
||||
}
|
||||
|
||||
public $openDialog(handle: number): Thenable<void> {
|
||||
let dialog = this.getDialog(handle);
|
||||
this._dialogService.showDialog(dialog);
|
||||
@@ -213,6 +222,21 @@ export class MainThreadModelViewDialog implements MainThreadModelViewDialogShape
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
$setDirty(handle: number, isDirty: boolean): void {
|
||||
let model = this.getEditor(handle);
|
||||
if (model) {
|
||||
model.setDirty(isDirty);
|
||||
}
|
||||
}
|
||||
|
||||
private getEditor(handle: number): ModelViewInputModel {
|
||||
let model = this._editorInputModels.get(handle);
|
||||
if (!model) {
|
||||
throw new Error('No editor matching the given handle');
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
private getDialog(handle: number): Dialog {
|
||||
let dialog = this._dialogs.get(handle);
|
||||
if (!dialog) {
|
||||
|
||||
@@ -672,10 +672,11 @@ export interface ExtHostModelViewDialogShape {
|
||||
$updateWizardPageInfo(handle: number, pageHandles: number[], currentPageIndex: number): void;
|
||||
$validateNavigation(handle: number, info: sqlops.window.modelviewdialog.WizardPageChangeInfo): Thenable<boolean>;
|
||||
$validateDialogClose(handle: number): Thenable<boolean>;
|
||||
$handleSave(handle: number): Thenable<boolean>;
|
||||
}
|
||||
|
||||
export interface MainThreadModelViewDialogShape extends IDisposable {
|
||||
$openEditor(modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable<void>;
|
||||
$openEditor(handle: number, modelViewId: string, title: string, options?: sqlops.ModelViewEditorOptions, position?: vscode.ViewColumn): Thenable<void>;
|
||||
$openDialog(handle: number): Thenable<void>;
|
||||
$closeDialog(handle: number): Thenable<void>;
|
||||
$setDialogDetails(handle: number, details: IModelViewDialogDetails): Thenable<void>;
|
||||
@@ -688,6 +689,7 @@ export interface MainThreadModelViewDialogShape extends IDisposable {
|
||||
$addWizardPage(wizardHandle: number, pageHandle: number, pageIndex: number): Thenable<void>;
|
||||
$removeWizardPage(wizardHandle: number, pageIndex: number): Thenable<void>;
|
||||
$setWizardPage(wizardHandle: number, pageIndex: number): Thenable<void>;
|
||||
$setDirty(handle: number, isDirty: boolean): void;
|
||||
}
|
||||
export interface ExtHostQueryEditorShape {
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ import { ShowCurrentReleaseNotesAction } from 'sql/workbench/update/releaseNotes
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IConfigurationRegistry, Extensions as ConfigExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
|
||||
new Actions.BackupAction().registerTask(false);
|
||||
new Actions.RestoreAction().registerTask(false);
|
||||
new Actions.BackupAction().registerTask();
|
||||
new Actions.RestoreAction().registerTask();
|
||||
new Actions.NewQueryAction().registerTask();
|
||||
new Actions.ConfigureDashboardAction().registerTask();
|
||||
|
||||
|
||||
@@ -302,6 +302,12 @@ export class BackupAction extends Task {
|
||||
}
|
||||
|
||||
runTask(accessor: ServicesAccessor, profile: IConnectionProfile): TPromise<void> {
|
||||
if (!profile) {
|
||||
let objectExplorerService = accessor.get<IObjectExplorerService>(IObjectExplorerService);
|
||||
let connectionManagementService = accessor.get<IConnectionManagementService>(IConnectionManagementService);
|
||||
let workbenchEditorService = accessor.get<IEditorService>(IEditorService);
|
||||
profile = TaskUtilities.getCurrentGlobalConnection(objectExplorerService, connectionManagementService, workbenchEditorService);
|
||||
}
|
||||
let configurationService = accessor.get<IWorkspaceConfigurationService>(IWorkspaceConfigurationService);
|
||||
let previewFeaturesEnabled: boolean = configurationService.getValue('workbench')['enablePreviewFeatures'];
|
||||
if (!previewFeaturesEnabled) {
|
||||
|
||||
@@ -108,13 +108,13 @@ export class ErrorMessageDialog extends Modal {
|
||||
private updateIconTitle(): void {
|
||||
switch (this._severity) {
|
||||
case Severity.Error:
|
||||
this.titleIconClassName = 'icon error';
|
||||
this.titleIconClassName = 'sql icon error';
|
||||
break;
|
||||
case Severity.Warning:
|
||||
this.titleIconClassName = 'icon warning';
|
||||
this.titleIconClassName = 'sql icon warning';
|
||||
break;
|
||||
case Severity.Info:
|
||||
this.titleIconClassName = 'icon info';
|
||||
this.titleIconClassName = 'sql icon info';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
10
src/typings/globals/slickgrid/index.d.ts
vendored
10
src/typings/globals/slickgrid/index.d.ts
vendored
@@ -764,6 +764,16 @@ declare namespace Slick {
|
||||
**/
|
||||
destroy(): void;
|
||||
|
||||
/**
|
||||
* Sets selected ranges for the grid
|
||||
*/
|
||||
setSelectedRanges(ranges: Slick.Range[]);
|
||||
|
||||
/**
|
||||
* Gets selected ranges for the grid
|
||||
*/
|
||||
getSelectedRanges(): Slick.Range[];
|
||||
|
||||
onSelectedRangesChanged: Slick.Event<E>;
|
||||
}
|
||||
|
||||
|
||||
@@ -88,7 +88,8 @@ Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(new Vie
|
||||
VIEWLET_ID,
|
||||
nls.localize('debug', "Debug"),
|
||||
'debug',
|
||||
3
|
||||
// {{SQL CARBON EDIT}}
|
||||
13
|
||||
));
|
||||
|
||||
const openViewletKb: IKeybindings = {
|
||||
|
||||
@@ -122,7 +122,8 @@ const viewletDescriptor = new ViewletDescriptor(
|
||||
VIEWLET_ID,
|
||||
localize('extensions', "Extensions"),
|
||||
'extensions',
|
||||
4
|
||||
// {{SQL CARBON EDIT}}
|
||||
14
|
||||
);
|
||||
|
||||
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets)
|
||||
|
||||
@@ -40,7 +40,7 @@ import { getMultiSelectedEditorContexts } from 'vs/workbench/browser/parts/edito
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorService, SIDE_GROUP, IResourceEditorReplacement } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService';
|
||||
import { isEqual, basenameOrAuthority } from 'vs/base/common/resources';
|
||||
import { ltrim } from 'vs/base/common/strings';
|
||||
@@ -165,7 +165,7 @@ function save(
|
||||
// {{SQL CARBON EDIT}}
|
||||
queryEditorService.onSaveAsCompleted(resource, target);
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
@@ -215,14 +215,27 @@ function saveAll(saveAllArguments: any, editorService: IEditorService, untitledE
|
||||
// Save all
|
||||
return textFileService.saveAll(saveAllArguments).then((result) => {
|
||||
groupIdToUntitledResourceInput.forEach((inputs, groupId) => {
|
||||
// {{SQL CARBON EDIT}}
|
||||
// Update untitled resources to the saved ones, so we open the proper files
|
||||
|
||||
let replacementPairs: IResourceEditorReplacement[] = [];
|
||||
inputs.forEach(i => {
|
||||
const targetResult = result.results.filter(r => r.success && r.source.toString() === i.resource.toString()).pop();
|
||||
if (targetResult) {
|
||||
i.resource = targetResult.target;
|
||||
//i.resource = targetResult.target;
|
||||
let editor = i;
|
||||
const replacement: IResourceInput = {
|
||||
resource: targetResult.target,
|
||||
encoding: i.encoding,
|
||||
options: {
|
||||
pinned: true,
|
||||
viewState: undefined
|
||||
}
|
||||
};
|
||||
replacementPairs.push({ editor: editor, replacement: replacement });
|
||||
}
|
||||
});
|
||||
editorService.openEditors(inputs, groupId);
|
||||
editorService.replaceEditors(replacementPairs, groupId);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -71,7 +71,8 @@ Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(new Vie
|
||||
VIEWLET_ID,
|
||||
nls.localize('explore', "Explorer"),
|
||||
'explore',
|
||||
0
|
||||
// {{SQL CARBON EDIT}}
|
||||
10
|
||||
));
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
|
||||
@@ -39,7 +39,8 @@ const viewletDescriptor = new ViewletDescriptor(
|
||||
VIEWLET_ID,
|
||||
localize('source control', "Source Control"),
|
||||
'scm',
|
||||
2
|
||||
// {{SQL CARBON EDIT}}
|
||||
12
|
||||
);
|
||||
|
||||
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets)
|
||||
|
||||
@@ -455,7 +455,8 @@ Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(new Vie
|
||||
VIEW_ID,
|
||||
nls.localize('name', "Search"),
|
||||
'search',
|
||||
1
|
||||
// {{SQL CARBON EDIT}}
|
||||
11
|
||||
));
|
||||
|
||||
Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(new PanelDescriptor(
|
||||
|
||||
@@ -284,47 +284,48 @@ export class WinUserSetupContribution implements IWorkbenchContribution {
|
||||
}
|
||||
|
||||
private onUpdateStateChange(state: UpdateState): void {
|
||||
if (state.type !== StateType.Idle) {
|
||||
return;
|
||||
}
|
||||
// {{SQL CARBON EDIT}}
|
||||
return;
|
||||
// if (state.type !== StateType.Idle) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (state.updateType !== UpdateType.Setup) {
|
||||
return;
|
||||
}
|
||||
// if (state.updateType !== UpdateType.Setup) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (!this.environmentService.isBuilt || this.environmentService.disableUpdates) {
|
||||
return;
|
||||
}
|
||||
// if (!this.environmentService.isBuilt || this.environmentService.disableUpdates) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
const neverShowAgain = new NeverShowAgain(WinUserSetupContribution.KEY, this.storageService);
|
||||
// const neverShowAgain = new NeverShowAgain(WinUserSetupContribution.KEY, this.storageService);
|
||||
// if (!neverShowAgain.shouldShow()) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
if (!neverShowAgain.shouldShow()) {
|
||||
return;
|
||||
}
|
||||
// const handle = this.notificationService.prompt(
|
||||
// severity.Info,
|
||||
// nls.localize('usersetup', "We recommend switching to our new User Setup distribution of {0} for Windows! Click [here]({1}) to learn more.", product.nameShort, WinUserSetupContribution.READ_MORE),
|
||||
// [
|
||||
// {
|
||||
// label: nls.localize('downloadnow', "Download"),
|
||||
// run: () => {
|
||||
// const url = product.quality === 'insider'
|
||||
// ? (process.arch === 'ia32' ? WinUserSetupContribution.INSIDER_URL_32BIT : WinUserSetupContribution.INSIDER_URL)
|
||||
// : (process.arch === 'ia32' ? WinUserSetupContribution.STABLE_URL_32BIT : WinUserSetupContribution.STABLE_URL);
|
||||
|
||||
const handle = this.notificationService.prompt(
|
||||
severity.Info,
|
||||
nls.localize('usersetup', "We recommend switching to our new User Setup distribution of {0} for Windows! Click [here]({1}) to learn more.", product.nameShort, WinUserSetupContribution.READ_MORE),
|
||||
[
|
||||
{
|
||||
label: nls.localize('downloadnow', "Download"),
|
||||
run: () => {
|
||||
const url = product.quality === 'insider'
|
||||
? (process.arch === 'ia32' ? WinUserSetupContribution.INSIDER_URL_32BIT : WinUserSetupContribution.INSIDER_URL)
|
||||
: (process.arch === 'ia32' ? WinUserSetupContribution.STABLE_URL_32BIT : WinUserSetupContribution.STABLE_URL);
|
||||
|
||||
return this.openerService.open(URI.parse(url));
|
||||
}
|
||||
},
|
||||
{
|
||||
label: nls.localize('neveragain', "Don't Show Again"),
|
||||
isSecondary: true,
|
||||
run: () => {
|
||||
neverShowAgain.action.run(handle);
|
||||
neverShowAgain.action.dispose();
|
||||
}
|
||||
}]
|
||||
);
|
||||
// return this.openerService.open(URI.parse(url));
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// label: nls.localize('neveragain', "Don't Show Again"),
|
||||
// isSecondary: true,
|
||||
// run: () => {
|
||||
// neverShowAgain.action.run(handle);
|
||||
// neverShowAgain.action.dispose();
|
||||
// }
|
||||
// }]
|
||||
// );
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
||||
@@ -233,18 +233,12 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
||||
return this.doOpenEditor(targetGroup, editor, editorOptions);
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
// Untyped Text Editor Support
|
||||
const textInput = <IResourceEditor>editor;
|
||||
let typedInput = this.createInput(textInput);
|
||||
const typedInput = this.createInput(textInput);
|
||||
if (typedInput) {
|
||||
const editorOptions = TextEditorOptions.from(textInput);
|
||||
const targetGroup = this.findTargetGroup(typedInput, editorOptions, optionsOrGroup as IEditorGroup | GroupIdentifier);
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
// Convert input into custom type if it's one of the ones we support
|
||||
typedInput = convertEditorInput(typedInput, editorOptions, this.instantiationService);
|
||||
|
||||
return this.doOpenEditor(targetGroup, typedInput, editorOptions);
|
||||
}
|
||||
|
||||
@@ -501,12 +495,12 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
||||
const untitledInput = <IUntitledResourceInput>input;
|
||||
if (!untitledInput.resource || typeof untitledInput.filePath === 'string' || (untitledInput.resource instanceof URI && untitledInput.resource.scheme === Schemas.untitled)) {
|
||||
// {{SQL CARBON EDIT}}
|
||||
return this.untitledEditorService.createOrGet(
|
||||
return convertEditorInput(this.untitledEditorService.createOrGet(
|
||||
untitledInput.filePath ? URI.file(untitledInput.filePath) : untitledInput.resource,
|
||||
'sql',
|
||||
untitledInput.contents,
|
||||
untitledInput.encoding
|
||||
);
|
||||
), undefined, this.instantiationService);
|
||||
}
|
||||
|
||||
// Resource Editor Support
|
||||
@@ -516,8 +510,9 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
||||
if (!label && resourceInput.resource.scheme !== Schemas.data) {
|
||||
label = basename(resourceInput.resource.fsPath); // derive the label from the path (but not for data URIs)
|
||||
}
|
||||
|
||||
return this.createOrGet(resourceInput.resource, this.instantiationService, label, resourceInput.description, resourceInput.encoding, options && options.forceFileInput) as EditorInput;
|
||||
// {{SQL CARBON EDIT}}
|
||||
return convertEditorInput(this.createOrGet(resourceInput.resource, this.instantiationService, label, resourceInput.description, resourceInput.encoding, options && options.forceFileInput) as EditorInput,
|
||||
undefined, this.instantiationService);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user