mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-22 01:25:38 -05:00
Add AsyncServerTree (#11838)
* wip * Fixes * More fixes * more fixes * Disable when preview features disabled * remove unused imports * Handle promises * PR feedback * Single default ServerGroup color value
This commit is contained in:
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#E8E8E8" d="M6 4v8l4-4-4-4zm1 2.414L8.586 8 7 9.586V6.414z"/></svg>
|
||||
|
Before Width: | Height: | Size: 139 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#212121" d="M1.29.07V16h9.59a3.31,3.31,0,0,1-1.94-1H2.29V11h6a3.31,3.31,0,0,1,.54-.8A1.81,1.81,0,0,1,9,10H2.29v-9h8.53v8a3.68,3.68,0,0,1,.58,0l.39,0h0v-9Z"/><path class="cls-1" fill="#212121" d="M3.3,1.8V4.25H5.75V1.8Zm2,2H3.8V2.3H5.25Z"/><circle fill="#3bb44a" cx="11.24" cy="12.52" r="3.47"/></svg>
|
||||
|
Before Width: | Height: | Size: 373 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#fff" d="M1.29.07V16h9.59a3.31,3.31,0,0,1-1.94-1H2.29V11h6a3.31,3.31,0,0,1,.54-.8A1.81,1.81,0,0,1,9,10H2.29v-9h8.53v8a3.68,3.68,0,0,1,.58,0l.39,0h0v-9Z"/><path fill="#fff" d="M3.3,1.8V4.25H5.75V1.8Zm2,2H3.8V2.3H5.25Z"/><circle fill="#3bb44a" cx="11.24" cy="12.52" r="3.47"/></svg>
|
||||
|
Before Width: | Height: | Size: 353 B |
@@ -1,138 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* --- Registered servers tree viewlet --- */
|
||||
.server-explorer-viewlet .monaco-tree .monaco-tree-row .content .server-group {
|
||||
cursor: default;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Bold font style does not go well with CJK fonts */
|
||||
.server-explorer-viewlet:lang(zh-Hans) .monaco-tree .monaco-tree-row .server-group,
|
||||
.server-explorer-viewlet:lang(zh-Hant) .monaco-tree .monaco-tree-row .server-group,
|
||||
.server-explorer-viewlet:lang(ja) .monaco-tree .monaco-tree-row .server-group,
|
||||
.server-explorer-viewlet:lang(ko) .monaco-tree .monaco-tree-row .server-group { font-weight: normal; }
|
||||
|
||||
/* High Contrast Theming */
|
||||
.monaco-workbench.hc-black .server-explorer-viewlet .server-group {
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.monaco-workbench > .activitybar .monaco-action-bar .action-label.serverTree {
|
||||
background-size: 22px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 50% !important;
|
||||
}
|
||||
|
||||
.server-explorer-viewlet .object-explorer-view {
|
||||
height: calc(100% - 36px);
|
||||
}
|
||||
|
||||
.server-explorer-viewlet .server-group {
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.server-explorer-viewlet .monaco-action-bar .action-label {
|
||||
margin-right: 0.3em;
|
||||
margin-left: 0.3em;
|
||||
line-height: 15px;
|
||||
width: 10px !important;
|
||||
height: 10px !important;
|
||||
}
|
||||
|
||||
/* Add space beneath the button */
|
||||
.new-connection .monaco-text-button {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
/* display action buttons on hover */
|
||||
.server-explorer-viewlet .monaco-tree .monaco-tree-row > .content {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* Added to display the tree in connection dialog */
|
||||
.server-explorer-viewlet {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.explorer-servers {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* search box */
|
||||
.server-explorer-viewlet .search-box {
|
||||
padding-bottom: 4px;
|
||||
margin: auto;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
/* OE and connection element group */
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile,
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group {
|
||||
padding: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* OE and connection label */
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .label,
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group > .label {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* OE and connection icon */
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon,
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group > .icon {
|
||||
float: left;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.connected {
|
||||
background: url('connected_active_server.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.connected,
|
||||
.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.connected{
|
||||
background: url('connected_active_server_inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.disconnected {
|
||||
background: url('disconnected_server.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.disconnected,
|
||||
.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page.disconnected{
|
||||
background: url('disconnected_server_inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
/* loading for OE node */
|
||||
.server-explorer-viewlet .monaco-tree .monaco-tree-rows > .monaco-tree-row > .codicon.in-progress .connection-tile:before,
|
||||
.server-explorer-viewlet .monaco-tree .monaco-tree-rows > .monaco-tree-row > .codicon.in-progress .object-element-group:before {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 36px;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: -35px;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.expanded.has-children > .content.server-group:before {
|
||||
background: url('expanded-dark.svg') 50% 50% no-repeat;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children > .content.server-group:before {
|
||||
background: url('collapsed-dark.svg') 50% 50% no-repeat;
|
||||
}
|
||||
|
||||
/* Add connection button */
|
||||
.server-explorer-viewlet .button-section {
|
||||
padding: 20px;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#212121" d="M1.42.06V16H11a3.31,3.31,0,0,1-1.94-1H2.42V11h6a3.31,3.31,0,0,1,.54-.8,1.81,1.81,0,0,1,.19-.2H2.42v-9H11v8a3.68,3.68,0,0,1,.58,0l.39,0h0v-9Z"/><path fill="#212121" d="M3.43,1.79V4.24H5.89V1.79Zm2,2H3.93V2.29H5.39Z"/><path fill="#d02e00" d="M11.08,16a2.22,2.22,0,0,0,.45,0,2.59,2.59,0,0,0,.4,0Z"/><path fill="#d02e00" d="M12,9.08h0l-.39,0a3.68,3.68,0,0,0-.58,0A3.41,3.41,0,0,0,9.14,10a1.81,1.81,0,0,0-.19.2,3.46,3.46,0,0,0-.89,2.3,3.4,3.4,0,0,0,.85,2.26,1.29,1.29,0,0,0,.16.17A3.31,3.31,0,0,0,11,16H12a3.46,3.46,0,0,0,2.17-1.13,3.41,3.41,0,0,0,.88-2.3A3.47,3.47,0,0,0,12,9.08Zm0,5.72a1.72,1.72,0,0,1-.39,0,2.23,2.23,0,0,1-.77-.14,2.29,2.29,0,0,1-1.54-2.17A2.22,2.22,0,0,1,9.79,11a2.29,2.29,0,0,1,1-.67,2.23,2.23,0,0,1,.77-.14,1.72,1.72,0,0,1,.39,0h0a2.3,2.3,0,0,1,0,4.53Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 864 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#fff" d="M1.42.06V16H11a3.31,3.31,0,0,1-1.94-1H2.42V11h6a3.31,3.31,0,0,1,.54-.8,1.81,1.81,0,0,1,.19-.2H2.42v-9H11v8a3.68,3.68,0,0,1,.58,0l.39,0h0v-9Z"/><path fill="#fff" d="M3.43,1.79V4.24H5.89V1.79Zm2,2H3.93V2.29H5.39Z"/><path fill="#d02e00" d="M11.08,16a2.22,2.22,0,0,0,.45,0,2.59,2.59,0,0,0,.4,0Z"/><path fill="#d02e00" d="M12,9.08h0l-.39,0a3.68,3.68,0,0,0-.58,0A3.41,3.41,0,0,0,9.14,10a1.81,1.81,0,0,0-.19.2,3.46,3.46,0,0,0-.89,2.3,3.4,3.4,0,0,0,.85,2.26,1.29,1.29,0,0,0,.16.17A3.31,3.31,0,0,0,11,16H12a3.46,3.46,0,0,0,2.17-1.13,3.41,3.41,0,0,0,.88-2.3A3.47,3.47,0,0,0,12,9.08Zm0,5.72a1.72,1.72,0,0,1-.39,0,2.23,2.23,0,0,1-.77-.14,2.29,2.29,0,0,1-1.54-2.17A2.22,2.22,0,0,1,9.79,11a2.29,2.29,0,0,1,1-.67,2.23,2.23,0,0,1,.77-.14,1.72,1.72,0,0,1,.39,0h0a2.3,2.3,0,0,1,0,4.53Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 858 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="#E8E8E8" d="M11 10H5.344L11 4.414V10z"/></svg>
|
||||
|
Before Width: | Height: | Size: 118 B |
@@ -14,7 +14,6 @@ import { Action } from 'vs/base/common/actions';
|
||||
import { TreeSelectionHandler } from 'sql/workbench/services/objectExplorer/browser/treeSelectionHandler';
|
||||
import { ObjectExplorerActionsContext, getTreeNode } from 'sql/workbench/services/objectExplorer/browser/objectExplorerActions';
|
||||
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { TreeUpdateUtils } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService';
|
||||
@@ -91,7 +90,7 @@ export class OEManageConnectionAction extends Action {
|
||||
|
||||
private async doManage(actionContext: ObjectExplorerActionsContext): Promise<boolean> {
|
||||
let treeNode: TreeNode = undefined;
|
||||
let connectionProfile: IConnectionProfile = undefined;
|
||||
let connectionProfile: ConnectionProfile = undefined;
|
||||
if (actionContext instanceof ObjectExplorerActionsContext) {
|
||||
// Must use a real connection profile for this action due to lookup
|
||||
connectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, actionContext.connectionProfile);
|
||||
|
||||
@@ -18,13 +18,14 @@ import {
|
||||
} from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction';
|
||||
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
import { IViewDescriptorService } from 'vs/workbench/common/views';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { AsyncServerTree } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
|
||||
export class ConnectionViewletPanel extends ViewPane {
|
||||
|
||||
@@ -82,7 +83,7 @@ export class ConnectionViewletPanel extends ViewPane {
|
||||
this._root = container;
|
||||
}
|
||||
|
||||
get serversTree(): ITree {
|
||||
get serversTree(): ITree | AsyncServerTree {
|
||||
return this._serverTreeView.tree;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/* --- Registered servers tree viewlet --- */
|
||||
.server-explorer-viewlet .monaco-tree .monaco-tree-row .content .server-group {
|
||||
.server-explorer-viewlet .monaco-tree .monaco-tree-row .content .server-group,
|
||||
.server-explorer-viewlet .monaco-list .monaco-list-row .content .server-group {
|
||||
cursor: default;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
@@ -15,7 +16,13 @@
|
||||
.server-explorer-viewlet:lang(zh-Hans) .monaco-tree .monaco-tree-row .server-group,
|
||||
.server-explorer-viewlet:lang(zh-Hant) .monaco-tree .monaco-tree-row .server-group,
|
||||
.server-explorer-viewlet:lang(ja) .monaco-tree .monaco-tree-row .server-group,
|
||||
.server-explorer-viewlet:lang(ko) .monaco-tree .monaco-tree-row .server-group { font-weight: normal; }
|
||||
.server-explorer-viewlet:lang(ko) .monaco-tree .monaco-tree-row .server-group,
|
||||
.server-explorer-viewlet:lang(zh-Hans) .monaco-list .monaco-list-row .server-group,
|
||||
.server-explorer-viewlet:lang(zh-Hant) .monaco-list .monaco-list-row .server-group,
|
||||
.server-explorer-viewlet:lang(ja) .monaco-list .monaco-list-row .server-group,
|
||||
.server-explorer-viewlet:lang(ko) .monaco-list .monaco-list-row .server-group {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
/* High Contrast Theming */
|
||||
.monaco-workbench.hc-black .server-explorer-viewlet .server-group {
|
||||
@@ -48,14 +55,19 @@
|
||||
|
||||
/* Add space beneath the button */
|
||||
.new-connection .monaco-text-button {
|
||||
margin-bottom: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
/* display action buttons on hover */
|
||||
.server-explorer-viewlet .monaco-tree .monaco-tree-row > .content {
|
||||
.server-explorer-viewlet .monaco-tree .monaco-tree-row > .content,
|
||||
.server-explorer-viewlet .monaco-list .monaco-list-row {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.server-explorer-viewlet .monaco-tl-row {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Added to display the tree in connection dialog */
|
||||
.server-explorer-viewlet {
|
||||
height: 100%;
|
||||
@@ -74,7 +86,9 @@ margin-bottom: 2px;
|
||||
|
||||
/* OE and connection element group */
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile,
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group {
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group,
|
||||
.monaco-list .monaco-list-rows > .monaco-list-row .connection-tile,
|
||||
.monaco-list .monaco-list-rows > .monaco-list-row .object-element-group {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
padding-top: 3px;
|
||||
@@ -84,26 +98,33 @@ margin-bottom: 2px;
|
||||
|
||||
/* OE and connection label */
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .label,
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group > .label {
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group > .label,
|
||||
.monaco-list .monaco-list-rows > .monaco-list-row .connection-tile > .label,
|
||||
.monaco-list .monaco-list-rows > .monaco-list-row .object-element-group > .label {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* OE and connection icon */
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon,
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group > .icon {
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .object-element-group > .icon,
|
||||
.monaco-list .monaco-list-rows > .monaco-list-row .connection-tile > .icon,
|
||||
.monaco-list .monaco-list-rows > .monaco-list-row .object-element-group > .icon {
|
||||
float: left;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page {
|
||||
.monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page,
|
||||
.monaco-list .monaco-list-rows > .monaco-list-row .connection-tile > .icon.server-page {
|
||||
background: url('default_server.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page,
|
||||
.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page{
|
||||
.hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row > .content > .connection-tile > .icon.server-page,
|
||||
.vs-dark .monaco-list .monaco-list-rows > .monaco-list-row .connection-tile > .icon.server-page,
|
||||
.hc-black .monaco-list .monaco-list-rows > .monaco-list-row .connection-tile > .icon.server-page {
|
||||
background: url('default_server_inverse.svg') center center no-repeat;
|
||||
}
|
||||
|
||||
@@ -118,11 +139,40 @@ margin-bottom: 2px;
|
||||
left: -35px;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.expanded.has-children > .content.server-group:before {
|
||||
.monaco-list .connection-tile > .icon.server-page::after {
|
||||
position: absolute;
|
||||
height: 0.25rem;
|
||||
width: 0.25rem;
|
||||
top: 14px;
|
||||
left: 45px;
|
||||
border-radius: 100%;
|
||||
content:"";
|
||||
font-size: 100%;
|
||||
line-height: 100%;
|
||||
color:white;
|
||||
text-align:center;
|
||||
vertical-align:middle;
|
||||
}
|
||||
|
||||
/* Connected badge */
|
||||
.monaco-list .connection-tile > .icon.server-page.connected::after {
|
||||
border: 0.12rem solid rgba(59, 180, 74, 100%);
|
||||
background: rgba(59, 180, 74, 100%);
|
||||
}
|
||||
|
||||
/* Disconnected badge */
|
||||
.monaco-list .connection-tile > .icon.server-page.disconnected::after {
|
||||
border: 0.12rem solid rgba(208, 46, 0, 100%);
|
||||
background: rgba(255, 255, 255, 80%);
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.expanded.has-children > .content.server-group:before,
|
||||
.monaco-list .monaco-list-rows.show-twisties > .monaco-list-row.expanded.has-children > .content.server-group:before {
|
||||
background: url('expanded-dark.svg') 50% 50% no-repeat;
|
||||
}
|
||||
|
||||
.monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children > .content.server-group:before {
|
||||
.monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children > .content.server-group:before,
|
||||
.monaco-list .monaco-list-rows.show-twisties > .monaco-list-row.has-children > .content.server-group:before {
|
||||
background: url('collapsed-dark.svg') 50% 50% no-repeat;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { SERVER_GROUP_CONFIG } from 'sql/workbench/services/serverGroup/common/interfaces';
|
||||
import { horizontalScrollingKey } from 'vs/platform/list/browser/listService';
|
||||
import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree';
|
||||
import { ObjectExplorerActionsContext } from 'sql/workbench/services/objectExplorer/browser/objectExplorerActions';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
|
||||
/**
|
||||
* ServerTreeview implements the dynamic tree view.
|
||||
@@ -48,7 +53,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
|
||||
private _buttonSection: HTMLElement;
|
||||
private _treeSelectionHandler: TreeSelectionHandler;
|
||||
private _activeConnectionsFilterAction: ActiveConnectionsFilterAction;
|
||||
private _tree: ITree;
|
||||
private _tree: ITree | AsyncServerTree;
|
||||
private _onSelectionOrFocusChange: Emitter<void>;
|
||||
private _actionProvider: ServerTreeActionProvider;
|
||||
|
||||
@@ -59,7 +64,9 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
|
||||
@IThemeService private _themeService: IThemeService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService,
|
||||
@IConfigurationService private _configurationService: IConfigurationService,
|
||||
@ICapabilitiesService capabilitiesService: ICapabilitiesService
|
||||
@ICapabilitiesService capabilitiesService: ICapabilitiesService,
|
||||
@IContextMenuService private _contextMenuService: IContextMenuService,
|
||||
@IKeybindingService private _keybindingService: IKeybindingService
|
||||
) {
|
||||
super();
|
||||
this._activeConnectionsFilterAction = this._instantiationService.createInstance(
|
||||
@@ -71,9 +78,17 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
|
||||
this._onSelectionOrFocusChange = new Emitter();
|
||||
this._actionProvider = this._instantiationService.createInstance(ServerTreeActionProvider);
|
||||
capabilitiesService.onCapabilitiesRegistered(async () => {
|
||||
if (this._connectionManagementService.hasRegisteredServers()) {
|
||||
await this.refreshTree();
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
// Refresh the tree input now that the capabilities are registered so that we can
|
||||
// get the full ConnectionProfiles with the server info updated properly
|
||||
const treeInput = TreeUpdateUtils.getTreeInput(this._connectionManagementService);
|
||||
await this._tree.setInput(treeInput);
|
||||
this._treeSelectionHandler.onTreeActionStateChange(false);
|
||||
} else {
|
||||
if (this._connectionManagementService.hasRegisteredServers()) {
|
||||
await this.refreshTree();
|
||||
this._treeSelectionHandler.onTreeActionStateChange(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
this.registerCommands();
|
||||
@@ -97,7 +112,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
|
||||
return this._actionProvider;
|
||||
}
|
||||
|
||||
public get tree(): ITree {
|
||||
public get tree(): ITree | AsyncServerTree {
|
||||
return this._tree;
|
||||
}
|
||||
|
||||
@@ -143,11 +158,30 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
|
||||
}
|
||||
|
||||
const horizontalScrollEnabled: boolean = this._configurationService.getValue(horizontalScrollingKey) || false;
|
||||
this._tree = this._register(TreeCreationUtils.createRegisteredServersTree(container, this._instantiationService, horizontalScrollEnabled));
|
||||
//this._tree.setInput(undefined);
|
||||
this._tree = this._register(TreeCreationUtils.createServersTree(container, this._instantiationService, this._configurationService, horizontalScrollEnabled));
|
||||
this._register(this._tree.onDidChangeSelection((event) => this.onSelected(event)));
|
||||
this._register(this._tree.onDidBlur(() => this._onSelectionOrFocusChange.fire()));
|
||||
this._register(this._tree.onDidChangeFocus(() => this._onSelectionOrFocusChange.fire()));
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
this._register(this._tree.onContextMenu(e => this.onContextMenu(e)));
|
||||
this._register(this._tree.onMouseDblClick(e => {
|
||||
// Open dashboard on double click for server and database nodes
|
||||
let connectionProfile: ConnectionProfile;
|
||||
if (e.element instanceof ConnectionProfile) {
|
||||
connectionProfile = e.element;
|
||||
} else if (e.element instanceof TreeNode) {
|
||||
if (TreeUpdateUtils.isAvailableDatabaseNode(e.element)) {
|
||||
connectionProfile = TreeUpdateUtils.getConnectionProfile(e.element);
|
||||
}
|
||||
}
|
||||
if (connectionProfile) {
|
||||
this._connectionManagementService.showDashboard(connectionProfile);
|
||||
}
|
||||
}));
|
||||
this._register(this._connectionManagementService.onConnectionChanged(() => {
|
||||
this.refreshTree().catch(err => errors.onUnexpectedError);
|
||||
}));
|
||||
}
|
||||
|
||||
// Theme styler
|
||||
this._register(attachListStyler(this._tree, this._themeService));
|
||||
@@ -171,7 +205,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
|
||||
this.showError(args.errorMessage);
|
||||
}
|
||||
if (args.connection) {
|
||||
this.onObjectExplorerSessionCreated(args.connection);
|
||||
this.onObjectExplorerSessionCreated(args.connection).catch(err => errors.onUnexpectedError);
|
||||
}
|
||||
}));
|
||||
}
|
||||
@@ -182,7 +216,14 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
|
||||
|
||||
const expandGroups: boolean = this._configurationService.getValue(SERVER_GROUP_CONFIG)[SERVER_GROUP_AUTOEXPAND_CONFIG];
|
||||
if (expandGroups) {
|
||||
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(root));
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
await Promise.all(ConnectionProfileGroup.getSubgroups(root).map(subgroup => {
|
||||
return this._tree.expand(subgroup);
|
||||
}));
|
||||
} else {
|
||||
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(root));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (root && !root.hasValidConnections) {
|
||||
@@ -200,30 +241,55 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
|
||||
return uri && startsWith(uri, ConnectionUtils.uriPrefixes.default) && !isBackupRestoreUri;
|
||||
}
|
||||
|
||||
private async handleAddConnectionProfile(newProfile: IConnectionProfile): Promise<void> {
|
||||
if (newProfile) {
|
||||
const groups = this._connectionManagementService.getConnectionGroups();
|
||||
const profile = ConnectionUtils.findProfileInGroup(newProfile, groups);
|
||||
if (profile) {
|
||||
newProfile = profile;
|
||||
}
|
||||
}
|
||||
|
||||
private async handleAddConnectionProfile(newProfile?: IConnectionProfile): Promise<void> {
|
||||
if (this._buttonSection) {
|
||||
hide(this._buttonSection);
|
||||
this._activeConnectionsFilterAction.enabled = true;
|
||||
}
|
||||
const currentSelections = this._tree.getSelection();
|
||||
const currentSelectedElement = currentSelections && currentSelections.length >= 1 ? currentSelections[0] : undefined;
|
||||
const newProfileIsSelected = currentSelectedElement && newProfile ? currentSelectedElement.id === newProfile.id : false;
|
||||
if (newProfile && currentSelectedElement && !newProfileIsSelected) {
|
||||
this._tree.clearSelection();
|
||||
}
|
||||
await this.refreshTree();
|
||||
if (newProfile && !newProfileIsSelected) {
|
||||
await this._tree.reveal(newProfile);
|
||||
this._tree.select(newProfile);
|
||||
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
// When new connection groups are added the event is fired with undefined so
|
||||
// we still want to refresh the tree in that case to pick up the changes
|
||||
await this.refreshTree();
|
||||
if (newProfile) {
|
||||
const currentSelections = this._tree.getSelection();
|
||||
const currentSelectedElement = currentSelections && currentSelections.length >= 1 ? currentSelections[0] : undefined;
|
||||
const newProfileIsSelected = currentSelectedElement && currentSelectedElement.id === newProfile.id;
|
||||
// Clear any other selected elements first
|
||||
if (currentSelectedElement && !newProfileIsSelected) {
|
||||
this._tree.setSelection([]);
|
||||
}
|
||||
const newConnectionProfile = this.getConnectionInTreeInput(newProfile.id);
|
||||
if (newConnectionProfile) {
|
||||
// Re-render to update the connection status badge
|
||||
this._tree.rerender(newConnectionProfile);
|
||||
this._tree.setSelection([newConnectionProfile]);
|
||||
this._tree.expand(newConnectionProfile);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
if (newProfile) {
|
||||
const groups = this._connectionManagementService.getConnectionGroups();
|
||||
const profile = ConnectionUtils.findProfileInGroup(newProfile, groups);
|
||||
if (profile) {
|
||||
newProfile = profile;
|
||||
}
|
||||
}
|
||||
|
||||
const currentSelections = this._tree.getSelection();
|
||||
const currentSelectedElement = currentSelections && currentSelections.length >= 1 ? currentSelections[0] : undefined;
|
||||
const newProfileIsSelected = currentSelectedElement && newProfile ? currentSelectedElement.id === newProfile.id : false;
|
||||
if (newProfile && currentSelectedElement && !newProfileIsSelected) {
|
||||
this._tree.clearSelection();
|
||||
}
|
||||
await this.refreshTree();
|
||||
if (newProfile && !newProfileIsSelected) {
|
||||
await this._tree.reveal(newProfile);
|
||||
this._tree.select(newProfile);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private showError(errorMessage: string) {
|
||||
@@ -232,66 +298,83 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
|
||||
}
|
||||
}
|
||||
|
||||
private getConnectionInTreeInput(connectionId: string): ConnectionProfile {
|
||||
const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService);
|
||||
const connections = ConnectionProfileGroup.getConnectionsInGroup(root);
|
||||
const results = connections.filter(con => {
|
||||
if (connectionId === con.id) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
/**
|
||||
* Gets the ConnectionProfile object in the tree for the specified ID, or undefined if it doesn't exist.
|
||||
* @param connectionId The connection ID to search for
|
||||
*/
|
||||
private getConnectionInTreeInput(connectionId: string): ConnectionProfile | undefined {
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
const root = this._tree.getInput();
|
||||
const connections = ConnectionProfileGroup.getConnectionsInGroup(root);
|
||||
return connections.find(conn => conn.id === connectionId);
|
||||
} else {
|
||||
const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService);
|
||||
const connections = ConnectionProfileGroup.getConnectionsInGroup(root);
|
||||
const results = connections.filter(con => {
|
||||
if (connectionId === con.id) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (results && results.length > 0) {
|
||||
return results[0];
|
||||
}
|
||||
});
|
||||
if (results && results.length > 0) {
|
||||
return results[0];
|
||||
return undefined;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private onObjectExplorerSessionCreated(connection: IConnectionProfile) {
|
||||
const conn = this.getConnectionInTreeInput(connection.id);
|
||||
if (conn) {
|
||||
this._tree.refresh(conn).then(() => {
|
||||
return this._tree.expand(conn).then(() => {
|
||||
return this._tree.reveal(conn, 0.5).then(() => {
|
||||
this._treeSelectionHandler.onTreeActionStateChange(false);
|
||||
});
|
||||
});
|
||||
}).then(null, errors.onUnexpectedError);
|
||||
private async onObjectExplorerSessionCreated(connection: IConnectionProfile): Promise<void> {
|
||||
const element = this.getConnectionInTreeInput(connection.id);
|
||||
if (element) {
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
this._tree.rerender(element);
|
||||
} else {
|
||||
await this._tree.refresh(element);
|
||||
}
|
||||
await this._tree.expand(element);
|
||||
await this._tree.reveal(element, 0.5);
|
||||
this._treeSelectionHandler.onTreeActionStateChange(false);
|
||||
}
|
||||
}
|
||||
|
||||
public addObjectExplorerNodeAndRefreshTree(connection: IConnectionProfile): void {
|
||||
hide(this.messages);
|
||||
if (!this._objectExplorerService.getObjectExplorerNode(connection)) {
|
||||
this._objectExplorerService.updateObjectExplorerNodes(connection).then(() => {
|
||||
// The oe request is sent. an event will be raised when the session is created
|
||||
}, error => {
|
||||
});
|
||||
this._objectExplorerService.updateObjectExplorerNodes(connection).catch(e => errors.onUnexpectedError(e));
|
||||
}
|
||||
}
|
||||
|
||||
public deleteObjectExplorerNodeAndRefreshTree(connection: IConnectionProfile): Promise<void> {
|
||||
public async deleteObjectExplorerNodeAndRefreshTree(connection: IConnectionProfile): Promise<void> {
|
||||
if (connection) {
|
||||
const conn = this.getConnectionInTreeInput(connection.id);
|
||||
if (conn) {
|
||||
return this._objectExplorerService.deleteObjectExplorerNode(conn).then(async () => {
|
||||
await this._objectExplorerService.deleteObjectExplorerNode(conn);
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
// Collapse the node before refreshing so the refresh doesn't try to fetch
|
||||
// the children again (which causes it to try and connect)
|
||||
this._tree.collapse(conn);
|
||||
await this.refreshTree();
|
||||
} else {
|
||||
await this._tree.collapse(conn);
|
||||
return this._tree.refresh(conn);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public refreshTree(): Promise<void> {
|
||||
public async refreshTree(): Promise<void> {
|
||||
hide(this.messages);
|
||||
this.clearOtherActions();
|
||||
return TreeUpdateUtils.registeredServerUpdate(this._tree, this._connectionManagementService);
|
||||
}
|
||||
|
||||
public refreshElement(element: any): Promise<void> {
|
||||
return this._tree.refresh(element);
|
||||
public async refreshElement(element: ServerTreeElement): Promise<void> {
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
return this._tree.updateChildren(element);
|
||||
} else {
|
||||
return this._tree.refresh(element);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -350,9 +433,19 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
|
||||
this._tree.setInput(treeInput).then(async () => {
|
||||
if (isHidden(this.messages)) {
|
||||
this._tree.getFocus();
|
||||
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(treeInput));
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
await Promise.all(ConnectionProfileGroup.getSubgroups(treeInput).map(subgroup => {
|
||||
this._tree.expand(subgroup);
|
||||
}));
|
||||
} else {
|
||||
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(treeInput));
|
||||
}
|
||||
} else {
|
||||
this._tree.clearFocus();
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
this._tree.setFocus([]);
|
||||
} else {
|
||||
this._tree.clearFocus();
|
||||
}
|
||||
}
|
||||
}, errors.onUnexpectedError);
|
||||
} else {
|
||||
@@ -382,9 +475,19 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
|
||||
this._tree.setInput(treeInput).then(async () => {
|
||||
if (isHidden(this.messages)) {
|
||||
this._tree.getFocus();
|
||||
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(treeInput));
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
await Promise.all(ConnectionProfileGroup.getSubgroups(treeInput).map(subgroup => {
|
||||
this._tree.expand(subgroup);
|
||||
}));
|
||||
} else {
|
||||
await this._tree.expandAll(ConnectionProfileGroup.getSubgroups(treeInput));
|
||||
}
|
||||
} else {
|
||||
this._tree.clearFocus();
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
this._tree.setFocus([]);
|
||||
} else {
|
||||
this._tree.clearFocus();
|
||||
}
|
||||
}
|
||||
}, errors.onUnexpectedError);
|
||||
}
|
||||
@@ -450,17 +553,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
|
||||
this._tree.layout(height);
|
||||
}
|
||||
|
||||
/**
|
||||
* set the visibility of the view
|
||||
*/
|
||||
public setVisible(visible: boolean): void {
|
||||
if (visible) {
|
||||
this._tree.onVisible();
|
||||
} else {
|
||||
this._tree.onHidden();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of selected nodes in the tree
|
||||
*/
|
||||
@@ -472,48 +564,108 @@ export class ServerTreeView extends Disposable implements IServerTreeView {
|
||||
* Get whether the tree view currently has focus
|
||||
*/
|
||||
public isFocused(): boolean {
|
||||
return this._tree.isDOMFocused();
|
||||
return this._tree.getHTMLElement() === document.activeElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether the given element is expanded or collapsed
|
||||
*/
|
||||
public setExpandedState(element: TreeNode | ConnectionProfile, expandedState: TreeItemCollapsibleState): Promise<void> {
|
||||
public async setExpandedState(element: ServerTreeElement, expandedState: TreeItemCollapsibleState): Promise<void> {
|
||||
if (expandedState === TreeItemCollapsibleState.Collapsed) {
|
||||
return this._tree.collapse(element);
|
||||
} else if (expandedState === TreeItemCollapsibleState.Expanded) {
|
||||
return this._tree.expand(element);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reveal the given element in the tree
|
||||
*/
|
||||
public reveal(element: TreeNode | ConnectionProfile): Promise<void> {
|
||||
public async reveal(element: ServerTreeElement): Promise<void> {
|
||||
return this._tree.reveal(element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the given element in the tree and clear any other selections
|
||||
*/
|
||||
public setSelected(element: TreeNode | ConnectionProfile, selected: boolean, clearOtherSelections: boolean): Promise<void> {
|
||||
public async setSelected(element: ServerTreeElement, selected: boolean, clearOtherSelections: boolean): Promise<void> {
|
||||
if (clearOtherSelections || (selected && clearOtherSelections !== false)) {
|
||||
this._tree.clearSelection();
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
this._tree.setSelection([]);
|
||||
} else {
|
||||
this._tree.clearSelection();
|
||||
}
|
||||
|
||||
}
|
||||
if (selected) {
|
||||
this._tree.select(element);
|
||||
return this._tree.reveal(element);
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
this._tree.setSelection(this._tree.getSelection().concat(element));
|
||||
this._tree.reveal(element);
|
||||
} else {
|
||||
this._tree.select(element);
|
||||
return this._tree.reveal(element);
|
||||
}
|
||||
} else {
|
||||
this._tree.deselect(element);
|
||||
return Promise.resolve();
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
this._tree.setSelection(this._tree.getSelection().filter(item => item !== element));
|
||||
} else {
|
||||
this._tree.deselect(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given element in the tree is expanded
|
||||
*/
|
||||
public isExpanded(element: TreeNode | ConnectionProfile): boolean {
|
||||
return this._tree.isExpanded(element);
|
||||
public isExpanded(element: ServerTreeElement): boolean {
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
return !this._tree.getNode(element).collapsed;
|
||||
} else {
|
||||
return this._tree.isExpanded(element);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Return actions in the context menu
|
||||
*/
|
||||
private onContextMenu(e: ITreeContextMenuEvent<ServerTreeElement>): boolean {
|
||||
e.browserEvent.preventDefault();
|
||||
e.browserEvent.stopPropagation();
|
||||
this._tree.setSelection([e.element]);
|
||||
|
||||
let actionContext: any;
|
||||
if (e.element instanceof TreeNode) {
|
||||
let context = new ObjectExplorerActionsContext();
|
||||
context.nodeInfo = e.element.toNodeInfo();
|
||||
// Note: getting DB name before, but intentionally not using treeUpdateUtils.getConnectionProfile as it replaces
|
||||
// the connection ID with a new one. This breaks a number of internal tasks
|
||||
context.connectionProfile = e.element.getConnectionProfile().toIConnectionProfile();
|
||||
context.connectionProfile.databaseName = e.element.getDatabaseName();
|
||||
actionContext = context;
|
||||
} else if (e.element instanceof ConnectionProfile) {
|
||||
let context = new ObjectExplorerActionsContext();
|
||||
context.connectionProfile = e.element.toIConnectionProfile();
|
||||
context.isConnectionNode = true;
|
||||
actionContext = context;
|
||||
} else {
|
||||
// TODO: because the connection group is used as a context object and isn't serializable,
|
||||
// the Group-level context menu is not currently extensible
|
||||
actionContext = e.element;
|
||||
}
|
||||
|
||||
this._contextMenuService.showContextMenu({
|
||||
getAnchor: () => e.anchor,
|
||||
getActions: () => this._actionProvider.getActions(this._tree, e.element),
|
||||
getKeyBinding: (action) => this._keybindingService.lookupKeybinding(action.id),
|
||||
onHide: (wasCancelled?: boolean) => {
|
||||
if (wasCancelled) {
|
||||
this._tree.domFocus();
|
||||
}
|
||||
},
|
||||
getActionsContext: () => (actionContext)
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { localize } from 'vs/nls';
|
||||
import { SERVER_GROUP_CONFIG, SERVER_GROUP_COLORS_CONFIG } from 'sql/workbench/services/serverGroup/common/interfaces';
|
||||
import { DefaultServerGroupColor } from 'sql/workbench/services/serverGroup/common/serverGroupViewModel';
|
||||
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(Extensions.Configuration);
|
||||
|
||||
@@ -29,7 +30,7 @@ const serverGroupConfig: IConfigurationNode = {
|
||||
'#98AFC7',
|
||||
'#4452A6',
|
||||
'#6A6599',
|
||||
'#515151'
|
||||
DefaultServerGroupColor
|
||||
]
|
||||
},
|
||||
[SERVER_GROUP_CONFIG + '.' + SERVER_GROUP_AUTOEXPAND_CONFIG]: {
|
||||
@@ -40,4 +41,18 @@ const serverGroupConfig: IConfigurationNode = {
|
||||
}
|
||||
};
|
||||
|
||||
const serverTreeConfig: IConfigurationNode = {
|
||||
'id': 'serverTree',
|
||||
'title': 'Server Tree',
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'serverTree.useAsyncServerTree': {
|
||||
'type': 'boolean',
|
||||
'default': true,
|
||||
'description': localize('serverTree.useAsyncServerTree', "(Preview) Use the new async server tree for the Servers view and Connection Dialog with support for new features such as dynamic node filtering.")
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
configurationRegistry.registerConfiguration(serverGroupConfig);
|
||||
configurationRegistry.registerConfiguration(serverTreeConfig);
|
||||
|
||||
@@ -20,8 +20,6 @@ import * as LocalizedConstants from 'sql/workbench/services/connection/browser/
|
||||
import { ObjectExplorerService, ObjectExplorerNodeEventArgs } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService';
|
||||
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
|
||||
import { NodeType } from 'sql/workbench/services/objectExplorer/common/nodeType';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
import { ServerTreeDataSource } from 'sql/workbench/services/objectExplorer/browser/serverTreeDataSource';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { ObjectExplorerActionsContext } from 'sql/workbench/services/objectExplorer/browser/objectExplorerActions';
|
||||
@@ -35,6 +33,12 @@ import { IViewsService, IView, ViewContainerLocation, ViewContainer, IViewPaneCo
|
||||
import { ConsoleLogService } from 'vs/platform/log/common/log';
|
||||
import { IProgressIndicator } from 'vs/platform/progress/common/progress';
|
||||
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
|
||||
import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { TestAccessibilityService, TestListService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { TestConfigurationService } from 'sql/platform/connection/test/common/testConfigurationService';
|
||||
import { ServerTreeDataSource } from 'sql/workbench/services/objectExplorer/browser/serverTreeDataSource';
|
||||
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||
import { AsyncServerTree } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
|
||||
suite('SQL Connection Tree Action tests', () => {
|
||||
let errorMessageService: TypeMoq.Mock<TestErrorMessageService>;
|
||||
@@ -152,8 +156,14 @@ suite('SQL Connection Tree Action tests', () => {
|
||||
}
|
||||
};
|
||||
|
||||
let manageConnectionAction: OEManageConnectionAction = new OEManageConnectionAction(OEManageConnectionAction.ID,
|
||||
OEManageConnectionAction.LABEL, connectionManagementService.object, capabilitiesService, instantiationService.object, objectExplorerService.object, viewsService);
|
||||
let manageConnectionAction: OEManageConnectionAction = new OEManageConnectionAction(
|
||||
OEManageConnectionAction.ID,
|
||||
OEManageConnectionAction.LABEL,
|
||||
connectionManagementService.object,
|
||||
capabilitiesService,
|
||||
instantiationService.object,
|
||||
objectExplorerService.object,
|
||||
viewsService);
|
||||
|
||||
let actionContext = new ObjectExplorerActionsContext();
|
||||
actionContext.connectionProfile = connection.toIConnectionProfile();
|
||||
@@ -190,8 +200,14 @@ suite('SQL Connection Tree Action tests', () => {
|
||||
return treeSelectionMock.object;
|
||||
});
|
||||
|
||||
let manageConnectionAction: OEManageConnectionAction = new OEManageConnectionAction(OEManageConnectionAction.ID,
|
||||
OEManageConnectionAction.LABEL, connectionManagementService.object, capabilitiesService, instantiationService.object, objectExplorerService.object, undefined);
|
||||
let manageConnectionAction: OEManageConnectionAction = new OEManageConnectionAction(
|
||||
OEManageConnectionAction.ID,
|
||||
OEManageConnectionAction.LABEL,
|
||||
connectionManagementService.object,
|
||||
capabilitiesService,
|
||||
instantiationService.object,
|
||||
objectExplorerService.object,
|
||||
undefined);
|
||||
|
||||
let actionContext = new ObjectExplorerActionsContext();
|
||||
actionContext.connectionProfile = connection.toIConnectionProfile();
|
||||
@@ -444,7 +460,7 @@ suite('SQL Connection Tree Action tests', () => {
|
||||
tree.setup(x => x.refresh(TypeMoq.It.isAny())).returns(() => Promise.resolve(null));
|
||||
tree.setup(x => x.expand(TypeMoq.It.isAny())).returns(() => Promise.resolve(null));
|
||||
tree.setup(x => x.collapse(TypeMoq.It.isAny())).returns(() => Promise.resolve(null));
|
||||
let connectionAction: RefreshAction = new RefreshAction(RefreshAction.ID,
|
||||
let refreshAction: RefreshAction = new RefreshAction(RefreshAction.ID,
|
||||
RefreshAction.LABEL,
|
||||
tree.object,
|
||||
connection,
|
||||
@@ -453,7 +469,7 @@ suite('SQL Connection Tree Action tests', () => {
|
||||
undefined,
|
||||
logService);
|
||||
|
||||
return connectionAction.run().then((value) => {
|
||||
return refreshAction.run().then((value) => {
|
||||
connectionManagementService.verify(x => x.isConnected(undefined, TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
||||
objectExplorerService.verify(x => x.getObjectExplorerNode(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
||||
objectExplorerService.verify(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
||||
@@ -547,6 +563,200 @@ suite('SQL Connection Tree Action tests', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// chgagnon TODO - skipping for now since mocking and instanceof don't work well together. Will re-enable once old tree is removed
|
||||
test.skip('RefreshConnectionAction - AsyncServerTree - refresh should be called if connection status is connect', () => {
|
||||
let isConnectedReturnValue: boolean = true;
|
||||
let sqlProvider = {
|
||||
providerId: mssqlProviderName,
|
||||
displayName: 'MSSQL',
|
||||
connectionOptions: [],
|
||||
};
|
||||
|
||||
capabilitiesService.capabilities[mssqlProviderName] = { connection: sqlProvider };
|
||||
|
||||
let connection = new ConnectionProfile(capabilitiesService, {
|
||||
connectionName: 'Test',
|
||||
savePassword: false,
|
||||
groupFullName: 'testGroup',
|
||||
serverName: 'testServerName',
|
||||
databaseName: 'testDatabaseName',
|
||||
authenticationType: 'inetgrated',
|
||||
password: 'test',
|
||||
userName: 'testUsername',
|
||||
groupId: undefined,
|
||||
providerName: mssqlProviderName,
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: 'testID'
|
||||
});
|
||||
let conProfGroup = new ConnectionProfileGroup('testGroup', undefined, 'testGroup', undefined, undefined);
|
||||
conProfGroup.connections = [connection];
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
|
||||
connectionManagementService.callBase = true;
|
||||
connectionManagementService.setup(x => x.getConnectionGroups()).returns(() => [conProfGroup]);
|
||||
connectionManagementService.setup(x => x.getActiveConnections()).returns(() => [connection]);
|
||||
connectionManagementService.setup(x => x.addSavedPassword(TypeMoq.It.isAny())).returns(() => new Promise<ConnectionProfile>((resolve) => {
|
||||
resolve(connection);
|
||||
}));
|
||||
connectionManagementService.setup(x => x.isConnected(undefined, TypeMoq.It.isAny())).returns(() => isConnectedReturnValue);
|
||||
|
||||
let objectExplorerSession = {
|
||||
success: true,
|
||||
sessionId: '1234',
|
||||
rootNode: {
|
||||
nodePath: 'testServerName\tables',
|
||||
nodeType: NodeType.Folder,
|
||||
label: 'Tables',
|
||||
isLeaf: false,
|
||||
metadata: null,
|
||||
nodeSubType: '',
|
||||
nodeStatus: '',
|
||||
errorMessage: ''
|
||||
},
|
||||
errorMessage: ''
|
||||
};
|
||||
|
||||
let tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\Db1\tables', '', '', null, null, undefined, undefined);
|
||||
tablesNode.connection = connection;
|
||||
tablesNode.session = objectExplorerSession;
|
||||
let table1Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined);
|
||||
let table2Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined);
|
||||
tablesNode.children = [table1Node, table2Node];
|
||||
let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Loose, connectionManagementService.object);
|
||||
objectExplorerService.callBase = true;
|
||||
objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => tablesNode);
|
||||
objectExplorerService.setup(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve([table1Node, table2Node]));
|
||||
const treeMock = TypeMoq.Mock.ofType(AsyncServerTree, TypeMoq.MockBehavior.Strict,
|
||||
'ConnectionTreeActionsTest', // user
|
||||
$('div'), // container
|
||||
{}, // delegate
|
||||
[], // renderers
|
||||
{}, // data source
|
||||
{}, // options
|
||||
new MockContextKeyService(), // IContextKeyService
|
||||
new TestListService(), // IListService,
|
||||
undefined, // IThemeService,
|
||||
new TestConfigurationService(), // IConfigurationService,
|
||||
undefined, // IKeybindingService,
|
||||
new TestAccessibilityService()); // IAccessibilityService
|
||||
treeMock.callBase = true;
|
||||
treeMock.setup(x => x.expand(TypeMoq.It.isAny())).returns(() => Promise.resolve(null));
|
||||
treeMock.setup(x => x.collapse(TypeMoq.It.isAny())).returns(() => true);
|
||||
treeMock.setup(x => x.updateChildren(TypeMoq.It.isAny())).returns(() => Promise.resolve());
|
||||
let refreshAction: RefreshAction = new RefreshAction(RefreshAction.ID,
|
||||
RefreshAction.LABEL,
|
||||
treeMock.instance,
|
||||
connection,
|
||||
connectionManagementService.object,
|
||||
objectExplorerService.object,
|
||||
undefined,
|
||||
logService);
|
||||
|
||||
return refreshAction.run().then((value) => {
|
||||
connectionManagementService.verify(x => x.isConnected(undefined, TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
||||
objectExplorerService.verify(x => x.getObjectExplorerNode(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
||||
objectExplorerService.verify(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
||||
treeMock.verify(x => x.updateChildren(TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
||||
});
|
||||
});
|
||||
|
||||
test('RefreshConnectionAction - AsyncServerTree - refresh should not be called if connection status is not connect', () => {
|
||||
let isConnectedReturnValue: boolean = false;
|
||||
let sqlProvider = {
|
||||
providerId: mssqlProviderName,
|
||||
displayName: 'MSSQL',
|
||||
connectionOptions: []
|
||||
};
|
||||
|
||||
capabilitiesService.capabilities[mssqlProviderName] = { connection: sqlProvider };
|
||||
|
||||
let connection = new ConnectionProfile(capabilitiesService, {
|
||||
connectionName: 'Test',
|
||||
savePassword: false,
|
||||
groupFullName: 'testGroup',
|
||||
serverName: 'testServerName',
|
||||
databaseName: 'testDatabaseName',
|
||||
authenticationType: 'inetgrated',
|
||||
password: 'test',
|
||||
userName: 'testUsername',
|
||||
groupId: undefined,
|
||||
providerName: mssqlProviderName,
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: 'testID'
|
||||
});
|
||||
let conProfGroup = new ConnectionProfileGroup('testGroup', undefined, 'testGroup', undefined, undefined);
|
||||
conProfGroup.connections = [connection];
|
||||
let connectionManagementService = TypeMoq.Mock.ofType(TestConnectionManagementService, TypeMoq.MockBehavior.Strict);
|
||||
connectionManagementService.callBase = true;
|
||||
connectionManagementService.setup(x => x.getConnectionGroups()).returns(() => [conProfGroup]);
|
||||
connectionManagementService.setup(x => x.getActiveConnections()).returns(() => [connection]);
|
||||
connectionManagementService.setup(x => x.addSavedPassword(TypeMoq.It.isAny())).returns(() => new Promise<ConnectionProfile>((resolve) => {
|
||||
resolve(connection);
|
||||
}));
|
||||
connectionManagementService.setup(x => x.isConnected(undefined, TypeMoq.It.isAny())).returns(() => isConnectedReturnValue);
|
||||
|
||||
let objectExplorerSession = {
|
||||
success: true,
|
||||
sessionId: '1234',
|
||||
rootNode: {
|
||||
nodePath: 'testServerName\tables',
|
||||
nodeType: NodeType.Folder,
|
||||
label: 'Tables',
|
||||
isLeaf: false,
|
||||
metadata: null,
|
||||
nodeSubType: '',
|
||||
nodeStatus: '',
|
||||
errorMessage: ''
|
||||
},
|
||||
errorMessage: ''
|
||||
};
|
||||
|
||||
let tablesNode = new TreeNode(NodeType.Folder, 'Tables', false, 'testServerName\Db1\tables', '', '', null, null, undefined, undefined);
|
||||
tablesNode.connection = connection;
|
||||
tablesNode.session = objectExplorerSession;
|
||||
let table1Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined);
|
||||
let table2Node = new TreeNode(NodeType.Table, 'dbo.Table1', false, 'testServerName\tables\dbo.Table1', '', '', tablesNode, null, undefined, undefined);
|
||||
tablesNode.children = [table1Node, table2Node];
|
||||
let objectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService, TypeMoq.MockBehavior.Loose, connectionManagementService.object);
|
||||
objectExplorerService.callBase = true;
|
||||
objectExplorerService.setup(x => x.getObjectExplorerNode(TypeMoq.It.isAny())).returns(() => tablesNode);
|
||||
objectExplorerService.setup(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => Promise.resolve([table1Node, table2Node]));
|
||||
let tree = TypeMoq.Mock.ofType<AsyncServerTree>(AsyncServerTree, TypeMoq.MockBehavior.Loose,
|
||||
'ConnectionTreeActionsTest', // user
|
||||
$('div'), // container
|
||||
{}, // delegate
|
||||
[], // renderers
|
||||
{}, // data source
|
||||
{}, // options
|
||||
new MockContextKeyService(), // IContextKeyService
|
||||
new TestListService(), // IListService,
|
||||
undefined, // IThemeService,
|
||||
new TestConfigurationService(), // IConfigurationService,
|
||||
undefined, // IKeybindingService,
|
||||
new TestAccessibilityService()); // IAccessibilityService
|
||||
tree.callBase = true;
|
||||
|
||||
tree.setup(x => x.updateChildren(TypeMoq.It.isAny())).returns(() => Promise.resolve());
|
||||
tree.setup(x => x.expand(TypeMoq.It.isAny())).returns(() => Promise.resolve(null));
|
||||
let connectionAction: RefreshAction = new RefreshAction(RefreshAction.ID,
|
||||
RefreshAction.LABEL,
|
||||
tree.object,
|
||||
connection,
|
||||
connectionManagementService.object,
|
||||
objectExplorerService.object,
|
||||
undefined,
|
||||
logService);
|
||||
|
||||
return connectionAction.run().then((value) => {
|
||||
connectionManagementService.verify(x => x.isConnected(undefined, TypeMoq.It.isAny()), TypeMoq.Times.atLeastOnce());
|
||||
objectExplorerService.verify(x => x.getObjectExplorerNode(TypeMoq.It.isAny()), TypeMoq.Times.exactly(0));
|
||||
objectExplorerService.verify(x => x.refreshTreeNode(TypeMoq.It.isAny(), TypeMoq.It.isAny()), TypeMoq.Times.exactly(0));
|
||||
tree.verify(x => x.updateChildren(TypeMoq.It.isAny()), TypeMoq.Times.exactly(0));
|
||||
tree.verify(x => x.expand(TypeMoq.It.isAny()), TypeMoq.Times.exactly(0));
|
||||
});
|
||||
});
|
||||
|
||||
test('EditConnectionAction - test if show connection dialog is called', () => {
|
||||
let connectionManagementService = createConnectionManagementService(true, undefined);
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ suite('ServerTreeView onAddConnectionProfile handler tests', () => {
|
||||
);
|
||||
mockConnectionManagementService.setup(x => x.getConnectionGroups()).returns(x => []);
|
||||
mockConnectionManagementService.setup(x => x.hasRegisteredServers()).returns(() => true);
|
||||
serverTreeView = new ServerTreeView(mockConnectionManagementService.object, instantiationService, undefined, new TestThemeService(), undefined, undefined, capabilitiesService);
|
||||
serverTreeView = new ServerTreeView(mockConnectionManagementService.object, instantiationService, undefined, new TestThemeService(), undefined, undefined, capabilitiesService, undefined, undefined);
|
||||
mockTree = TypeMoq.Mock.ofType(TestTree);
|
||||
(serverTreeView as any)._tree = mockTree.object;
|
||||
mockRefreshTreeMethod = TypeMoq.Mock.ofType(Function);
|
||||
@@ -64,15 +64,6 @@ suite('ServerTreeView onAddConnectionProfile handler tests', () => {
|
||||
mockTree.verify(x => x.layout(TypeMoq.It.isAnyNumber()), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
test('setVisibility', async () => {
|
||||
mockTree.setup(x => x.onVisible());
|
||||
mockTree.setup(x => x.onHidden());
|
||||
serverTreeView.setVisible(true);
|
||||
mockTree.verify(x => x.onVisible(), TypeMoq.Times.once());
|
||||
serverTreeView.setVisible(false);
|
||||
mockTree.verify(x => x.onHidden(), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
test('getSelection', async () => {
|
||||
mockTree.setup(x => x.getSelection());
|
||||
|
||||
@@ -81,9 +72,9 @@ suite('ServerTreeView onAddConnectionProfile handler tests', () => {
|
||||
});
|
||||
|
||||
test('isFocused', async () => {
|
||||
mockTree.setup(x => x.isDOMFocused());
|
||||
mockTree.setup(x => x.getHTMLElement());
|
||||
serverTreeView.isFocused();
|
||||
mockTree.verify(x => x.isDOMFocused(), TypeMoq.Times.once());
|
||||
mockTree.verify(x => x.getHTMLElement(), TypeMoq.Times.once());
|
||||
});
|
||||
|
||||
test('reveal', async () => {
|
||||
|
||||
@@ -27,6 +27,7 @@ import { getErrorMessage } from 'vs/base/common/errors';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import { AsyncServerTree } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
|
||||
//#region -- Data Explorer
|
||||
export const SCRIPT_AS_CREATE_COMMAND_ID = 'dataExplorer.scriptAsCreate';
|
||||
@@ -322,7 +323,11 @@ export async function handleOeRefreshCommand(accessor: ServicesAccessor, args: O
|
||||
const tree = objectExplorerService.getServerTreeView().tree;
|
||||
try {
|
||||
await objectExplorerService.refreshTreeNode(treeNode.getSession(), treeNode);
|
||||
await tree.refresh(treeNode);
|
||||
if (tree instanceof AsyncServerTree) {
|
||||
await tree.updateChildren(treeNode);
|
||||
} else {
|
||||
await tree.refresh(treeNode);
|
||||
}
|
||||
} catch (err) {
|
||||
// Display message to the user but also log the entire error to the console for the stack trace
|
||||
notificationService.error(localize('refreshError', "An error occurred refreshing node '{0}': {1}", args.nodeInfo.label, getErrorMessage(err)));
|
||||
|
||||
@@ -10,6 +10,7 @@ import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
|
||||
import { Modal } from 'sql/workbench/browser/modal/modal';
|
||||
import { IConnectionManagementService, INewConnectionParams } from 'sql/platform/connection/common/connectionManagement';
|
||||
import * as DialogHelper from 'sql/workbench/browser/modal/dialogHelper';
|
||||
import { TreeCreationUtils } from 'sql/workbench/services/objectExplorer/browser/treeCreationUtils';
|
||||
import { TabbedPanel, PanelTabIdentifier } from 'sql/base/browser/ui/panel/panel';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
@@ -39,15 +40,16 @@ import { ViewPane, IPaneColors } from 'vs/workbench/browser/parts/views/viewPane
|
||||
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { ITreeView } from 'sql/workbench/common/views';
|
||||
import { IConnectionProfile } from 'azdata';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { TreeUpdateUtils, IExpandableTree } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils';
|
||||
import { SavedConnectionTreeController } from 'sql/workbench/services/connection/browser/savedConnectionTreeController';
|
||||
import { TreeCreationUtils } from 'sql/workbench/services/objectExplorer/browser/treeCreationUtils';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||
import { RecentConnectionTreeController, RecentConnectionActionsProvider } from 'sql/workbench/services/connection/browser/recentConnectionTreeController';
|
||||
import { RecentConnectionActionsProvider, RecentConnectionTreeController } from 'sql/workbench/services/connection/browser/recentConnectionTreeController';
|
||||
import { ClearRecentConnectionsAction } from 'sql/workbench/services/connection/browser/connectionActions';
|
||||
import { combinedDisposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export interface OnShowUIResponse {
|
||||
selectedProviderDisplayName: string;
|
||||
@@ -65,8 +67,8 @@ export class ConnectionDialogWidget extends Modal implements IViewPaneContainer
|
||||
private _closeButton: Button;
|
||||
private _providerTypeSelectBox: SelectBox;
|
||||
private _newConnectionParams: INewConnectionParams;
|
||||
private _recentConnectionTree: ITree;
|
||||
private _savedConnectionTree: ITree;
|
||||
private _recentConnectionTree: AsyncServerTree | ITree;
|
||||
private _savedConnectionTree: AsyncServerTree | ITree;
|
||||
private _connectionUIContainer: HTMLElement;
|
||||
private _databaseDropdownExpanded: boolean;
|
||||
private _actionbar: ActionBar;
|
||||
@@ -115,7 +117,8 @@ export class ConnectionDialogWidget extends Modal implements IViewPaneContainer
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IClipboardService clipboardService: IClipboardService,
|
||||
@ILogService logService: ILogService,
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService
|
||||
@ITextResourcePropertiesService textResourcePropertiesService: ITextResourcePropertiesService,
|
||||
@IConfigurationService private _configurationService: IConfigurationService
|
||||
) {
|
||||
super(
|
||||
localize('connection', "Connection"),
|
||||
@@ -222,21 +225,37 @@ export class ConnectionDialogWidget extends Modal implements IViewPaneContainer
|
||||
});
|
||||
|
||||
this._panel.onTabChange(async c => {
|
||||
// convert to old VS Code tree interface with expandable methods
|
||||
const expandableTree: IExpandableTree = <IExpandableTree>this._savedConnectionTree;
|
||||
if (this._savedConnectionTree instanceof AsyncServerTree) {
|
||||
if (c === savedConnectionTabId && this._savedConnectionTree.contentHeight === 0) {
|
||||
// Update saved connection tree
|
||||
await TreeUpdateUtils.structuralTreeUpdate(this._savedConnectionTree, 'saved', this.connectionManagementService, this._providers);
|
||||
|
||||
if (c === savedConnectionTabId && expandableTree.getContentHeight() === 0) {
|
||||
// Update saved connection tree
|
||||
await TreeUpdateUtils.structuralTreeUpdate(this._savedConnectionTree, 'saved', this.connectionManagementService, this._providers);
|
||||
|
||||
if (expandableTree.getContentHeight() > 0) {
|
||||
DOM.hide(this._noSavedConnection);
|
||||
DOM.show(this._savedConnection);
|
||||
} else {
|
||||
DOM.show(this._noSavedConnection);
|
||||
DOM.hide(this._savedConnection);
|
||||
if (this._savedConnectionTree.contentHeight > 0) {
|
||||
DOM.hide(this._noSavedConnection);
|
||||
DOM.show(this._savedConnection);
|
||||
} else {
|
||||
DOM.show(this._noSavedConnection);
|
||||
DOM.hide(this._savedConnection);
|
||||
}
|
||||
this._savedConnectionTree.layout(DOM.getTotalHeight(this._savedConnectionTree.getHTMLElement()));
|
||||
}
|
||||
} else {
|
||||
// convert to old VS Code tree interface with expandable methods
|
||||
const expandableTree: IExpandableTree = <IExpandableTree>this._savedConnectionTree;
|
||||
|
||||
if (c === savedConnectionTabId && expandableTree.getContentHeight() === 0) {
|
||||
// Update saved connection tree
|
||||
await TreeUpdateUtils.structuralTreeUpdate(this._savedConnectionTree, 'saved', this.connectionManagementService, this._providers);
|
||||
|
||||
if (expandableTree.getContentHeight() > 0) {
|
||||
DOM.hide(this._noSavedConnection);
|
||||
DOM.show(this._savedConnection);
|
||||
} else {
|
||||
DOM.show(this._noSavedConnection);
|
||||
DOM.hide(this._savedConnection);
|
||||
}
|
||||
this._savedConnectionTree.layout(DOM.getTotalHeight(this._savedConnectionTree.getHTMLElement()));
|
||||
}
|
||||
this._savedConnectionTree.layout(DOM.getTotalHeight(this._savedConnectionTree.getHTMLElement()));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -362,9 +381,7 @@ export class ConnectionDialogWidget extends Modal implements IViewPaneContainer
|
||||
const leftClick = (element: any, eventish: ICancelableEvent, origin: string) => {
|
||||
// element will be a server group if the tree is clicked rather than a item
|
||||
const isDoubleClick = origin === 'mouse' && (eventish as MouseEvent).detail === 2;
|
||||
if (element instanceof ConnectionProfile) {
|
||||
this.onConnectionClick(element, isDoubleClick);
|
||||
}
|
||||
this.onConnectionClick(element, isDoubleClick);
|
||||
};
|
||||
const actionProvider = this.instantiationService.createInstance(RecentConnectionActionsProvider);
|
||||
const controller = new RecentConnectionTreeController(leftClick, actionProvider, this.connectionManagementService, this.contextMenuService);
|
||||
@@ -380,7 +397,11 @@ export class ConnectionDialogWidget extends Modal implements IViewPaneContainer
|
||||
// We're just using the connections to determine if there are connections to show, dispose them right after to clean up their handlers
|
||||
recentConnections.forEach(conn => conn.dispose());
|
||||
});
|
||||
this._recentConnectionTree = TreeCreationUtils.createConnectionTree(treeContainer, this.instantiationService, controller);
|
||||
this._recentConnectionTree = TreeCreationUtils.createConnectionTree(treeContainer, this.instantiationService, this._configurationService, localize('connectionDialog.recentConnections', "Recent Connections"), controller);
|
||||
if (this._recentConnectionTree instanceof AsyncServerTree) {
|
||||
this._recentConnectionTree.onMouseClick(e => this.onConnectionClick(e.element, false));
|
||||
this._recentConnectionTree.onMouseDblClick(e => this.onConnectionClick(e.element, true));
|
||||
}
|
||||
|
||||
// Theme styler
|
||||
this._register(styler.attachListStyler(this._recentConnectionTree, this._themeService));
|
||||
@@ -406,7 +427,11 @@ export class ConnectionDialogWidget extends Modal implements IViewPaneContainer
|
||||
};
|
||||
|
||||
const controller = new SavedConnectionTreeController(leftClick);
|
||||
this._savedConnectionTree = TreeCreationUtils.createConnectionTree(treeContainer, this.instantiationService, controller);
|
||||
this._savedConnectionTree = TreeCreationUtils.createConnectionTree(treeContainer, this.instantiationService, this._configurationService, localize('connectionDialog.savedConnections', "Saved Connections"), controller);
|
||||
if (this._savedConnectionTree instanceof AsyncServerTree) {
|
||||
this._savedConnectionTree.onMouseClick(e => this.onConnectionClick(e.element, false));
|
||||
this._savedConnectionTree.onMouseDblClick(e => this.onConnectionClick(e.element, true));
|
||||
}
|
||||
|
||||
// Theme styler
|
||||
this._register(styler.attachListStyler(this._savedConnectionTree, this._themeService));
|
||||
@@ -419,7 +444,10 @@ export class ConnectionDialogWidget extends Modal implements IViewPaneContainer
|
||||
DOM.append(noSavedConnectionContainer, DOM.$('.no-saved-connections')).innerText = noSavedConnectionLabel;
|
||||
}
|
||||
|
||||
private onConnectionClick(element: IConnectionProfile, connect: boolean = false) {
|
||||
private onConnectionClick(element: ServerTreeElement, connect: boolean = false): void {
|
||||
if (!(element instanceof ConnectionProfile)) {
|
||||
return;
|
||||
}
|
||||
if (connect) {
|
||||
this.connect(element);
|
||||
} else {
|
||||
@@ -443,9 +471,12 @@ export class ConnectionDialogWidget extends Modal implements IViewPaneContainer
|
||||
DOM.show(this._noRecentConnection);
|
||||
}
|
||||
await TreeUpdateUtils.structuralTreeUpdate(this._recentConnectionTree, 'recent', this.connectionManagementService, this._providers);
|
||||
this._recentConnectionTree.layout(DOM.getTotalHeight(this._recentConnectionTree.getHTMLElement()));
|
||||
|
||||
// // reset saved connection tree
|
||||
await this._savedConnectionTree.setInput([]);
|
||||
if (!(this._savedConnectionTree instanceof AsyncServerTree)) {
|
||||
// reset saved connection tree
|
||||
await this._savedConnectionTree.setInput([]);
|
||||
}
|
||||
|
||||
// call layout with view height
|
||||
this.initDialog();
|
||||
@@ -616,7 +647,7 @@ export class ConnectionDialogWidget extends Modal implements IViewPaneContainer
|
||||
const disposable = combinedDisposable(pane, paneStyler);
|
||||
const paneItem = { pane, disposable };
|
||||
treeView.onDidChangeSelection(e => {
|
||||
if (e.length > 0 && e[0].payload) {
|
||||
if (e.length > 0 && e[0].payload instanceof ConnectionProfile) {
|
||||
this.onConnectionClick(e[0].payload);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -995,6 +995,7 @@ export class ConnectionManagementService extends Disposable implements IConnecti
|
||||
let id = Utils.generateUri(source);
|
||||
this._telemetryService.sendActionEvent(TelemetryKeys.TelemetryView.Shell, TelemetryKeys.MoveServerGroup);
|
||||
return this._connectionStore.changeGroupIdForConnection(source, targetGroupId).then(result => {
|
||||
this._onAddConnectionProfile.fire(source);
|
||||
if (id && targetGroupId) {
|
||||
source.groupId = targetGroupId;
|
||||
}
|
||||
|
||||
@@ -162,3 +162,8 @@
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.connection-dialog .explorer-servers .connection-profile .monaco-tl-twistie {
|
||||
/* Hide twisties */
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
|
||||
import { ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
|
||||
/**
|
||||
* Implements the DataSource(that returns a parent/children of an element) for the recent connection tree
|
||||
*/
|
||||
export class AsyncRecentConnectionTreeDataSource implements IAsyncDataSource<ConnectionProfileGroup, ServerTreeElement> {
|
||||
|
||||
/**
|
||||
* Returns a boolean value indicating whether the element has children.
|
||||
*/
|
||||
public hasChildren(element: ServerTreeElement): boolean {
|
||||
if (element instanceof ConnectionProfileGroup) {
|
||||
return element.hasChildren();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element's children as an array in a promise.
|
||||
*/
|
||||
public async getChildren(element: ServerTreeElement): Promise<Iterable<ServerTreeElement>> {
|
||||
if (element instanceof ConnectionProfileGroup) {
|
||||
return element.getChildren();
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import { WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService';
|
||||
import { FuzzyScore } from 'vs/base/common/filters';
|
||||
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
|
||||
export class AsyncServerTree extends WorkbenchAsyncDataTree<ConnectionProfileGroup, ServerTreeElement, FuzzyScore> { }
|
||||
|
||||
export type ServerTreeElement = ConnectionProfile | ConnectionProfileGroup | TreeNode;
|
||||
@@ -0,0 +1,76 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
|
||||
import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService';
|
||||
import { TreeUpdateUtils } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMessageService';
|
||||
import { IAsyncDataSource } from 'vs/base/browser/ui/tree/tree';
|
||||
import { ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
|
||||
/**
|
||||
* Implements the DataSource(that returns a parent/children of an element) for the server tree
|
||||
*/
|
||||
export class AsyncServerTreeDataSource implements IAsyncDataSource<ConnectionProfileGroup, ServerTreeElement> {
|
||||
|
||||
constructor(
|
||||
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService
|
||||
) {
|
||||
}
|
||||
/**
|
||||
* Returns a boolean value indicating whether the element has children.
|
||||
*/
|
||||
public hasChildren(element: ServerTreeElement): boolean {
|
||||
if (element instanceof ConnectionProfile) {
|
||||
return true;
|
||||
} else if (element instanceof ConnectionProfileGroup) {
|
||||
return element.hasChildren();
|
||||
} else if (element instanceof TreeNode) {
|
||||
return !element.isAlwaysLeaf;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element's children as an array in a promise.
|
||||
*/
|
||||
public async getChildren(element: ServerTreeElement): Promise<ServerTreeElement[]> {
|
||||
try {
|
||||
if (element instanceof ConnectionProfile) {
|
||||
return await TreeUpdateUtils.getAsyncConnectionNodeChildren(element, this._connectionManagementService, this._objectExplorerService);
|
||||
} else if (element instanceof ConnectionProfileGroup) {
|
||||
return (element as ConnectionProfileGroup).getChildren();
|
||||
} else if (element instanceof TreeNode) {
|
||||
if (element.children) {
|
||||
return element.children;
|
||||
} else {
|
||||
return await this._objectExplorerService.resolveTreeNodeChildren(element.getSession(), element);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (element instanceof TreeNode) {
|
||||
element.errorStateMessage = err.message ?? err;
|
||||
}
|
||||
if (err.message) {
|
||||
this.showError(err.message);
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private showError(errorMessage: string) {
|
||||
if (this._errorMessageService) {
|
||||
this._errorMessageService.showDialog(Severity.Error, '', errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
|
||||
import { ServerTreeRenderer } from 'sql/workbench/services/objectExplorer/browser/serverTreeRenderer';
|
||||
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
|
||||
export class AsyncServerTreeDelegate implements IListVirtualDelegate<ServerTreeElement> {
|
||||
|
||||
getHeight(element: ServerTreeElement): number {
|
||||
if (element instanceof ConnectionProfileGroup) {
|
||||
return ServerTreeRenderer.CONNECTION_GROUP_HEIGHT;
|
||||
} else if (element instanceof ConnectionProfile) {
|
||||
return ServerTreeRenderer.CONNECTION_HEIGHT;
|
||||
} else {
|
||||
return ServerTreeRenderer.OBJECTEXPLORER_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
getTemplateId(element: ServerTreeElement): string {
|
||||
if (element instanceof ConnectionProfileGroup) {
|
||||
return ServerTreeRenderer.CONNECTION_GROUP_TEMPLATE_ID;
|
||||
} else if (element instanceof ConnectionProfile) {
|
||||
return ServerTreeRenderer.CONNECTION_TEMPLATE_ID;
|
||||
} else {
|
||||
return ServerTreeRenderer.OBJECTEXPLORER_TEMPLATE_ID;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { TreeUpdateUtils } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils';
|
||||
import { IDragAndDropData } from 'vs/base/browser/dnd';
|
||||
import { ITreeDragAndDrop, ITreeDragOverReaction, TreeDragOverReactions } from 'vs/base/browser/ui/tree/tree';
|
||||
import { ServerTreeDragAndDrop } from 'sql/workbench/services/objectExplorer/browser/dragAndDropController';
|
||||
import { IDragAndDrop } from 'vs/base/parts/tree/browser/tree';
|
||||
import { ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
|
||||
/**
|
||||
* Implements drag and drop for the server tree
|
||||
*/
|
||||
export class AsyncServerTreeDragAndDrop implements ITreeDragAndDrop<ServerTreeElement> {
|
||||
|
||||
private _dragAndDrop: IDragAndDrop;
|
||||
|
||||
constructor(
|
||||
@IConnectionManagementService connectionManagementService: IConnectionManagementService,
|
||||
) {
|
||||
this._dragAndDrop = new ServerTreeDragAndDrop(connectionManagementService);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a uri if the given element should be allowed to drag.
|
||||
* Returns null, otherwise.
|
||||
*/
|
||||
public getDragURI(element: ServerTreeElement): string {
|
||||
return this._dragAndDrop.getDragURI(undefined, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a label(name) to display when dragging the element.
|
||||
*/
|
||||
public getDragLabel(elements: ServerTreeElement[]): string {
|
||||
return this._dragAndDrop.getDragLabel(undefined, elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the drag operation starts.
|
||||
*/
|
||||
public onDragStart(dragAndDropData: IDragAndDropData, originalEvent: DragEvent): void {
|
||||
// Force the event cast while in preview - we don't use any of the mouse properties on the
|
||||
// implementation so this is fine for now
|
||||
return this._dragAndDrop.onDragStart(undefined, dragAndDropData, <any>originalEvent);
|
||||
}
|
||||
|
||||
public onDragOver(data: IDragAndDropData, targetElement: ServerTreeElement, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction {
|
||||
// Force the event cast while in preview - we don't use any of the mouse properties on the
|
||||
// implementation so this is fine for now
|
||||
const canDragOver = this._dragAndDrop.onDragOver(undefined, data, targetElement, <any>originalEvent);
|
||||
|
||||
if (canDragOver.accept) {
|
||||
return TreeDragOverReactions.acceptBubbleDown(true);
|
||||
} else {
|
||||
return { accept: false };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a drop in the server tree.
|
||||
*/
|
||||
public drop(data: IDragAndDropData, targetElement: ServerTreeElement, targetIndex: number, originalEvent: DragEvent): void {
|
||||
// Force the event cast while in preview - we don't use any of the mouse properties on the
|
||||
// implementation so this is fine for now
|
||||
|
||||
// TODO: chgagnon Drop on root node
|
||||
this._dragAndDrop.drop(undefined, data, targetElement, <any>originalEvent);
|
||||
}
|
||||
|
||||
public onDragEnd(originalEvent: DragEvent): void {
|
||||
TreeUpdateUtils.isInDragAndDrop = false;
|
||||
}
|
||||
}
|
||||
|
||||
export class AsyncRecentConnectionsDragAndDrop implements ITreeDragAndDrop<ServerTreeElement> {
|
||||
|
||||
/**
|
||||
* Returns a uri if the given element should be allowed to drag.
|
||||
* Returns null, otherwise.
|
||||
*/
|
||||
public getDragURI(element: ServerTreeElement): string | null {
|
||||
if (element instanceof ConnectionProfile) {
|
||||
return (<ConnectionProfile>element).id;
|
||||
}
|
||||
else if (element instanceof ConnectionProfileGroup) {
|
||||
return (<ConnectionProfileGroup>element).id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a label(name) to display when dragging the element.
|
||||
*/
|
||||
public getDragLabel(elements: ServerTreeElement[]): string {
|
||||
if (elements[0] instanceof ConnectionProfile) {
|
||||
return elements[0].serverName;
|
||||
}
|
||||
else if (elements[0] instanceof ConnectionProfileGroup) {
|
||||
return elements[0].name;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a DragOverReaction indicating whether sources can be
|
||||
* dropped into target or some parent of the target.
|
||||
*/
|
||||
public onDragOver(data: IDragAndDropData, targetElement: ServerTreeElement, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction {
|
||||
return { accept: false };
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle drop in the server tree.
|
||||
*/
|
||||
public drop(data: IDragAndDropData, targetElement: ServerTreeElement, targetIndex: number, originalEvent: DragEvent): void {
|
||||
// No op
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IIdentityProvider } from 'vs/base/browser/ui/list/list';
|
||||
import { ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
|
||||
export class AsyncServerTreeIdentityProvider implements IIdentityProvider<ServerTreeElement> {
|
||||
getId(element: ServerTreeElement): { toString(): string; } {
|
||||
return element.id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/objectTypes/objecttypes';
|
||||
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
|
||||
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
|
||||
import { iconRenderer } from 'sql/workbench/services/objectExplorer/browser/iconRenderer';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ITreeRenderer, ITreeNode } from 'vs/base/browser/ui/tree/tree';
|
||||
import { FuzzyScore } from 'vs/base/common/filters';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list';
|
||||
import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget';
|
||||
import { ServerTreeRenderer } from 'sql/workbench/services/objectExplorer/browser/serverTreeRenderer';
|
||||
import { ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
import { DefaultServerGroupColor } from 'sql/workbench/services/serverGroup/common/serverGroupViewModel';
|
||||
|
||||
class ConnectionProfileGroupTemplate extends Disposable {
|
||||
private _root: HTMLElement;
|
||||
private _nameContainer: HTMLElement;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement
|
||||
) {
|
||||
super();
|
||||
container.parentElement.classList.add('server-group');
|
||||
container.classList.add('server-group');
|
||||
this._root = dom.append(container, dom.$('.server-group'));
|
||||
this._nameContainer = dom.append(this._root, dom.$('span.name'));
|
||||
}
|
||||
|
||||
set(element: ConnectionProfileGroup) {
|
||||
let rowElement = findParentElement(this._root, 'monaco-list-row');
|
||||
if (rowElement) {
|
||||
if (element.color) {
|
||||
rowElement.style.background = element.color;
|
||||
} else {
|
||||
// If the group doesn't contain specific color, assign the default color
|
||||
rowElement.style.background = DefaultServerGroupColor;
|
||||
}
|
||||
}
|
||||
if (element.description && (element.description !== '')) {
|
||||
this._root.title = element.description;
|
||||
}
|
||||
this._nameContainer.hidden = false;
|
||||
this._nameContainer.textContent = element.name;
|
||||
}
|
||||
}
|
||||
|
||||
export class ConnectionProfileGroupRenderer implements ITreeRenderer<ConnectionProfileGroup, FuzzyScore, ConnectionProfileGroupTemplate> {
|
||||
|
||||
readonly templateId: string = ServerTreeRenderer.CONNECTION_GROUP_TEMPLATE_ID;
|
||||
|
||||
constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService) { }
|
||||
|
||||
renderTemplate(container: HTMLElement): ConnectionProfileGroupTemplate {
|
||||
return this._instantiationService.createInstance(ConnectionProfileGroupTemplate, container);
|
||||
}
|
||||
renderElement(node: ITreeNode<ConnectionProfileGroup, FuzzyScore>, index: number, template: ConnectionProfileGroupTemplate): void {
|
||||
template.set(node.element);
|
||||
}
|
||||
disposeTemplate(templateData: ConnectionProfileGroupTemplate): void {
|
||||
templateData.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionProfileTemplate extends Disposable {
|
||||
|
||||
private _root: HTMLElement;
|
||||
private _icon: HTMLElement;
|
||||
private _label: HTMLElement;
|
||||
/**
|
||||
* _isCompact is used to render connections tiles with and without the action buttons.
|
||||
* When set to true, like in the connection dialog recent connections tree, the connection
|
||||
* tile is rendered without the action buttons( such as connect, new query).
|
||||
*/
|
||||
constructor(
|
||||
container: HTMLElement,
|
||||
private _isCompact: boolean,
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService
|
||||
) {
|
||||
super();
|
||||
container.parentElement.classList.add('connection-profile');
|
||||
this._root = dom.append(container, dom.$('.connection-tile'));
|
||||
this._icon = dom.append(this._root, dom.$('div.icon server-page'));
|
||||
this._label = dom.append(this._root, dom.$('div.label'));
|
||||
}
|
||||
|
||||
set(element: ConnectionProfile) {
|
||||
if (!this._isCompact) {
|
||||
let iconPath: IconPath = getIconPath(element, this._connectionManagementService);
|
||||
if (this._connectionManagementService.isConnected(undefined, element)) {
|
||||
this._icon.classList.remove('disconnected');
|
||||
this._icon.classList.add('connected');
|
||||
renderServerIcon(this._icon, iconPath, true);
|
||||
} else {
|
||||
this._icon.classList.remove('connected');
|
||||
this._icon.classList.add('disconnected');
|
||||
renderServerIcon(this._icon, iconPath, false);
|
||||
}
|
||||
}
|
||||
|
||||
let label = element.title;
|
||||
if (!element.isConnectionOptionsValid) {
|
||||
label = localize('loading', "Loading...");
|
||||
}
|
||||
|
||||
this._label.textContent = label;
|
||||
this._root.title = element.serverInfo;
|
||||
}
|
||||
}
|
||||
|
||||
export class ConnectionProfileRenderer implements ITreeRenderer<ConnectionProfile, FuzzyScore, ConnectionProfileTemplate> {
|
||||
|
||||
readonly templateId: string = ServerTreeRenderer.CONNECTION_TEMPLATE_ID;
|
||||
|
||||
constructor(
|
||||
private _isCompact: boolean,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService) { }
|
||||
|
||||
renderTemplate(container: HTMLElement): ConnectionProfileTemplate {
|
||||
return this._instantiationService.createInstance(ConnectionProfileTemplate, container, this._isCompact);
|
||||
}
|
||||
renderElement(node: ITreeNode<ConnectionProfile, FuzzyScore>, index: number, template: ConnectionProfileTemplate): void {
|
||||
template.set(node.element);
|
||||
}
|
||||
disposeTemplate(templateData: ConnectionProfileTemplate): void {
|
||||
templateData.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
class TreeNodeTemplate extends Disposable {
|
||||
private _root: HTMLElement;
|
||||
private _icon: HTMLElement;
|
||||
private _label: HTMLElement;
|
||||
|
||||
constructor(
|
||||
container: HTMLElement
|
||||
) {
|
||||
super();
|
||||
this._root = dom.append(container, dom.$('.object-element-group'));
|
||||
this._icon = dom.append(this._root, dom.$('div.object-icon'));
|
||||
this._label = dom.append(this._root, dom.$('div.label'));
|
||||
}
|
||||
|
||||
set(element: TreeNode) {
|
||||
// Use an explicitly defined iconType first. If not defined, fall back to using nodeType and
|
||||
// other compount indicators instead.
|
||||
let iconName: string = undefined;
|
||||
if (element.iconType) {
|
||||
iconName = (typeof element.iconType === 'string') ? element.iconType : element.iconType.id;
|
||||
} else {
|
||||
iconName = element.nodeTypeId;
|
||||
if (element.nodeStatus) {
|
||||
iconName = element.nodeTypeId + '_' + element.nodeStatus;
|
||||
}
|
||||
if (element.nodeSubType) {
|
||||
iconName = element.nodeTypeId + '_' + element.nodeSubType;
|
||||
}
|
||||
}
|
||||
|
||||
let tokens: string[] = [];
|
||||
for (let index = 1; index < this._icon.classList.length; index++) {
|
||||
tokens.push(this._icon.classList.item(index));
|
||||
}
|
||||
this._icon.classList.remove(...tokens);
|
||||
this._icon.classList.add('icon');
|
||||
let iconLowerCaseName = iconName.toLocaleLowerCase();
|
||||
this._icon.classList.add(iconLowerCaseName);
|
||||
|
||||
if (element.iconPath) {
|
||||
iconRenderer.putIcon(this._icon, element.iconPath);
|
||||
}
|
||||
|
||||
this._label.textContent = element.label;
|
||||
this._root.title = element.label;
|
||||
}
|
||||
}
|
||||
|
||||
export class TreeNodeRenderer implements ITreeRenderer<TreeNode, FuzzyScore, TreeNodeTemplate> {
|
||||
|
||||
readonly templateId: string = ServerTreeRenderer.OBJECTEXPLORER_TEMPLATE_ID;
|
||||
|
||||
constructor(@IInstantiationService private readonly _instantiationService: IInstantiationService) { }
|
||||
|
||||
renderTemplate(container: HTMLElement): TreeNodeTemplate {
|
||||
return this._instantiationService.createInstance(TreeNodeTemplate, container);
|
||||
}
|
||||
renderElement(node: ITreeNode<TreeNode, FuzzyScore>, index: number, template: TreeNodeTemplate): void {
|
||||
template.set(node.element);
|
||||
}
|
||||
disposeTemplate(templateData: TreeNodeTemplate): void {
|
||||
templateData.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerTreeKeyboardNavigationLabelProvider implements IKeyboardNavigationLabelProvider<ServerTreeElement> {
|
||||
|
||||
constructor() { }
|
||||
|
||||
getKeyboardNavigationLabel(element: ServerTreeElement): { toString(): string; } {
|
||||
if (element instanceof ConnectionProfileGroup) {
|
||||
return element.groupName;
|
||||
} else if (element instanceof ConnectionProfile) {
|
||||
return element.title;
|
||||
} else {
|
||||
return element.label;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerTreeAccessibilityProvider implements IListAccessibilityProvider<ServerTreeElement> {
|
||||
|
||||
constructor(private _widgetAriaLabel: string) { }
|
||||
|
||||
getWidgetAriaLabel(): string {
|
||||
return this._widgetAriaLabel;
|
||||
}
|
||||
|
||||
getAriaLabel(element: ServerTreeElement): string | null {
|
||||
if (element instanceof ConnectionProfileGroup) {
|
||||
return element.fullName;
|
||||
} else if (element instanceof ConnectionProfile) {
|
||||
return element.title;
|
||||
}
|
||||
return element.label;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first parent which contains the className
|
||||
*/
|
||||
function findParentElement(container: HTMLElement, className: string): HTMLElement {
|
||||
let currentElement = container;
|
||||
while (currentElement) {
|
||||
if (currentElement.className.indexOf(className) > -1) {
|
||||
break;
|
||||
}
|
||||
currentElement = currentElement.parentElement;
|
||||
}
|
||||
return currentElement;
|
||||
}
|
||||
|
||||
function getIconPath(connection: ConnectionProfile, connectionManagementService: IConnectionManagementService): IconPath {
|
||||
if (!connection) { return undefined; }
|
||||
|
||||
if (connection['iconPath']) {
|
||||
return connection['iconPath'];
|
||||
}
|
||||
|
||||
let iconId = connectionManagementService.getConnectionIconId(connection.id);
|
||||
if (!iconId) { return undefined; }
|
||||
|
||||
let providerProperties = connectionManagementService.getProviderProperties(connection.providerName);
|
||||
if (!providerProperties) { return undefined; }
|
||||
|
||||
let iconPath: IconPath = undefined;
|
||||
let pathConfig: URI | IconPath | { id: string, path: IconPath }[] = providerProperties['iconPath'];
|
||||
if (Array.isArray(pathConfig)) {
|
||||
for (const e of pathConfig) {
|
||||
if (!e.id || e.id === iconId) {
|
||||
iconPath = e.path;
|
||||
connection['iconPath'] = iconPath;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (pathConfig['light']) {
|
||||
iconPath = pathConfig as IconPath;
|
||||
connection['iconPath'] = iconPath;
|
||||
} else {
|
||||
let singlePath = pathConfig as URI;
|
||||
iconPath = { light: singlePath, dark: singlePath };
|
||||
connection['iconPath'] = iconPath;
|
||||
}
|
||||
return iconPath;
|
||||
}
|
||||
|
||||
function renderServerIcon(element: HTMLElement, iconPath: IconPath, isConnected: boolean): void {
|
||||
if (!element) { return; }
|
||||
if (iconPath) {
|
||||
iconRenderer.putIcon(element, iconPath);
|
||||
}
|
||||
}
|
||||
|
||||
interface IconPath {
|
||||
light: URI;
|
||||
dark: URI;
|
||||
}
|
||||
@@ -18,6 +18,7 @@ import { IErrorMessageService } from 'sql/platform/errorMessage/common/errorMess
|
||||
import { UNSAVED_GROUP_ID } from 'sql/platform/connection/common/constants';
|
||||
import { IServerGroupController } from 'sql/platform/serverGroup/common/serverGroupController';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
|
||||
export interface IServerView {
|
||||
showFilteredTree(filter: string): void;
|
||||
@@ -28,20 +29,18 @@ export class RefreshAction extends Action {
|
||||
|
||||
public static ID = 'objectExplorer.refresh';
|
||||
public static LABEL = localize('connectionTree.refresh', "Refresh");
|
||||
private _tree: ITree;
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
tree: ITree,
|
||||
private element: IConnectionProfile | TreeNode,
|
||||
private _tree: AsyncServerTree | ITree,
|
||||
private element: ServerTreeElement,
|
||||
@IConnectionManagementService private _connectionManagementService: IConnectionManagementService,
|
||||
@IObjectExplorerService private _objectExplorerService: IObjectExplorerService,
|
||||
@IErrorMessageService private _errorMessageService: IErrorMessageService,
|
||||
@ILogService private _logService: ILogService
|
||||
) {
|
||||
super(id, label);
|
||||
this._tree = tree;
|
||||
}
|
||||
public async run(): Promise<boolean> {
|
||||
let treeNode: TreeNode;
|
||||
@@ -66,7 +65,11 @@ export class RefreshAction extends Action {
|
||||
this.showError(error);
|
||||
return true;
|
||||
}
|
||||
await this._tree.refresh(this.element);
|
||||
if (this._tree instanceof AsyncServerTree) {
|
||||
await this._tree.updateChildren(this.element);
|
||||
} else {
|
||||
await this._tree.refresh(this.element);
|
||||
}
|
||||
} catch (ex) {
|
||||
this._logService.error(ex);
|
||||
return true;
|
||||
|
||||
@@ -20,6 +20,17 @@ export function supportsNodeNameDrop(nodeId: string): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function supportsFolderNodeNameDrop(nodeId: string, label: string): boolean {
|
||||
if (nodeId === 'Folder' && label === 'Columns') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function escapeString(input: string | undefined): string | undefined {
|
||||
return input?.replace(/]/g, ']]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements drag and drop for the server tree
|
||||
*/
|
||||
@@ -42,7 +53,7 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
|
||||
return (<ConnectionProfileGroup>element).id;
|
||||
} else if (supportsNodeNameDrop(element.nodeTypeId)) {
|
||||
return (<TreeNode>element).id;
|
||||
} else if (element.nodeTypeId === 'Folder' && element.label === 'Columns' && element.children) {
|
||||
} else if (supportsFolderNodeNameDrop(element.nodeTypeId, element.label) && element.children) {
|
||||
return (<TreeNode>element).id;
|
||||
} else {
|
||||
return undefined;
|
||||
@@ -83,17 +94,17 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
|
||||
const data = dragAndDropData.getData();
|
||||
const element = data[0];
|
||||
if (supportsNodeNameDrop(element.nodeTypeId)) {
|
||||
escapedSchema = this.escapeString(element.metadata.schema);
|
||||
escapedName = this.escapeString(element.metadata.name);
|
||||
escapedSchema = escapeString(element.metadata.schema);
|
||||
escapedName = escapeString(element.metadata.name);
|
||||
finalString = escapedSchema ? `[${escapedSchema}].[${escapedName}]` : `[${escapedName}]`;
|
||||
originalEvent.dataTransfer.setData(DataTransfers.RESOURCES, JSON.stringify([`${element.nodeTypeId}:${element.id}?${finalString}`]));
|
||||
}
|
||||
if (element.nodeTypeId === 'Folder' && element.label === 'Columns') {
|
||||
if (supportsFolderNodeNameDrop(element.nodeTypeId, element.label)) {
|
||||
// get children
|
||||
let returnString = '';
|
||||
for (let child of element.children) {
|
||||
escapedSchema = this.escapeString(child.metadata.schema);
|
||||
escapedName = this.escapeString(child.metadata.name);
|
||||
escapedSchema = escapeString(child.metadata.schema);
|
||||
escapedName = escapeString(child.metadata.name);
|
||||
finalString = escapedSchema ? `[${escapedSchema}].[${escapedName}]` : `[${escapedName}]`;
|
||||
returnString = returnString ? `${returnString},${finalString}` : `${finalString}`;
|
||||
}
|
||||
@@ -103,14 +114,6 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
|
||||
return;
|
||||
}
|
||||
|
||||
private escapeString(input: string | undefined): string | undefined {
|
||||
if (input) {
|
||||
let output = input.replace(/]/g, ']]');
|
||||
return output;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
public canDragToConnectionProfileGroup(source: any, targetConnectionProfileGroup: ConnectionProfileGroup) {
|
||||
let canDragOver: boolean = true;
|
||||
@@ -177,12 +180,18 @@ export class ServerTreeDragAndDrop implements IDragAndDrop {
|
||||
if (source instanceof ConnectionProfile) {
|
||||
// Change group id of profile
|
||||
this._connectionManagementService.changeGroupIdForConnection(source, targetConnectionProfileGroup.id).then(() => {
|
||||
TreeUpdateUtils.registeredServerUpdate(tree, self._connectionManagementService, targetConnectionProfileGroup);
|
||||
if (tree) {
|
||||
TreeUpdateUtils.registeredServerUpdate(tree, self._connectionManagementService, targetConnectionProfileGroup);
|
||||
}
|
||||
|
||||
});
|
||||
} else if (source instanceof ConnectionProfileGroup) {
|
||||
// Change parent id of group
|
||||
this._connectionManagementService.changeGroupIdForConnectionGroup(source, targetConnectionProfileGroup).then(() => {
|
||||
TreeUpdateUtils.registeredServerUpdate(tree, self._connectionManagementService);
|
||||
if (tree) {
|
||||
TreeUpdateUtils.registeredServerUpdate(tree, self._connectionManagementService);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ import { values } from 'vs/base/common/collections';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { ServerTreeActionProvider } from 'sql/workbench/services/objectExplorer/browser/serverTreeActionProvider';
|
||||
import { ITree } from 'vs/base/parts/tree/browser/tree';
|
||||
import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
|
||||
export const SERVICE_ID = 'ObjectExplorerService';
|
||||
|
||||
@@ -32,22 +34,22 @@ export interface NodeExpandInfoWithProviderId extends azdata.ObjectExplorerExpan
|
||||
}
|
||||
|
||||
export interface IServerTreeView {
|
||||
readonly tree: ITree;
|
||||
readonly tree: ITree | AsyncServerTree;
|
||||
readonly onSelectionOrFocusChange: Event<void>;
|
||||
isObjectExplorerConnectionUri(uri: string): boolean;
|
||||
deleteObjectExplorerNodeAndRefreshTree(profile: ConnectionProfile): Promise<void>;
|
||||
getSelection(): Array<ConnectionProfile | TreeNode>;
|
||||
getSelection(): Array<ServerTreeElement>;
|
||||
isFocused(): boolean;
|
||||
refreshElement(node: TreeNode): Promise<void>;
|
||||
readonly treeActionProvider: { getActions: (tree: ITree, node: TreeNode | ConnectionProfile) => IAction[] }
|
||||
isExpanded(node: TreeNode | ConnectionProfile): boolean;
|
||||
reveal(node: TreeNode | ConnectionProfile): Promise<void>;
|
||||
setExpandedState(node: TreeNode | ConnectionProfile, state: TreeItemCollapsibleState): Promise<void>;
|
||||
setSelected(node: TreeNode | ConnectionProfile, selected: boolean, clearOtherSelections: boolean): Promise<void>;
|
||||
readonly treeActionProvider: ServerTreeActionProvider;
|
||||
isExpanded(node: ServerTreeElement): boolean;
|
||||
reveal(node: ServerTreeElement): Promise<void>;
|
||||
setExpandedState(node: ServerTreeElement, state: TreeItemCollapsibleState): Promise<void>;
|
||||
setSelected(node: ServerTreeElement, selected: boolean, clearOtherSelections: boolean): Promise<void>;
|
||||
refreshTree(): Promise<void>;
|
||||
readonly activeConnectionsFilterAction: IAction;
|
||||
renderBody(container: HTMLElement): Promise<void>;
|
||||
layout(size: number);
|
||||
layout(size: number): void;
|
||||
}
|
||||
|
||||
export interface IObjectExplorerService {
|
||||
@@ -78,7 +80,7 @@ export interface IObjectExplorerService {
|
||||
|
||||
registerNodeProvider(expander: azdata.ObjectExplorerNodeProvider): void;
|
||||
|
||||
getObjectExplorerNode(connection: IConnectionProfile): TreeNode;
|
||||
getObjectExplorerNode(connection: IConnectionProfile): TreeNode | undefined;
|
||||
|
||||
updateObjectExplorerNodes(connectionProfile: IConnectionProfile): Promise<void>;
|
||||
|
||||
@@ -203,7 +205,7 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
||||
|
||||
public async updateObjectExplorerNodes(connection: IConnectionProfile): Promise<void> {
|
||||
const withPassword = await this._connectionManagementService.addSavedPassword(connection);
|
||||
let connectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, withPassword);
|
||||
const connectionProfile = ConnectionProfile.fromIConnectionProfile(this._capabilitiesService, withPassword);
|
||||
return this.updateNewObjectExplorerNode(connectionProfile);
|
||||
}
|
||||
|
||||
@@ -330,7 +332,7 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
||||
}
|
||||
}
|
||||
|
||||
public getObjectExplorerNode(connection: IConnectionProfile): TreeNode {
|
||||
public getObjectExplorerNode(connection: IConnectionProfile): TreeNode | undefined {
|
||||
return this._activeObjectExplorerNodes[connection.id];
|
||||
}
|
||||
|
||||
@@ -678,7 +680,7 @@ export class ObjectExplorerService implements IObjectExplorerService {
|
||||
*/
|
||||
public async getNodeActions(connectionId: string, nodePath: string): Promise<string[]> {
|
||||
const node = await this.getTreeNode(connectionId, nodePath);
|
||||
let actions = this._serverTreeView.treeActionProvider.getActions(this._serverTreeView.tree, this.getTreeItem(node));
|
||||
const actions = this._serverTreeView.treeActionProvider.getActions(this._serverTreeView.tree, this.getTreeItem(node));
|
||||
return actions.filter(action => action.label).map(action => action.label);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import { IQueryManagementService } from 'sql/workbench/services/query/common/que
|
||||
import { ServerInfoContextKey } from 'sql/workbench/services/connection/common/serverInfoContextKey';
|
||||
import { fillInActions } from 'vs/platform/actions/browser/menuEntryActionViewItem';
|
||||
import { firstIndex, find } from 'vs/base/common/arrays';
|
||||
import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
|
||||
/**
|
||||
* Provides actions for the server tree elements
|
||||
@@ -40,19 +41,15 @@ export class ServerTreeActionProvider {
|
||||
) {
|
||||
}
|
||||
|
||||
public hasActions(tree: ITree, element: any): boolean {
|
||||
return element instanceof ConnectionProfileGroup || (element instanceof ConnectionProfile) || (element instanceof TreeNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return actions given an element in the tree
|
||||
*/
|
||||
public getActions(tree: ITree, element: any): IAction[] {
|
||||
public getActions(tree: AsyncServerTree | ITree, element: ServerTreeElement): IAction[] {
|
||||
if (element instanceof ConnectionProfile) {
|
||||
return this.getConnectionActions(tree, element);
|
||||
}
|
||||
if (element instanceof ConnectionProfileGroup) {
|
||||
return this.getConnectionProfileGroupActions(tree, element);
|
||||
return this.getConnectionProfileGroupActions(element);
|
||||
}
|
||||
if (element instanceof TreeNode) {
|
||||
return this.getObjectExplorerNodeActions({
|
||||
@@ -61,18 +58,13 @@ export class ServerTreeActionProvider {
|
||||
treeNode: element
|
||||
});
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public hasSecondaryActions(tree: ITree, element: any): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return actions for connection elements
|
||||
*/
|
||||
public getConnectionActions(tree: ITree, profile: ConnectionProfile): IAction[] {
|
||||
private getConnectionActions(tree: AsyncServerTree | ITree, profile: ConnectionProfile): IAction[] {
|
||||
let node = new TreeNode(NodeType.Server, '', false, '', '', '', undefined, undefined, undefined, undefined);
|
||||
return this.getAllActions({
|
||||
tree: tree,
|
||||
@@ -158,7 +150,7 @@ export class ServerTreeActionProvider {
|
||||
/**
|
||||
* Return actions for connection group elements
|
||||
*/
|
||||
public getConnectionProfileGroupActions(tree: ITree, element: ConnectionProfileGroup): IAction[] {
|
||||
private getConnectionProfileGroupActions(element: ConnectionProfileGroup): IAction[] {
|
||||
return [
|
||||
this._instantiationService.createInstance(AddServerAction, AddServerAction.ID, AddServerAction.LABEL),
|
||||
this._instantiationService.createInstance(EditServerGroupAction, EditServerGroupAction.ID, EditServerGroupAction.LABEL, element),
|
||||
@@ -202,7 +194,7 @@ export class ServerTreeActionProvider {
|
||||
}
|
||||
|
||||
interface ObjectExplorerContext {
|
||||
tree: ITree;
|
||||
tree: AsyncServerTree | ITree;
|
||||
profile: ConnectionProfile;
|
||||
treeNode?: TreeNode;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ export class ServerTreeDataSource implements IDataSource {
|
||||
*/
|
||||
public async getChildren(tree: ITree, element: any): Promise<(ConnectionProfile | ConnectionProfileGroup | TreeNode)[]> {
|
||||
if (element instanceof ConnectionProfile) {
|
||||
return TreeUpdateUtils.getObjectExplorerNode(<ConnectionProfile>element, this._connectionManagementService, this._objectExplorerService);
|
||||
return TreeUpdateUtils.getConnectionNodeChildren(<ConnectionProfile>element, this._objectExplorerService);
|
||||
} else if (element instanceof ConnectionProfileGroup) {
|
||||
return (element as ConnectionProfileGroup).getChildren();
|
||||
} else if (element instanceof TreeNode) {
|
||||
|
||||
@@ -15,6 +15,7 @@ import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode'
|
||||
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
|
||||
import { badgeRenderer, iconRenderer } from 'sql/workbench/services/objectExplorer/browser/iconRenderer';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { DefaultServerGroupColor } from 'sql/workbench/services/serverGroup/common/serverGroupViewModel';
|
||||
|
||||
export interface IConnectionTemplateData {
|
||||
root: HTMLElement;
|
||||
@@ -44,10 +45,10 @@ export class ServerTreeRenderer implements IRenderer {
|
||||
|
||||
public static CONNECTION_HEIGHT = 23;
|
||||
public static CONNECTION_GROUP_HEIGHT = 38;
|
||||
private static CONNECTION_TEMPLATE_ID = 'connectionProfile';
|
||||
private static CONNECTION_GROUP_TEMPLATE_ID = 'connectionProfileGroup';
|
||||
public static CONNECTION_TEMPLATE_ID = 'connectionProfile';
|
||||
public static CONNECTION_GROUP_TEMPLATE_ID = 'connectionProfileGroup';
|
||||
public static OBJECTEXPLORER_HEIGHT = 23;
|
||||
private static OBJECTEXPLORER_TEMPLATE_ID = 'objectExplorer';
|
||||
public static OBJECTEXPLORER_TEMPLATE_ID = 'objectExplorer';
|
||||
/**
|
||||
* _isCompact is used to render connections tiles with and without the action buttons.
|
||||
* When set to true, like in the connection dialog recent connections tree, the connection
|
||||
@@ -238,7 +239,7 @@ export class ServerTreeRenderer implements IRenderer {
|
||||
rowElement.style.background = connectionProfileGroup.color;
|
||||
} else {
|
||||
// If the group doesn't contain specific color, assign the default color
|
||||
rowElement.style.background = '#515151';
|
||||
rowElement.style.background = DefaultServerGroupColor;
|
||||
}
|
||||
}
|
||||
if (connectionProfileGroup.description && (connectionProfileGroup.description !== '')) {
|
||||
|
||||
@@ -15,48 +15,130 @@ import { IController } from 'vs/base/parts/tree/browser/tree';
|
||||
import { ServerTreeDragAndDrop, RecentConnectionsDragAndDrop } from 'sql/workbench/services/objectExplorer/browser/dragAndDropController';
|
||||
import { RecentConnectionDataSource } from 'sql/workbench/services/objectExplorer/browser/recentConnectionDataSource';
|
||||
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
|
||||
import { IWorkbenchAsyncDataTreeOptions } from 'vs/platform/list/browser/listService';
|
||||
import { ConnectionProfileGroupRenderer, ConnectionProfileRenderer, TreeNodeRenderer, ServerTreeAccessibilityProvider, ServerTreeKeyboardNavigationLabelProvider } from 'sql/workbench/services/objectExplorer/browser/asyncServerTreeRenderer';
|
||||
import { AsyncServerTreeIdentityProvider } from 'sql/workbench/services/objectExplorer/browser/asyncServerTreeIdentityProvider';
|
||||
import { FuzzyScore } from 'vs/base/common/filters';
|
||||
import { AsyncServerTreeDelegate } from 'sql/workbench/services/objectExplorer/browser/asyncServerTreeDelegate';
|
||||
import { AsyncRecentConnectionsDragAndDrop, AsyncServerTreeDragAndDrop } from 'sql/workbench/services/objectExplorer/browser/asyncServerTreeDragAndDrop';
|
||||
import { AsyncRecentConnectionTreeDataSource } from 'sql/workbench/services/objectExplorer/browser/asyncRecentConnectionTreeDataSource';
|
||||
import { AsyncServerTreeDataSource } from 'sql/workbench/services/objectExplorer/browser/asyncServerTreeDataSource';
|
||||
import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export class TreeCreationUtils {
|
||||
/**
|
||||
* Create a Recent Connections tree
|
||||
*/
|
||||
public static createConnectionTree(treeContainer: HTMLElement, instantiationService: IInstantiationService, useController?: IController): Tree {
|
||||
const dataSource = instantiationService.createInstance(RecentConnectionDataSource);
|
||||
const renderer = instantiationService.createInstance(ServerTreeRenderer, true);
|
||||
const controller = useController ? useController : new DefaultController();
|
||||
const dnd = instantiationService.createInstance(RecentConnectionsDragAndDrop);
|
||||
const filter = new DefaultFilter();
|
||||
const sorter = undefined;
|
||||
const accessibilityProvider = new DefaultAccessibilityProvider();
|
||||
public static createConnectionTree(treeContainer: HTMLElement, instantiationService: IInstantiationService, configurationService: IConfigurationService, ariaLabel: string, useController?: IController): Tree | AsyncServerTree {
|
||||
if (useAsyncServerTree(configurationService)) {
|
||||
const dataSource = instantiationService.createInstance(AsyncRecentConnectionTreeDataSource);
|
||||
const connectionProfileGroupRender = instantiationService.createInstance(ConnectionProfileGroupRenderer);
|
||||
const connectionProfileRenderer = instantiationService.createInstance(ConnectionProfileRenderer, true);
|
||||
const treeNodeRenderer = instantiationService.createInstance(TreeNodeRenderer);
|
||||
const dnd = instantiationService.createInstance(AsyncRecentConnectionsDragAndDrop);
|
||||
const identityProvider = instantiationService.createInstance(AsyncServerTreeIdentityProvider);
|
||||
|
||||
return new Tree(treeContainer, { dataSource, renderer, controller, dnd, filter, sorter, accessibilityProvider },
|
||||
{
|
||||
indentPixels: 0,
|
||||
twistiePixels: 0,
|
||||
ariaLabel: nls.localize('treeAriaLabel', "Recent Connections")
|
||||
});
|
||||
const treeOptions: IWorkbenchAsyncDataTreeOptions<ServerTreeElement, FuzzyScore> = {
|
||||
keyboardSupport: true,
|
||||
accessibilityProvider: new ServerTreeAccessibilityProvider(ariaLabel),
|
||||
keyboardNavigationLabelProvider: instantiationService.createInstance(ServerTreeKeyboardNavigationLabelProvider),
|
||||
dnd: dnd,
|
||||
identityProvider: identityProvider
|
||||
};
|
||||
|
||||
return instantiationService.createInstance(
|
||||
AsyncServerTree,
|
||||
'ServerTreeView',
|
||||
treeContainer,
|
||||
new AsyncServerTreeDelegate(),
|
||||
[
|
||||
connectionProfileGroupRender,
|
||||
connectionProfileRenderer,
|
||||
treeNodeRenderer
|
||||
],
|
||||
dataSource,
|
||||
treeOptions,
|
||||
);
|
||||
} else {
|
||||
const dataSource = instantiationService.createInstance(RecentConnectionDataSource);
|
||||
const renderer = instantiationService.createInstance(ServerTreeRenderer, true);
|
||||
const controller = useController ? useController : new DefaultController();
|
||||
const dnd = instantiationService.createInstance(RecentConnectionsDragAndDrop);
|
||||
const filter = new DefaultFilter();
|
||||
const sorter = undefined;
|
||||
const accessibilityProvider = new DefaultAccessibilityProvider();
|
||||
|
||||
return new Tree(treeContainer, { dataSource, renderer, controller, dnd, filter, sorter, accessibilityProvider },
|
||||
{
|
||||
indentPixels: 0,
|
||||
twistiePixels: 0,
|
||||
ariaLabel: nls.localize('treeAriaLabel', "Recent Connections")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Servers viewlet tree
|
||||
*/
|
||||
public static createRegisteredServersTree(treeContainer: HTMLElement, instantiationService: IInstantiationService, horizontalScrollMode: boolean = false): Tree {
|
||||
public static createServersTree(treeContainer: HTMLElement,
|
||||
instantiationService: IInstantiationService,
|
||||
configurationService: IConfigurationService,
|
||||
horizontalScrollMode: boolean = false): Tree | AsyncServerTree {
|
||||
|
||||
const dataSource = instantiationService.createInstance(ServerTreeDataSource);
|
||||
const actionProvider = instantiationService.createInstance(ServerTreeActionProvider);
|
||||
const renderer = instantiationService.createInstance(ServerTreeRenderer, false);
|
||||
const controller = instantiationService.createInstance(ServerTreeController, actionProvider);
|
||||
const dnd = instantiationService.createInstance(ServerTreeDragAndDrop);
|
||||
const filter = new DefaultFilter();
|
||||
const sorter = undefined;
|
||||
const accessibilityProvider = new DefaultAccessibilityProvider();
|
||||
if (useAsyncServerTree(configurationService)) {
|
||||
const dataSource = instantiationService.createInstance(AsyncServerTreeDataSource);
|
||||
const connectionProfileGroupRender = instantiationService.createInstance(ConnectionProfileGroupRenderer);
|
||||
const connectionProfileRenderer = instantiationService.createInstance(ConnectionProfileRenderer, false);
|
||||
const treeNodeRenderer = instantiationService.createInstance(TreeNodeRenderer);
|
||||
const dnd = instantiationService.createInstance(AsyncServerTreeDragAndDrop);
|
||||
const identityProvider = instantiationService.createInstance(AsyncServerTreeIdentityProvider);
|
||||
|
||||
const treeOptions: IWorkbenchAsyncDataTreeOptions<ServerTreeElement, FuzzyScore> = {
|
||||
keyboardSupport: true,
|
||||
accessibilityProvider: new ServerTreeAccessibilityProvider(nls.localize('serversAriaLabel', "Servers")),
|
||||
keyboardNavigationLabelProvider: instantiationService.createInstance(ServerTreeKeyboardNavigationLabelProvider),
|
||||
openOnSingleClick: true,
|
||||
openOnFocus: true,
|
||||
dnd: dnd,
|
||||
identityProvider: identityProvider
|
||||
};
|
||||
|
||||
return instantiationService.createInstance(
|
||||
AsyncServerTree,
|
||||
'ServerTreeView',
|
||||
treeContainer,
|
||||
new AsyncServerTreeDelegate(),
|
||||
[
|
||||
connectionProfileGroupRender,
|
||||
connectionProfileRenderer,
|
||||
treeNodeRenderer
|
||||
],
|
||||
dataSource,
|
||||
treeOptions
|
||||
);
|
||||
} else {
|
||||
const dataSource = instantiationService.createInstance(ServerTreeDataSource);
|
||||
const actionProvider = instantiationService.createInstance(ServerTreeActionProvider);
|
||||
const renderer = instantiationService.createInstance(ServerTreeRenderer, false);
|
||||
const controller = instantiationService.createInstance(ServerTreeController, actionProvider);
|
||||
const dnd = instantiationService.createInstance(ServerTreeDragAndDrop);
|
||||
const filter = new DefaultFilter();
|
||||
const sorter = undefined;
|
||||
const accessibilityProvider = new DefaultAccessibilityProvider();
|
||||
|
||||
return new Tree(treeContainer, { dataSource, renderer, controller, dnd, filter, sorter, accessibilityProvider },
|
||||
{
|
||||
indentPixels: 10,
|
||||
twistiePixels: 20,
|
||||
ariaLabel: nls.localize('treeCreation.regTreeAriaLabel', "Servers"),
|
||||
horizontalScrollMode: horizontalScrollMode ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden
|
||||
});
|
||||
}
|
||||
|
||||
return new Tree(treeContainer, { dataSource, renderer, controller, dnd, filter, sorter, accessibilityProvider },
|
||||
{
|
||||
indentPixels: 10,
|
||||
twistiePixels: 20,
|
||||
ariaLabel: nls.localize('treeCreation.regTreeAriaLabel', "Servers"),
|
||||
horizontalScrollMode: horizontalScrollMode ? ScrollbarVisibility.Auto : ScrollbarVisibility.Hidden
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function useAsyncServerTree(configurationService: IConfigurationService): boolean {
|
||||
return configurationService.getValue('workbench.enablePreviewFeatures') && configurationService.getValue('serverTree.useAsyncServerTree');
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/br
|
||||
// import { IProgressRunner, IProgressService } from 'vs/platform/progress/common/progress';
|
||||
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
|
||||
import { TreeUpdateUtils } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils';
|
||||
import { AsyncServerTree } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
|
||||
export class TreeSelectionHandler {
|
||||
// progressRunner: IProgressRunner;
|
||||
@@ -44,9 +45,9 @@ export class TreeSelectionHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle select ion of tree element
|
||||
* Handle selection of tree element
|
||||
*/
|
||||
public onTreeSelect(event: any, tree: ITree, connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, connectionCompleteCallback: () => void) {
|
||||
public onTreeSelect(event: any, tree: AsyncServerTree | ITree, connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, connectionCompleteCallback: () => void) {
|
||||
let sendSelectionEvent = ((event: any, selection: any, isDoubleClick: boolean, userInteraction: boolean) => {
|
||||
// userInteraction: defensive - don't touch this something else is handling it.
|
||||
if (userInteraction === true && this._lastClicked && this._lastClicked[0] === selection[0]) {
|
||||
@@ -93,44 +94,50 @@ export class TreeSelectionHandler {
|
||||
*
|
||||
* @param connectionCompleteCallback A function that gets called after a connection is established due to the selection, if needed
|
||||
*/
|
||||
private handleTreeItemSelected(connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, isDoubleClick: boolean, isKeyboard: boolean, selection: any[], tree: ITree, connectionCompleteCallback: () => void): void {
|
||||
let connectionProfile: ConnectionProfile = undefined;
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: undefined,
|
||||
saveTheConnection: true,
|
||||
showConnectionDialogOnError: true,
|
||||
showFirewallRuleOnError: true,
|
||||
showDashboard: isDoubleClick // only show the dashboard if the action is double click
|
||||
};
|
||||
if (selection && selection.length > 0 && (selection[0] instanceof ConnectionProfile)) {
|
||||
connectionProfile = <ConnectionProfile>selection[0];
|
||||
|
||||
if (connectionProfile) {
|
||||
private handleTreeItemSelected(connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, isDoubleClick: boolean, isKeyboard: boolean, selection: any[], tree: AsyncServerTree | ITree, connectionCompleteCallback: () => void): void {
|
||||
if (tree instanceof AsyncServerTree) {
|
||||
if (selection && selection.length > 0 && (selection[0] instanceof ConnectionProfile)) {
|
||||
this.onTreeActionStateChange(true);
|
||||
|
||||
TreeUpdateUtils.connectAndCreateOeSession(connectionProfile, options, connectionManagementService, objectExplorerService, tree).then(sessionCreated => {
|
||||
if (!sessionCreated) {
|
||||
this.onTreeActionStateChange(false);
|
||||
}
|
||||
if (connectionCompleteCallback) {
|
||||
connectionCompleteCallback();
|
||||
}
|
||||
}, error => {
|
||||
this.onTreeActionStateChange(false);
|
||||
});
|
||||
}
|
||||
} else if (isDoubleClick && selection && selection.length > 0 && (selection[0] instanceof TreeNode)) {
|
||||
let treeNode = selection[0];
|
||||
if (TreeUpdateUtils.isAvailableDatabaseNode(treeNode)) {
|
||||
connectionProfile = TreeUpdateUtils.getConnectionProfile(treeNode);
|
||||
} else {
|
||||
let connectionProfile: ConnectionProfile = undefined;
|
||||
let options: IConnectionCompletionOptions = {
|
||||
params: undefined,
|
||||
saveTheConnection: true,
|
||||
showConnectionDialogOnError: true,
|
||||
showFirewallRuleOnError: true,
|
||||
showDashboard: isDoubleClick // only show the dashboard if the action is double click
|
||||
};
|
||||
if (selection && selection.length > 0 && (selection[0] instanceof ConnectionProfile)) {
|
||||
connectionProfile = <ConnectionProfile>selection[0];
|
||||
|
||||
if (connectionProfile) {
|
||||
connectionManagementService.showDashboard(connectionProfile);
|
||||
this.onTreeActionStateChange(true);
|
||||
|
||||
TreeUpdateUtils.connectAndCreateOeSession(connectionProfile, options, connectionManagementService, objectExplorerService, tree).then(sessionCreated => {
|
||||
if (!sessionCreated) {
|
||||
this.onTreeActionStateChange(false);
|
||||
}
|
||||
if (connectionCompleteCallback) {
|
||||
connectionCompleteCallback();
|
||||
}
|
||||
}, error => {
|
||||
this.onTreeActionStateChange(false);
|
||||
});
|
||||
}
|
||||
} else if (isDoubleClick && selection && selection.length > 0 && (selection[0] instanceof TreeNode)) {
|
||||
let treeNode = selection[0];
|
||||
if (TreeUpdateUtils.isAvailableDatabaseNode(treeNode)) {
|
||||
connectionProfile = TreeUpdateUtils.getConnectionProfile(treeNode);
|
||||
if (connectionProfile) {
|
||||
connectionManagementService.showDashboard(connectionProfile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isKeyboard) {
|
||||
tree.toggleExpansion(selection[0]);
|
||||
if (isKeyboard) {
|
||||
tree.toggleExpansion(selection[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/br
|
||||
import { NodeType } from 'sql/workbench/services/objectExplorer/common/nodeType';
|
||||
|
||||
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree';
|
||||
|
||||
export interface IExpandableTree extends ITree {
|
||||
/**
|
||||
@@ -47,13 +47,13 @@ export class TreeUpdateUtils {
|
||||
/**
|
||||
* Set input for the tree.
|
||||
*/
|
||||
public static structuralTreeUpdate(tree: ITree, viewKey: 'recent' | 'active' | 'saved', connectionManagementService: IConnectionManagementService, providers?: string[]): Promise<void> {
|
||||
public static async structuralTreeUpdate(tree: AsyncServerTree | ITree, viewKey: 'recent' | 'active' | 'saved', connectionManagementService: IConnectionManagementService, providers?: string[]): Promise<void> {
|
||||
// convert to old VS Code tree interface with expandable methods
|
||||
let expandableTree: IExpandableTree = <IExpandableTree>tree;
|
||||
|
||||
let selectedElement: any;
|
||||
let targetsToExpand: any[];
|
||||
if (tree) {
|
||||
if (tree && !(tree instanceof AsyncServerTree)) {
|
||||
let selection = tree.getSelection();
|
||||
if (selection && selection.length === 1) {
|
||||
selectedElement = <any>selection[0];
|
||||
@@ -72,10 +72,13 @@ export class TreeUpdateUtils {
|
||||
treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService, providers);
|
||||
}
|
||||
const previousTreeInput = tree.getInput();
|
||||
return tree.setInput(treeInput).then(async () => {
|
||||
if (previousTreeInput instanceof Disposable) {
|
||||
previousTreeInput.dispose();
|
||||
}
|
||||
await tree.setInput(treeInput);
|
||||
|
||||
if (previousTreeInput instanceof Disposable) {
|
||||
previousTreeInput.dispose();
|
||||
}
|
||||
|
||||
if (tree && !(tree instanceof AsyncServerTree)) {
|
||||
// Make sure to expand all folders that where expanded in the previous session
|
||||
if (targetsToExpand) {
|
||||
await tree.expandAll(targetsToExpand);
|
||||
@@ -83,64 +86,64 @@ export class TreeUpdateUtils {
|
||||
if (selectedElement) {
|
||||
tree.select(selectedElement);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set input for the registered servers tree.
|
||||
*/
|
||||
public static registeredServerUpdate(tree: ITree, connectionManagementService: IConnectionManagementService, elementToSelect?: any): Promise<void> {
|
||||
// convert to old VS Code tree interface with expandable methods
|
||||
let expandableTree: IExpandableTree = <IExpandableTree>tree;
|
||||
public static async registeredServerUpdate(tree: ITree | AsyncServerTree, connectionManagementService: IConnectionManagementService, elementToSelect?: any): Promise<void> {
|
||||
if (tree instanceof AsyncServerTree) {
|
||||
const treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService);
|
||||
await tree.setInput(treeInput);
|
||||
tree.rerender();
|
||||
} else {
|
||||
// convert to old VS Code tree interface with expandable methods
|
||||
let expandableTree: IExpandableTree = <IExpandableTree>tree;
|
||||
|
||||
let selectedElement: any = elementToSelect;
|
||||
let targetsToExpand: any[];
|
||||
let selectedElement: any = elementToSelect;
|
||||
let targetsToExpand: any[];
|
||||
|
||||
// Focus
|
||||
tree.domFocus();
|
||||
// Focus
|
||||
tree.domFocus();
|
||||
|
||||
if (tree) {
|
||||
let selection = tree.getSelection();
|
||||
if (!selectedElement) {
|
||||
if (selection && selection.length === 1) {
|
||||
selectedElement = <any>selection[0];
|
||||
if (tree) {
|
||||
let selection = tree.getSelection();
|
||||
if (!selectedElement) {
|
||||
if (selection && selection.length === 1) {
|
||||
selectedElement = <any>selection[0];
|
||||
}
|
||||
}
|
||||
targetsToExpand = expandableTree.getExpandedElements();
|
||||
if (selectedElement && targetsToExpand.indexOf(selectedElement) === -1) {
|
||||
targetsToExpand.push(selectedElement);
|
||||
}
|
||||
}
|
||||
targetsToExpand = expandableTree.getExpandedElements();
|
||||
if (selectedElement && targetsToExpand.indexOf(selectedElement) === -1) {
|
||||
targetsToExpand.push(selectedElement);
|
||||
}
|
||||
}
|
||||
|
||||
let treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService);
|
||||
if (treeInput) {
|
||||
if (treeInput !== tree.getInput()) {
|
||||
return tree.setInput(treeInput).then(async () => {
|
||||
// Make sure to expand all folders that where expanded in the previous session
|
||||
if (targetsToExpand) {
|
||||
await tree.expandAll(targetsToExpand);
|
||||
}
|
||||
if (selectedElement) {
|
||||
tree.select(selectedElement);
|
||||
}
|
||||
tree.getFocus();
|
||||
}, errors.onUnexpectedError);
|
||||
let treeInput = TreeUpdateUtils.getTreeInput(connectionManagementService);
|
||||
if (treeInput) {
|
||||
if (treeInput !== tree.getInput()) {
|
||||
return tree.setInput(treeInput).then(async () => {
|
||||
// Make sure to expand all folders that where expanded in the previous session
|
||||
if (targetsToExpand) {
|
||||
await tree.expandAll(targetsToExpand);
|
||||
}
|
||||
if (selectedElement) {
|
||||
tree.select(selectedElement);
|
||||
}
|
||||
tree.getFocus();
|
||||
}, onUnexpectedError);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
public static getTreeInput(connectionManagementService: IConnectionManagementService, providers?: string[]): ConnectionProfileGroup {
|
||||
|
||||
let groups = connectionManagementService.getConnectionGroups(providers);
|
||||
if (groups && groups.length > 0) {
|
||||
let treeInput = groups[0];
|
||||
treeInput.name = 'root';
|
||||
groups.forEach(cpg => cpg.dispose());
|
||||
return treeInput;
|
||||
}
|
||||
// Should never get to this case.
|
||||
return undefined;
|
||||
public static getTreeInput(connectionManagementService: IConnectionManagementService, providers?: string[]): ConnectionProfileGroup | undefined {
|
||||
const groups = connectionManagementService.getConnectionGroups(providers);
|
||||
const input = groups.find(group => group.isRoot);
|
||||
// Dispose of the unused groups to clean up their handlers
|
||||
groups.filter(g => g !== input).forEach(g => g.dispose());
|
||||
return input;
|
||||
}
|
||||
|
||||
public static hasObjectExplorerNode(connection: ConnectionProfile, connectionManagementService: IConnectionManagementService): boolean {
|
||||
@@ -149,10 +152,10 @@ export class TreeUpdateUtils {
|
||||
}
|
||||
|
||||
public static async connectIfNotConnected(
|
||||
connection: IConnectionProfile,
|
||||
connection: ConnectionProfile,
|
||||
options: IConnectionCompletionOptions,
|
||||
connectionManagementService: IConnectionManagementService,
|
||||
tree: ITree): Promise<ConnectionProfile | undefined> {
|
||||
tree: AsyncServerTree | ITree): Promise<ConnectionProfile | undefined> {
|
||||
if (!connectionManagementService.isProfileConnected(connection)) {
|
||||
// don't try to reconnect if currently connecting
|
||||
if (connectionManagementService.isProfileConnecting(connection)) {
|
||||
@@ -161,7 +164,15 @@ export class TreeUpdateUtils {
|
||||
// else if we aren't connected or connecting then try to connect
|
||||
} else {
|
||||
let callbacks: IConnectionCallbacks = undefined;
|
||||
if (tree) {
|
||||
if (tree instanceof AsyncServerTree) {
|
||||
callbacks = {
|
||||
onConnectStart: undefined,
|
||||
onConnectReject: undefined,
|
||||
onConnectSuccess: undefined,
|
||||
onDisconnect: undefined,
|
||||
onConnectCanceled: undefined,
|
||||
};
|
||||
} else if (tree) {
|
||||
// Show the spinner in OE by adding the 'loading' trait to the connection, and set up callbacks to hide the spinner
|
||||
tree.addTraits('loading', [connection]);
|
||||
let rejectOrCancelCallback = () => {
|
||||
@@ -176,12 +187,13 @@ export class TreeUpdateUtils {
|
||||
onConnectCanceled: rejectOrCancelCallback,
|
||||
};
|
||||
}
|
||||
|
||||
const result = await connectionManagementService.connect(connection, undefined, options, callbacks);
|
||||
if (result.connected) {
|
||||
let existingConnection = connectionManagementService.findExistingConnection(connection);
|
||||
return existingConnection;
|
||||
} else {
|
||||
throw new Error('connection failed');
|
||||
throw new Error(result.errorMessage);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -202,8 +214,8 @@ export class TreeUpdateUtils {
|
||||
* @param connectionManagementService Connection management service instance
|
||||
* @param objectExplorerService Object explorer service instance
|
||||
*/
|
||||
public static async connectAndCreateOeSession(connection: IConnectionProfile, options: IConnectionCompletionOptions,
|
||||
connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, tree: ITree): Promise<boolean> {
|
||||
public static async connectAndCreateOeSession(connection: ConnectionProfile, options: IConnectionCompletionOptions,
|
||||
connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService, tree: AsyncServerTree | ITree): Promise<boolean> {
|
||||
const connectedConnection = await TreeUpdateUtils.connectIfNotConnected(connection, options, connectionManagementService, tree);
|
||||
if (connectedConnection) {
|
||||
// append group ID and original display name to build unique OE session ID
|
||||
@@ -213,7 +225,6 @@ export class TreeUpdateUtils {
|
||||
let rootNode: TreeNode = objectExplorerService.getObjectExplorerNode(connectedConnection);
|
||||
if (!rootNode) {
|
||||
await objectExplorerService.updateObjectExplorerNodes(connectedConnection);
|
||||
rootNode = objectExplorerService.getObjectExplorerNode(connectedConnection);
|
||||
return true;
|
||||
// The oe request is sent. an event will be raised when the session is created
|
||||
} else {
|
||||
@@ -224,50 +235,75 @@ export class TreeUpdateUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static getObjectExplorerNode(connection: ConnectionProfile, connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService): Promise<TreeNode[]> {
|
||||
return new Promise<TreeNode[]>((resolve, reject) => {
|
||||
if (connection.isDisconnecting) {
|
||||
resolve([]);
|
||||
} else {
|
||||
let rootNode = objectExplorerService.getObjectExplorerNode(connection);
|
||||
if (rootNode) {
|
||||
objectExplorerService.resolveTreeNodeChildren(rootNode.getSession(), rootNode).then(() => {
|
||||
resolve(rootNode.children);
|
||||
}, expandError => {
|
||||
resolve([]);
|
||||
});
|
||||
|
||||
} else {
|
||||
resolve([]);
|
||||
public static async getConnectionNodeChildren(connection: ConnectionProfile, objectExplorerService: IObjectExplorerService): Promise<TreeNode[]> {
|
||||
if (connection.isDisconnecting) {
|
||||
return [];
|
||||
} else {
|
||||
let rootNode = objectExplorerService.getObjectExplorerNode(connection);
|
||||
if (rootNode) {
|
||||
try {
|
||||
await objectExplorerService.resolveTreeNodeChildren(rootNode.getSession(), rootNode);
|
||||
return rootNode.children;
|
||||
} catch (err) {
|
||||
onUnexpectedError(err);
|
||||
return [];
|
||||
}
|
||||
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static getObjectExplorerParent(objectExplorerNode: TreeNode, connectionManagementService: IConnectionManagementService): any {
|
||||
public static async getAsyncConnectionNodeChildren(connection: ConnectionProfile, connectionManagementService: IConnectionManagementService, objectExplorerService: IObjectExplorerService): Promise<TreeNode[]> {
|
||||
if (connection.isDisconnecting) {
|
||||
return [];
|
||||
} else {
|
||||
let rootNode = objectExplorerService.getObjectExplorerNode(connection);
|
||||
if (rootNode) {
|
||||
await objectExplorerService.resolveTreeNodeChildren(rootNode.getSession(), rootNode);
|
||||
return rootNode.children;
|
||||
} else {
|
||||
const options: IConnectionCompletionOptions = {
|
||||
params: undefined,
|
||||
saveTheConnection: true,
|
||||
showConnectionDialogOnError: true,
|
||||
showFirewallRuleOnError: true,
|
||||
showDashboard: false
|
||||
};
|
||||
// Need to wait for the OE service to update its nodes in order to resolve the children
|
||||
const nodesUpdatedPromise = new Promise((resolve, reject) => {
|
||||
objectExplorerService.onUpdateObjectExplorerNodes(e => {
|
||||
if (e.errorMessage) {
|
||||
reject(new Error(e.errorMessage));
|
||||
}
|
||||
if (e.connection.id === connection.id) {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
await TreeUpdateUtils.connectAndCreateOeSession(connection, options, connectionManagementService, objectExplorerService, undefined);
|
||||
await nodesUpdatedPromise;
|
||||
let rootNode = objectExplorerService.getObjectExplorerNode(connection);
|
||||
await objectExplorerService.resolveTreeNodeChildren(rootNode.getSession(), rootNode);
|
||||
return rootNode.children;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static getObjectExplorerParent(objectExplorerNode: TreeNode, connectionManagementService: IConnectionManagementService): ServerTreeElement | undefined {
|
||||
if (objectExplorerNode && objectExplorerNode.parent) {
|
||||
// if object explorer node's parent is root, return connection profile
|
||||
if (!objectExplorerNode.parent.parent) {
|
||||
let connectionId = objectExplorerNode.getConnectionProfile().id;
|
||||
|
||||
const connectionId = objectExplorerNode.getConnectionProfile().id;
|
||||
// get connection profile from connection profile groups
|
||||
let root = TreeUpdateUtils.getTreeInput(connectionManagementService);
|
||||
let connections = ConnectionProfileGroup.getConnectionsInGroup(root);
|
||||
let results = connections.filter(con => {
|
||||
if (connectionId === con.id) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (results && results.length > 0) {
|
||||
return results[0];
|
||||
}
|
||||
const root = TreeUpdateUtils.getTreeInput(connectionManagementService);
|
||||
return ConnectionProfileGroup.getConnectionsInGroup(root).find(conn => connectionId === conn.id);
|
||||
} else {
|
||||
return objectExplorerNode.parent;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
|
||||
import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup';
|
||||
import { ConnectionManagementService } from 'sql/workbench/services/connection/browser/connectionManagementService';
|
||||
import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices';
|
||||
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
|
||||
import { ConnectionProviderProperties } from 'sql/platform/capabilities/common/capabilitiesService';
|
||||
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
|
||||
import { mssqlProviderName } from 'sql/platform/connection/common/constants';
|
||||
import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode';
|
||||
import * as TypeMoq from 'typemoq';
|
||||
import * as assert from 'assert';
|
||||
import { AsyncServerTreeDragAndDrop } from 'sql/workbench/services/objectExplorer/browser/asyncServerTreeDragAndDrop';
|
||||
|
||||
|
||||
|
||||
suite('AsyncServerTreeDragAndDrop', () => {
|
||||
let serverTreeDragAndDrop: AsyncServerTreeDragAndDrop;
|
||||
let msSQLCapabilities: ConnectionProviderProperties;
|
||||
let capabilitiesService: TestCapabilitiesService;
|
||||
|
||||
let iConnectionProfileId: IConnectionProfile = {
|
||||
connectionName: 'new name',
|
||||
serverName: 'new server',
|
||||
databaseName: 'database',
|
||||
userName: 'user',
|
||||
password: 'password',
|
||||
authenticationType: '',
|
||||
savePassword: true,
|
||||
groupFullName: 'g2/g2-2',
|
||||
groupId: 'group id',
|
||||
getOptionsKey: undefined!,
|
||||
matches: undefined!,
|
||||
providerName: mssqlProviderName,
|
||||
options: {},
|
||||
saveProfile: true,
|
||||
id: 'd936bb32-422b-49c3-963f-ae9532d63dc5'
|
||||
};
|
||||
|
||||
let connectionProfile = new ConnectionProfile(capabilitiesService, iConnectionProfileId);
|
||||
let connectionProfileArray = [connectionProfile];
|
||||
let connectionProfileGroupId = new ConnectionProfileGroup('name', undefined, 'd936bb32-422b-49c3-963f-ae9532d63dc5', 'color', 'description');
|
||||
let connectionProfileGroupArray = [connectionProfileGroupId];
|
||||
let treeNode = new TreeNode('Column', 'label', undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
|
||||
let treeNodeArray = [treeNode];
|
||||
|
||||
setup(() => {
|
||||
let instantiationService = new TestInstantiationService();
|
||||
instantiationService.stub(IStorageService, new TestStorageService());
|
||||
let mockConnectionManagementService = TypeMoq.Mock.ofType(ConnectionManagementService, TypeMoq.MockBehavior.Strict,
|
||||
undefined, //connection store
|
||||
undefined, // connectionstatusmanager
|
||||
undefined, // connectiondialog service
|
||||
instantiationService, // instantiation service
|
||||
undefined, // editor service
|
||||
undefined, // telemetryservice
|
||||
undefined, // configuration service
|
||||
new TestCapabilitiesService(), // capabilities service
|
||||
);
|
||||
serverTreeDragAndDrop = new AsyncServerTreeDragAndDrop(mockConnectionManagementService.object);
|
||||
|
||||
capabilitiesService = new TestCapabilitiesService();
|
||||
capabilitiesService.capabilities[mssqlProviderName] = { connection: msSQLCapabilities };
|
||||
});
|
||||
|
||||
|
||||
test('create new serverTreeDragAndDrop object should create serverTreeDragAndDrop object successfully', async () => {
|
||||
|
||||
assert.equal(serverTreeDragAndDrop !== null || serverTreeDragAndDrop !== undefined, true);
|
||||
});
|
||||
|
||||
test('able to get DragURI', async () => {
|
||||
let uri = serverTreeDragAndDrop.getDragURI(connectionProfile);
|
||||
assert.equal(connectionProfile.id, uri);
|
||||
|
||||
let uriGroup = serverTreeDragAndDrop.getDragURI(connectionProfileGroupId);
|
||||
assert.equal(connectionProfileGroupId.id, uriGroup);
|
||||
|
||||
let uriUndefined = serverTreeDragAndDrop.getDragURI(undefined);
|
||||
assert.equal(null, uriUndefined);
|
||||
|
||||
});
|
||||
|
||||
test('able to get DragLabel', async () => {
|
||||
let label = serverTreeDragAndDrop.getDragLabel(connectionProfileArray);
|
||||
assert.equal(connectionProfileArray[0].serverName, label);
|
||||
|
||||
let labelGroup = serverTreeDragAndDrop.getDragLabel(connectionProfileGroupArray);
|
||||
assert.equal(connectionProfileGroupArray[0].name, labelGroup);
|
||||
|
||||
let labelTreeNode = serverTreeDragAndDrop.getDragLabel(treeNodeArray);
|
||||
assert.equal(treeNodeArray[0].label, labelTreeNode);
|
||||
|
||||
let labelUndefined = serverTreeDragAndDrop.getDragLabel(undefined);
|
||||
assert.equal(undefined, labelUndefined);
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
@@ -9,17 +9,18 @@ import * as TypeChecker from 'vs/base/common/types';
|
||||
import { localize } from 'vs/nls';
|
||||
import * as strings from 'vs/base/common/strings';
|
||||
|
||||
export const DefaultServerGroupColor = '#515151';
|
||||
|
||||
export class ServerGroupViewModel {
|
||||
public groupName: string;
|
||||
public groupDescription?: string;
|
||||
public groupColor?: string;
|
||||
public colors: string[] = ['#515151', '#004760', '#771b00', '#700060', '#a17d01', '#006749', '#654502', '#3A0293'];
|
||||
public colors: string[] = [DefaultServerGroupColor, '#004760', '#771b00', '#700060', '#a17d01', '#006749', '#654502', '#3A0293'];
|
||||
|
||||
private _domainModel?: IConnectionProfileGroup;
|
||||
private _editMode: boolean;
|
||||
private readonly _addServerGroupTitle: string = localize('serverGroup.addServerGroup', "Add server group");
|
||||
private readonly _editServerGroupTitle: string = localize('serverGroup.editServerGroup', "Edit server group");
|
||||
private readonly _defaultColor: string = '#515151';
|
||||
|
||||
constructor(domainModel?: IConnectionProfileGroup, colors?: string[]) {
|
||||
// keep reference to domain model to be able to see if there are pending changes
|
||||
@@ -37,7 +38,7 @@ export class ServerGroupViewModel {
|
||||
// initialize defaults for a new group
|
||||
this.groupName = '';
|
||||
this.groupDescription = '';
|
||||
this.groupColor = this._defaultColor;
|
||||
this.groupColor = DefaultServerGroupColor;
|
||||
|
||||
this._editMode = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user