Compare commits

...

44 Commits

Author SHA1 Message Date
Matt Irvine
05040425df Add OE node refresh API method (#2578)
* Initial working commit for refreshing OE node via API

* Add test and fix up code

* Run tsfmt

* Fix test
2018-09-13 18:43:47 -07:00
Alan Ren
36f7c283b8 use latest slickgrid library (#2584) 2018-09-13 18:43:06 -07:00
Anthony Dresser
9fe4237033 Maintain Query State (#2571)
* add results view stating

* working through the bugs

* handle various resizing bugs

* gnale resizing better

* fix tests by adding missing node module

* formatting

* refactor interfaces out to get around testing restrictions

* more refactoring of importants to avoid loading errors
2018-09-13 18:42:29 -07:00
Alan Ren
b03c0a3e2d accessibility setting based select database dropdown (#2579) 2018-09-13 15:07:08 -07:00
Aditya Bist
87946996ed added context to chart buttons so they work (#2575) 2018-09-13 14:22:57 -07:00
AlexFsmn
cc55023440 Disabled connection name input when connecting to a server. (#2566)
* Disabled connection name input when connecting to a server.
#2557

* Fixed reset state of connection inputs
2018-09-13 12:36:28 -07:00
Karl Burtram
1f19dfc50d Update SQL Ops to 0.33.5 2018-09-13 08:06:27 -07:00
Alan Ren
950a440350 fix the connection issue when opening new query after connection (#2561) 2018-09-13 08:05:46 -07:00
Aditya Bist
c92b88bfaf Bug/extension contribution (#2560)
* revert 4ab5d84b94

* fixed extensions
2018-09-12 23:04:59 -07:00
Abbie Petchtes
10875f26dc add divcontainer in modelview (#2559)
* add divcontainer in modelview

* address comment
2018-09-12 20:18:40 -07:00
Kevin Cunnane
d62e809c18 Support isDirty flag for model view editors and begin plumb through of save support (#2547)
* Add dirty and save support to model view

* Add issue # for a TODO
2018-09-12 14:35:19 -07:00
Alan Ren
d85bf4f6dd fix the account not found error when creating firewall rules (#2543) 2018-09-12 13:32:40 -07:00
Anthony Dresser
801e201cc3 change active cell during change to fix focus shift (#2545) 2018-09-12 13:22:01 -07:00
Anthony Dresser
e18e0da0c1 Fix sizing error when switching windows (#2544)
* add work around for when we need to resize while we don't have a dimension to resize off of

* formatting
2018-09-12 13:21:51 -07:00
Karl Burtram
d046b0a412 Update SQL Ops to 0.33.4 2018-09-12 13:09:41 -07:00
Karl Burtram
72084b8fc1 Fix build break in Git extension (#2538) 2018-09-11 21:48:44 -07:00
Anthony Dresser
2639b2bd2c Fix bug around debounced event not being flushed in time (#2536)
* fix bug around debounced event not being flushed in time

* add comment
2018-09-11 21:22:25 -07:00
Anthony Dresser
82aa493dfd Reduce message panel min size to 0 (#2534)
* reduce message panel minimum size to 0; attempt to restore panel sizing on requery sizes; default grid panel size to 80%

* formatting
2018-09-11 21:22:06 -07:00
Karl Burtram
6c3c7c40b5 Turn-off Git missing prompt (#2533) 2018-09-11 21:21:44 -07:00
Anthony Dresser
5616751c04 fix grid action bar not updating (#2532) 2018-09-11 21:21:30 -07:00
Anthony Dresser
7d898ca34d Fix grid gaps (#2531)
* modifying grid gaps

* reduce gaps and increase gap for action bar
2018-09-11 21:21:06 -07:00
Kevin Cunnane
e26556b21a Fixes #2523 (#2528)
The IdGenerator was recreated each time and had a high likelihood of conflicts. Invitably after adding dozens or hundreds of icons you'll start seeing the CSS class replaced and overridden.

The solution is to do like elsewhere: have 1 const that is loaded on first import of the file and keeps a global track.

Side note is that it'd be a good idea to cache CSS rules with the same iconPath so we don't create lots of additional rules unnecessarily. If we reuse the same icon a bunch we should cache them - #2524 is tracking this.
2018-09-11 21:20:38 -07:00
Anthony Dresser
89e6d363e2 Selection in grid context (#2527)
* update action context on selection change

* correctly add ranges rather than a new range for every row

* add required functions to typings
2018-09-11 17:10:53 -07:00
Anthony Dresser
c559ac7be9 expand messages panel on error (#2519) 2018-09-11 17:09:40 -07:00
Anthony Dresser
b3fbe47f0a add min size for row num column (#2518) 2018-09-11 17:09:17 -07:00
Anthony Dresser
c73af4c480 add check for selection model in edit data (#2517) 2018-09-11 17:08:41 -07:00
Alan Ren
8887fe1eac fix the save and save all for untitled file (#2526) 2018-09-11 17:06:28 -07:00
Kevin Cunnane
ed861a6c96 Revert "Fixes #2523" (#2525)
This reverts commit e63bb6a8ec.
2018-09-11 16:42:27 -07:00
Kevin Cunnane
e63bb6a8ec Fixes #2523
The IdGenerator was recreated each time and had a high likelihood of conflicts. Invitably after adding dozens or hundreds of icons you'll start seeing the CSS class replaced and overridden.

The solution is to do like elsewhere: have 1 const that is loaded on first import of the file and keeps a global track.

Side note is that it'd be a good idea to cache CSS rules with the same iconPath so we don't create lots of additional rules unnecessarily. If we reuse the same icon a bunch we should cache them - #2524 is tracking this.
2018-09-11 16:36:17 -07:00
Anthony Dresser
8ec09d25ce add listener to change action bar on maximize change (#2505) 2018-09-11 12:42:06 -07:00
Anthony Dresser
a9a01ae479 fixes a rendering problem in splitview (#2512) 2018-09-11 12:18:03 -07:00
Karl Burtram
31a3864789 Disable the User Setup prompt (#2501) 2018-09-11 11:52:04 -07:00
Abbie Petchtes
a5c537197c add animation when button is clicked and fix title in button (#2488)
* add animation similar to toolbar in vscode and fix title in button

* remove bur method in button
2018-09-11 10:37:02 -07:00
Anthony Dresser
4ea13bdbc0 add select all handler to grid (#2496) 2018-09-10 21:18:39 -07:00
Anthony Dresser
b06ddf2dc7 change cursor in message panel to default (#2494) 2018-09-10 21:18:18 -07:00
Karl Burtram
2c45ac9df3 Reorder Connection Name field in Connection Dialog (#2498) 2018-09-10 21:17:57 -07:00
Anthony Dresser
7735f68502 Add check for potential failure in handling drag (#2499)
* add check for potential failure in handling drag

* move check to avoid ui glitches
2018-09-10 21:17:42 -07:00
Anthony Dresser
ffb0f5a1c7 add grid styles (#2483) 2018-09-10 21:17:26 -07:00
Anthony Dresser
80c7f9e855 add logic to hide and add grid panel based on size (#2481) 2018-09-10 21:16:54 -07:00
Alan Ren
709ef4e39f Alanren/icon overwrite issue (#2484)
* fix the error icon too large issue

* formatting
2018-09-10 15:55:40 -07:00
Anthony Dresser
4ceb869420 remove autosize and change column header css to properly respect column sizes (#2480) 2018-09-10 14:59:32 -07:00
Anthony Dresser
432a209184 fix error message formatting (#2477) 2018-09-10 14:58:51 -07:00
Karl Burtram
8444271c58 Fix Action Bar viewlet ordering (#2472) 2018-09-09 19:58:27 -07:00
Karl Burtram
2bc97c23d4 Bump Electron to 2.0.8 and SQL Ops to 0.33.3 (#2466) 2018-09-07 22:08:36 -07:00
90 changed files with 1437 additions and 426 deletions

View File

@@ -1,3 +1,3 @@
disturl "https://atom.io/download/electron"
target "2.0.7"
target "2.0.8"
runtime "electron"

View File

@@ -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();
}

View File

@@ -1,6 +1,6 @@
{
"name": "sqlops",
"version": "0.33.2",
"version": "0.33.5",
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
"author": {
"name": "Microsoft Corporation"
@@ -64,7 +64,7 @@
"reflect-metadata": "^0.1.8",
"rxjs": "5.4.0",
"semver": "^5.5.0",
"slickgrid": "github:anthonydresser/SlickGrid#2.3.25",
"slickgrid": "github:anthonydresser/SlickGrid#2.3.27",
"spdlog": "0.7.1",
"sudo-prompt": "8.2.0",
"svg.js": "^2.2.5",

View File

@@ -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([{

View File

@@ -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 {

View File

@@ -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';
});

View File

@@ -180,14 +180,16 @@ export class TabbedPanel extends Disposable implements IThemable {
}
public layout(dimension: Dimension): void {
this._currentDimensions = dimension;
this.$parent.style('height', dimension.height + 'px');
this.$parent.style('width', dimension.width + 'px');
this.$header.style('width', dimension.width + 'px');
this.$body.style('width', dimension.width + 'px');
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
this.$body.style('height', bodyHeight + 'px');
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight));
if (dimension) {
this._currentDimensions = dimension;
this.$parent.style('height', dimension.height + 'px');
this.$parent.style('width', dimension.width + 'px');
this.$header.style('width', dimension.width + 'px');
this.$body.style('width', dimension.width + 'px');
const bodyHeight = dimension.height - (this._headerVisible ? this.headersize : 0);
this.$body.style('height', bodyHeight + 'px');
this._layoutCurrentTab(new Dimension(dimension.width, bodyHeight));
}
}
private _layoutCurrentTab(dimension: Dimension): void {

View File

@@ -109,6 +109,9 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
private _onDidSashReset = new Emitter<void>();
readonly onDidSashReset = this._onDidSashReset.event;
private _onScroll = new Emitter<number>();
readonly onScroll = this._onScroll.event;
get length(): number {
return this.viewItems.length;
}
@@ -124,6 +127,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
debounceEvent(this.scrollable.onScroll, (l, e) => e, 25)(e => {
this.render(e.scrollTop, e.height);
this.relayout();
this._onScroll.fire(e.scrollTop);
});
let domNode = this.scrollable.getDomNode();
dom.addClass(this.el, 'monaco-scroll-split-view');
@@ -330,9 +334,16 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndex);
}
public setScrollPosition(position: number) {
this.scrollable.setScrollPosition({ scrollTop: position });
}
layout(size: number): void {
const previousSize = 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);
}

View File

@@ -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;
}

View File

@@ -1,7 +1,5 @@
import { mixin } from 'vs/base/common/objects';
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
const defaultOptions: ICellRangeSelectorOptions = {
selectionCss: {
'border': '2px dashed blue'
@@ -44,6 +42,8 @@ export class CellRangeSelector<T> implements ICellRangeSelector<T> {
public onCellRangeSelected = new Slick.Event<{ range: Slick.Range }>();
constructor(private options: ICellRangeSelectorOptions) {
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
this.options = mixin(this.options, defaultOptions, false);
}
@@ -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,

View File

@@ -6,8 +6,6 @@ import { isUndefinedOrNull } from 'vs/base/common/types';
import { CellRangeSelector, ICellRangeSelector } from 'sql/base/browser/ui/table/plugins/cellRangeSelector';
require.__$__nodeRequire('slickgrid/plugins/slick.cellrangedecorator');
export interface ICellSelectionModelOptions {
cellRangeSelector?: any;
selectActiveCell?: boolean;

View File

@@ -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;

View File

@@ -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,

View File

@@ -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() {

View File

@@ -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
};
}

View File

@@ -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");
}

View File

@@ -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 });

View File

@@ -123,6 +123,7 @@ export class ConnectionStore {
.then(savedCred => {
if (savedCred) {
credentialsItem.password = savedCred.password;
credentialsItem.options['password'] = savedCred.password;
}
resolve({ profile: credentialsItem, savedCred: !!savedCred });
},

View File

@@ -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();
}
@@ -492,6 +492,7 @@ export class ConnectionWidget {
this._databaseNameInputBox.enabled = false;
this._userNameInputBox.disable();
this._passwordInputBox.disable();
this._connectionNameInputBox.disable();
this._rememberPasswordCheckBox.enabled = false;
if (this._authTypeSelectBox) {
this._authTypeSelectBox.disable();
@@ -503,6 +504,7 @@ export class ConnectionWidget {
this._serverGroupSelectBox.enable();
this._serverNameInputBox.enable();
this._connectionNameInputBox.enable();
this._databaseNameInputBox.enabled = true;
let currentAuthType: AuthenticationType = undefined;
if (this._authTypeSelectBox) {

View File

@@ -10,6 +10,7 @@ import * as TelemetryUtils from 'sql/common/telemetryUtilities';
import { IInsightsView, IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { memoize, unmemoize } from 'sql/base/common/decorators';
import { mixin } from 'sql/base/common/objects';
import { LegendPosition, DataDirection, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import * as colors from 'vs/platform/theme/common/colorRegistry';
import { Color } from 'vs/base/common/color';
@@ -21,29 +22,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
declare var Chart: any;
export enum ChartType {
Bar = 'bar',
Doughnut = 'doughnut',
HorizontalBar = 'horizontalBar',
Line = 'line',
Pie = 'pie',
TimeSeries = 'timeSeries',
Scatter = 'scatter'
}
export enum DataDirection {
Vertical = 'vertical',
Horizontal = 'horizontal'
}
export enum LegendPosition {
Top = 'top',
Bottom = 'bottom',
Left = 'left',
Right = 'right',
None = 'none'
}
export function customMixin(destination: any, source: any, overwrite?: boolean): any {
if (types.isObject(source)) {
mixin(destination, source, overwrite, customMixin);

View File

@@ -0,0 +1,32 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export enum ChartType {
Bar = 'bar',
Doughnut = 'doughnut',
HorizontalBar = 'horizontalBar',
Line = 'line',
Pie = 'pie',
TimeSeries = 'timeSeries',
Scatter = 'scatter'
}
export enum DataDirection {
Vertical = 'vertical',
Horizontal = 'horizontal'
}
export enum LegendPosition {
Top = 'top',
Bottom = 'bottom',
Left = 'left',
Right = 'right',
None = 'none'
}
export enum DataType {
Number = 'number',
Point = 'point'
}

View File

@@ -3,8 +3,9 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ChartInsight, ChartType, customMixin, IChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { ChartInsight, customMixin, IChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { mixin } from 'sql/base/common/objects';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
import * as colors from 'vs/platform/theme/common/colorRegistry';

View File

@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import PieChart from './pieChart.component';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
export default class DoughnutChart extends PieChart {
protected readonly chartType: ChartType = ChartType.Doughnut;

View File

@@ -3,8 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import BarChart from './barChart.component';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
export default class HorizontalBarChart extends BarChart {
protected readonly chartType: ChartType = ChartType.HorizontalBar;

View File

@@ -3,16 +3,13 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ChartType, customMixin, defaultChartConfig, IDataSet, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { mixin } from 'vs/base/common/objects';
import { defaultChartConfig, IDataSet, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import BarChart, { IBarChartConfig } from './barChart.component';
import { memoize, unmemoize } from 'sql/base/common/decorators';
import { mixin } from 'vs/base/common/objects';
import { clone } from 'sql/base/common/objects';
export enum DataType {
Number = 'number',
Point = 'point'
}
import { ChartType, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
export interface ILineConfig extends IBarChartConfig {
dataType?: DataType;

View File

@@ -3,7 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ChartInsight, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { ChartInsight } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
export default class PieChart extends ChartInsight {
protected readonly chartType: ChartType = ChartType.Pie;

View File

@@ -3,11 +3,12 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ChartType, defaultChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { defaultChartConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import LineChart, { ILineConfig } from './lineChart.component';
import { clone } from 'sql/base/common/objects';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { mixin } from 'vs/base/common/objects';
import { clone } from 'sql/base/common/objects';
const defaultScatterConfig = mixin(clone(defaultChartConfig), { dataType: 'point', dataDirection: 'horizontal' }) as ILineConfig;

View File

@@ -3,9 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { defaultChartConfig, IPointDataSet, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { defaultChartConfig, IPointDataSet } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import LineChart, { ILineConfig } from './lineChart.component';
import { clone } from 'sql/base/common/objects';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { mixin } from 'vs/base/common/objects';
import { Color } from 'vs/base/common/color';

View File

@@ -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() {

View File

@@ -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}}

View File

@@ -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 {

View File

@@ -16,7 +16,7 @@ import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
import { IInsightData, IInsightsView, IInsightsConfig } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { QueryEditor } from 'sql/parts/query/editor/queryEditor';
import { DataType, ILineConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
import { ILineConfig } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
import * as PathUtilities from 'sql/common/pathUtilities';
import { IChartViewActionContext, CopyAction, CreateInsightAction, SaveImageAction } from 'sql/parts/grid/views/query/chartViewerActions';
import * as WorkbenchUtils from 'sql/workbench/common/sqlWorkbenchUtils';
@@ -24,10 +24,11 @@ import * as Constants from 'sql/parts/query/common/constants';
import { SelectBox as AngularSelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IClipboardService } from 'sql/platform/clipboard/common/clipboardService';
import { LegendPosition, DataDirection, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
/* Insights */
import {
ChartInsight, DataDirection, LegendPosition
ChartInsight
} from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { IDisposable } from 'vs/base/common/lifecycle';

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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);

View 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 : {};
}
}

View File

@@ -0,0 +1,5 @@
.divContainer {
display: block;
height: 100%;
}

View File

@@ -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();
}
}

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -72,6 +72,8 @@ export interface IObjectExplorerService {
getActiveConnectionNodes(): TreeNode[];
getTreeNode(connectionId: string, nodePath: string): Thenable<TreeNode>;
refreshNodeInView(connectionId: string, nodePath: string): Thenable<TreeNode>;
}
interface SessionStatus {
@@ -476,6 +478,20 @@ export class ObjectExplorerService implements IObjectExplorerService {
return Object.values(this._activeObjectExplorerNodes);
}
public async refreshNodeInView(connectionId: string, nodePath: string): Promise<TreeNode> {
// Get the tree node and call refresh from the provider
let treeNode = await this.getTreeNode(connectionId, nodePath);
await this.refreshTreeNode(treeNode.getSession(), treeNode);
// Get the new tree node, refresh it in the view, and expand it if needed
treeNode = await this.getTreeNode(connectionId, nodePath);
await this._serverTreeView.refreshElement(treeNode);
if (treeNode.children.length > 0) {
await treeNode.setExpandedState(TreeItemCollapsibleState.Expanded);
}
return treeNode;
}
private async setNodeExpandedState(treeNode: TreeNode, expandedState: TreeItemCollapsibleState): Promise<void> {
treeNode = await this.getUpdatedTreeNode(treeNode);
let expandNode = this.getTreeItem(treeNode);

View File

@@ -39,7 +39,7 @@ const viewletDescriptor = new ViewletDescriptor(
VIEWLET_ID,
'Servers',
'connectionViewlet',
-100
0
);
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor);

View File

@@ -138,7 +138,7 @@ export class ServerTreeView {
let expandGroups: boolean = self._configurationService.getValue(SERVER_GROUP_CONFIG)[SERVER_GROUP_AUTOEXPAND_CONFIG];
if (expandGroups) {
self._tree.expandAll(ConnectionProfileGroup.getSubgroups(root));
self._tree.expandAll(ConnectionProfileGroup.getSubgroups(root));
}
if (root && !root.hasValidConnections) {
@@ -244,6 +244,10 @@ export class ServerTreeView {
TreeUpdateUtils.registeredServerUpdate(this._tree, this._connectionManagementService);
}
public refreshElement(element: any): Thenable<void> {
return this._tree.refresh(element);
}
/**
* Filter connections based on view (recent/active)
*/

View File

@@ -10,6 +10,22 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { EditorInput } from 'vs/workbench/common/editor';
import { Emitter } from 'vs/base/common/event';
import { GridPanelState } from 'sql/parts/query/editor/gridPanel';
import { MessagePanelState } from 'sql/parts/query/editor/messagePanel';
import { QueryPlanState } from 'sql/parts/queryPlan/queryPlan';
import { ChartState } from 'sql/parts/query/editor/charting/chartView';
export class ResultsViewState {
public gridPanelState: GridPanelState = new GridPanelState();
public messagePanelState: MessagePanelState = new MessagePanelState();
public chartState: ChartState = new ChartState();
public queryPlanState: QueryPlanState = new QueryPlanState();
public gridPanelSize: number;
public messagePanelSize: number;
public activeTab: string;
public visibleTabs: Set<string> = new Set<string>();
}
/**
* Input for the QueryResultsEditor. This input helps with logic for the viewing and editing of
* data in the results grid.
@@ -29,6 +45,8 @@ export class QueryResultsInput extends EditorInput {
public readonly onRestoreViewStateEmitter = new Emitter<void>();
public readonly onSaveViewStateEmitter = new Emitter<void>();
public readonly state = new ResultsViewState();
constructor(private _uri: string) {
super();
this._visible = false;

View File

@@ -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> {

View File

@@ -8,10 +8,9 @@
import { localize } from 'vs/nls';
import { Registry } from 'vs/platform/registry/common/platform';
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { Extensions, IInsightRegistry } from 'sql/platform/dashboard/common/insightRegistry';
import { DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
import { InsightType, IInsightOptions } from './insights/interfaces';
import { DataDirection, ChartType, LegendPosition, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
const insightRegistry = Registry.as<IInsightRegistry>(Extensions.InsightContribution);

View File

@@ -15,7 +15,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
export class ChartTab implements IPanelTab {
public readonly title = localize('chartTabTitle', 'Chart');
public readonly identifier = generateUuid();
public readonly identifier = 'ChartTab';
public readonly view: ChartView;
constructor(@IInstantiationService instantiationService: IInstantiationService) {

View File

@@ -12,11 +12,11 @@ import { Insight } from './insights/insight';
import QueryRunner from 'sql/parts/query/execution/queryRunner';
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { ChartOptions, IChartOption, ControlType } from './chartOptions';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox';
import { IInsightOptions } from './insights/interfaces';
import { CopyAction, SaveImageAction, CreateInsightAction, IChartActionContext } from './actions';
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { Dimension, $, getContentHeight, getContentWidth } from 'vs/base/browser/dom';
import { SelectBox } from 'vs/base/browser/ui/selectBox/selectBox';
@@ -27,6 +27,14 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { attachSelectBoxStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { isUndefinedOrNull } from 'vs/base/common/types';
export class ChartState {
dataId: { batchId: number, resultId: number };
options: IInsightOptions = {
type: ChartType.Bar
};
}
declare class Proxy {
constructor(object, handler);
@@ -43,6 +51,8 @@ export class ChartView implements IPanelView {
private _copyAction: CopyAction;
private _saveAction: SaveImageAction;
private _state: ChartState;
private options: IInsightOptions = {
type: ChartType.Bar
};
@@ -61,7 +71,7 @@ export class ChartView implements IPanelView {
private chartingContainer: HTMLElement;
private optionDisposables: IDisposable[] = [];
private optionMap: { [x: string]: HTMLElement } = {};
private optionMap: { [x: string]: { element: HTMLElement; set: (val) => void } } = {};
constructor(
@IContextViewService private _contextViewService: IContextViewService,
@@ -95,6 +105,10 @@ export class ChartView implements IPanelView {
}
let result = Reflect.set(target, key, value, receiver);
// mirror the change in our state
if (self.state) {
Reflect.set(self.state.options, key, value);
}
if (change) {
self.taskbar.context = <IChartActionContext>{ options: self.options, insight: self.insight ? self.insight.insight : undefined };
@@ -138,6 +152,7 @@ export class ChartView implements IPanelView {
}
public chart(dataId: { batchId: number, resultId: number }) {
this.state.dataId = dataId;
this._currentData = dataId;
this.shouldGraph();
}
@@ -172,7 +187,7 @@ export class ChartView implements IPanelView {
});
}
}
// if we have the necessary information but the information isn't avaiable yet,
// if we have the necessary information but the information isn't available yet,
// we should be smart and retrying when the information might be available
}
}
@@ -180,7 +195,9 @@ export class ChartView implements IPanelView {
private buildOptions() {
dispose(this.optionDisposables);
this.optionDisposables = [];
this.optionMap = {};
this.optionMap = {
'type': this.optionMap['type']
};
new Builder(this.typeControls).clearChildren();
this.updateActionbar();
@@ -200,9 +217,9 @@ export class ChartView implements IPanelView {
let option = ChartOptions[this.options.type].find(e => e.configEntry === key);
if (option && option.if) {
if (option.if(this.options)) {
new Builder(this.optionMap[key]).show();
new Builder(this.optionMap[key].element).show();
} else {
new Builder(this.optionMap[key]).hide();
new Builder(this.optionMap[key].element).hide();
}
}
}
@@ -211,6 +228,7 @@ export class ChartView implements IPanelView {
private updateActionbar() {
if (this.insight && this.insight.isCopyable) {
this.taskbar.context = { insight: this.insight.insight, options: this.options };
this.taskbar.setContent([
{ action: this._createInsightAction },
{ action: this._copyAction },
@@ -226,57 +244,104 @@ export class ChartView implements IPanelView {
label.innerText = option.label;
let optionContainer = $('div.option-container');
optionContainer.appendChild(label);
let setFunc: (val) => void;
let value = this.state ? this.state.options[option.configEntry] || option.default : option.default;
switch (option.type) {
case ControlType.checkbox:
let checkbox = new Checkbox(optionContainer, {
label: '',
ariaLabel: option.label,
checked: option.default,
checked: value,
onChange: () => {
if (this.options[option.configEntry] !== checkbox.checked) {
this.options[option.configEntry] = checkbox.checked;
this.insight.options = this.options;
if (this.insight) {
this.insight.options = this.options;
}
}
}
});
setFunc = (val: boolean) => {
checkbox.checked = val;
};
break;
case ControlType.combo:
let dropdown = new SelectBox(option.displayableOptions || option.options, 0, this._contextViewService);
dropdown.select(option.options.indexOf(option.default));
dropdown.select(option.options.indexOf(value));
dropdown.render(optionContainer);
dropdown.onDidSelect(e => {
if (this.options[option.configEntry] !== option.options[e.index]) {
this.options[option.configEntry] = option.options[e.index];
this.insight.options = this.options;
if (this.insight) {
this.insight.options = this.options;
}
}
});
setFunc = (val: string) => {
if (!isUndefinedOrNull(val)) {
dropdown.select(option.options.indexOf(val));
}
};
this.optionDisposables.push(attachSelectBoxStyler(dropdown, this._themeService));
break;
case ControlType.input:
let input = new InputBox(optionContainer, this._contextViewService);
input.value = option.default || '';
input.value = value || '';
input.onDidChange(e => {
if (this.options[option.configEntry] !== e) {
this.options[option.configEntry] = e;
this.insight.options = this.options;
if (this.insight) {
this.insight.options = this.options;
}
}
});
setFunc = (val: string) => {
if (!isUndefinedOrNull(val)) {
input.value = val;
}
};
this.optionDisposables.push(attachInputBoxStyler(input, this._themeService));
break;
case ControlType.numberInput:
let numberInput = new InputBox(optionContainer, this._contextViewService, { type: 'number' });
numberInput.value = option.default || '';
numberInput.value = value || '';
numberInput.onDidChange(e => {
if (this.options[option.configEntry] !== Number(e)) {
this.options[option.configEntry] = Number(e);
this.insight.options = this.options;
if (this.insight) {
this.insight.options = this.options;
}
}
});
setFunc = (val: string) => {
if (!isUndefinedOrNull(val)) {
input.value = val;
}
};
this.optionDisposables.push(attachInputBoxStyler(numberInput, this._themeService));
break;
}
this.optionMap[option.configEntry] = optionContainer;
this.optionMap[option.configEntry] = { element: optionContainer, set: setFunc };
container.appendChild(optionContainer);
this.options[option.configEntry] = option.default;
this.options[option.configEntry] = value;
}
}
public set state(val: ChartState) {
this._state = val;
if (this.state.options) {
for (let key in this.state.options) {
if (this.state.options.hasOwnProperty(key)) {
this.options[key] = this.state.options[key];
this.optionMap[key].set(this.state.options[key]);
}
}
}
if (this.state.dataId) {
this.chart(this.state.dataId);
}
}
public get state(): ChartState {
return this._state;
}
}

View File

@@ -13,9 +13,9 @@ import * as colors from 'vs/platform/theme/common/colorRegistry';
import { editorLineNumbers } from 'vs/editor/common/view/editorColorRegistry';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { IInsightOptions, IInsight } from './interfaces';
import { ChartType, DataDirection, LegendPosition } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
const noneLineGraphs = [ChartType.Doughnut, ChartType.Pie];

View File

@@ -6,15 +6,15 @@
'use strict';
import { Graph } from './graphInsight';
import { ChartType, DataDirection } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { Builder } from 'vs/base/browser/builder';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { DataDirection, ChartType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
import { ImageInsight } from './imageInsight';
import { TableInsight } from './tableInsight';
import { IInsightOptions, IInsight, InsightType, IInsightCtor } from './interfaces';
import { CountInsight } from './countInsight';
import { Builder } from 'vs/base/browser/builder';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Dimension } from 'vs/base/browser/dom';
const defaultOptions: IInsightOptions = {

View File

@@ -4,11 +4,10 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { Dimension } from 'vs/base/browser/dom';
import { IInsightData } from 'sql/parts/dashboard/widgets/insights/interfaces';
import { ChartType, LegendPosition, DataDirection } from 'sql/parts/dashboard/widgets/insights/views/charts/chartInsight.component';
import { Dimension } from 'vs/base/browser/dom';
import { DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/types/lineChart.component';
import { DataDirection, ChartType, LegendPosition, DataType } from 'sql/parts/dashboard/widgets/insights/views/charts/interfaces';
export interface IInsightOptions {
type: InsightType | ChartType;

View File

@@ -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,21 +40,26 @@ 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 class GridPanelState {
public tableStates: GridTableState[] = [];
public scrollPosition: number;
public collapsed = false;
}
export interface IGridTableState {
canBeMaximized: boolean;
@@ -66,13 +73,29 @@ 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;
constructor(state?: IGridTableState) {
if (state) {
this._maximized = state.maximized;
this.canBeMaximized = state.canBeMaximized;
private _canBeMaximized: boolean;
/* The top row of the current scroll */
public scrollPosition = 0;
public selection: Slick.Range[];
public activeCell: Slick.Cell;
constructor(public readonly resultId: number, public readonly batchId: number) {
}
public get canBeMaximized(): boolean {
return this._canBeMaximized;
}
public set canBeMaximized(val: boolean) {
if (val === this._canBeMaximized) {
return;
}
this._canBeMaximized = val;
this._onCanBeMaximizedChange.fire(val);
}
public get maximized(): boolean {
@@ -86,10 +109,6 @@ export class GridTableState {
this._maximized = val;
this._onMaximizedChange.fire(val);
}
public clone(): GridTableState {
return new GridTableState({ canBeMaximized: this.canBeMaximized, maximized: this.maximized });
}
}
export class GridPanel extends ViewletPanel {
@@ -103,6 +122,7 @@ export class GridPanel extends ViewletPanel {
private runner: QueryRunner;
private maximizedGrid: GridTable<any>;
private _state: GridPanelState;
constructor(
options: IViewletPanelOptions,
@@ -114,6 +134,16 @@ export class GridPanel extends ViewletPanel {
) {
super(options, keybindingService, contextMenuService, configurationService);
this.splitView = new ScrollableSplitView(this.container, { enableResizing: false });
this.splitView.onScroll(e => {
if (this.state) {
this.state.scrollPosition = e;
}
});
this.onDidChange(e => {
if (this.state) {
this.state.collapsed = !this.isExpanded();
}
});
}
protected renderBody(container: HTMLElement): void {
@@ -151,6 +181,10 @@ export class GridPanel extends ViewletPanel {
this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize;
}, 0);
if (this.state && this.state.scrollPosition) {
this.splitView.setScrollPosition(this.state.scrollPosition);
}
}
private addResultSet(resultSet: sqlops.ResultSetSummary | sqlops.ResultSetSummary[]) {
@@ -164,8 +198,18 @@ export class GridPanel extends ViewletPanel {
let tables: GridTable<any>[] = [];
for (let set of resultsToAdd) {
let tableState = new GridTableState();
let table = this.instantiationService.createInstance(GridTable, this.runner, tableState, set);
let tableState: GridTableState;
if (this._state) {
tableState = this.state.tableStates.find(e => e.batchId === set.batchId && e.resultId === set.id);
}
if (!tableState) {
tableState = new GridTableState(set.id, set.batchId);
if (this._state) {
this._state.tableStates.push(tableState);
}
}
let table = this.instantiationService.createInstance(GridTable, this.runner, set);
table.state = tableState;
tableState.onMaximizedChange(e => {
if (e) {
this.maximizeTable(table.id);
@@ -224,6 +268,24 @@ export class GridPanel extends ViewletPanel {
this.splitView.addViews(this.tables, this.tables.map(i => i.minimumSize));
}
}
public set state(val: GridPanelState) {
this._state = val;
this.tables.map(t => {
let state = this.state.tableStates.find(s => s.batchId === t.resultSet.batchId && s.resultId === t.resultSet.id);
if (!state) {
this.state.tableStates.push(t.state);
}
if (state) {
t.state = state;
}
});
this.setExpanded(!this.state.collapsed);
}
public get state(): GridPanelState {
return this._state;
}
}
class GridTable<T> extends Disposable implements IView {
@@ -242,13 +304,14 @@ class GridTable<T> extends Disposable implements IView {
public id = generateUuid();
readonly element: HTMLElement = this.container;
private _state: GridTableState;
// this handles if the row count is small, like 4-5 rows
private readonly maxSize = ((this.resultSet.rowCount) * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT + BOTTOM_PADDING;
private readonly maxSize = ((this.resultSet.rowCount) * ROW_HEIGHT) + HEADER_HEIGHT + ESTIMATED_SCROLL_BAR_HEIGHT;
constructor(
private runner: QueryRunner,
public state: GridTableState,
private resultSet: sqlops.ResultSetSummary,
public readonly resultSet: sqlops.ResultSetSummary,
@IContextMenuService private contextMenuService: IContextMenuService,
@IInstantiationService private instantiationService: IInstantiationService,
@IEditorService private editorService: IEditorService,
@@ -257,7 +320,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 +357,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 +372,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 +380,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 +397,58 @@ 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 });
this.selectionModel.onSelectedRangesChanged.subscribe(e => {
if (this.state) {
this.state.selection = this.selectionModel.getSelectedRanges();
}
});
this.table.grid.onScroll.subscribe(e => {
if (this.state) {
this.state.scrollPosition = this.table.grid.getViewport().top;
}
});
this.table.grid.onActiveCellChanged.subscribe(e => {
if (this.state) {
this.state.activeCell = this.table.grid.getActiveCell();
}
});
this.setupState();
}
private setupState() {
// change actionbar on maximize change
this.state.onMaximizedChange(this.rebuildActionBar, this);
this.state.onCanBeMaximizedChange(this.rebuildActionBar, this);
if (this.state.scrollPosition) {
this.table.grid.scrollRowToTop(this.state.scrollPosition);
}
if (this.state.selection) {
this.selectionModel.setSelectedRanges(this.state.selection);
}
if (this.state.activeCell) {
this.table.setActiveCell(this.state.activeCell.row, this.state.activeCell.cell);
}
}
public get state(): GridTableState {
return this._state;
}
public set state(val: GridTableState) {
this._state = val;
}
private onTableClick(event: ITableMouseEvent) {
@@ -372,6 +464,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 +518,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 +553,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 +570,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 +579,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);
}
});
}

View File

@@ -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 {

View File

@@ -0,0 +1,5 @@
.monaco-select-box {
cursor: pointer;
min-width: 150px;
padding: 2px;
}

View File

@@ -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,9 +55,15 @@ interface IBatchTemplate extends IMessageTemplate {
const TemplateIds = {
MESSAGE: 'message',
BATCH: 'batch',
MODEL: 'model'
MODEL: 'model',
ERROR: 'error'
};
export class MessagePanelState {
public scrollPosition: number;
public collapsed = false;
}
export class MessagePanel extends ViewletPanel {
private ds = new MessageDataSource();
private renderer = new MessageRenderer();
@@ -67,6 +72,7 @@ export class MessagePanel extends ViewletPanel {
private container = $('div message-tree').getHTMLElement();
private queryRunnerDisposables: IDisposable[] = [];
private _state: MessagePanelState;
private tree: ITree;
@@ -86,6 +92,16 @@ export class MessagePanel extends ViewletPanel {
renderer: this.renderer,
controller: this.controller
}, { keyboardSupport: false });
this.tree.onDidScroll(e => {
if (this.state) {
this.state.scrollPosition = this.tree.getScrollPosition();
}
});
this.onDidChange(e => {
if (this.state) {
this.state.collapsed = !this.isExpanded();
}
});
}
protected renderBody(container: HTMLElement): void {
@@ -99,8 +115,12 @@ export class MessagePanel extends ViewletPanel {
protected layoutBody(size: number): void {
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.layout(size);
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
if (this.state && this.state.scrollPosition) {
this.tree.setScrollPosition(this.state.scrollPosition);
} else {
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
}
}
}
@@ -113,17 +133,30 @@ 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);
}
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.refresh(this.model).then(() => {
if (previousScrollPosition === 1) {
if (hasError) {
this.setExpanded(true);
}
if (this.state.scrollPosition) {
this.tree.refresh(this.model).then(() => {
this.tree.setScrollPosition(1);
}
});
});
} else {
const previousScrollPosition = this.tree.getScrollPosition();
this.tree.refresh(this.model).then(() => {
if (previousScrollPosition === 1) {
this.tree.setScrollPosition(1);
}
});
}
this.maximumBodySize = this.model.messages.length * 22;
}
private reset() {
@@ -131,6 +164,17 @@ export class MessagePanel extends ViewletPanel {
this.model.totalExecuteMessage = undefined;
this.tree.refresh(this.model);
}
public set state(val: MessagePanelState) {
this._state = val;
if (this.state.scrollPosition) {
this.tree.setScrollPosition(this.state.scrollPosition);
}
this.setExpanded(!this.state.collapsed);
}
public get state(): MessagePanelState {
return this._state;
}
}
class MessageDataSource implements IDataSource {
@@ -176,6 +220,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 +237,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;

View File

@@ -43,7 +43,6 @@ import {
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { IEditorDescriptorService } from 'sql/parts/query/editor/editorDescriptorService';
import { IConnectionManagementService } from 'sql/parts/connection/common/connectionManagement';
import { attachEditableDropdownStyler } from 'sql/common/theme/styler';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { CancellationToken } from 'vs/base/common/cancellation';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
@@ -500,7 +499,7 @@ export class QueryEditor extends BaseEditor {
public get listDatabasesActionItem(): ListDatabasesActionItem {
if (!this._listDatabasesActionItem) {
this._listDatabasesActionItem = this._instantiationService.createInstance(ListDatabasesActionItem, this, this._listDatabasesAction);
this._register(attachEditableDropdownStyler(this._listDatabasesActionItem, this.themeService));
this._register(this._listDatabasesActionItem.attachStyler(this.themeService));
}
return this._listDatabasesActionItem;
}

View File

@@ -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);
}

View File

@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput';
import { QueryResultsInput, ResultsViewState } from 'sql/parts/query/common/queryResultsInput';
import { TabbedPanel, IPanelTab, IPanelView } from 'sql/base/browser/ui/panel/panel';
import { IQueryModelService } from '../execution/queryModel';
import QueryRunner from 'sql/parts/query/execution/queryRunner';
@@ -14,11 +14,10 @@ import { ChartTab } from './charting/chartTab';
import { QueryPlanTab } from 'sql/parts/queryPlan/queryPlan';
import * as nls from 'vs/nls';
import * as UUID from 'vs/base/common/uuid';
import { PanelViewlet } from 'vs/workbench/browser/parts/views/panelViewlet';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import * as DOM from 'vs/base/browser/dom';
import { once } from 'vs/base/common/event';
import { once, anyEvent } from 'vs/base/common/event';
class ResultsView implements IPanelView {
private panelViewlet: PanelViewlet;
@@ -26,25 +25,72 @@ class ResultsView implements IPanelView {
private messagePanel: MessagePanel;
private container = document.createElement('div');
private currentDimension: DOM.Dimension;
private needsGridResize = false;
private _state: ResultsViewState;
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') });
constructor(private instantiationService: IInstantiationService) {
this.panelViewlet = this.instantiationService.createInstance(PanelViewlet, 'resultsView', { showHeaderInTitleWhenSingleView: false });
this.gridPanel = this.instantiationService.createInstance(GridPanel, { title: nls.localize('gridPanel', 'Results'), id: 'gridPanel' });
this.messagePanel = this.instantiationService.createInstance(MessagePanel, { title: nls.localize('messagePanel', 'Messages'), minimumBodySize: 0, id: 'messagePanel' });
this.gridPanel.render();
this.messagePanel.render();
this.panelViewlet.create(this.container).then(() => {
this.gridPanel.setVisible(false);
this.panelViewlet.addPanels([
{ panel: this.gridPanel, size: 1000, index: 0 },
{ panel: this.messagePanel, size: this.messagePanel.minimumSize, index: 1 }
]);
});
let gridResizeList = this.gridPanel.onDidChange(e => {
this.panelViewlet.resizePanel(this.gridPanel, this.gridPanel.maximumSize);
anyEvent(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(e => {
let size = this.gridPanel.maximumBodySize;
if (size < 1 && this.gridPanel.isVisible()) {
this.gridPanel.setVisible(false);
this.panelViewlet.removePanels([this.gridPanel]);
this.gridPanel.layout(0);
} else if (size > 0 && !this.gridPanel.isVisible()) {
this.gridPanel.setVisible(true);
let panelSize: number;
if (this.state && this.state.gridPanelSize) {
panelSize = this.state.gridPanelSize;
} else if (this.currentDimension) {
panelSize = Math.round(this.currentDimension.height * .7);
} else {
panelSize = 200;
this.needsGridResize = true;
}
this.panelViewlet.addPanels([{ panel: this.gridPanel, index: 0, size: panelSize }]);
}
});
let resizeList = anyEvent(this.gridPanel.onDidChange, this.messagePanel.onDidChange)(() => {
let panelSize: number;
if (this.state && this.state.gridPanelSize) {
panelSize = this.state.gridPanelSize;
} else if (this.currentDimension) {
panelSize = Math.round(this.currentDimension.height * .7);
} else {
panelSize = 200;
this.needsGridResize = true;
}
if (this.state.messagePanelSize) {
this.panelViewlet.resizePanel(this.gridPanel, this.state.messagePanelSize);
}
this.panelViewlet.resizePanel(this.gridPanel, panelSize);
})
// once the user changes the sash we should stop trying to resize the grid
once(this.panelViewlet.onDidSashChange)(e => {
gridResizeList.dispose();
this.needsGridResize = false;
resizeList.dispose();
});
this.panelViewlet.onDidSashChange(e => {
if (this.state) {
if (this.gridPanel.isExpanded()) {
this.state.gridPanelSize = this.panelViewlet.getPanelSize(this.gridPanel);
}
if (this.messagePanel.isExpanded()) {
this.state.messagePanelSize = this.panelViewlet.getPanelSize(this.messagePanel);
}
}
});
}
@@ -59,6 +105,9 @@ class ResultsView implements IPanelView {
this.gridPanel.layout(dimension.height);
}
this.currentDimension = dimension;
if (this.needsGridResize) {
this.panelViewlet.resizePanel(this.gridPanel, this.state.gridPanelSize || Math.round(this.currentDimension.height * .7));
}
}
remove(): void {
@@ -73,11 +122,21 @@ class ResultsView implements IPanelView {
public hideResultHeader() {
this.gridPanel.headerVisible = false;
}
public set state(val: ResultsViewState) {
this._state = val;
this.gridPanel.state = val.gridPanelState;
this.messagePanel.state = val.messagePanelState;
}
public get state(): ResultsViewState {
return this._state;
}
}
class ResultsTab implements IPanelTab {
public readonly title = nls.localize('resultsTabTitle', 'Results');
public readonly identifier = UUID.generateUuid();
public readonly identifier = 'resultsTab';
public readonly view: ResultsView;
constructor(instantiationService: IInstantiationService) {
@@ -105,6 +164,12 @@ export class QueryResultsView {
this.chartTab = new ChartTab(instantiationService);
this._panelView = new TabbedPanel(container, { showHeaderWhenSingleView: false });
this.qpTab = new QueryPlanTab();
this._panelView.pushTab(this.resultsTab);
this._panelView.onTabChange(e => {
if (this.input) {
this.input.state.activeTab = e;
}
});
}
public style() {
@@ -112,11 +177,24 @@ export class QueryResultsView {
public set input(input: QueryResultsInput) {
this._input = input;
this.resultsTab.view.state = this.input.state;
this.qpTab.view.state = this.input.state.queryPlanState;
this.chartTab.view.state = this.input.state.chartState;
let queryRunner = this.queryModelService._getQueryInfo(input.uri).queryRunner;
this.resultsTab.queryRunner = queryRunner;
this.chartTab.queryRunner = queryRunner;
if (!this._panelView.contains(this.resultsTab)) {
this._panelView.pushTab(this.resultsTab);
if (this.input.state.visibleTabs.has(this.chartTab.identifier)) {
if (!this._panelView.contains(this.chartTab)) {
this._panelView.pushTab(this.chartTab);
}
}
if (this.input.state.visibleTabs.has(this.qpTab.identifier)) {
if (!this._panelView.contains(this.qpTab)) {
this._panelView.pushTab(this.qpTab);
}
}
if (this.input.state.activeTab) {
this._panelView.showTab(this.input.state.activeTab);
}
}
@@ -133,6 +211,7 @@ export class QueryResultsView {
}
public chartData(dataId: { resultId: number, batchId: number }): void {
this.input.state.visibleTabs.add(this.chartTab.identifier);
if (!this._panelView.contains(this.chartTab)) {
this._panelView.pushTab(this.chartTab);
}
@@ -142,6 +221,7 @@ export class QueryResultsView {
}
public showPlan(xml: string) {
this.input.state.visibleTabs.add(this.qpTab.identifier);
if (!this._panelView.contains(this.qpTab)) {
this._panelView.pushTab(this.qpTab);
}

View File

@@ -3,6 +3,7 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!sql/parts/query/editor/media/queryActions';
import * as nls from 'vs/nls';
import { Builder, $ } from 'vs/base/browser/builder';
import { Dropdown } from 'sql/base/browser/ui/editableDropdown/dropdown';
@@ -12,6 +13,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { TPromise } from 'vs/base/common/winjs.base';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { attachEditableDropdownStyler, attachSelectBoxStyler } from 'sql/common/theme/styler';
import { ISelectionData } from 'sqlops';
import {
@@ -25,6 +27,8 @@ import { QueryEditor } from 'sql/parts/query/editor/queryEditor';
import { IQueryModelService } from 'sql/parts/query/execution/queryModel';
import { INotificationService } from 'vs/platform/notification/common/notification';
import Severity from 'vs/base/common/severity';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
/**
* Action class that query-based Actions will extend. This base class automatically handles activating and
@@ -431,6 +435,9 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
private _isConnected: boolean;
private $databaseListDropdown: Builder;
private _dropdown: Dropdown;
private _databaseSelectBox: SelectBox;
private _isInAccessibilityMode: boolean;
private readonly _selectDatabaseString: string = nls.localize("selectDatabase", "Select Database");
// CONSTRUCTOR /////////////////////////////////////////////////////////
constructor(
@@ -439,23 +446,33 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
@INotificationService private _notificationService: INotificationService,
@IContextViewService contextViewProvider: IContextViewService,
@IThemeService themeService: IThemeService
@IThemeService themeService: IThemeService,
@IConfigurationService private readonly _configurationService: IConfigurationService
) {
super();
this._toDispose = [];
this.$databaseListDropdown = $('.databaseListDropdown');
let selectString = nls.localize("selectDatabase", "Select Database");
this._dropdown = new Dropdown(this.$databaseListDropdown.getHTMLElement(), contextViewProvider, themeService, {
strictSelection: true,
placeholder: selectString,
ariaLabel: selectString,
actionLabel: nls.localize('listDatabases.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
});
this._dropdown.onValueChange(s => this.databaseSelected(s));
this._isInAccessibilityMode = this._configurationService.getValue('editor.accessibilitySupport') === 'on';
if (this._isInAccessibilityMode) {
this._databaseSelectBox = new SelectBox([this._selectDatabaseString], this._selectDatabaseString, contextViewProvider, undefined, { ariaLabel: this._selectDatabaseString });
this._databaseSelectBox.render(this.$databaseListDropdown.getHTMLElement());
this._databaseSelectBox.onDidSelect(e => { this.databaseSelected(e.selected); });
this._databaseSelectBox.disable();
} else {
this._dropdown = new Dropdown(this.$databaseListDropdown.getHTMLElement(), contextViewProvider, themeService, {
strictSelection: true,
placeholder: this._selectDatabaseString,
ariaLabel: this._selectDatabaseString,
actionLabel: nls.localize('listDatabases.toggleDatabaseNameDropdown', 'Select Database Toggle Dropdown')
});
this._dropdown.onValueChange(s => this.databaseSelected(s));
this._toDispose.push(this._dropdown.onFocus(() => { self.onDropdownFocus(); }));
}
// Register event handlers
let self = this;
this._toDispose.push(this._dropdown.onFocus(() => { self.onDropdownFocus(); }));
this._toDispose.push(this._connectionManagementService.onConnectionChanged(params => { self.onConnectionChanged(params); }));
}
@@ -465,7 +482,12 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
}
public style(styles) {
this._dropdown.style(styles);
if (this._isInAccessibilityMode) {
this._databaseSelectBox.style(styles);
}
else {
this._dropdown.style(styles);
}
}
public setActionContext(context: any): void {
@@ -477,11 +499,27 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
}
public focus(): void {
this._dropdown.focus();
if (this._isInAccessibilityMode) {
this._databaseSelectBox.focus();
} else {
this._dropdown.focus();
}
}
public blur(): void {
this._dropdown.blur();
if (this._isInAccessibilityMode) {
this._databaseSelectBox.blur();
} else {
this._dropdown.blur();
}
}
public attachStyler(themeService: IThemeService): IDisposable {
if (this._isInAccessibilityMode) {
return attachSelectBoxStyler(this, themeService);
} else {
return attachEditableDropdownStyler(this, themeService);
}
}
public dispose(): void {
@@ -496,9 +534,15 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
public onDisconnect(): void {
this._isConnected = false;
this._dropdown.enabled = false;
this._currentDatabaseName = undefined;
this._dropdown.value = '';
if (this._isInAccessibilityMode) {
this._databaseSelectBox.disable();
this._databaseSelectBox.setOptions([this._selectDatabaseString]);
} else {
this._dropdown.enabled = false;
this._dropdown.value = '';
}
}
// PRIVATE HELPERS /////////////////////////////////////////////////////
@@ -515,22 +559,22 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
this._connectionManagementService.changeDatabase(this._editor.uri, dbName)
.then(
result => {
if (!result) {
result => {
if (!result) {
this.resetDatabaseName();
this._notificationService.notify({
severity: Severity.Error,
message: nls.localize('changeDatabase.failed', "Failed to change database")
});
}
},
error => {
this.resetDatabaseName();
this._notificationService.notify({
severity: Severity.Error,
message: nls.localize('changeDatabase.failed', "Failed to change database")
message: nls.localize('changeDatabase.failedWithError', "Failed to change database {0}", error)
});
}
},
error => {
this.resetDatabaseName();
this._notificationService.notify({
severity: Severity.Error,
message: nls.localize('changeDatabase.failedWithError', "Failed to change database {0}", error)
});
});
}
private getCurrentDatabaseName() {
@@ -545,7 +589,11 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
}
private resetDatabaseName() {
this._dropdown.value = this.getCurrentDatabaseName();
if (this._isInAccessibilityMode) {
this._databaseSelectBox.selectWithOptionName(this.getCurrentDatabaseName());
} else {
this._dropdown.value = this.getCurrentDatabaseName();
}
}
private onConnectionChanged(connParams: IConnectionParams): void {
@@ -579,9 +627,26 @@ export class ListDatabasesActionItem extends EventEmitter implements IActionItem
private updateConnection(databaseName: string) {
this._isConnected = true;
this._dropdown.enabled = true;
this._currentDatabaseName = databaseName;
this._dropdown.value = databaseName;
if (this._isInAccessibilityMode) {
this._databaseSelectBox.enable();
let self = this;
let uri = self._editor.connectedUri;
if (!uri) {
return;
}
self._connectionManagementService.listDatabases(uri)
.then(result => {
if (result && result.databaseNames) {
this._databaseSelectBox.setOptions(result.databaseNames);
}
this._databaseSelectBox.selectWithOptionName(databaseName);
});
} else {
this._dropdown.enabled = true;
this._dropdown.value = databaseName;
}
}
// TESTING PROPERTIES //////////////////////////////////////////////////

View File

@@ -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;

View File

@@ -13,9 +13,13 @@ import { localize } from 'vs/nls';
import * as UUID from 'vs/base/common/uuid';
import { Builder } from 'vs/base/browser/builder';
export class QueryPlanState {
xml: string;
}
export class QueryPlanTab implements IPanelTab {
public readonly title = localize('queryPlanTitle', 'Query Plan');
public readonly identifier = UUID.generateUuid();
public readonly identifier = 'QueryPlanTab';
public readonly view: QueryPlanView;
constructor() {
@@ -27,6 +31,7 @@ export class QueryPlanView implements IPanelView {
private qp: QueryPlan;
private xml: string;
private container = document.createElement('div');
private _state: QueryPlanState;
public render(container: HTMLElement): void {
if (!this.qp) {
@@ -47,6 +52,20 @@ export class QueryPlanView implements IPanelView {
} else {
this.xml = xml;
}
if (this.state) {
this.state.xml = xml;
}
}
public set state(val: QueryPlanState) {
this._state = val;
if (this.state.xml) {
this.showPlan(this.state.xml);
}
}
public get state(): QueryPlanState {
return this._state;
}
}

View File

@@ -93,7 +93,7 @@ const viewletDescriptor = new ViewletDescriptor(
VIEWLET_ID,
'Task History',
'taskHistoryViewlet',
-90
1
);
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor);

View File

@@ -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);
}
};

5
src/sql/sqlops.d.ts vendored
View File

@@ -185,6 +185,11 @@ declare module 'sqlops' {
* Get the parent node. Returns undefined if there is none.
*/
getParent(): Thenable<ObjectExplorerNode>;
/**
* Refresh the node, expanding it if it has children
*/
refresh(): Thenable<void>;
}
}

View File

@@ -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 {

View File

@@ -131,6 +131,7 @@ export enum FrequencyRelativeIntervals {
export enum ModelComponentTypes {
NavContainer,
DivContainer,
FlexContainer,
Card,
InputBox,

View File

@@ -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> {

View File

@@ -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(

View File

@@ -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);

View File

@@ -9,7 +9,7 @@ import { ExtHostObjectExplorerShape, SqlMainContext, MainThreadObjectExplorerSha
import * as sqlops from 'sqlops';
import * as vscode from 'vscode';
export class ExtHostObjectExplorer implements ExtHostObjectExplorerShape {
export class ExtHostObjectExplorer implements ExtHostObjectExplorerShape {
private _proxy: MainThreadObjectExplorerShape;
@@ -44,7 +44,7 @@ class ExtHostObjectExplorerNode implements sqlops.objectexplorer.ObjectExplorerN
public errorMessage: string;
constructor(nodeInfo: sqlops.NodeInfo, connectionId: string, private _proxy: MainThreadObjectExplorerShape) {
Object.entries(nodeInfo).forEach(([key, value]) => this[key] = value);
this.getDetailsFromInfo(nodeInfo);
this.connectionId = connectionId;
}
@@ -71,4 +71,12 @@ class ExtHostObjectExplorerNode implements sqlops.objectexplorer.ObjectExplorerN
}
return this._proxy.$getNode(this.connectionId, this.nodePath.slice(0, parentPathEndIndex)).then(nodeInfo => nodeInfo ? new ExtHostObjectExplorerNode(nodeInfo, this.connectionId, this._proxy) : undefined);
}
refresh(): Thenable<void> {
return this._proxy.$refresh(this.connectionId, this.nodePath).then(nodeInfo => this.getDetailsFromInfo(nodeInfo));
}
private getDetailsFromInfo(nodeInfo: sqlops.NodeInfo): void {
Object.entries(nodeInfo).forEach(([key, value]) => this[key] = value);
}
}

View File

@@ -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) {

View File

@@ -15,6 +15,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import * as TaskUtilities from 'sql/workbench/common/taskUtilities';
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { TreeItemCollapsibleState } from 'sql/parts/objectExplorer/common/treeNode';
@extHostNamedCustomer(SqlMainContext.MainThreadObjectExplorer)
export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape {
@@ -50,7 +51,7 @@ export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape {
public $getActiveConnectionNodes(): Thenable<NodeInfoWithConnection[]> {
let connectionNodes = this._objectExplorerService.getActiveConnectionNodes();
return Promise.resolve(connectionNodes.map(node => {
return {connectionId: node.connection.id, nodeInfo: node.toNodeInfo()};
return { connectionId: node.connection.id, nodeInfo: node.toNodeInfo() };
}));
}
@@ -73,4 +74,8 @@ export class MainThreadObjectExplorer implements MainThreadObjectExplorerShape {
public $findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames: string[]): Thenable<sqlops.NodeInfo[]> {
return this._objectExplorerService.findNodes(connectionId, type, schema, name, database, parentObjectNames);
}
public $refresh(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo> {
return this._objectExplorerService.refreshNodeInView(connectionId, nodePath).then(node => node.toNodeInfo());
}
}

View File

@@ -36,7 +36,7 @@ export abstract class ExtHostAccountManagementShape {
export abstract class ExtHostConnectionManagementShape {
$onConnectionOpened(handleId: string, connection: sqlops.connection.Connection): void { throw ni; }
}
}
export abstract class ExtHostDataProtocolShape {
@@ -663,6 +663,7 @@ export interface MainThreadObjectExplorerShape extends IDisposable {
$getChildren(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo[]>;
$isExpanded(connectionId: string, nodePath: string): Thenable<boolean>;
$findNodes(connectionId: string, type: string, schema: string, name: string, database: string, parentObjectNames: string[]): Thenable<sqlops.NodeInfo[]>;
$refresh(connectionId: string, nodePath: string): Thenable<sqlops.NodeInfo>;
}
export interface ExtHostModelViewDialogShape {
@@ -672,10 +673,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 +690,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 {
}

View File

@@ -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();

View File

@@ -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) {

View File

@@ -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;
}
}

View File

@@ -301,7 +301,8 @@ suite('SQL Object Explorer Service tests', () => {
reveal: element => Promise.resolve() as Thenable<void>,
setSelected: (element, selected, clearOtherSelections) => undefined,
isExpanded: element => undefined,
onSelectionOrFocusChange: Event.None
onSelectionOrFocusChange: Event.None,
refreshElement: (element) => Promise.resolve() as Thenable<void>
} as ServerTreeView);
});
@@ -738,4 +739,22 @@ suite('SQL Object Explorer Service tests', () => {
}, err => done(err));
});
});
test('refreshInView refreshes the node, expands it, and returns the refreshed node', async () => {
// Set up the session and tree view
await objectExplorerService.createNewSession('MSSQL', connection);
objectExplorerService.onSessionCreated(1, objectExplorerSession);
serverTreeView.setup(x => x.refreshElement(TypeMoq.It.isAny())).returns(() => Promise.resolve());
objectExplorerService.registerServerTreeView(serverTreeView.object);
// Refresh the node
let nodePath = objectExplorerSession.rootNode.nodePath;
let refreshedNode = await objectExplorerService.refreshNodeInView(connection.id, nodePath);
// Verify that it was refreshed, expanded, and the refreshed detailed were returned
sqlOEProvider.verify(x => x.refreshNode(TypeMoq.It.is(refreshNode => refreshNode.nodePath === nodePath)), TypeMoq.Times.once());
refreshedNode.children.forEach((childNode, index) => {
assert.equal(childNode.nodePath, objectExplorerExpandInfoRefresh.nodes[index].nodePath);
});
});
});

View File

@@ -28,6 +28,7 @@ import { ConnectionManagementService } from 'sql/parts/connection/common/connect
import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { TestThemeService } from 'sqltest/stubs/themeTestService';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import * as TypeMoq from 'typemoq';
import * as assert from 'assert';
@@ -40,6 +41,7 @@ suite('SQL QueryAction Tests', () => {
let editor: TypeMoq.Mock<QueryEditor>;
let calledRunQueryOnInput: boolean = undefined;
let testQueryInput: TypeMoq.Mock<QueryInput>;
let configurationService: TypeMoq.Mock<ConfigurationService>;
setup(() => {
// Setup a reusable mock QueryInput
@@ -56,6 +58,13 @@ suite('SQL QueryAction Tests', () => {
editor.setup(x => x.getSelection()).returns(() => undefined);
editor.setup(x => x.getSelection(false)).returns(() => undefined);
editor.setup(x => x.isSelectionEmpty()).returns(() => false);
configurationService = TypeMoq.Mock.ofInstance({
getValue: () => undefined,
onDidChangeConfiguration: () => undefined
} as any);
configurationService.setup(x => x.getValue(TypeMoq.It.isAny())).returns(() => {
return {};
});
});
test('setClass sets child CSS class correctly', (done) => {
@@ -463,7 +472,7 @@ suite('SQL QueryAction Tests', () => {
});
// If I query without having initialized anything, state should be clear
listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, undefined, undefined);
listItem = new ListDatabasesActionItem(editor.object, undefined, connectionManagementService.object, undefined, undefined, undefined, configurationService.object);
assert.equal(listItem.isEnabled(), false, 'do not expect dropdown enabled unless connected');
assert.equal(listItem.currentDatabaseName, undefined, 'do not expect dropdown to have entries unless connected');
@@ -498,7 +507,7 @@ suite('SQL QueryAction Tests', () => {
cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName });
// ... Create a database dropdown that has been connected
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null);
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
listItem.onConnected();
// If: I raise a connection changed event
@@ -522,7 +531,7 @@ suite('SQL QueryAction Tests', () => {
cms.setup(x => x.getConnectionProfile(TypeMoq.It.isAny())).returns(() => <IConnectionProfile>{ databaseName: databaseName });
// ... Create a database dropdown that has been connected
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null);
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
listItem.onConnected();
// If: I raise a connection changed event for the 'wrong' URI
@@ -549,7 +558,7 @@ suite('SQL QueryAction Tests', () => {
cms.setup(x => x.onConnectionChanged).returns(() => dbChangedEmitter.event);
// ... Create a database dropdown
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null);
let listItem = new ListDatabasesActionItem(editor.object, undefined, cms.object, null, null, null, configurationService.object);
// If: I raise a connection changed event
let eventParams = <IConnectionParams>{

View File

@@ -93,7 +93,7 @@ suite('SQL QueryEditor Tests', () => {
instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((classDef, editor, action) => {
if (classDef.ID) {
if (classDef.ID === 'listDatabaseQueryActionItem') {
return new ListDatabasesActionItem(editor, action, connectionManagementService.object, undefined, undefined, undefined);
return new ListDatabasesActionItem(editor, action, connectionManagementService.object, undefined, undefined, undefined, configurationService.object);
}
}
// Default
@@ -344,7 +344,7 @@ suite('SQL QueryEditor Tests', () => {
queryActionInstantiationService.setup(x => x.createInstance(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny()))
.returns((definition, editor, action, selectBox) => {
if (definition.ID === 'listDatabaseQueryActionItem') {
let item = new ListDatabasesActionItem(editor, action, queryConnectionService.object, undefined, undefined, undefined);
let item = new ListDatabasesActionItem(editor, action, queryConnectionService.object, undefined, undefined, undefined,configurationService.object);
return item;
}
// Default

View File

@@ -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>;
}

View File

@@ -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 = {

View File

@@ -122,7 +122,8 @@ const viewletDescriptor = new ViewletDescriptor(
VIEWLET_ID,
localize('extensions', "Extensions"),
'extensions',
4
// {{SQL CARBON EDIT}}
14
);
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets)

View File

@@ -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);
});
});
}

View File

@@ -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}}

View File

@@ -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)

View File

@@ -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(

View File

@@ -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 {

View File

@@ -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;

View File

@@ -61,7 +61,7 @@ function main() {
'bootstrap': `../${ out }/bootstrap`
},
catchError: true,
// {{SQL CARBON EDIT}}
// {{SQL CARBON EDIT}}
nodeModules: [
'@angular/common',
'@angular/core',
@@ -70,6 +70,7 @@ function main() {
'@angular/platform-browser-dynamic',
'@angular/router',
'angular2-grid',
'ng2-charts/ng2-charts',
'rxjs/add/observable/of',
'rxjs/Observable',
'rxjs/Subject',

View File

@@ -6111,9 +6111,9 @@ slice-ansi@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
"slickgrid@github:anthonydresser/SlickGrid#2.3.25":
version "2.3.25"
resolved "https://codeload.github.com/anthonydresser/SlickGrid/tar.gz/5fa498a3df7ed671958bedeac1863c41352c7825"
"slickgrid@github:anthonydresser/SlickGrid#2.3.27":
version "2.3.27"
resolved "https://codeload.github.com/anthonydresser/SlickGrid/tar.gz/712a59307942c8fdb18708b6d1a501880a74db8d"
dependencies:
jquery ">=1.8.0"
jquery-ui ">=1.8.0"