Compare commits

...

18 Commits

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", "buffer-stream-reader": "^0.1.1",
"bytes": "^3.1.0", "bytes": "^3.1.0",
"clipboardy": "^1.2.3", "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", "error-ex": "^1.3.2",
"figures": "^2.0.0", "figures": "^2.0.0",
"opener": "^1.4.3", "opener": "^1.4.3",

View File

@@ -172,19 +172,27 @@ function saveProfileAndCreateNotebook(profile: azdata.IConnectionProfile): Promi
return handleNewNotebookTask(undefined, profile); 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> { 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 // 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 // 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, { let editor = await azdata.nb.showNotebookDocument(untitledUri, {
connectionProfile: profile, connectionProfile: profile,
providerId: jupyterNotebookProviderId, preview: false
preview: false,
defaultKernel: {
name: 'pyspark3kernel',
display_name: 'PySpark3',
language: 'python'
}
}); });
if (oeContext && oeContext.nodeInfo && oeContext.nodeInfo.nodePath) { if (oeContext && oeContext.nodeInfo && oeContext.nodeInfo.nodePath) {
// Get the file path after '/HDFS' // Get the file path after '/HDFS'
@@ -221,7 +229,6 @@ async function handleOpenNotebookTask(profile: azdata.IConnectionProfile): Promi
} else { } else {
await azdata.nb.showNotebookDocument(fileUri, { await azdata.nb.showNotebookDocument(fileUri, {
connectionProfile: profile, connectionProfile: profile,
providerId: jupyterNotebookProviderId,
preview: false preview: false
}); });
} }

View File

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

View File

