Compare commits

...

18 Commits
1.9.0 ... 1.5.2

Author SHA1 Message Date
Karl Burtram
f74080c963 Bump ADS to 1.5.2 2019-03-21 22:57:32 -07:00
Raj
32cd41d076 Merge master 25b1d4b673 2019-03-21 21:38:04 -07:00
Raj
0d7d1cf375 #4618: Notebook JSON has extra } after save (#4627) 2019-03-21 21:31:53 -07:00
Chris LaFreniere
8b826063b4 Have notebook grids respond to theme change events (#4624) 2019-03-21 21:31:05 -07:00
Charles Gagnon
6a500715a7 Fix recent connections list to use <default> DB if no DB is specified by the user when a connection is made (#4564) 2019-03-15 17:34:25 -07:00
Cory Rivera
5e02486ace Check error in webhdfs.sendRequest before trying to check response code. (#4561) 2019-03-15 17:34:15 -07:00
Karl Burtram
16dbef978e Fix missing Azure account name 2019-03-15 15:40:58 -07:00
Alan Ren
f9081773dd make connectiondialog react to provider event (#4544)
* make connectiondialog react to provider event

* fix unit test error

* code review comments
2019-03-15 15:16:40 -07:00
Karl Burtram
b3799d4506 Revert "Grid scrolling bugs (#4396)"
This reverts commit ace6012c1c.
2019-03-15 14:12:18 -07:00
Karl Burtram
2ab9e4b861 Revert "change sizing behavior to allow the messages to fulling collapse down (set results to have no max height) (#4313)"
This reverts commit 7de294a58e.
2019-03-15 13:37:56 -07:00
Charles Gagnon
a1d8bae654 Fix dropdowns flickering every other time they're opened. The hide message code was being invoked which called hideContextView (the actual dropdown part) even if no message was ever displayed. Now we'll delay setting the message to null and only call hideContextView if we actually had a message to display. (#4528)
Also fixed small issue where messages that didn't have a container would throw an error when trying to call removeClass (since this.element is pulled from the container and thus was undefined.

Tested that the flicker is gone and that messages still show up correctly
2019-03-15 11:38:37 -07:00
Kevin Cunnane
b265c53f9c FIX #4513 Notebook stuck at changing kernel (#4518)
* FIX #4513 Notebook stuck at changing kernel
- Intra-provider kernel change didn't happen because we only tried changing kernel on new session creation.
- Inverted the logic (e.g. did the right thing) and renamed the method so it's clearer what we're doing & what the boolean value should be
- Manually tested all the known scenarios
2019-03-15 11:38:29 -07:00
Alan Ren
ac7d19133a use the new dataprotocol client (#4515) 2019-03-15 11:38:23 -07:00
Kevin Cunnane
ab3895a4c2 Fix #4505 Notebooks: New Notebook will not work if existing untitled notebooks are rehydrated (#4506)
* Fix #4505 Notebooks: New Notebook will not work if existing untitled notebooks are rehydrated
* Also fixes #4508
* Unify behavior across New Notebook entry points
- Use Notebook-{n} as the standard in both entry points
- Use SQL as default provider in both
- Ensure both check for other names and only use free number
2019-03-15 11:38:16 -07:00
udeeshagautam
a230e24af0 Fix for : 4471 Backup/Restore shows in Context Menu for Azure DB (#4498)
* Remove Back Restore from Cloud db's Context menu

* checking for null

* Cleaning up the check
2019-03-15 11:38:08 -07:00
Kevin Cunnane
8354ccd76d Fix #4029 Ensure changeKernels always resolves, even in error states (#4488)
* Fix #4029 Ensure changeKernels always resolves, even in error states
This is necessary to unblock reverting the kernel on canceling  Python install
- startSession now correctly sets up kernel information, since a kernel is loaded there.
- Remove call to change kernel on session initialize. This isn't needed due to refactor
- Handle kernel change failure by attempting to fall back to old kernel
- ExtensionHost $startNewSession now ensures errors are sent across the wire.
- Update AttachTo and Kernel dropdowns so they handle kernel being available. This is needed since other changes mean the session is likely ready before these get going

* Fix to handle python cancel and load existing scenarios
- Made changes to handle failure flow when Python dialog is canceled
- Made changes to handle initial load fail. Kernel and Attach To dropdowns show No Kernel / None and you can choose a kernel
- Added error wrapping in ext host so that string errors make it across and aren't lost.
2019-03-15 11:37:58 -07:00
Karl Burtram
3d60ee9030 Merge branch 'master' into release/1.5 2019-03-14 10:04:56 -07:00
Alan Ren
5faf2d9cb2 bump up the extension version for new release (#4456)
* bump up the extension version for new release

* change url for dacpac ext
2019-03-13 16:01:28 -07:00
28 changed files with 377 additions and 209 deletions

View File

@@ -21,7 +21,7 @@
"buffer-stream-reader": "^0.1.1",
"bytes": "^3.1.0",
"clipboardy": "^1.2.3",
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.15",
"dataprotocol-client": "github:Microsoft/sqlops-dataprotocolclient#0.2.16",
"error-ex": "^1.3.2",
"figures": "^2.0.0",
"opener": "^1.4.3",

View File

@@ -172,19 +172,27 @@ function saveProfileAndCreateNotebook(profile: azdata.IConnectionProfile): Promi
return handleNewNotebookTask(undefined, profile);
}
function findNextUntitledEditorName(): string {
let nextVal = untitledCounter;
// Note: this will go forever if it's coded wrong, or you have inifinite Untitled notebooks!
while (true) {
let title = `Notebook-${nextVal++}`;
let hasTextDoc = vscode.workspace.textDocuments.findIndex(doc => doc.isUntitled && doc.fileName === title) > -1;
let hasNotebookDoc = azdata.nb.notebookDocuments.findIndex(doc => doc.isUntitled && doc.fileName === title) > -1;
if (!hasTextDoc && !hasNotebookDoc) {
untitledCounter = nextVal;
return title;
}
}
}
async function handleNewNotebookTask(oeContext?: azdata.ObjectExplorerContext, profile?: azdata.IConnectionProfile): Promise<void> {
// Ensure we get a unique ID for the notebook. For now we're using a different prefix to the built-in untitled files
// to handle this. We should look into improving this in the future
let untitledUri = vscode.Uri.parse(`untitled:Notebook-${untitledCounter++}`);
let title = findNextUntitledEditorName();
let untitledUri = vscode.Uri.parse(`untitled:${title}`);
let editor = await azdata.nb.showNotebookDocument(untitledUri, {
connectionProfile: profile,
providerId: jupyterNotebookProviderId,
preview: false,
defaultKernel: {
name: 'pyspark3kernel',
display_name: 'PySpark3',
language: 'python'
}
preview: false
});
if (oeContext && oeContext.nodeInfo && oeContext.nodeInfo.nodePath) {
// Get the file path after '/HDFS'
@@ -221,7 +229,6 @@ async function handleOpenNotebookTask(profile: azdata.IConnectionProfile): Promi
} else {
await azdata.nb.showNotebookDocument(fileUri, {
connectionProfile: profile,
providerId: jupyterNotebookProviderId,
preview: false
});
}

View File

@@ -213,11 +213,12 @@ export class WebHDFS {
request(requestParams, (error, response, body) => {
if (!callback) { return; }
if (this.isSuccess(response)) {
callback(undefined, response);
} else if (error || this.isError(response)) {
if (error || this.isError(response)) {
let hdfsError = this.parseError(response, body, error);
callback(hdfsError, response);
} else if (this.isSuccess(response)) {
callback(undefined, response);
} else {
let hdfsError = new HdfsError(
localize('webhdfs.unexpectedRedirect', 'Unexpected Redirect'),

View File

@@ -174,9 +174,9 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.15":
version "0.2.15"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/a2cd2db109de882f0959f7b6421c86afa585f460"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.16":
version "0.2.16"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/3c4e70405022ab5333cf1505deddd643223db3de"
dependencies:
vscode-languageclient "3.5.1"

View File

@@ -20,7 +20,7 @@ const JUPYTER_NOTEBOOK_PROVIDER = 'jupyter';
const msgSampleCodeDataFrame = localize('msgSampleCodeDataFrame', 'This sample code loads the file into a data frame and shows the first 10 results.');
const noNotebookVisible = localize('noNotebookVisible', 'No notebook editor is active');
let counter = 0;
let untitledCounter = 0;
export let controller: JupyterController;
@@ -54,7 +54,7 @@ export function activate(extensionContext: vscode.ExtensionContext) {
}
function newNotebook(connectionProfile: azdata.IConnectionProfile) {
let title = `Untitled-${counter++}`;
let title = findNextUntitledEditorName();
let untitledUri = vscode.Uri.parse(`untitled:${title}`);
let options: azdata.nb.NotebookShowOptions = connectionProfile ? {
viewColumn: null,
@@ -71,6 +71,20 @@ function newNotebook(connectionProfile: azdata.IConnectionProfile) {
});
}
function findNextUntitledEditorName(): string {
let nextVal = untitledCounter;
// Note: this will go forever if it's coded wrong, or you have infinite Untitled notebooks!
while (true) {
let title = `Notebook-${nextVal++}`;
let hasTextDoc = vscode.workspace.textDocuments.findIndex(doc => doc.isUntitled && doc.fileName === title) > -1;
let hasNotebookDoc = azdata.nb.notebookDocuments.findIndex(doc => doc.isUntitled && doc.fileName === title) > -1;
if (!hasTextDoc && !hasNotebookDoc) {
untitledCounter = nextVal;
return title;
}
}
}
async function openNotebook(): Promise<void> {
try {
let filter = {};
@@ -123,7 +137,7 @@ async function addCell(cellType: azdata.nb.CellType): Promise<void> {
async function analyzeNotebook(oeContext?: azdata.ObjectExplorerContext): Promise<void> {
// Ensure we get a unique ID for the notebook. For now we're using a different prefix to the built-in untitled files
// to handle this. We should look into improving this in the future
let untitledUri = vscode.Uri.parse(`untitled:Notebook-${counter++}`);
let untitledUri = vscode.Uri.parse(`untitled:Notebook-${untitledCounter++}`);
let editor = await azdata.nb.showNotebookDocument(untitledUri, {
connectionProfile: oeContext ? oeContext.connectionProfile : undefined,

View File

@@ -62,7 +62,7 @@ export class LocalJupyterServerManager implements nb.ServerManager, vscode.Dispo
this._onServerStarted.fire();
} catch (error) {
this.apiWrapper.showErrorMessage(localize('startServerFailed', 'Starting local Notebook server failed with error: {0}', utils.getErrorMessage(error)));
// this is caught and notified up the stack, no longer showing a message here
throw error;
}
}

View File

@@ -1,6 +1,6 @@
{
"name": "azuredatastudio",
"version": "1.5.1",
"version": "1.5.2",
"distro": "8c3e97e3425cc9814496472ab73e076de2ba99ee",
"author": {
"name": "Microsoft Corporation"

View File

@@ -140,14 +140,7 @@ export class HeightMap {
let viewItem = this.heightMap[i];
let delta = viewItem.height - size;
viewItem.height = size;
// update all items after this item
for (let j = i + 1; j < this.heightMap.length; j++) {
this.heightMap[j].top -= delta;
}
}
protected updateTop(item: string, top: number): void {

View File

@@ -63,6 +63,7 @@ interface ISashEvent {
interface IViewItem extends HeightIViewItem {
view: IView;
size: number;
container: HTMLElement;
disposable: IDisposable;
layout(): void;
@@ -188,6 +189,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
this.scrollable = new ScrollableElement(this.el, { vertical: options.verticalScrollbarVisibility });
debounceEvent(this.scrollable.onScroll, (l, e) => e, types.isNumber(this.options.scrollDebounce) ? this.options.scrollDebounce : 25)(e => {
this.render(e.scrollTop, e.height);
this.relayout();
this._onScroll.fire(e.scrollTop);
});
let domNode = this.scrollable.getDomNode();
@@ -247,12 +249,12 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
const onRemove = view.onRemove ? () => view.onRemove() : () => { };
const layoutContainer = this.orientation === Orientation.VERTICAL
? () => item.container.style.height = `${item.height}px`
: () => item.container.style.width = `${item.height}px`;
? () => item.container.style.height = `${item.size}px`
: () => item.container.style.width = `${item.size}px`;
const layout = () => {
layoutContainer();
item.view.layout(item.height, this.orientation);
item.view.layout(item.size, this.orientation);
};
let viewSize: number;
@@ -265,7 +267,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
viewSize = view.minimumSize;
}
const item: IViewItem = { onAdd, onRemove, view, container, layout, disposable, height: viewSize, top: 0, width: 0 };
const item: IViewItem = { onAdd, onRemove, view, container, size: viewSize, layout, disposable, height: viewSize, top: 0, width: 0 };
this.viewItems.splice(currentIndex, 0, item);
this.onInsertItems(new ArrayIterator([item]), currentIndex > 0 ? this.viewItems[currentIndex - 1].view.id : undefined);
@@ -314,6 +316,14 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
if (!types.isArray(sizes) && sizes.type === 'distribute') {
this.distributeViewSizes();
}
// Re-render the views. Set lastRenderTop and lastRenderHeight to undefined since
// this isn't actually scrolling up or down
let scrollTop = this.lastRenderTop;
let viewHeight = this.lastRenderHeight;
this.lastRenderTop = undefined;
this.lastRenderHeight = undefined;
this.render(scrollTop, viewHeight);
}
addView(view: IView, size: number | Sizing, index = this.viewItems.length): void {
@@ -342,12 +352,12 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
const onRemove = view.onRemove ? () => view.onRemove() : () => { };
const layoutContainer = this.orientation === Orientation.VERTICAL
? () => item.container.style.height = `${item.height}px`
: () => item.container.style.width = `${item.height}px`;
? () => item.container.style.height = `${item.size}px`
: () => item.container.style.width = `${item.size}px`;
const layout = () => {
layoutContainer();
item.view.layout(item.height, this.orientation);
item.view.layout(item.size, this.orientation);
};
let viewSize: number;
@@ -360,7 +370,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
viewSize = view.minimumSize;
}
const item: IViewItem = { onAdd, onRemove, view, container, layout, disposable, height: viewSize, top: 0, width: 0 };
const item: IViewItem = { onAdd, onRemove, view, container, size: viewSize, layout, disposable, height: viewSize, top: 0, width: 0 };
this.viewItems.splice(index, 0, item);
this.onInsertItems(new ArrayIterator([item]), index > 0 ? this.viewItems[index - 1].view.id : undefined);
@@ -470,7 +480,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
}
private relayout(lowPriorityIndex?: number, highPriorityIndex?: number): void {
const contentSize = this.viewItems.reduce((r, i) => r + i.height, 0);
const contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
this.resize(this.viewItems.length - 1, this.size - contentSize, undefined, lowPriorityIndex, highPriorityIndex);
this.distributeEmptySpace();
@@ -494,7 +504,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
} else {
for (let i = 0; i < this.viewItems.length; i++) {
const item = this.viewItems[i];
this.updateSize(item.view.id, clamp(Math.round(this.proportions[i] * size), item.view.minimumSize, item.view.maximumSize));
item.size = clamp(Math.round(this.proportions[i] * size), item.view.minimumSize, item.view.maximumSize);
}
}
@@ -504,7 +514,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
private saveProportions(): void {
if (this.contentSize > 0) {
this.proportions = this.viewItems.map(i => i.height / this.contentSize);
this.proportions = this.viewItems.map(i => i.size / this.contentSize);
}
}
@@ -518,7 +528,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
]);
const resetSashDragState = (start: number, alt: boolean) => {
const sizes = this.viewItems.map(i => i.height);
const sizes = this.viewItems.map(i => i.size);
let minDelta = Number.NEGATIVE_INFINITY;
let maxDelta = Number.POSITIVE_INFINITY;
@@ -534,12 +544,12 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
if (isLastSash) {
const viewItem = this.viewItems[index];
minDelta = (viewItem.view.minimumSize - viewItem.height) / 2;
maxDelta = (viewItem.view.maximumSize - viewItem.height) / 2;
minDelta = (viewItem.view.minimumSize - viewItem.size) / 2;
maxDelta = (viewItem.view.maximumSize - viewItem.size) / 2;
} else {
const viewItem = this.viewItems[index + 1];
minDelta = (viewItem.height - viewItem.view.maximumSize) / 2;
maxDelta = (viewItem.height - viewItem.view.minimumSize) / 2;
minDelta = (viewItem.size - viewItem.view.maximumSize) / 2;
maxDelta = (viewItem.size - viewItem.view.minimumSize) / 2;
}
}
@@ -558,11 +568,11 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
if (alt) {
const isLastSash = index === this.sashItems.length - 1;
const newSizes = this.viewItems.map(i => i.height);
const newSizes = this.viewItems.map(i => i.size);
const viewItemIndex = isLastSash ? index : index + 1;
const viewItem = this.viewItems[viewItemIndex];
const newMinDelta = viewItem.height - viewItem.view.maximumSize;
const newMaxDelta = viewItem.height - viewItem.view.minimumSize;
const newMinDelta = viewItem.size - viewItem.view.maximumSize;
const newMaxDelta = viewItem.size - viewItem.view.minimumSize;
const resizeIndex = isLastSash ? index - 1 : index + 1;
this.resize(resizeIndex, -newDelta, newSizes, undefined, undefined, newMinDelta, newMaxDelta);
@@ -585,23 +595,23 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
return;
}
size = typeof size === 'number' ? size : item.height;
size = typeof size === 'number' ? size : item.size;
size = clamp(size, item.view.minimumSize, item.view.maximumSize);
if (this.inverseAltBehavior && index > 0) {
// In this case, we want the view to grow or shrink both sides equally
// so we just resize the "left" side by half and let `resize` do the clamping magic
this.resize(index - 1, Math.floor((item.height - size) / 2));
this.resize(index - 1, Math.floor((item.size - size) / 2));
this.distributeEmptySpace();
this.layoutViews();
} else {
item.size = size;
this.updateSize(item.view.id, size);
this.updateSize(item.view.id, size);
let top = item.top + item.height;
let top = item.top + item.size;
for (let i = index + 1; i < this.viewItems.length; i++) {
let currentItem = this.viewItems[i];
this.updateTop(currentItem.view.id, top);
top += currentItem.height;
top += currentItem.size;
}
this.relayout(index);
}
@@ -621,12 +631,12 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
const item = this.viewItems[index];
size = Math.round(size);
size = clamp(size, item.view.minimumSize, item.view.maximumSize);
let delta = size - item.height;
let delta = size - item.size;
if (delta !== 0 && index < this.viewItems.length - 1) {
const downIndexes = range(index + 1, this.viewItems.length);
const collapseDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].height - this.viewItems[i].view.minimumSize), 0);
const expandDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - this.viewItems[i].height), 0);
const collapseDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].size - this.viewItems[i].view.minimumSize), 0);
const expandDown = downIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - this.viewItems[i].size), 0);
const deltaDown = clamp(delta, -expandDown, collapseDown);
this.resize(index, deltaDown);
@@ -635,8 +645,8 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
if (delta !== 0 && index > 0) {
const upIndexes = range(index - 1, -1);
const collapseUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].height - this.viewItems[i].view.minimumSize), 0);
const expandUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - this.viewItems[i].height), 0);
const collapseUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].size - this.viewItems[i].view.minimumSize), 0);
const expandUp = upIndexes.reduce((r, i) => r + (this.viewItems[i].view.maximumSize - this.viewItems[i].size), 0);
const deltaUp = clamp(-delta, -collapseUp, expandUp);
this.resize(index - 1, deltaUp);
@@ -661,7 +671,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
return -1;
}
return this.viewItems[index].height;
return this.viewItems[index].size;
}
@@ -756,7 +766,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
private resize(
index: number,
delta: number,
sizes = this.viewItems.map(i => i.height),
sizes = this.viewItems.map(i => i.size),
lowPriorityIndex?: number,
highPriorityIndex?: number,
overloadMinDelta: number = Number.NEGATIVE_INFINITY,
@@ -800,10 +810,8 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
const viewDelta = size - upSizes[i];
deltaUp -= viewDelta;
this.updateSize(item.view.id, size);
item.size = size;
this.dirtyState = true;
this.lastRenderTop = 0;
this.lastRenderHeight = 0;
}
for (let i = 0, deltaDown = delta; i < downItems.length; i++) {
@@ -812,32 +820,30 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
const viewDelta = size - downSizes[i];
deltaDown += viewDelta;
this.updateSize(item.view.id, size);
item.size = size;
this.dirtyState = true;
this.lastRenderTop = 0;
this.lastRenderHeight = 0;
}
return delta;
}
private distributeEmptySpace(): void {
let contentSize = this.viewItems.reduce((r, i) => r + i.height, 0);
let contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
let emptyDelta = this.size - contentSize;
for (let i = this.viewItems.length - 1; emptyDelta !== 0 && i >= 0; i--) {
const item = this.viewItems[i];
const size = clamp(item.height + emptyDelta, item.view.minimumSize, item.view.maximumSize);
const viewDelta = size - item.height;
const size = clamp(item.size + emptyDelta, item.view.minimumSize, item.view.maximumSize);
const viewDelta = size - item.size;
emptyDelta -= viewDelta;
this.updateSize(item.view.id, size);
item.size = size;
}
}
private layoutViews(): void {
// Save new content size
this.contentSize = this.viewItems.reduce((r, i) => r + i.height, 0);
this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
if (this.dirtyState) {
for (let i = this.indexAt(this.lastRenderTop); i <= this.indexAfter(this.lastRenderTop + this.lastRenderHeight) - 1; i++) {
@@ -859,7 +865,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable {
let position = 0;
for (let i = 0; i < this.sashItems.length; i++) {
position += this.viewItems[i].height;
position += this.viewItems[i].size;
if (this.sashItems[i].sash === sash) {
return position;

View File

@@ -154,11 +154,13 @@ export class SelectBox extends vsSelectBox {
public showMessage(message: IMessage): void {
this.message = message;
dom.removeClass(this.element, 'idle');
dom.removeClass(this.element, 'info');
dom.removeClass(this.element, 'warning');
dom.removeClass(this.element, 'error');
dom.addClass(this.element, this.classForType(message.type));
if (this.element) {
dom.removeClass(this.element, 'idle');
dom.removeClass(this.element, 'info');
dom.removeClass(this.element, 'warning');
dom.removeClass(this.element, 'error');
dom.addClass(this.element, this.classForType(message.type));
}
// ARIA Support
let alertText: string;
@@ -213,8 +215,6 @@ export class SelectBox extends vsSelectBox {
}
public hideMessage(): void {
this.message = null;
dom.removeClass(this.element, 'info');
dom.removeClass(this.element, 'warning');
dom.removeClass(this.element, 'error');
@@ -222,10 +222,12 @@ export class SelectBox extends vsSelectBox {
this._hideMessage();
this.applyStyles();
this.message = null;
}
private _hideMessage(): void {
if (this.contextViewProvider) {
if (this.message && this.contextViewProvider) {
this.contextViewProvider.hideContextView();
}
}

View File

@@ -51,6 +51,7 @@
.account-view .list-row {
padding: 12px;
line-height: 18px !important;
}
.account-view .list-row .icon {

View File

@@ -13,6 +13,7 @@ import { MimeModel } from 'sql/parts/notebook/outputs/common/mimemodel';
import * as outputProcessor from 'sql/parts/notebook/outputs/common/outputProcessor';
import { RenderMimeRegistry } from 'sql/parts/notebook/outputs/registry';
import 'vs/css!sql/parts/notebook/outputs/style/index';
import { IThemeService } from 'vs/platform/theme/common/themeService';
export const OUTPUT_SELECTOR: string = 'output-component';
@@ -31,7 +32,8 @@ export class OutputComponent extends AngularDisposable implements OnInit {
constructor(
@Inject(INotebookService) private _notebookService: INotebookService
@Inject(INotebookService) private _notebookService: INotebookService,
@Inject(IThemeService) private _themeService: IThemeService
) {
super();
this.registry = _notebookService.getMimeRegistry();
@@ -49,6 +51,7 @@ export class OutputComponent extends AngularDisposable implements OnInit {
let node = this.outputElement.nativeElement;
let output = this.cellOutput;
let options = outputProcessor.getBundleOptions({ value: output, trusted: this.trustedMode });
options.themeService = this._themeService;
// TODO handle safe/unsafe mapping
this.createRenderedMimetype(options, node);
}

View File

@@ -9,9 +9,9 @@
'use strict';
import { nb } from 'azdata';
import * as nls from 'vs/nls';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { localize } from 'vs/nls';
import { IClientSession, IKernelPreference, IClientSessionOptions } from './modelInterfaces';
import { Deferred } from 'sql/base/common/promise';
@@ -70,12 +70,14 @@ export class ClientSession implements IClientSession {
await this.initializeSession();
await this.updateCachedKernelSpec();
} catch (err) {
this._errorMessage = notebookUtils.getErrorMessage(err);
this._errorMessage = notebookUtils.getErrorMessage(err) || localize('clientSession.unknownError', "An error occurred while starting the notebook session");
}
// Always resolving for now. It's up to callers to check for error case
this._isReady = true;
this._ready.resolve();
this._kernelChangeCompleted.resolve();
if (!this.isInErrorState && this._session && this._session.kernel) {
await this.notifyKernelChanged(undefined, this._session.kernel);
}
}
private async startServer(): Promise<void> {
@@ -83,7 +85,7 @@ export class ClientSession implements IClientSession {
if (serverManager && !serverManager.isStarted) {
await serverManager.startServer();
if (!serverManager.isStarted) {
throw new Error(nls.localize('ServerNotStarted', 'Server did not start for unknown reason'));
throw new Error(localize('ServerNotStarted', "Server did not start for unknown reason"));
}
this.isServerStarted = serverManager.isStarted;
} else {
@@ -116,7 +118,7 @@ export class ClientSession implements IClientSession {
} catch (err) {
// TODO move registration
if (err && err.response && err.response.status === 501) {
this.options.notificationService.warn(nls.localize('kernelRequiresConnection', 'Kernel {0} was not found. The default kernel will be used instead.', kernelName));
this.options.notificationService.warn(localize('kernelRequiresConnection', "Kernel {0} was not found. The default kernel will be used instead.", kernelName));
session = await this.notebookManager.sessionManager.startNew({
path: this.notebookUri.fsPath,
kernelName: undefined
@@ -246,6 +248,11 @@ export class ClientSession implements IClientSession {
this._isReady = kernel.isReady;
await this.updateCachedKernelSpec();
// Send resolution events to listeners
await this.notifyKernelChanged(oldKernel, newKernel);
return kernel;
}
private async notifyKernelChanged(oldKernel: nb.IKernel, newKernel: nb.IKernel): Promise<void> {
let changeArgs: nb.IKernelChangedArgs = {
oldValue: oldKernel,
newValue: newKernel
@@ -255,7 +262,6 @@ export class ClientSession implements IClientSession {
// Wait on connection configuration to complete before resolving full kernel change
this._kernelChangeCompleted.resolve();
this._kernelChangedEmitter.fire(changeArgs);
return kernel;
}
private async updateCachedKernelSpec(): Promise<void> {

View File

@@ -354,7 +354,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
cellIndex: index
});
} else {
this.notifyError(localize('deleteCellFailed', 'Failed to delete cell.'));
this.notifyError(localize('deleteCellFailed', "Failed to delete cell."));
}
}
@@ -392,7 +392,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
this._onErrorEmitter.fire({ message: error, severity: Severity.Error });
}
public async startSession(manager: INotebookManager, displayName?: string): Promise<void> {
public async startSession(manager: INotebookManager, displayName?: string, setErrorStateOnFail?: boolean): Promise<void> {
if (displayName) {
let standardKernel = this._notebookOptions.standardKernels.find(kernel => kernel.displayName === displayName);
this._defaultKernel = displayName ? { name: standardKernel.name, display_name: standardKernel.displayName } : this._defaultKernel;
@@ -406,7 +406,6 @@ export class NotebookModel extends Disposable implements INotebookModel {
});
if (!this._activeClientSession) {
this.updateActiveClientSession(clientSession);
}
let profile = new ConnectionProfile(this._notebookOptions.capabilitiesService, this.connectionProfile);
@@ -420,16 +419,31 @@ export class NotebookModel extends Disposable implements INotebookModel {
this._activeConnection = undefined;
}
clientSession.onKernelChanging(async (e) => {
await this.loadActiveContexts(e);
});
clientSession.statusChanged(async (session) => {
this._kernelsChangedEmitter.fire(session.kernel);
});
await clientSession.initialize();
// By somehow we have to wait for ready, otherwise may not be called for some cases.
await clientSession.ready;
if (clientSession.isInErrorState) {
this.setErrorState(clientSession.errorMessage);
} else {
this._onClientSessionReady.fire(clientSession);
// Once session is loaded, can use the session manager to retrieve useful info
this.loadKernelInfo(clientSession, this.defaultKernel.display_name);
if (clientSession.kernel) {
await clientSession.kernel.ready;
await this.updateKernelInfoOnKernelChange(clientSession.kernel);
}
if (clientSession.isInErrorState) {
if (setErrorStateOnFail) {
this.setErrorState(clientSession.errorMessage);
} else {
throw new Error(clientSession.errorMessage);
}
}
this._onClientSessionReady.fire(clientSession);
this._kernelChangedEmitter.fire({
oldValue: undefined,
newValue: clientSession.kernel
});
}
}
@@ -552,10 +566,63 @@ export class NotebookModel extends Disposable implements INotebookModel {
this.doChangeKernel(displayName, true);
}
public async doChangeKernel(displayName: string, mustSetProvider: boolean = true): Promise<void> {
if (mustSetProvider) {
await this.setProviderIdAndStartSession(displayName);
private async doChangeKernel(displayName: string, mustSetProvider: boolean = true, restoreOnFail: boolean = true): Promise<void> {
if (!displayName) {
// Can't change to an undefined kernel
return;
}
let oldDisplayName = this._activeClientSession && this._activeClientSession.kernel ? this._activeClientSession.kernel.name : undefined;
try {
let changeKernelNeeded = true;
if (mustSetProvider) {
let providerChanged = await this.tryStartSessionByChangingProviders(displayName);
// If provider was changed, a new session with new kernel is already created. We can skip calling changeKernel.
changeKernelNeeded = !providerChanged;
}
if (changeKernelNeeded) {
let spec = this.findSpec(displayName);
if (this._activeClientSession && this._activeClientSession.isReady) {
let kernel = await this._activeClientSession.changeKernel(spec, this._oldKernel);
try {
await kernel.ready;
await this.updateKernelInfoOnKernelChange(kernel);
} catch (err2) {
// TODO should we handle this in any way?
console.log(`doChangeKernel: ignoring error ${notebookUtils.getErrorMessage(err2)}`);
}
}
}
} catch (err) {
if (oldDisplayName && restoreOnFail) {
this.notifyError(localize('changeKernelFailedRetry', "Failed to change kernel. Kernel {0} will be used. Error was: {1}", oldDisplayName, notebookUtils.getErrorMessage(err)));
// Clear out previous kernel
let failedProviderId = this.tryFindProviderForKernel(displayName, true);
let oldProviderId = this.tryFindProviderForKernel(oldDisplayName, true);
if (failedProviderId !== oldProviderId) {
// We need to clear out the old kernel information so we switch providers. Otherwise in the SQL -> Jupyter -> SQL failure case,
// we would never reset the providers
this._oldKernel = undefined;
}
return this.doChangeKernel(oldDisplayName, mustSetProvider, false);
} else {
this.notifyError(localize('changeKernelFailed', "Failed to change kernel due to error: {0}", notebookUtils.getErrorMessage(err)));
this._kernelChangedEmitter.fire({
newValue: undefined,
oldValue: undefined
});
}
}
// Else no need to do anything
}
private async updateKernelInfoOnKernelChange(kernel: nb.IKernel) {
await this.updateKernelInfo(kernel);
if (kernel.info) {
this.updateLanguageInfo(kernel.info.language_info);
}
}
private findSpec(displayName: string) {
let spec = this.getKernelSpecFromDisplayName(displayName);
if (spec) {
// Ensure that the kernel we try to switch to is a valid kernel; if not, use the default
@@ -563,24 +630,11 @@ export class NotebookModel extends Disposable implements INotebookModel {
if (kernelSpecs && kernelSpecs.length > 0 && kernelSpecs.findIndex(k => k.display_name === spec.display_name) < 0) {
spec = kernelSpecs.find(spec => spec.name === this.notebookManager.sessionManager.specs.defaultKernel);
}
} else {
}
else {
spec = notebookConstants.sqlKernelSpec;
}
if (this._activeClientSession && this._activeClientSession.isReady) {
return this._activeClientSession.changeKernel(spec, this._oldKernel)
.then((kernel) => {
this.updateKernelInfo(kernel);
kernel.ready.then(() => {
if (kernel.info) {
this.updateLanguageInfo(kernel.info.language_info);
}
}, err => undefined);
}).catch((err) => {
this.notifyError(localize('changeKernelFailed', 'Failed to change kernel: {0}', notebookUtils.getErrorMessage(err)));
// TODO should revert kernels dropdown
});
}
return Promise.resolve();
return spec;
}
public async changeContext(server: string, newConnection?: IConnectionProfile, hideErrorMessage?: boolean): Promise<void> {
@@ -613,7 +667,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
});
} catch (err) {
let msg = notebookUtils.getErrorMessage(err);
this.notifyError(localize('changeContextFailed', 'Changing context failed: {0}', msg));
this.notifyError(localize('changeContextFailed', "Changing context failed: {0}", msg));
}
}
@@ -631,17 +685,6 @@ export class NotebookModel extends Disposable implements INotebookModel {
}
}
private loadKernelInfo(clientSession: IClientSession, displayName: string): void {
clientSession.onKernelChanging(async (e) => {
await this.loadActiveContexts(e);
});
clientSession.statusChanged(async (session) => {
this._kernelsChangedEmitter.fire(session.kernel);
});
this.doChangeKernel(displayName, false);
}
// Get default language if saved in notebook file
// Otherwise, default to python
private getDefaultLanguageInfo(notebook: nb.INotebookContents): nb.ILanguageInfo {
@@ -703,7 +746,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
private setErrorState(errMsg: string): void {
this._inErrorState = true;
let msg = localize('startSessionFailed', 'Could not start session: {0}', errMsg);
let msg = localize('startSessionFailed', "Could not start session: {0}", errMsg);
this.notifyError(msg);
}
@@ -730,7 +773,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
}
await this.shutdownActiveSession();
} catch (err) {
this.notifyError(localize('shutdownError', 'An error occurred when closing the notebook: {0}', err));
this.notifyError(localize('shutdownError', "An error occurred when closing the notebook: {0}", err));
}
}
@@ -740,7 +783,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
await this._activeClientSession.ready;
}
catch (err) {
this.notifyError(localize('shutdownClientSessionError', 'A client session error occurred when closing the notebook: {0}', err));
this.notifyError(localize('shutdownClientSessionError', "A client session error occurred when closing the notebook: {0}", err));
}
await this._activeClientSession.shutdown();
this.clearClientSessionListeners();
@@ -794,46 +837,39 @@ export class NotebookModel extends Disposable implements INotebookModel {
* Set _providerId and start session if it is new provider
* @param displayName Kernel dispay name
*/
private async setProviderIdAndStartSession(displayName: string): Promise<void> {
private async tryStartSessionByChangingProviders(displayName: string): Promise<boolean> {
if (displayName) {
if (this._activeClientSession && this._activeClientSession.isReady) {
this._oldKernel = this._activeClientSession.kernel;
let providerId = this.tryFindProviderForKernel(displayName);
}
let providerId = this.tryFindProviderForKernel(displayName);
if (providerId) {
if (providerId !== this._providerId) {
this._providerId = providerId;
this._onProviderIdChanged.fire(this._providerId);
if (providerId && providerId !== this._providerId) {
this._providerId = providerId;
this._onProviderIdChanged.fire(this._providerId);
await this.shutdownActiveSession();
try {
let manager = this.getNotebookManager(providerId);
if (manager) {
await this.startSession(manager, displayName);
} else {
throw new Error(localize('ProviderNoManager', "Can't find notebook manager for provider {0}", providerId));
}
}
catch (err) {
console.log(err);
}
} else {
console.log(`No provider found supporting the kernel: ${displayName}`);
}
await this.shutdownActiveSession();
let manager = this.getNotebookManager(providerId);
if (manager) {
await this.startSession(manager, displayName, false);
} else {
throw new Error(localize('ProviderNoManager', "Can't find notebook manager for provider {0}", providerId));
}
return true;
}
}
return false;
}
private tryFindProviderForKernel(displayName: string) {
private tryFindProviderForKernel(displayName: string, alwaysReturnId: boolean = false): string {
if (!displayName) {
return undefined;
}
let standardKernel = this.getStandardKernelFromDisplayName(displayName);
if (standardKernel && this._oldKernel && this._oldKernel.name !== standardKernel.name) {
if (this._kernelDisplayNameToNotebookProviderIds.has(displayName)) {
return this._kernelDisplayNameToNotebookProviderIds.get(displayName);
if (standardKernel) {
let providerId = this._kernelDisplayNameToNotebookProviderIds.get(displayName);
if (alwaysReturnId || (!this._oldKernel || this._oldKernel.name !== standardKernel.name)) {
return providerId;
}
}
return undefined;
@@ -883,7 +919,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
};
}
onCellChange(cell: CellModel, change: NotebookChangeType): void {
onCellChange(cell: ICellModel, change: NotebookChangeType): void {
let changeInfo: NotebookContentChange = {
changeType: change,
cells: [cell]

View File

@@ -248,7 +248,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
this._model = this._register(model);
this.updateToolbarComponents(this._model.trustedMode);
this._modelRegisteredDeferred.resolve(this._model);
await model.startSession(this.model.notebookManager);
await model.startSession(this.model.notebookManager, undefined, true);
this.detectChanges();
}

View File

@@ -235,18 +235,24 @@ export class KernelsDropdown extends SelectBox {
this._register(this.model.kernelChanged((changedArgs: azdata.nb.IKernelChangedArgs) => {
this.updateKernel(changedArgs.newValue);
}));
let kernel = this.model.clientSession && this.model.clientSession.kernel;
this.updateKernel(kernel);
}
// Update SelectBox values
public updateKernel(kernel: azdata.nb.IKernel) {
if (kernel) {
let kernels: string[] = this.model.standardKernelsDisplayName();
if (kernel && kernel.isReady) {
let standardKernel = this.model.getStandardKernelFromName(kernel.name);
let kernels: string[] = this.model.standardKernelsDisplayName();
if (kernels && standardKernel) {
let index = kernels.findIndex((kernel => kernel === standardKernel.displayName));
this.setOptions(kernels, index);
}
} else if (this.model.clientSession.isInErrorState) {
let noKernelName = localize('noKernel', "No Kernel");
kernels.unshift(noKernelName);
this.setOptions(kernels, 0);
}
}
@@ -283,22 +289,26 @@ export class AttachToDropdown extends SelectBox {
public updateModel(model: INotebookModel): void {
this.model = model as NotebookModel;
this._register(model.contextsChanged(() => {
let kernelDisplayName: string = this.getKernelDisplayName();
if (kernelDisplayName) {
this.loadAttachToDropdown(this.model, kernelDisplayName);
}
this.handleContextsChanged();
}));
this._register(this.model.contextsLoading(() => {
this.setOptions([msgLoadingContexts], 0);
}));
this.handleContextsChanged();
}
private handleContextsChanged(showSelectConnection?: boolean) {
let kernelDisplayName: string = this.getKernelDisplayName();
if (kernelDisplayName) {
this.loadAttachToDropdown(this.model, kernelDisplayName, showSelectConnection);
} else if (this.model.clientSession.isInErrorState) {
this.setOptions([localize('noContextAvailable', "None")], 0);
}
}
private updateAttachToDropdown(model: INotebookModel): void {
model.onValidConnectionSelected(validConnection => {
let kernelDisplayName: string = this.getKernelDisplayName();
if (kernelDisplayName) {
this.loadAttachToDropdown(this.model, kernelDisplayName, !validConnection);
}
this.handleContextsChanged(!validConnection);
});
}

View File

@@ -101,7 +101,8 @@ export class NotebookEditorModel extends EditorModel {
let content = JSON.stringify(notebookModel.toJSON(), undefined, ' ');
let model = this.textEditorModel.textEditorModel;
let endLine = model.getLineCount();
let endCol = model.getLineLength(endLine);
let endCol = model.getLineMaxColumn(endLine);
this.textEditorModel.textEditorModel.applyEdits([{
range: new Range(1, 1, endLine, endCol),
text: content

View File

@@ -4,6 +4,7 @@
|----------------------------------------------------------------------------*/
import { IRenderMime } from './renderMimeInterfaces';
import { ReadonlyJSONObject } from '../../models/jsonext';
import { IThemeService } from 'vs/platform/theme/common/themeService';
/**
* The default mime model implementation.
@@ -17,6 +18,7 @@ export class MimeModel implements IRenderMime.IMimeModel {
this._data = options.data || {};
this._metadata = options.metadata || {};
this._callback = options.callback;
this._themeService = options.themeService;
}
/**
@@ -38,6 +40,10 @@ export class MimeModel implements IRenderMime.IMimeModel {
return this._metadata;
}
get themeService(): IThemeService {
return this._themeService;
}
/**
* Set the data associated with the model.
*
@@ -54,6 +60,7 @@ export class MimeModel implements IRenderMime.IMimeModel {
private _callback: (options: IRenderMime.ISetDataOptions) => void;
private _data: ReadonlyJSONObject;
private _metadata: ReadonlyJSONObject;
private _themeService: IThemeService;
}
/**
@@ -83,5 +90,10 @@ export namespace MimeModel {
* The initial mime metadata.
*/
metadata?: ReadonlyJSONObject;
/**
* Theme service used to react to theme change events
*/
themeService?: IThemeService;
}
}

View File

@@ -4,6 +4,7 @@
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/
import { ReadonlyJSONObject } from '../../models/jsonext';
import { IThemeService } from 'vs/platform/theme/common/themeService';
/**
* A namespace for rendermime associated interfaces.
@@ -37,6 +38,11 @@ export namespace IRenderMime {
* containing the new data.
*/
setData(options: ISetDataOptions): void;
/**
* Theme service used to react to theme change events
*/
readonly themeService: IThemeService;
}
/**

View File

@@ -9,6 +9,8 @@ import { textFormatter } from 'sql/parts/grid/services/sharedServices';
import { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
import { escape } from 'sql/base/common/strings';
import { IDataResource } from 'sql/workbench/services/notebook/sql/sqlSessionManager';
import { attachTableStyler } from 'sql/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
/**
* Render DataResource as a grid into a host node.
@@ -64,6 +66,7 @@ export function renderDataResource(
// Set the height dynamically if the grid's height is < 500px high; otherwise, set height to 500px
tableContainer.style.height = rowsHeight >= 500 ? '500px' : rowsHeight.toString() + 'px';
attachTableStyler(detailTable, options.themeService);
host.appendChild(tableContainer);
detailTable.resizeCanvas();
@@ -116,5 +119,10 @@ export namespace renderDataResource {
* The DataResource source to render.
*/
source: string;
/**
* Theme service used to react to theme change events
*/
themeService?: IThemeService;
}
}

View File

@@ -8,6 +8,7 @@ import * as renderers from './renderers';
import { IRenderMime } from './common/renderMimeInterfaces';
import { ReadonlyJSONObject } from '../models/jsonext';
import * as tableRenderers from 'sql/parts/notebook/outputs/tableRenderers';
import { IThemeService } from 'vs/platform/theme/common/themeService';
/**
* A common base class for mime renderers.
@@ -372,7 +373,8 @@ export class RenderedDataResource extends RenderedCommon {
render(model: IRenderMime.IMimeModel): Promise<void> {
return tableRenderers.renderDataResource({
host: this.node,
source: JSON.stringify(model.data[this.mimeType])
source: JSON.stringify(model.data[this.mimeType]),
themeService: model.themeService
});
}
}

View File

@@ -181,7 +181,10 @@ export class ServerTreeActionProvider extends ContributableActionProvider {
this.addScriptingActions(context, actions);
if (isAvailableDatabaseNode) {
let serverInfo = this._connectionManagementService.getServerInfo(context.profile.id);
let isCloud = serverInfo && serverInfo.isCloud;
if (isAvailableDatabaseNode && !isCloud) {
this.addBackupAction(context, actions);
this.addRestoreAction(context, actions);
}

View File

@@ -35,7 +35,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { range } from 'vs/base/common/arrays';
import { Orientation, Sizing } from 'vs/base/browser/ui/splitview/splitview';
import { Orientation } from 'vs/base/browser/ui/splitview/splitview';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { generateUuid } from 'vs/base/common/uuid';
import { TPromise } from 'vs/base/common/winjs.base';
@@ -145,7 +145,6 @@ export class GridPanel extends ViewletPanel {
@IInstantiationService private instantiationService: IInstantiationService
) {
super(options, keybindingService, contextMenuService, configurationService);
this.maximumBodySize = 0;
this.splitView = new ScrollableSplitView(this.container, { enableResizing: false, verticalScrollbarVisibility: ScrollbarVisibility.Visible });
this.splitView.onScroll(e => {
if (this.state && this.splitView.length !== 0) {
@@ -196,6 +195,9 @@ export class GridPanel extends ViewletPanel {
}
return p;
}, []));
this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize;
}, 0);
if (this.state && this.state.scrollPosition) {
this.splitView.setScrollPosition(this.state.scrollPosition);
@@ -218,6 +220,10 @@ export class GridPanel extends ViewletPanel {
t.state.canBeMaximized = this.tables.length > 1;
});
this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize;
}, 0);
if (this.state && this.state.scrollPosition) {
this.splitView.setScrollPosition(this.state.scrollPosition);
}
@@ -244,6 +250,9 @@ export class GridPanel extends ViewletPanel {
}
const sizeChanges = () => {
this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize;
}, 0);
if (this.state && this.state.scrollPosition) {
this.splitView.setScrollPosition(this.state.scrollPosition);
@@ -270,9 +279,6 @@ export class GridPanel extends ViewletPanel {
}
private addResultSet(resultSet: azdata.ResultSetSummary[]) {
if (resultSet.length > 0) {
this.maximumBodySize = Number.POSITIVE_INFINITY;
}
let tables: GridTable<any>[] = [];
for (let set of resultSet) {
@@ -302,7 +308,7 @@ export class GridPanel extends ViewletPanel {
// possible to need a sort?
if (isUndefinedOrNull(this.maximizedGrid)) {
this.splitView.addViews(tables, Sizing.Distribute, this.splitView.length);
this.splitView.addViews(tables, tables.map(i => i.minimumSize), this.splitView.length);
}
this.tables = this.tables.concat(tables);
@@ -316,12 +322,15 @@ export class GridPanel extends ViewletPanel {
for (let i = this.splitView.length - 1; i >= 0; i--) {
this.splitView.removeView(i);
}
this.maximumBodySize = 0;
dispose(this.tables);
dispose(this.tableDisposable);
this.tableDisposable = [];
this.tables = [];
this.maximizedGrid = undefined;
this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize;
}, 0);
}
private maximizeTable(tableid: string): void {
@@ -345,7 +354,7 @@ export class GridPanel extends ViewletPanel {
this.maximizedGrid.state.maximized = false;
this.maximizedGrid = undefined;
this.splitView.removeView(0);
this.splitView.addViews(this.tables, Sizing.Distribute);
this.splitView.addViews(this.tables, this.tables.map(i => i.minimumSize));
}
}
@@ -716,7 +725,7 @@ class GridTable<T> extends Disposable implements IView {
}
public get maximumSize(): number {
return Number.POSITIVE_INFINITY;
return Math.max(this.maxSize, ACTIONBAR_HEIGHT + BOTTOM_PADDING);
}
private loadData(offset: number, count: number): Thenable<T[]> {

View File

@@ -468,8 +468,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
// The connected succeeded so add it to our active connections now, optionally adding it to the MRU based on
// the options.saveTheConnection setting
let connectionMgmtInfo = this._connectionStatusManager.findConnection(uri);
let activeConnection = connectionMgmtInfo.connectionProfile;
this.tryAddActiveConnection(connectionMgmtInfo, activeConnection, options.saveTheConnection);
this.tryAddActiveConnection(connectionMgmtInfo, connection, options.saveTheConnection);
if (callbacks.onConnectSuccess) {
callbacks.onConnectSuccess(options.params);

View File

@@ -81,23 +81,27 @@ export class ExtHostNotebook implements ExtHostNotebookShape {
$startNewSession(managerHandle: number, options: azdata.nb.ISessionOptions): Thenable<INotebookSessionDetails> {
return this._withSessionManager(managerHandle, async (sessionManager) => {
let session = await sessionManager.startNew(options);
let sessionId = this._addNewAdapter(session);
let kernelDetails: INotebookKernelDetails = undefined;
if (session.kernel) {
kernelDetails = this.saveKernel(session.kernel);
try {
let session = await sessionManager.startNew(options);
let sessionId = this._addNewAdapter(session);
let kernelDetails: INotebookKernelDetails = undefined;
if (session.kernel) {
kernelDetails = this.saveKernel(session.kernel);
}
let details: INotebookSessionDetails = {
sessionId: sessionId,
id: session.id,
path: session.path,
name: session.name,
type: session.type,
status: session.status,
canChangeKernels: session.canChangeKernels,
kernelDetails: kernelDetails
};
return details;
} catch (error) {
throw typeof(error) === 'string' ? new Error(error) : error;
}
let details: INotebookSessionDetails = {
sessionId: sessionId,
id: session.id,
path: session.path,
name: session.name,
type: session.type,
status: session.status,
canChangeKernels: session.canChangeKernels,
kernelDetails: kernelDetails
};
return details;
});
}
@@ -267,7 +271,16 @@ export class ExtHostNotebook implements ExtHostNotebookShape {
if (manager === undefined) {
return TPromise.wrapError<R>(new Error(localize('errNoManager', 'No Manager found')));
}
return TPromise.wrap(callback(manager));
return TPromise.wrap(this.callbackWithErrorWrap<R>(callback, manager));
}
private async callbackWithErrorWrap<R>(callback: (manager: NotebookManagerAdapter) => R | PromiseLike<R>, manager: NotebookManagerAdapter): Promise<R> {
try {
let value = await callback(manager);
return value;
} catch (error) {
throw typeof(error) === 'string' ? new Error(error) : error;
}
}
private _withServerManager<R>(handle: number, callback: (manager: azdata.nb.ServerManager) => R | PromiseLike<R>): TPromise<R> {

View File

@@ -5,6 +5,7 @@
'use strict';
import * as azdata from 'azdata';
import * as path from 'path';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
@@ -32,7 +33,6 @@ import { NotebookChangeType, CellTypes } from 'sql/parts/notebook/models/contrac
import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService';
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
import { notebookModeId } from 'sql/common/constants';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
class MainThreadNotebookEditor extends Disposable {
private _contentChangedEmitter = new Emitter<NotebookContentChange>();
@@ -371,8 +371,9 @@ export class MainThreadNotebookDocumentsAndEditors extends Disposable implements
};
let isUntitled: boolean = uri.scheme === Schemas.untitled;
const fileInput: UntitledEditorInput = isUntitled ? this._untitledEditorService.createOrGet(uri, notebookModeId) : undefined;
let input = this._instantiationService.createInstance(NotebookInput, uri.fsPath, uri, fileInput);
const fileInput = isUntitled ? this._untitledEditorService.createOrGet(uri, notebookModeId) :
this._editorService.createInput({resource: uri, language: notebookModeId});
let input = this._instantiationService.createInstance(NotebookInput, path.basename(uri.fsPath), uri, fileInput);
input.isTrusted = isUntitled;
input.defaultKernel = options.defaultKernel;
input.connectionProfile = new ConnectionProfile(this._capabilitiesService, options.connectionProfile);

View File

@@ -90,7 +90,38 @@ export class ConnectionDialogService implements IConnectionDialogService {
@IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService,
@IClipboardService private _clipboardService: IClipboardService,
@ICommandService private _commandService: ICommandService
) { }
) {
this.initializeConnectionProviders();
}
/**
* Set the initial value for the connection provider and listen to the provider change event
*/
private initializeConnectionProviders() {
this.setConnectionProviders();
if (this._capabilitiesService) {
this._capabilitiesService.onCapabilitiesRegistered(() => {
this.setConnectionProviders();
if (this._connectionDialog) {
this._connectionDialog.updateConnectionProviders(this._providerTypes, this._providerNameToDisplayNameMap);
}
});
}
}
/**
* Update the available provider types using the values from capabilities service
*/
private setConnectionProviders() {
if (this._capabilitiesService) {
this._providerTypes = [];
this._providerNameToDisplayNameMap = {};
entries(this._capabilitiesService.providers).forEach(p => {
this._providerTypes.push(p[1].connection.displayName);
this._providerNameToDisplayNameMap[p[0]] = p[1].connection.displayName;
});
}
}
/**
* Gets the default provider with the following actions
@@ -351,13 +382,6 @@ export class ConnectionDialogService implements IConnectionDialogService {
this._inputModel = model;
return new Promise<void>((resolve, reject) => {
// only create the provider maps first time the dialog gets called
if (this._providerTypes.length === 0) {
entries(this._capabilitiesService.providers).forEach(p => {
this._providerTypes.push(p[1].connection.displayName);
this._providerNameToDisplayNameMap[p[0]] = p[1].connection.displayName;
});
}
this.updateModelServerCapabilities(model);
// If connecting from a query editor set "save connection" to false
if (params && params.input && params.connectionType === ConnectionType.editor) {

View File

@@ -102,6 +102,17 @@ export class ConnectionDialogWidget extends Modal {
super(localize('connection', 'Connection'), TelemetryKeys.Connection, _partService, telemetryService, clipboardService, _workbenchThemeService, contextKeyService, { hasSpinner: true, hasErrors: true });
}
/**
* Update the available connection providers, this is called when new providers are registered
* So that the connection type dropdown always has up to date values
*/
public updateConnectionProviders(providerTypeOptions: string[],
providerNameToDisplayNameMap: { [providerDisplayName: string]: string }) {
this.providerTypeOptions = providerTypeOptions;
this.providerNameToDisplayNameMap = providerNameToDisplayNameMap;
this.refresh();
}
public refresh(): void {
let filteredProviderTypes = this.providerTypeOptions;