@@ -174,9 +174,9 @@ dashdash@^1.12.0:
dependencies: dependencies:
assert-plus "^1.0.0" assert-plus "^1.0.0"
"dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.15": "dataprotocol-client@github:Microsoft/sqlops-dataprotocolclient#0.2.16":
version "0.2.15" version "0.2.16"
resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/a2cd2db109de882f0959f7b6421c86afa585f460" resolved "https://codeload.github.com/Microsoft/sqlops-dataprotocolclient/tar.gz/3c4e70405022ab5333cf1505deddd643223db3de"
dependencies: dependencies:
vscode-languageclient "3.5.1" 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 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'); const noNotebookVisible = localize('noNotebookVisible', 'No notebook editor is active');
let counter = 0; let untitledCounter = 0;
export let controller: JupyterController; export let controller: JupyterController;
@@ -54,7 +54,7 @@ export function activate(extensionContext: vscode.ExtensionContext) {
} }
function newNotebook(connectionProfile: azdata.IConnectionProfile) { function newNotebook(connectionProfile: azdata.IConnectionProfile) {
let title = `Untitled-${counter++}`; let title = findNextUntitledEditorName();
let untitledUri = vscode.Uri.parse(`untitled:${title}`); let untitledUri = vscode.Uri.parse(`untitled:${title}`);
let options: azdata.nb.NotebookShowOptions = connectionProfile ? { let options: azdata.nb.NotebookShowOptions = connectionProfile ? {
viewColumn: null, 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> { async function openNotebook(): Promise<void> {
try { try {
let filter = {}; let filter = {};
@@ -123,7 +137,7 @@ async function addCell(cellType: azdata.nb.CellType): Promise<void> {
async function analyzeNotebook(oeContext?: azdata.ObjectExplorerContext): 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 // 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 // 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, { let editor = await azdata.nb.showNotebookDocument(untitledUri, {
connectionProfile: oeContext ? oeContext.connectionProfile : undefined, connectionProfile: oeContext ? oeContext.connectionProfile : undefined,

View File

@@ -62,7 +62,7 @@ export class LocalJupyterServerManager implements nb.ServerManager, vscode.Dispo
this._onServerStarted.fire(); this._onServerStarted.fire();
} catch (error) { } 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; throw error;
} }
} }

View File

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

View File

@@ -140,14 +140,7 @@ export class HeightMap {
let viewItem = this.heightMap[i]; let viewItem = this.heightMap[i];
let delta = viewItem.height - size;
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 { protected updateTop(item: string, top: number): void {

View File

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

View File

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

View File

@@ -51,6 +51,7 @@
.account-view .list-row { .account-view .list-row {
padding: 12px; padding: 12px;
line-height: 18px !important;
} }
.account-view .list-row .icon { .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 * as outputProcessor from 'sql/parts/notebook/outputs/common/outputProcessor';
import { RenderMimeRegistry } from 'sql/parts/notebook/outputs/registry'; import { RenderMimeRegistry } from 'sql/parts/notebook/outputs/registry';
import 'vs/css!sql/parts/notebook/outputs/style/index'; import 'vs/css!sql/parts/notebook/outputs/style/index';
import { IThemeService } from 'vs/platform/theme/common/themeService';
export const OUTPUT_SELECTOR: string = 'output-component'; export const OUTPUT_SELECTOR: string = 'output-component';
@@ -31,7 +32,8 @@ export class OutputComponent extends AngularDisposable implements OnInit {
constructor( constructor(
@Inject(INotebookService) private _notebookService: INotebookService @Inject(INotebookService) private _notebookService: INotebookService,
@Inject(IThemeService) private _themeService: IThemeService
) { ) {
super(); super();
this.registry = _notebookService.getMimeRegistry(); this.registry = _notebookService.getMimeRegistry();
@@ -49,6 +51,7 @@ export class OutputComponent extends AngularDisposable implements OnInit {
let node = this.outputElement.nativeElement; let node = this.outputElement.nativeElement;
let output = this.cellOutput; let output = this.cellOutput;
let options = outputProcessor.getBundleOptions({ value: output, trusted: this.trustedMode }); let options = outputProcessor.getBundleOptions({ value: output, trusted: this.trustedMode });
options.themeService = this._themeService;
// TODO handle safe/unsafe mapping // TODO handle safe/unsafe mapping
this.createRenderedMimetype(options, node); this.createRenderedMimetype(options, node);
} }

View File

@@ -9,9 +9,9 @@
'use strict'; 'use strict';
import { nb } from 'azdata'; import { nb } from 'azdata';
import * as nls from 'vs/nls';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event'; import { Event, Emitter } from 'vs/base/common/event';
import { localize } from 'vs/nls';
import { IClientSession, IKernelPreference, IClientSessionOptions } from './modelInterfaces'; import { IClientSession, IKernelPreference, IClientSessionOptions } from './modelInterfaces';
import { Deferred } from 'sql/base/common/promise'; import { Deferred } from 'sql/base/common/promise';
@@ -70,12 +70,14 @@ export class ClientSession implements IClientSession {
await this.initializeSession(); await this.initializeSession();
await this.updateCachedKernelSpec(); await this.updateCachedKernelSpec();
} catch (err) { } 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 // Always resolving for now. It's up to callers to check for error case
this._isReady = true; this._isReady = true;
this._ready.resolve(); 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> { private async startServer(): Promise<void> {
@@ -83,7 +85,7 @@ export class ClientSession implements IClientSession {
if (serverManager && !serverManager.isStarted) { if (serverManager && !serverManager.isStarted) {
await serverManager.startServer(); await serverManager.startServer();
if (!serverManager.isStarted) { 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; this.isServerStarted = serverManager.isStarted;
} else { } else {
@@ -116,7 +118,7 @@ export class ClientSession implements IClientSession {
} catch (err) { } catch (err) {
// TODO move registration // TODO move registration
if (err && err.response && err.response.status === 501) { 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({ session = await this.notebookManager.sessionManager.startNew({
path: this.notebookUri.fsPath, path: this.notebookUri.fsPath,
kernelName: undefined kernelName: undefined
@@ -246,6 +248,11 @@ export class ClientSession implements IClientSession {
this._isReady = kernel.isReady; this._isReady = kernel.isReady;
await this.updateCachedKernelSpec(); await this.updateCachedKernelSpec();
// Send resolution events to listeners // 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 = { let changeArgs: nb.IKernelChangedArgs = {
oldValue: oldKernel, oldValue: oldKernel,
newValue: newKernel newValue: newKernel
@@ -255,7 +262,6 @@ export class ClientSession implements IClientSession {
// Wait on connection configuration to complete before resolving full kernel change // Wait on connection configuration to complete before resolving full kernel change
this._kernelChangeCompleted.resolve(); this._kernelChangeCompleted.resolve();
this._kernelChangedEmitter.fire(changeArgs); this._kernelChangedEmitter.fire(changeArgs);
return kernel;
} }
private async updateCachedKernelSpec(): Promise<void> { private async updateCachedKernelSpec(): Promise<void> {

View File

@@ -354,7 +354,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
cellIndex: index cellIndex: index
}); });
} else { } 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 }); 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) { if (displayName) {
let standardKernel = this._notebookOptions.standardKernels.find(kernel => kernel.displayName === displayName); let standardKernel = this._notebookOptions.standardKernels.find(kernel => kernel.displayName === displayName);
this._defaultKernel = displayName ? { name: standardKernel.name, display_name: standardKernel.displayName } : this._defaultKernel; 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) { if (!this._activeClientSession) {
this.updateActiveClientSession(clientSession); this.updateActiveClientSession(clientSession);
} }
let profile = new ConnectionProfile(this._notebookOptions.capabilitiesService, this.connectionProfile); let profile = new ConnectionProfile(this._notebookOptions.capabilitiesService, this.connectionProfile);
@@ -420,16 +419,31 @@ export class NotebookModel extends Disposable implements INotebookModel {
this._activeConnection = undefined; this._activeConnection = undefined;
} }
clientSession.onKernelChanging(async (e) => {
await this.loadActiveContexts(e);
});
clientSession.statusChanged(async (session) => {
this._kernelsChangedEmitter.fire(session.kernel);
});
await clientSession.initialize(); await clientSession.initialize();
// By somehow we have to wait for ready, otherwise may not be called for some cases. // By somehow we have to wait for ready, otherwise may not be called for some cases.
await clientSession.ready; await clientSession.ready;
if (clientSession.isInErrorState) { if (clientSession.kernel) {
this.setErrorState(clientSession.errorMessage); await clientSession.kernel.ready;
} else { await this.updateKernelInfoOnKernelChange(clientSession.kernel);
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.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); this.doChangeKernel(displayName, true);
} }
public async doChangeKernel(displayName: string, mustSetProvider: boolean = true): Promise<void> { private async doChangeKernel(displayName: string, mustSetProvider: boolean = true, restoreOnFail: boolean = true): Promise<void> {
if (mustSetProvider) { if (!displayName) {
await this.setProviderIdAndStartSession(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); let spec = this.getKernelSpecFromDisplayName(displayName);
if (spec) { if (spec) {
// Ensure that the kernel we try to switch to is a valid kernel; if not, use the default // 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) { 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); spec = kernelSpecs.find(spec => spec.name === this.notebookManager.sessionManager.specs.defaultKernel);
} }
} else { }
else {
spec = notebookConstants.sqlKernelSpec; spec = notebookConstants.sqlKernelSpec;
} }
if (this._activeClientSession && this._activeClientSession.isReady) { return spec;
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();
} }
public async changeContext(server: string, newConnection?: IConnectionProfile, hideErrorMessage?: boolean): Promise<void> { public async changeContext(server: string, newConnection?: IConnectionProfile, hideErrorMessage?: boolean): Promise<void> {
@@ -613,7 +667,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
}); });
} catch (err) { } catch (err) {
let msg = notebookUtils.getErrorMessage(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 // Get default language if saved in notebook file
// Otherwise, default to python // Otherwise, default to python
private getDefaultLanguageInfo(notebook: nb.INotebookContents): nb.ILanguageInfo { private getDefaultLanguageInfo(notebook: nb.INotebookContents): nb.ILanguageInfo {
@@ -703,7 +746,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
private setErrorState(errMsg: string): void { private setErrorState(errMsg: string): void {
this._inErrorState = true; 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); this.notifyError(msg);
} }
@@ -730,7 +773,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
} }
await this.shutdownActiveSession(); await this.shutdownActiveSession();
} catch (err) { } 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; await this._activeClientSession.ready;
} }
catch (err) { 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(); await this._activeClientSession.shutdown();
this.clearClientSessionListeners(); this.clearClientSessionListeners();
@@ -794,46 +837,39 @@ export class NotebookModel extends Disposable implements INotebookModel {
* Set _providerId and start session if it is new provider * Set _providerId and start session if it is new provider
* @param displayName Kernel dispay name * @param displayName Kernel dispay name
*/ */
private async setProviderIdAndStartSession(displayName: string): Promise<void> { private async tryStartSessionByChangingProviders(displayName: string): Promise<boolean> {
if (displayName) { if (displayName) {
if (this._activeClientSession && this._activeClientSession.isReady) { if (this._activeClientSession && this._activeClientSession.isReady) {
this._oldKernel = this._activeClientSession.kernel; this._oldKernel = this._activeClientSession.kernel;
let providerId = this.tryFindProviderForKernel(displayName); }
let providerId = this.tryFindProviderForKernel(displayName);
if (providerId) { if (providerId && providerId !== this._providerId) {
if (providerId !== this._providerId) { this._providerId = providerId;
this._providerId = providerId; this._onProviderIdChanged.fire(this._providerId);
this._onProviderIdChanged.fire(this._providerId);
await this.shutdownActiveSession(); await this.shutdownActiveSession();
let manager = this.getNotebookManager(providerId);
try { if (manager) {
let manager = this.getNotebookManager(providerId); await this.startSession(manager, displayName, false);
if (manager) { } else {
await this.startSession(manager, displayName); throw new Error(localize('ProviderNoManager', "Can't find notebook manager for provider {0}", providerId));
} 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}`);
}
} }
return true;
} }
} }
return false;
} }
private tryFindProviderForKernel(displayName: string) { private tryFindProviderForKernel(displayName: string, alwaysReturnId: boolean = false): string {
if (!displayName) { if (!displayName) {
return undefined; return undefined;
} }
let standardKernel = this.getStandardKernelFromDisplayName(displayName); let standardKernel = this.getStandardKernelFromDisplayName(displayName);
if (standardKernel && this._oldKernel && this._oldKernel.name !== standardKernel.name) { if (standardKernel) {
if (this._kernelDisplayNameToNotebookProviderIds.has(displayName)) { let providerId = this._kernelDisplayNameToNotebookProviderIds.get(displayName);
return this._kernelDisplayNameToNotebookProviderIds.get(displayName); if (alwaysReturnId || (!this._oldKernel || this._oldKernel.name !== standardKernel.name)) {
return providerId;
} }
} }
return undefined; 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 = { let changeInfo: NotebookContentChange = {
changeType: change, changeType: change,
cells: [cell] cells: [cell]

View File

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

View File

@@ -235,18 +235,24 @@ export class KernelsDropdown extends SelectBox {
this._register(this.model.kernelChanged((changedArgs: azdata.nb.IKernelChangedArgs) => { this._register(this.model.kernelChanged((changedArgs: azdata.nb.IKernelChangedArgs) => {
this.updateKernel(changedArgs.newValue); this.updateKernel(changedArgs.newValue);
})); }));
let kernel = this.model.clientSession && this.model.clientSession.kernel;
this.updateKernel(kernel);
} }
// Update SelectBox values // Update SelectBox values
public updateKernel(kernel: azdata.nb.IKernel) { 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 standardKernel = this.model.getStandardKernelFromName(kernel.name);
let kernels: string[] = this.model.standardKernelsDisplayName();
if (kernels && standardKernel) { if (kernels && standardKernel) {
let index = kernels.findIndex((kernel => kernel === standardKernel.displayName)); let index = kernels.findIndex((kernel => kernel === standardKernel.displayName));
this.setOptions(kernels, index); 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 { public updateModel(model: INotebookModel): void {
this.model = model as NotebookModel; this.model = model as NotebookModel;
this._register(model.contextsChanged(() => { this._register(model.contextsChanged(() => {
let kernelDisplayName: string = this.getKernelDisplayName(); this.handleContextsChanged();
if (kernelDisplayName) {
this.loadAttachToDropdown(this.model, kernelDisplayName);
}
})); }));
this._register(this.model.contextsLoading(() => { this._register(this.model.contextsLoading(() => {
this.setOptions([msgLoadingContexts], 0); 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 { private updateAttachToDropdown(model: INotebookModel): void {
model.onValidConnectionSelected(validConnection => { model.onValidConnectionSelected(validConnection => {
let kernelDisplayName: string = this.getKernelDisplayName(); this.handleContextsChanged(!validConnection);
if (kernelDisplayName) {
this.loadAttachToDropdown(this.model, kernelDisplayName, !validConnection);
}
}); });
} }

View File

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

View File

@@ -4,6 +4,7 @@
|----------------------------------------------------------------------------*/ |----------------------------------------------------------------------------*/
import { IRenderMime } from './renderMimeInterfaces'; import { IRenderMime } from './renderMimeInterfaces';
import { ReadonlyJSONObject } from '../../models/jsonext'; import { ReadonlyJSONObject } from '../../models/jsonext';
import { IThemeService } from 'vs/platform/theme/common/themeService';
/** /**
* The default mime model implementation. * The default mime model implementation.
@@ -17,6 +18,7 @@ export class MimeModel implements IRenderMime.IMimeModel {
this._data = options.data || {}; this._data = options.data || {};
this._metadata = options.metadata || {}; this._metadata = options.metadata || {};
this._callback = options.callback; this._callback = options.callback;
this._themeService = options.themeService;
} }
/** /**
@@ -38,6 +40,10 @@ export class MimeModel implements IRenderMime.IMimeModel {
return this._metadata; return this._metadata;
} }
get themeService(): IThemeService {
return this._themeService;
}
/** /**
* Set the data associated with the model. * Set the data associated with the model.
* *
@@ -54,6 +60,7 @@ export class MimeModel implements IRenderMime.IMimeModel {
private _callback: (options: IRenderMime.ISetDataOptions) => void; private _callback: (options: IRenderMime.ISetDataOptions) => void;
private _data: ReadonlyJSONObject; private _data: ReadonlyJSONObject;
private _metadata: ReadonlyJSONObject; private _metadata: ReadonlyJSONObject;
private _themeService: IThemeService;
} }
/** /**
@@ -83,5 +90,10 @@ export namespace MimeModel {
* The initial mime metadata. * The initial mime metadata.
*/ */
metadata?: ReadonlyJSONObject; 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. | Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/ |----------------------------------------------------------------------------*/
import { ReadonlyJSONObject } from '../../models/jsonext'; import { ReadonlyJSONObject } from '../../models/jsonext';
import { IThemeService } from 'vs/platform/theme/common/themeService';
/** /**
* A namespace for rendermime associated interfaces. * A namespace for rendermime associated interfaces.
@@ -37,6 +38,11 @@ export namespace IRenderMime {
* containing the new data. * containing the new data.
*/ */
setData(options: ISetDataOptions): void; 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 { RowNumberColumn } from 'sql/base/browser/ui/table/plugins/rowNumberColumn.plugin';
import { escape } from 'sql/base/common/strings'; import { escape } from 'sql/base/common/strings';
import { IDataResource } from 'sql/workbench/services/notebook/sql/sqlSessionManager'; 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. * 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 // 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'; tableContainer.style.height = rowsHeight >= 500 ? '500px' : rowsHeight.toString() + 'px';
attachTableStyler(detailTable, options.themeService);
host.appendChild(tableContainer); host.appendChild(tableContainer);
detailTable.resizeCanvas(); detailTable.resizeCanvas();
@@ -116,5 +119,10 @@ export namespace renderDataResource {
* The DataResource source to render. * The DataResource source to render.
*/ */
source: string; 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 { IRenderMime } from './common/renderMimeInterfaces';
import { ReadonlyJSONObject } from '../models/jsonext'; import { ReadonlyJSONObject } from '../models/jsonext';
import * as tableRenderers from 'sql/parts/notebook/outputs/tableRenderers'; import * as tableRenderers from 'sql/parts/notebook/outputs/tableRenderers';
import { IThemeService } from 'vs/platform/theme/common/themeService';
/** /**
* A common base class for mime renderers. * A common base class for mime renderers.
@@ -372,7 +373,8 @@ export class RenderedDataResource extends RenderedCommon {
render(model: IRenderMime.IMimeModel): Promise<void> { render(model: IRenderMime.IMimeModel): Promise<void> {
return tableRenderers.renderDataResource({ return tableRenderers.renderDataResource({
host: this.node, 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); 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.addBackupAction(context, actions);
this.addRestoreAction(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 { ViewletPanel, IViewletPanelOptions } from 'vs/workbench/browser/parts/views/panelViewlet';
import { isUndefinedOrNull } from 'vs/base/common/types'; import { isUndefinedOrNull } from 'vs/base/common/types';
import { range } from 'vs/base/common/arrays'; 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 { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { generateUuid } from 'vs/base/common/uuid'; import { generateUuid } from 'vs/base/common/uuid';
import { TPromise } from 'vs/base/common/winjs.base'; import { TPromise } from 'vs/base/common/winjs.base';
@@ -145,7 +145,6 @@ export class GridPanel extends ViewletPanel {
@IInstantiationService private instantiationService: IInstantiationService @IInstantiationService private instantiationService: IInstantiationService
) { ) {
super(options, keybindingService, contextMenuService, configurationService); super(options, keybindingService, contextMenuService, configurationService);
this.maximumBodySize = 0;
this.splitView = new ScrollableSplitView(this.container, { enableResizing: false, verticalScrollbarVisibility: ScrollbarVisibility.Visible }); this.splitView = new ScrollableSplitView(this.container, { enableResizing: false, verticalScrollbarVisibility: ScrollbarVisibility.Visible });
this.splitView.onScroll(e => { this.splitView.onScroll(e => {
if (this.state && this.splitView.length !== 0) { if (this.state && this.splitView.length !== 0) {
@@ -196,6 +195,9 @@ export class GridPanel extends ViewletPanel {
} }
return p; return p;
}, [])); }, []));
this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize;
}, 0);
if (this.state && this.state.scrollPosition) { if (this.state && this.state.scrollPosition) {
this.splitView.setScrollPosition(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; 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) { if (this.state && this.state.scrollPosition) {
this.splitView.setScrollPosition(this.state.scrollPosition); this.splitView.setScrollPosition(this.state.scrollPosition);
} }
@@ -244,6 +250,9 @@ export class GridPanel extends ViewletPanel {
} }
const sizeChanges = () => { const sizeChanges = () => {
this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize;
}, 0);
if (this.state && this.state.scrollPosition) { if (this.state && this.state.scrollPosition) {
this.splitView.setScrollPosition(this.state.scrollPosition); this.splitView.setScrollPosition(this.state.scrollPosition);
@@ -270,9 +279,6 @@ export class GridPanel extends ViewletPanel {
} }
private addResultSet(resultSet: azdata.ResultSetSummary[]) { private addResultSet(resultSet: azdata.ResultSetSummary[]) {
if (resultSet.length > 0) {
this.maximumBodySize = Number.POSITIVE_INFINITY;
}
let tables: GridTable<any>[] = []; let tables: GridTable<any>[] = [];
for (let set of resultSet) { for (let set of resultSet) {
@@ -302,7 +308,7 @@ export class GridPanel extends ViewletPanel {
// possible to need a sort? // possible to need a sort?
if (isUndefinedOrNull(this.maximizedGrid)) { 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); 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--) { for (let i = this.splitView.length - 1; i >= 0; i--) {
this.splitView.removeView(i); this.splitView.removeView(i);
} }
this.maximumBodySize = 0;
dispose(this.tables); dispose(this.tables);
dispose(this.tableDisposable); dispose(this.tableDisposable);
this.tableDisposable = []; this.tableDisposable = [];
this.tables = []; this.tables = [];
this.maximizedGrid = undefined; this.maximizedGrid = undefined;
this.maximumBodySize = this.tables.reduce((p, c) => {
return p + c.maximumSize;
}, 0);
} }
private maximizeTable(tableid: string): void { private maximizeTable(tableid: string): void {
@@ -345,7 +354,7 @@ export class GridPanel extends ViewletPanel {
this.maximizedGrid.state.maximized = false; this.maximizedGrid.state.maximized = false;
this.maximizedGrid = undefined; this.maximizedGrid = undefined;
this.splitView.removeView(0); 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 { 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[]> { 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 connected succeeded so add it to our active connections now, optionally adding it to the MRU based on
// the options.saveTheConnection setting // the options.saveTheConnection setting
let connectionMgmtInfo = this._connectionStatusManager.findConnection(uri); let connectionMgmtInfo = this._connectionStatusManager.findConnection(uri);
let activeConnection = connectionMgmtInfo.connectionProfile; this.tryAddActiveConnection(connectionMgmtInfo, connection, options.saveTheConnection);
this.tryAddActiveConnection(connectionMgmtInfo, activeConnection, options.saveTheConnection);
if (callbacks.onConnectSuccess) { if (callbacks.onConnectSuccess) {
callbacks.onConnectSuccess(options.params); callbacks.onConnectSuccess(options.params);

View File

@@ -81,23 +81,27 @@ export class ExtHostNotebook implements ExtHostNotebookShape {
$startNewSession(managerHandle: number, options: azdata.nb.ISessionOptions): Thenable<INotebookSessionDetails> { $startNewSession(managerHandle: number, options: azdata.nb.ISessionOptions): Thenable<INotebookSessionDetails> {
return this._withSessionManager(managerHandle, async (sessionManager) => { return this._withSessionManager(managerHandle, async (sessionManager) => {
let session = await sessionManager.startNew(options); try {
let sessionId = this._addNewAdapter(session); let session = await sessionManager.startNew(options);
let kernelDetails: INotebookKernelDetails = undefined; let sessionId = this._addNewAdapter(session);
if (session.kernel) { let kernelDetails: INotebookKernelDetails = undefined;
kernelDetails = this.saveKernel(session.kernel); 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) { if (manager === undefined) {
return TPromise.wrapError<R>(new Error(localize('errNoManager', 'No Manager found'))); 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> { private _withServerManager<R>(handle: number, callback: (manager: azdata.nb.ServerManager) => R | PromiseLike<R>): TPromise<R> {

View File

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

View File

@@ -90,7 +90,38 @@ export class ConnectionDialogService implements IConnectionDialogService {
@IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService, @IWorkspaceConfigurationService private _workspaceConfigurationService: IWorkspaceConfigurationService,
@IClipboardService private _clipboardService: IClipboardService, @IClipboardService private _clipboardService: IClipboardService,
@ICommandService private _commandService: ICommandService @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 * Gets the default provider with the following actions
@@ -351,13 +382,6 @@ export class ConnectionDialogService implements IConnectionDialogService {
this._inputModel = model; this._inputModel = model;
return new Promise<void>((resolve, reject) => { 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); this.updateModelServerCapabilities(model);
// If connecting from a query editor set "save connection" to false // If connecting from a query editor set "save connection" to false
if (params && params.input && params.connectionType === ConnectionType.editor) { 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 }); 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 { public refresh(): void {
let filteredProviderTypes = this.providerTypeOptions; let filteredProviderTypes = this.providerTypeOptions;