diff --git a/README.md b/README.md
index bfcf71c32..6a16b4974 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,7 @@ If you use Terraform to manage your infrastructure, MongoDB for VS Code helps yo
- `mdb.show`: Show or hide the MongoDB view.
- `mdb.defaultLimit`: The number of documents to fetch when viewing documents from a collection.
- `mdb.confirmRunAll`: Show a confirmation message before running commands in a playground.
+- `mdb.excludeFromPlaygroundsSearch`: Exclude files and folders while searching for playground in the the current workspace.
- `mdb.connectionSaving.hideOptionToChooseWhereToSaveNewConnections`: When a connection is added, a prompt is shown that let's the user decide where the new connection should be saved. When this setting is checked, the prompt is not shown and the default connection saving location setting is used.
- `mdb.connectionSaving.defaultConnectionSavingLocation`: When the setting that hides the option to choose where to save new connections is checked, this setting sets if and where new connections are saved.
- `mdb.useDefaultTemplateForPlayground`: Choose whether to use the default template for playground files or to start with an empty playground editor.
diff --git a/images/dark/file-light.svg b/images/dark/file-light.svg
new file mode 100644
index 000000000..ae7772f1f
--- /dev/null
+++ b/images/dark/file-light.svg
@@ -0,0 +1,4 @@
+
diff --git a/images/light/file-light.svg b/images/light/file-light.svg
new file mode 100644
index 000000000..0d13b1b37
--- /dev/null
+++ b/images/light/file-light.svg
@@ -0,0 +1,4 @@
+
diff --git a/package.json b/package.json
index ffd625657..2558c219f 100644
--- a/package.json
+++ b/package.json
@@ -71,6 +71,8 @@
"onCommand:mdb.removeConnection",
"onCommand:mdb.openMongoDBShell",
"onView:mongoDB",
+ "onView:mongoDBConnectionExplorer",
+ "onView:mongoDBPlaygroundsExplorer",
"onLanguage:json",
"onLanguage:mongodb"
],
@@ -87,16 +89,25 @@
"views": {
"mongoDB": [
{
- "id": "mongoDB",
- "name": "MongoDB",
- "when": "config.mdb.show == true"
+ "id": "mongoDBConnectionExplorer",
+ "name": "Connections",
+ "when": "config.mdb.showMongoDBConnectionExplorer == true"
+ },
+ {
+ "id": "mongoDBPlaygroundsExplorer",
+ "name": "Playgrounds",
+ "when": "config.mdb.showMongoDBPlaygrounds == true"
}
]
},
"viewsWelcome": [
{
- "view": "mongoDB",
+ "view": "mongoDBConnectionExplorer",
"contents": "No connections found.\n[Add Connection](command:mdb.connect)"
+ },
+ {
+ "view": "mongoDBPlaygroundsExplorer",
+ "contents": "No '.mongodb' playground files found in the workspace.\n[Create New Playground](command:mdb.createNewPlaygroundFromPlaygroundExplorer)"
}
],
"languages": [
@@ -168,10 +179,22 @@
"command": "mdb.createPlayground",
"title": "MongoDB: Create MongoDB Playground"
},
+ {
+ "command": "mdb.refreshPlaygrounds",
+ "title": "MongoDB: Refresh Playgrounds List"
+ },
+ {
+ "command": "mdb.refreshPlaygroundsFromTreeView",
+ "title": "Refresh"
+ },
{
"command": "mdb.createNewPlaygroundFromViewAction",
"title": "Create MongoDB Playground"
},
+ {
+ "command": "mdb.createNewPlaygroundFromPlaygroundExplorer",
+ "title": "Create MongoDB Playground"
+ },
{
"command": "mdb.changeActiveConnection",
"title": "MongoDB: Change Active Connection"
@@ -236,6 +259,10 @@
"dark": "images/dark/search-regular.svg"
}
},
+ {
+ "command": "mdb.openPlaygroundFromTreeItem",
+ "title": "Open Playground"
+ },
{
"command": "mdb.connectToConnectionTreeItem",
"title": "Connect"
@@ -317,176 +344,170 @@
"view/title": [
{
"command": "mdb.createNewPlaygroundFromViewAction",
- "when": "view == mongoDB"
+ "when": "view == mongoDBPlaygroundsExplorer"
+ },
+ {
+ "command": "mdb.refreshPlaygroundsFromTreeView",
+ "when": "view == mongoDBPlaygroundsExplorer"
},
{
"command": "mdb.addConnection",
- "when": "view == mongoDB"
+ "when": "view == mongoDBConnectionExplorer"
},
{
"command": "mdb.addConnectionWithURI",
- "when": "view == mongoDB"
+ "when": "view == mongoDBConnectionExplorer"
}
],
"view/item/context": [
- {
- "command": "mdb.addConnection",
- "when": "view == mongoDB && viewItem == mdbConnectionsTreeItem",
- "group": "inline"
- },
- {
- "command": "mdb.addConnection",
- "when": "view == mongoDB && viewItem == mdbConnectionsTreeItem"
- },
- {
- "command": "mdb.addConnectionWithURI",
- "when": "view == mongoDB && viewItem == mdbConnectionsTreeItem"
- },
{
"command": "mdb.addDatabase",
- "when": "view == mongoDB && viewItem == connectedConnectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == connectedConnectionTreeItem",
"group": "inline"
},
{
"command": "mdb.addDatabase",
- "when": "view == mongoDB && viewItem == connectedConnectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == connectedConnectionTreeItem",
"group": "1@1"
},
{
"command": "mdb.refreshConnection",
- "when": "view == mongoDB && viewItem == connectedConnectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == connectedConnectionTreeItem",
"group": "1@2"
},
{
"command": "mdb.treeViewOpenMongoDBShell",
- "when": "view == mongoDB && viewItem == connectedConnectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == connectedConnectionTreeItem",
"group": "2@1"
},
{
"command": "mdb.renameConnection",
- "when": "view == mongoDB && viewItem == connectedConnectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == connectedConnectionTreeItem",
"group": "3@1"
},
{
"command": "mdb.copyConnectionString",
- "when": "view == mongoDB && viewItem == connectedConnectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == connectedConnectionTreeItem",
"group": "4@1"
},
{
"command": "mdb.disconnectFromConnectionTreeItem",
- "when": "view == mongoDB && viewItem == connectedConnectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == connectedConnectionTreeItem",
"group": "5@1"
},
{
"command": "mdb.treeItemRemoveConnection",
- "when": "view == mongoDB && viewItem == connectedConnectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == connectedConnectionTreeItem",
"group": "5@2"
},
+ {
+ "command": "mdb.openPlaygroundFromTreeItem",
+ "when": "view == mongoDBPlaygroundsExplorer && viewItem == playgroundsTreeItem",
+ "group": "1@1"
+ },
{
"command": "mdb.connectToConnectionTreeItem",
- "when": "view == mongoDB && viewItem == disconnectedConnectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == disconnectedConnectionTreeItem",
"group": "1@1"
},
{
"command": "mdb.renameConnection",
- "when": "view == mongoDB && viewItem == disconnectedConnectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == disconnectedConnectionTreeItem",
"group": "2@1"
},
{
"command": "mdb.copyConnectionString",
- "when": "view == mongoDB && viewItem == disconnectedConnectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == disconnectedConnectionTreeItem",
"group": "3@1"
},
{
"command": "mdb.treeItemRemoveConnection",
- "when": "view == mongoDB && viewItem == disconnectedConnectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == disconnectedConnectionTreeItem",
"group": "4@1"
},
{
"command": "mdb.addCollection",
- "when": "view == mongoDB && viewItem == databaseTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == databaseTreeItem",
"group": "inline"
},
{
"command": "mdb.addCollection",
- "when": "view == mongoDB && viewItem == databaseTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == databaseTreeItem",
"group": "1@1"
},
{
"command": "mdb.refreshDatabase",
- "when": "view == mongoDB && viewItem == databaseTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == databaseTreeItem",
"group": "1@2"
},
{
"command": "mdb.copyDatabaseName",
- "when": "view == mongoDB && viewItem == databaseTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == databaseTreeItem",
"group": "2@1"
},
{
"command": "mdb.dropDatabase",
- "when": "view == mongoDB && viewItem == databaseTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == databaseTreeItem",
"group": "3@1"
},
{
"command": "mdb.viewCollectionDocuments",
- "when": "view == mongoDB && viewItem == collectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == collectionTreeItem",
"group": "1@1"
},
{
"command": "mdb.refreshCollection",
- "when": "view == mongoDB && viewItem == collectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == collectionTreeItem",
"group": "1@2"
},
{
"command": "mdb.copyCollectionName",
- "when": "view == mongoDB && viewItem == collectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == collectionTreeItem",
"group": "2@1"
},
{
"command": "mdb.dropCollection",
- "when": "view == mongoDB && viewItem == collectionTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == collectionTreeItem",
"group": "3@1"
},
{
"command": "mdb.searchForDocuments",
- "when": "view == mongoDB && viewItem == documentListTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == documentListTreeItem",
"group": "inline"
},
{
"command": "mdb.viewCollectionDocuments",
- "when": "view == mongoDB && viewItem == documentListTreeItem",
- "group": "1@1"
+ "when": "view == mongoDBConnectionExplorer && viewItem == documentListTreeItem"
},
{
"command": "mdb.refreshDocumentList",
- "when": "view == mongoDB && viewItem == documentListTreeItem",
- "group": "1@2"
+ "when": "view == mongoDBConnectionExplorer && viewItem == documentListTreeItem"
},
{
"command": "mdb.searchForDocuments",
- "when": "view == mongoDB && viewItem == documentListTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == documentListTreeItem",
"group": "2@1"
},
{
"command": "mdb.refreshSchema",
- "when": "view == mongoDB && viewItem == schemaTreeItem"
+ "when": "view == mongoDBConnectionExplorer && viewItem == schemaTreeItem"
},
{
"command": "mdb.copySchemaFieldName",
- "when": "view == mongoDB && viewItem == fieldTreeItem"
+ "when": "view == mongoDBConnectionExplorer && viewItem == fieldTreeItem"
},
{
"command": "mdb.createIndexFromTreeView",
- "when": "view == mongoDB && viewItem == indexListTreeItem",
+ "when": "view == mongoDBConnectionExplorer && viewItem == indexListTreeItem",
"group": "inline"
},
{
"command": "mdb.refreshIndexes",
- "when": "view == mongoDB && viewItem == indexListTreeItem"
+ "when": "view == mongoDBConnectionExplorer && viewItem == indexListTreeItem"
},
{
"command": "mdb.createIndexFromTreeView",
- "when": "view == mongoDB && viewItem == indexListTreeItem"
+ "when": "view == mongoDBConnectionExplorer && viewItem == indexListTreeItem"
}
],
"editor/title": [
@@ -497,6 +518,10 @@
}
],
"commandPalette": [
+ {
+ "command": "mdb.refreshPlaygroundsFromTreeView",
+ "when": "false"
+ },
{
"command": "mdb.searchForDocuments",
"when": "false"
@@ -513,6 +538,10 @@
"command": "mdb.createNewPlaygroundFromViewAction",
"when": "false"
},
+ {
+ "command": "mdb.createNewPlaygroundFromPlaygroundExplorer",
+ "when": "false"
+ },
{
"command": "mdb.changeActiveConnection",
"when": "false"
@@ -533,6 +562,10 @@
"command": "mdb.addDatabase",
"when": "false"
},
+ {
+ "command": "mdb.openPlaygroundFromTreeItem",
+ "when": "false"
+ },
{
"command": "mdb.connectToConnectionTreeItem",
"when": "false"
@@ -659,10 +692,38 @@
"default": "mongo",
"description": "The MongoDB shell to use."
},
- "mdb.show": {
+ "mdb.showMongoDBConnectionExplorer": {
+ "type": "boolean",
+ "default": true,
+ "description": "Show or hide the MongoDB connections view."
+ },
+ "mdb.showMongoDBPlaygrounds": {
"type": "boolean",
"default": true,
- "description": "Show or hide the MongoDB view."
+ "description": "Show or hide the MongoDB playgrounds view."
+ },
+ "mdb.excludeFromPlaygroundsSearch": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "Files and folders to exclude while searching for playground in the the current workspace.",
+ "default": [
+ "**/.!(todo|todos|task|tasks)/**",
+ "**/_output/**",
+ "**/bower_components/**",
+ "**/build/**",
+ "**/dist/**",
+ "**/node_modules/**",
+ "**/out/**",
+ "**/output/**",
+ "**/release/**",
+ "**/releases/**",
+ "**/static/**",
+ "**/target/**",
+ "**/third_party/**",
+ "**/vendor/**"
+ ]
},
"mdb.defaultLimit": {
"type": "number",
@@ -723,6 +784,7 @@
"debug": "^4.1.1",
"dotenv": "^8.2.0",
"encoding": "^0.1.12",
+ "micromatch": "^4.0.2",
"mongodb-cloud-info": "^1.1.2",
"mongodb-connection-model": "^16.1.4",
"mongodb-data-service": "^16.8.1",
diff --git a/src/editors/playgroundController.ts b/src/editors/playgroundController.ts
index fade0e6ae..ab1294ffe 100644
--- a/src/editors/playgroundController.ts
+++ b/src/editors/playgroundController.ts
@@ -371,6 +371,19 @@ export default class PlaygroundController {
return this.evaluatePlayground();
}
+ public openPlayground(filePath: string): Promise {
+ return new Promise(async (resolve) => {
+ await vscode.workspace.openTextDocument(filePath).then(
+ (doc) => vscode.window.showTextDocument(doc, 1, false),
+ (error) => {
+ vscode.window.showErrorMessage(`Unable to read file: ${filePath}`);
+ }
+ );
+
+ return resolve(true);
+ });
+ }
+
public deactivate(): void {
this._connectionController.removeEventListener(
DataServiceEventTypes.ACTIVE_CONNECTION_CHANGED,
diff --git a/src/explorer/documentListTreeItem.ts b/src/explorer/documentListTreeItem.ts
index a61ac9d6e..92031b46e 100644
--- a/src/explorer/documentListTreeItem.ts
+++ b/src/explorer/documentListTreeItem.ts
@@ -1,12 +1,11 @@
import * as vscode from 'vscode';
import * as numeral from 'numeral';
-const path = require('path');
-
import { createLogger } from '../logging';
import DocumentTreeItem from './documentTreeItem';
import TreeItemParent from './treeItemParentInterface';
import { getImagesPath } from '../extensionConstants';
+const path = require('path');
const log = createLogger('tree view document list');
// We fetch 1 more than this in order to see if there are more to fetch.
@@ -17,7 +16,7 @@ export const MAX_DOCUMENTS_VISIBLE = 10;
export const DOCUMENT_LIST_ITEM = 'documentListTreeItem';
export enum CollectionTypes {
collection = 'collection',
- view = 'view',
+ view = 'view'
}
const ITEM_LABEL = 'Documents';
@@ -201,6 +200,7 @@ export default class DocumentListTreeItem extends vscode.TreeItem
}
let documents;
+
try {
documents = await this.getDocuments();
} catch (err) {
diff --git a/src/explorer/explorerController.ts b/src/explorer/explorerController.ts
index 047dd6443..f1bdd9c21 100644
--- a/src/explorer/explorerController.ts
+++ b/src/explorer/explorerController.ts
@@ -29,14 +29,17 @@ export default class ExplorerController {
);
if (!this._treeView) {
- this._treeView = vscode.window.createTreeView('mongoDB', {
- treeDataProvider: this._treeController
- });
+ this._treeView = vscode.window.createTreeView(
+ 'mongoDBConnectionExplorer',
+ {
+ treeDataProvider: this._treeController
+ }
+ );
this._treeController.activateTreeViewEventHandlers(this._treeView);
}
};
- activateTreeView(): void {
+ activateConnectionsTreeView(): void {
// Listen for a change in connections to occur before we create the tree
// so that we show the `viewsWelcome` before any connections are added.
this._connectionController.addEventListener(
@@ -65,7 +68,9 @@ export default class ExplorerController {
}
// Exposed for testing.
- public getTreeView(): vscode.TreeView | undefined {
+ public getConnectionsTreeView():
+ | vscode.TreeView
+ | undefined {
return this._treeView;
}
diff --git a/src/explorer/explorerTreeController.ts b/src/explorer/explorerTreeController.ts
index a8a820579..3a2282373 100644
--- a/src/explorer/explorerTreeController.ts
+++ b/src/explorer/explorerTreeController.ts
@@ -1,36 +1,34 @@
import * as vscode from 'vscode';
-
import ConnectionController, {
DataServiceEventTypes
} from '../connectionController';
import { DOCUMENT_ITEM } from './documentTreeItem';
-import MDBConnectionsTreeItem from './mdbConnectionsTreeItem';
-
import { createLogger } from '../logging';
import { DOCUMENT_LIST_ITEM, CollectionTypes } from './documentListTreeItem';
+import ConnectionTreeItem from './connectionTreeItem';
+import { SavedConnection } from '../storage/storageController';
+import { sortTreeItemsByLabel } from './treeItemUtils';
const log = createLogger('explorer controller');
export default class ExplorerTreeController
implements vscode.TreeDataProvider {
private _connectionController: ConnectionController;
- private _mdbConnectionsTreeItem: MDBConnectionsTreeItem;
+ private _connectionTreeItems: { [key: string]: ConnectionTreeItem };
+ contextValue = 'explorerTreeController';
constructor(connectionController: ConnectionController) {
- this._mdbConnectionsTreeItem = new MDBConnectionsTreeItem(
- connectionController,
- {} // No cache to start.
- );
-
this._onDidChangeTreeData = new vscode.EventEmitter();
this.onDidChangeTreeData = this._onDidChangeTreeData.event;
-
this._connectionController = connectionController;
+
// Subscribe to changes in the connections.
this._connectionController.addEventListener(
DataServiceEventTypes.CONNECTIONS_DID_CHANGE,
this.refresh
);
+
+ this._connectionTreeItems = {}; // No cache to start.
}
removeListeners(): void {
@@ -122,7 +120,6 @@ export default class ExplorerTreeController
readonly onDidChangeTreeData: vscode.Event;
public refresh = (): Promise => {
- this._mdbConnectionsTreeItem.connectionsDidChange();
this._onDidChangeTreeData.fire();
return Promise.resolve(true);
@@ -132,21 +129,66 @@ export default class ExplorerTreeController
this._onDidChangeTreeData.fire();
}
- getTreeItem(element: MDBConnectionsTreeItem): vscode.TreeItem {
+ getTreeItem(element: vscode.TreeItem): vscode.TreeItem {
return element;
}
getChildren(element?: any): Thenable {
// When no element is present we are at the root.
if (!element) {
- // We rebuild the connections tree each time in order to
- // manually control the expanded state of tree items.
- this._mdbConnectionsTreeItem = new MDBConnectionsTreeItem(
- this._connectionController,
- this._mdbConnectionsTreeItem.getConnectionItemsCache()
- );
+ const connections = this._connectionController.getSavedConnections();
+ const pastConnectionTreeItems = this._connectionTreeItems;
+ this._connectionTreeItems = {};
+
+ // Create new connection tree items, using cached children wherever possible.
+ connections.forEach((connection: SavedConnection) => {
+ const isActiveConnection =
+ connection.id === this._connectionController.getActiveConnectionId();
+ const isBeingConnectedTo =
+ this._connectionController.isConnecting() &&
+ connection.id ===
+ this._connectionController.getConnectingConnectionId();
+
+ let connectionExpandedState = isActiveConnection
+ ? vscode.TreeItemCollapsibleState.Expanded
+ : vscode.TreeItemCollapsibleState.Collapsed;
- return Promise.resolve([this._mdbConnectionsTreeItem]);
+ if (
+ pastConnectionTreeItems[connection.id] &&
+ !pastConnectionTreeItems[connection.id].isExpanded
+ ) {
+ // Connection was manually collapsed while being active.
+ connectionExpandedState = vscode.TreeItemCollapsibleState.Collapsed;
+ }
+ if (
+ isActiveConnection &&
+ this._connectionController.isDisconnecting()
+ ) {
+ // Don't show a collapsable state when the connection is being disconnected from.
+ connectionExpandedState = vscode.TreeItemCollapsibleState.None;
+ }
+ if (isBeingConnectedTo) {
+ // Don't show a collapsable state when the connection is being connected to.
+ connectionExpandedState = vscode.TreeItemCollapsibleState.None;
+ }
+
+ this._connectionTreeItems[connection.id] = new ConnectionTreeItem(
+ connection.id,
+ connectionExpandedState,
+ isActiveConnection,
+ this._connectionController,
+ pastConnectionTreeItems[connection.id]
+ ? pastConnectionTreeItems[connection.id].cacheIsUpToDate
+ : false,
+ pastConnectionTreeItems[connection.id]
+ ? pastConnectionTreeItems[connection.id].getChildrenCache()
+ : {}
+ );
+ });
+
+ return Promise.resolve(
+ sortTreeItemsByLabel(Object.values(this._connectionTreeItems))
+ );
}
return element.getChildren();
diff --git a/src/explorer/index.ts b/src/explorer/index.ts
index b3cd5f00c..cb72b20fe 100644
--- a/src/explorer/index.ts
+++ b/src/explorer/index.ts
@@ -5,6 +5,7 @@ import DatabaseTreeItem from './databaseTreeItem';
import { CollectionTypes } from './documentListTreeItem';
import DocumentTreeItem from './documentTreeItem';
import SchemaTreeItem from './schemaTreeItem';
+import PlaygroundsExplorer from './playgroundsExplorer';
export {
CollectionTreeItem,
@@ -13,5 +14,6 @@ export {
DatabaseTreeItem,
ExplorerController,
DocumentTreeItem,
- SchemaTreeItem
+ SchemaTreeItem,
+ PlaygroundsExplorer
};
diff --git a/src/explorer/mdbConnectionsTreeItem.ts b/src/explorer/mdbConnectionsTreeItem.ts
deleted file mode 100644
index 855af6738..000000000
--- a/src/explorer/mdbConnectionsTreeItem.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-import * as vscode from 'vscode';
-
-import ConnectionController from '../connectionController';
-import ConnectionTreeItem from './connectionTreeItem';
-import TreeItemParent from './treeItemParentInterface';
-import { SavedConnection } from '../storage/storageController';
-import { sortTreeItemsByLabel } from './treeItemUtils';
-
-const rootLabel = 'Connections';
-const rootTooltip = 'Your MongoDB connections';
-
-export default class MDBConnectionsTreeItem extends vscode.TreeItem
- implements TreeItemParent, vscode.TreeDataProvider {
- contextValue = 'mdbConnectionsTreeItem';
-
- private _connectionController: ConnectionController;
- private _connectionTreeItems: { [key: string]: ConnectionTreeItem };
-
- isExpanded = true;
- cacheIsUpToDate = false; // Unused because this is a synchronous resource.
- needsToRefreshExpansionState = false;
-
- constructor(
- connectionController: ConnectionController,
- existingConnectionItemsCache: { [key: string]: ConnectionTreeItem }
- ) {
- super(rootLabel, vscode.TreeItemCollapsibleState.Expanded);
-
- this._connectionController = connectionController;
-
- this._connectionTreeItems = existingConnectionItemsCache;
- }
-
- get tooltip(): string {
- return rootTooltip;
- }
-
- getTreeItem(element: vscode.TreeItem): vscode.TreeItem {
- return element;
- }
-
- getChildren(): Thenable {
- const connections = this._connectionController.getSavedConnections();
- const pastConnectionTreeItems = this._connectionTreeItems;
- this._connectionTreeItems = {};
-
- // Create new connection tree items, using cached children wherever possible.
- connections.forEach((connection: SavedConnection) => {
- const isActiveConnection =
- connection.id ===
- this._connectionController.getActiveConnectionId();
- const isBeingConnectedTo =
- this._connectionController.isConnecting() &&
- connection.id ===
- this._connectionController.getConnectingConnectionId();
-
- let connectionExpandedState = isActiveConnection
- ? vscode.TreeItemCollapsibleState.Expanded
- : vscode.TreeItemCollapsibleState.Collapsed;
-
- if (
- pastConnectionTreeItems[connection.id] &&
- !pastConnectionTreeItems[connection.id].isExpanded
- ) {
- // Connection was manually collapsed while being active.
- connectionExpandedState = vscode.TreeItemCollapsibleState.Collapsed;
- }
- if (isActiveConnection && this._connectionController.isDisconnecting()) {
- // Don't show a collapsable state when the connection is being disconnected from.
- connectionExpandedState = vscode.TreeItemCollapsibleState.None;
- }
- if (isBeingConnectedTo) {
- // Don't show a collapsable state when the connection is being connected to.
- connectionExpandedState = vscode.TreeItemCollapsibleState.None;
- }
-
- this._connectionTreeItems[connection.id] = new ConnectionTreeItem(
- connection.id,
- connectionExpandedState,
- isActiveConnection,
- this._connectionController,
- pastConnectionTreeItems[connection.id]
- ? pastConnectionTreeItems[connection.id].cacheIsUpToDate
- : false,
- pastConnectionTreeItems[connection.id]
- ? pastConnectionTreeItems[connection.id].getChildrenCache()
- : {}
- );
- });
-
- return Promise.resolve(
- sortTreeItemsByLabel(Object.values(this._connectionTreeItems))
- );
- }
-
- connectionsDidChange(): void {
- // When the connections change, like a connection is added or removed,
- // we want to open the connections dropdown if it's collapsed.
- if (!this.isExpanded) {
- this.needsToRefreshExpansionState = true;
- }
- }
-
- onDidCollapse(): void {
- this.isExpanded = false;
- }
-
- onDidExpand(): Promise {
- this.isExpanded = true;
- return Promise.resolve(true);
- }
-
- getConnectionItemsCache(): { [key: string]: ConnectionTreeItem } {
- return this._connectionTreeItems;
- }
-}
diff --git a/src/explorer/playgroundsExplorer.ts b/src/explorer/playgroundsExplorer.ts
new file mode 100644
index 000000000..3c19bb49b
--- /dev/null
+++ b/src/explorer/playgroundsExplorer.ts
@@ -0,0 +1,50 @@
+import * as vscode from 'vscode';
+import PlaygroundsTree from './playgroundsTree';
+import { createLogger } from '../logging';
+
+const log = createLogger('explorer controller');
+
+export default class PlaygroundsExplorer {
+ private _treeController: PlaygroundsTree;
+ private _treeView?: vscode.TreeView;
+
+ constructor() {
+ log.info('activate playgrounds explorer');
+ this._treeController = new PlaygroundsTree();
+ }
+
+ private createPlaygroundsTreeView = (): void => {
+ if (!this._treeView) {
+ this._treeView = vscode.window.createTreeView(
+ 'mongoDBPlaygroundsExplorer',
+ {
+ treeDataProvider: this._treeController
+ }
+ );
+ this._treeController.activateTreeViewEventHandlers(this._treeView);
+ }
+ };
+
+ public async activatePlaygroundsTreeView(): Promise {
+ this.createPlaygroundsTreeView();
+ }
+
+ public deactivate(): void {
+ if (this._treeView) {
+ this._treeView.dispose();
+ delete this._treeView;
+ }
+ }
+
+ public refresh(): Promise {
+ if (this._treeController) {
+ return this._treeController.refresh();
+ }
+
+ return Promise.reject(new Error('No tree to refresh.'));
+ }
+
+ public getTreeController(): PlaygroundsTree {
+ return this._treeController;
+ }
+}
diff --git a/src/explorer/playgroundsTree.ts b/src/explorer/playgroundsTree.ts
new file mode 100644
index 000000000..7181f443a
--- /dev/null
+++ b/src/explorer/playgroundsTree.ts
@@ -0,0 +1,246 @@
+import * as vscode from 'vscode';
+import * as fs from 'fs';
+import * as path from 'path';
+import PlaygroundsTreeHeader from './playgroundsTreeHeader';
+import { PLAYGROUND_ITEM } from './playgroundsTreeItem';
+import { createLogger } from '../logging';
+import PlaygroundsTreeItem from './playgroundsTreeItem';
+
+const micromatch = require('micromatch');
+const log = createLogger('explorer controller');
+
+export class FileStat implements vscode.FileStat {
+ constructor(private fsStat: fs.Stats) {}
+
+ get type(): vscode.FileType {
+ return this.fsStat.isFile()
+ ? vscode.FileType.File
+ : this.fsStat.isDirectory()
+ ? vscode.FileType.Directory
+ : this.fsStat.isSymbolicLink()
+ ? vscode.FileType.SymbolicLink
+ : vscode.FileType.Unknown;
+ }
+
+ get isFile(): boolean | undefined {
+ return this.fsStat.isFile();
+ }
+
+ get isDirectory(): boolean | undefined {
+ return this.fsStat.isDirectory();
+ }
+
+ get isSymbolicLink(): boolean | undefined {
+ return this.fsStat.isSymbolicLink();
+ }
+
+ get size(): number {
+ return this.fsStat.size;
+ }
+
+ get ctime(): number {
+ return this.fsStat.ctime.getTime();
+ }
+
+ get mtime(): number {
+ return this.fsStat.mtime.getTime();
+ }
+}
+
+export default class PlaygroundsTree
+ implements vscode.TreeDataProvider {
+ public excludeFromPlaygroundsSearch: string[];
+ private _playgroundsTreeHeaders: PlaygroundsTreeHeader[];
+ private _onDidChangeTreeData: vscode.EventEmitter;
+ private _playgroundsTreeItems: { [key: string]: PlaygroundsTreeItem };
+ readonly onDidChangeTreeData: vscode.Event;
+
+ contextValue = 'playgroundsTree';
+
+ constructor() {
+ this.excludeFromPlaygroundsSearch =
+ vscode.workspace
+ .getConfiguration('mdb')
+ .get('excludeFromPlaygroundsSearch') || [];
+ this._playgroundsTreeHeaders = [];
+ this._playgroundsTreeItems = {};
+ this._onDidChangeTreeData = new vscode.EventEmitter();
+ this.onDidChangeTreeData = this._onDidChangeTreeData.event;
+ }
+
+ public activateTreeViewEventHandlers = (
+ treeView: vscode.TreeView
+ ): void => {
+ treeView.onDidCollapseElement((event: any) => {
+ log.info('Tree item was collapsed:', event.element.label);
+
+ if (event.element.onDidCollapse) {
+ event.element.onDidCollapse();
+ }
+
+ if (event.element.doesNotRequireTreeUpdate) {
+ // When the element is already loaded (synchronous), we do not need to
+ // fully refresh the tree.
+ return;
+ }
+
+ this.onTreeItemUpdate();
+ });
+
+ treeView.onDidExpandElement(
+ (event: any): Promise => {
+ log.info('Tree item was expanded:', event.element.label);
+
+ return new Promise((resolve, reject) => {
+ if (!event.element.onDidExpand) {
+ return resolve();
+ }
+
+ event.element.onDidExpand().then(
+ () => {
+ if (event.element.doesNotRequireTreeUpdate) {
+ // When the element is already loaded (synchronous), we do not
+ // need to fully refresh the tree.
+ return resolve();
+ }
+
+ this.onTreeItemUpdate();
+
+ resolve();
+ },
+ (err: Error) => {
+ reject(err);
+ }
+ );
+ });
+ }
+ );
+
+ treeView.onDidChangeSelection((event: any) => {
+ if (event.selection && event.selection.length === 1) {
+ const selectedItem = event.selection[0];
+
+ if (selectedItem.contextValue === PLAYGROUND_ITEM) {
+ vscode.commands.executeCommand(
+ 'mdb.openPlaygroundFromTreeItem',
+ selectedItem
+ );
+ }
+ }
+ });
+ };
+
+ public refresh = (): Promise => {
+ this.excludeFromPlaygroundsSearch =
+ vscode.workspace
+ .getConfiguration('mdb')
+ .get('excludeFromPlaygroundsSearch') || [];
+
+ this._onDidChangeTreeData.fire();
+
+ return Promise.resolve(true);
+ };
+
+ public onTreeItemUpdate(): void {
+ this._onDidChangeTreeData.fire();
+ }
+
+ public getTreeItem(element: PlaygroundsTreeHeader): vscode.TreeItem {
+ return element;
+ }
+
+ private getFileNames(path: string): Promise {
+ return new Promise((resolve, reject) => {
+ fs.readdir(path, (error, files) => {
+ if (error) {
+ reject(error);
+ } else {
+ resolve(files);
+ }
+ });
+ });
+ }
+
+ private stat(path: string): Promise {
+ return new Promise((resolve, reject) => {
+ fs.stat(path, (error, stat) => {
+ if (error) {
+ reject(error);
+ } else {
+ resolve(stat);
+ }
+ });
+ });
+ }
+
+ private async getStat(path: string): Promise {
+ return new FileStat(await this.stat(path));
+ }
+
+ public async readDirectory(uri: vscode.Uri): Promise {
+ const fileNames = await this.getFileNames(uri.fsPath);
+
+ for (let i = 0; i < fileNames.length; i++) {
+ const fileName = fileNames[i];
+
+ try {
+ const stat = await this.getStat(path.join(uri.fsPath, fileName));
+ const fileUri = vscode.Uri.file(path.join(uri.fsPath, fileName));
+ const fileNameParts = fileName.split('.');
+
+ if (
+ stat.type === vscode.FileType.File &&
+ fileNameParts.length > 1 &&
+ fileNameParts.pop() === 'mongodb'
+ ) {
+ this._playgroundsTreeItems[fileUri.fsPath] = new PlaygroundsTreeItem(
+ fileName,
+ fileUri.fsPath
+ );
+ } else if (
+ stat.type === vscode.FileType.Directory &&
+ !micromatch.isMatch(fileName, this.excludeFromPlaygroundsSearch)
+ ) {
+ await this.readDirectory(fileUri);
+ }
+ } catch (error) {}
+ }
+ }
+
+ public async getPlaygrounds(folderUri: vscode.Uri): Promise {
+ this._playgroundsTreeItems = {};
+
+ await this.readDirectory(folderUri);
+
+ return this._playgroundsTreeItems;
+ }
+
+ public async getChildren(element?: any): Promise {
+ // When no element is present we are at the root.
+ if (!element) {
+ this._playgroundsTreeHeaders = [];
+
+ let workspaceFolders = vscode.workspace.workspaceFolders;
+
+ if (workspaceFolders) {
+ workspaceFolders = workspaceFolders.filter(
+ (folder) => folder.uri.scheme === 'file'
+ );
+
+ for (const folder of workspaceFolders) {
+ const playgrounds = await this.getPlaygrounds(folder.uri);
+
+ if (Object.keys(playgrounds).length > 0) {
+ this._playgroundsTreeHeaders.push(
+ new PlaygroundsTreeHeader(folder.uri, playgrounds)
+ );
+ }
+ }
+ }
+
+ return Promise.resolve(this._playgroundsTreeHeaders);
+ }
+
+ return element.getChildren();
+ }
+}
diff --git a/src/explorer/playgroundsTreeHeader.ts b/src/explorer/playgroundsTreeHeader.ts
new file mode 100644
index 000000000..b42ae8ae7
--- /dev/null
+++ b/src/explorer/playgroundsTreeHeader.ts
@@ -0,0 +1,50 @@
+import * as vscode from 'vscode';
+import TreeItemParent from './treeItemParentInterface';
+import PlaygroundsTreeItem from './playgroundsTreeItem';
+import { sortTreeItemsByLabel } from './treeItemUtils';
+
+const rootTooltip = 'Your MongoDB playgrounds';
+
+export default class PlaygroundsTreeHeader extends vscode.TreeItem
+ implements TreeItemParent, vscode.TreeDataProvider {
+ private _playgroundsTreeItems: { [key: string]: PlaygroundsTreeItem };
+
+ contextValue = 'playgroundsTreeHeader';
+ isExpanded = true;
+ doesNotRequireTreeUpdate = true;
+ cacheIsUpToDate = true;
+
+ constructor(
+ fileUri: vscode.Uri,
+ playgroundsTreeItems: {
+ [key: string]: PlaygroundsTreeItem;
+ }
+ ) {
+ super(fileUri.path, vscode.TreeItemCollapsibleState.Expanded);
+ this._playgroundsTreeItems = playgroundsTreeItems;
+ }
+
+ get tooltip(): string {
+ return rootTooltip;
+ }
+
+ public getTreeItem(element: vscode.TreeItem): vscode.TreeItem {
+ return element;
+ }
+
+ public async getChildren(): Promise {
+ return Promise.resolve(
+ sortTreeItemsByLabel(Object.values(this._playgroundsTreeItems))
+ );
+ }
+
+ public onDidCollapse(): void {
+ this.isExpanded = false;
+ }
+
+ public onDidExpand(): Promise {
+ this.isExpanded = true;
+
+ return Promise.resolve(true);
+ }
+}
diff --git a/src/explorer/playgroundsTreeItem.ts b/src/explorer/playgroundsTreeItem.ts
new file mode 100644
index 000000000..7ea69e020
--- /dev/null
+++ b/src/explorer/playgroundsTreeItem.ts
@@ -0,0 +1,46 @@
+import * as vscode from 'vscode';
+import path = require('path');
+import { getImagesPath } from '../extensionConstants';
+
+export const PLAYGROUND_ITEM = 'playgroundsTreeItem';
+
+export default class PlaygroundsTreeItem extends vscode.TreeItem
+ implements vscode.TreeDataProvider {
+ public filePath: string;
+
+ contextValue = PLAYGROUND_ITEM;
+
+ constructor(fileName: string, filePath: string) {
+ super(fileName);
+ this.filePath = filePath;
+ }
+
+ get tooltip(): string {
+ return this.filePath;
+ }
+
+ get description(): string {
+ return '';
+ }
+
+ get iconPath():
+ | string
+ | vscode.Uri
+ | { light: string | vscode.Uri; dark: string | vscode.Uri } {
+ const LIGHT = path.join(getImagesPath(), 'light');
+ const DARK = path.join(getImagesPath(), 'dark');
+
+ return {
+ light: path.join(LIGHT, 'file-light.svg'),
+ dark: path.join(DARK, 'file-light.svg')
+ };
+ }
+
+ public getTreeItem(element: PlaygroundsTreeItem): PlaygroundsTreeItem {
+ return element;
+ }
+
+ public getChildren(): Thenable {
+ return Promise.resolve([]);
+ }
+}
diff --git a/src/language/languageServerController.ts b/src/language/languageServerController.ts
index b83740c76..51f63966f 100644
--- a/src/language/languageServerController.ts
+++ b/src/language/languageServerController.ts
@@ -104,7 +104,7 @@ export default class LanguageServerController {
);
}
- public activate(): void {
+ public startLanguageServer(): void {
// Start the client. This will also launch the server
const disposable = this.client.start();
diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts
index 462620d8f..304bdd395 100644
--- a/src/mdbExtensionController.ts
+++ b/src/mdbExtensionController.ts
@@ -8,7 +8,11 @@ import * as vscode from 'vscode';
import ConnectionController from './connectionController';
import launchMongoShell from './commands/launchMongoShell';
import { EditorsController, PlaygroundController } from './editors';
-import { ExplorerController, CollectionTreeItem } from './explorer';
+import {
+ ExplorerController,
+ PlaygroundsExplorer,
+ CollectionTreeItem
+} from './explorer';
import { LanguageServerController } from './language';
import TelemetryController from './telemetry/telemetryController';
import { StatusView } from './views';
@@ -22,6 +26,7 @@ import DocumentTreeItem from './explorer/documentTreeItem';
import WebviewController from './views/webviewController';
import FieldTreeItem from './explorer/fieldTreeItem';
import IndexListTreeItem from './explorer/indexListTreeItem';
+import PlaygroundsTreeItem from './explorer/playgroundsTreeItem';
const log = createLogger('commands');
@@ -33,6 +38,7 @@ export default class MDBExtensionController implements vscode.Disposable {
_editorsController: EditorsController;
_playgroundController: PlaygroundController;
_explorerController: ExplorerController;
+ _playgroundsExplorer: PlaygroundsExplorer;
_statusView: StatusView;
_storageController: StorageController;
_telemetryController: TelemetryController;
@@ -69,6 +75,7 @@ export default class MDBExtensionController implements vscode.Disposable {
this._explorerController = new ExplorerController(
this._connectionController
);
+ this._playgroundsExplorer = new PlaygroundsExplorer();
this._playgroundController = new PlaygroundController(
context,
this._connectionController,
@@ -82,10 +89,11 @@ export default class MDBExtensionController implements vscode.Disposable {
}
activate(): void {
- this._explorerController.activateTreeView();
+ this._explorerController.activateConnectionsTreeView();
+ this._playgroundsExplorer.activatePlaygroundsTreeView();
this._connectionController.loadSavedConnections();
- this._telemetryController.activate();
- this._languageServerController.activate();
+ this._telemetryController.activateSegmentAnalytics();
+ this._languageServerController.startLanguageServer();
log.info('Registering commands...');
@@ -118,6 +126,9 @@ export default class MDBExtensionController implements vscode.Disposable {
this.registerCommand('mdb.createNewPlaygroundFromViewAction', () =>
this._playgroundController.createPlayground()
);
+ this.registerCommand('mdb.createNewPlaygroundFromPlaygroundExplorer', () =>
+ this._playgroundController.createPlayground()
+ );
this.registerCommand('mdb.runSelectedPlaygroundBlocks', () =>
this._playgroundController.runSelectedPlaygroundBlocks()
);
@@ -130,6 +141,9 @@ export default class MDBExtensionController implements vscode.Disposable {
this.registerCommand('mdb.changeActiveConnection', () =>
this._connectionController.changeActiveConnection()
);
+ this.registerCommand('mdb.refreshPlaygrounds', () =>
+ this._playgroundsExplorer.refresh()
+ );
this.registerCommand('mdb.startStreamLanguageServerLogs', () =>
this._languageServerController.startStreamLanguageServerLogs()
@@ -177,13 +191,20 @@ export default class MDBExtensionController implements vscode.Disposable {
this.registerCommand('mdb.addConnectionWithURI', () =>
this._connectionController.connectWithURI()
);
+ this.registerCommand('mdb.refreshPlaygroundsFromTreeView', () =>
+ this._playgroundsExplorer.refresh()
+ );
+ this.registerCommand(
+ 'mdb.openPlaygroundFromTreeItem',
+ (playgroundsTreeItem: PlaygroundsTreeItem) =>
+ this._playgroundController.openPlayground(playgroundsTreeItem.filePath)
+ );
this.registerCommand(
'mdb.connectToConnectionTreeItem',
- (connectionTreeItem: ConnectionTreeItem) => {
- return this._connectionController.connectWithConnectionId(
+ (connectionTreeItem: ConnectionTreeItem) =>
+ this._connectionController.connectWithConnectionId(
connectionTreeItem.connectionId
- );
- }
+ )
);
this.registerCommand('mdb.disconnectFromConnectionTreeItem', () => {
// In order for this command to be activated, the connection must
@@ -195,6 +216,7 @@ export default class MDBExtensionController implements vscode.Disposable {
(connectionTreeItem: ConnectionTreeItem) => {
connectionTreeItem.resetCache();
this._explorerController.refresh();
+
return Promise.resolve(true);
}
);
@@ -273,18 +295,18 @@ export default class MDBExtensionController implements vscode.Disposable {
);
this.registerCommand(
'mdb.searchForDocuments',
- (element: DocumentListTreeItem): Promise => {
- return this._playgroundController.createPlaygroundForSearch(
+ (element: DocumentListTreeItem): Promise =>
+ this._playgroundController.createPlaygroundForSearch(
element.databaseName,
element.collectionName
- );
- }
+ )
);
this.registerCommand(
'mdb.copyDatabaseName',
async (element: DatabaseTreeItem) => {
await vscode.env.clipboard.writeText(element.databaseName);
vscode.window.showInformationMessage('Copied to clipboard.');
+
return true;
}
);
@@ -439,6 +461,7 @@ export default class MDBExtensionController implements vscode.Disposable {
// TODO: Cancel active queries/playgrounds.
this._connectionController.disconnect();
this._explorerController.deactivate();
+ this._playgroundsExplorer.deactivate();
this._playgroundController.deactivate();
this._telemetryController.deactivate();
this._languageServerController.deactivate();
diff --git a/src/telemetry/telemetryController.ts b/src/telemetry/telemetryController.ts
index 4421c85ae..2c9d8a6b5 100644
--- a/src/telemetry/telemetryController.ts
+++ b/src/telemetry/telemetryController.ts
@@ -128,7 +128,7 @@ export default class TelemetryController {
return this._segmentKey;
}
- public activate(): void {
+ public activateSegmentAnalytics(): void {
if (this._segmentKey) {
this._segmentAnalytics = new SegmentAnalytics(this._segmentKey, {
// Segment batches messages and flushes asynchronously to the server.
diff --git a/src/test/suite/explorer/explorerController.test.ts b/src/test/suite/explorer/explorerController.test.ts
index 179905e99..5bf8f4bd0 100644
--- a/src/test/suite/explorer/explorerController.test.ts
+++ b/src/test/suite/explorer/explorerController.test.ts
@@ -46,29 +46,6 @@ suite('Explorer Controller Test Suite', function () {
sinon.restore();
});
- test('should have a connections root', async () => {
- const testExplorerController =
- mdbTestExtension.testExtensionController._explorerController;
- const treeController = testExplorerController.getTreeController();
-
- assert(!!treeController, 'Tree controller should not be undefined');
-
- try {
- const treeControllerChildren = await treeController.getChildren();
-
- assert(
- treeControllerChildren.length === 1,
- `Tree controller should have 1 child, found ${treeControllerChildren.length}`
- );
- assert(
- treeControllerChildren[0].label === 'Connections',
- 'Tree controller should have a "Connections" child'
- );
- } catch (error) {
- assert(false, error);
- }
- });
-
test('it updates the connections to account for a change in the connection controller', async () => {
const testConnectionController =
mdbTestExtension.testExtensionController._connectionController;
@@ -90,18 +67,7 @@ suite('Explorer Controller Test Suite', function () {
testConnectionController.setConnnectingConnectionId(mockConnectionId);
testConnectionController.setConnnecting(true);
- const treeControllerChildren = await treeController.getChildren();
-
- assert(
- treeControllerChildren.length === 1,
- `Tree controller should have 1 child, found ${treeControllerChildren.length}`
- );
- assert(
- treeControllerChildren[0].label === 'Connections',
- 'Tree controller should have a "Connections" child'
- );
-
- const connectionsItems = await treeControllerChildren[0].getChildren();
+ const connectionsItems = await treeController.getChildren();
assert(
connectionsItems.length === 1,
@@ -118,7 +84,7 @@ suite('Explorer Controller Test Suite', function () {
}
});
- test('when a connection is added and connected it is added to the tree and expanded', async () => {
+ test('when a connection is added and connected it is added to the tree', async () => {
const testConnectionController =
mdbTestExtension.testExtensionController._connectionController;
const testExplorerController =
@@ -147,8 +113,7 @@ suite('Explorer Controller Test Suite', function () {
}' found ${activeId}`
);
- const treeControllerChildren = await treeController.getChildren();
- const connectionsItems = await treeControllerChildren[0].getChildren();
+ const connectionsItems = await treeController.getChildren();
assert(
connectionsItems.length === 1,
@@ -162,10 +127,6 @@ suite('Explorer Controller Test Suite', function () {
connectionsItems[0].description === 'connected',
'There should be a connection tree item with the description "connected"'
);
- assert(
- connectionsItems[0].isExpanded,
- 'Expected the connection tree item to be expanded'
- );
});
test('only the active connection is displayed as connected in the tree', async () => {
@@ -205,8 +166,7 @@ suite('Explorer Controller Test Suite', function () {
/* Silent fail (should fail) */
}
- const treeControllerChildren = await treeController.getChildren();
- const connectionsItems = await treeControllerChildren[0].getChildren();
+ const connectionsItems = await treeController.getChildren();
assert(
connectionsItems.length === 2,
@@ -259,8 +219,7 @@ suite('Explorer Controller Test Suite', function () {
storageLocation: StorageScope.WORKSPACE
};
- const treeControllerChildren = await treeController.getChildren();
- const connectionsItems = await treeControllerChildren[0].getChildren();
+ const connectionsItems = await treeController.getChildren();
assert(
connectionsItems.length === 3,
@@ -277,7 +236,8 @@ suite('Explorer Controller Test Suite', function () {
testConnectionController._connections.zzz.name = '111';
- const afterAdditionConnectionsItems = await treeControllerChildren[0].getChildren();
+ const afterAdditionConnectionsItems = await treeController.getChildren();
+
assert(
afterAdditionConnectionsItems[0].label === '111',
`First connection tree item should have label "111" found ${afterAdditionConnectionsItems[0].label}`
@@ -297,12 +257,7 @@ suite('Explorer Controller Test Suite', function () {
TEST_DATABASE_URI
);
- const treeControllerChildren = await treeController.getChildren();
- const connectionsItems = await treeControllerChildren[0].getChildren();
-
- // Expand the connection.
- treeControllerChildren[0].onDidExpand();
-
+ const connectionsItems = await treeController.getChildren();
const databaseItems = await connectionsItems[0].getChildren();
assert(
@@ -328,9 +283,7 @@ suite('Explorer Controller Test Suite', function () {
TEST_DATABASE_URI
);
- const rootTreeItem = await treeController.getChildren();
- const connectionsTreeItem = rootTreeItem[0];
- const connectionsItems = await connectionsTreeItem.getChildren();
+ const connectionsItems = await treeController.getChildren();
// Expand the connection.
const testConnectionTreeItem = connectionsItems[0];
@@ -378,7 +331,7 @@ suite('Explorer Controller Test Suite', function () {
const testExplorerController =
mdbTestExtension.testExtensionController._explorerController;
- assert(testExplorerController.getTreeView() === undefined);
+ assert(testExplorerController.getConnectionsTreeView() === undefined);
});
test('tree view should call create tree view after a "CONNECTIONS_DID_CHANGE" event', async () => {
@@ -387,7 +340,7 @@ suite('Explorer Controller Test Suite', function () {
const testExplorerController =
mdbTestExtension.testExtensionController._explorerController;
- testExplorerController.activateTreeView();
+ testExplorerController.activateConnectionsTreeView();
const treeControllerStub = sinon.stub().returns(null);
diff --git a/src/test/suite/explorer/playgroundsExplorer.test.ts b/src/test/suite/explorer/playgroundsExplorer.test.ts
new file mode 100644
index 000000000..44bcaf0cf
--- /dev/null
+++ b/src/test/suite/explorer/playgroundsExplorer.test.ts
@@ -0,0 +1,134 @@
+import * as assert from 'assert';
+import * as vscode from 'vscode';
+import { before, afterEach } from 'mocha';
+import { mdbTestExtension } from '../stubbableMdbExtension';
+import PlaygroundsTree from './../../../explorer/playgroundsTree';
+
+suite('Playgrounds Controller Test Suite', () => {
+ const testPlaygroundsExplorer =
+ mdbTestExtension.testExtensionController._playgroundsExplorer;
+ let excludeFromPlaygroundsSearchDefault: string[];
+
+ before(async () => {
+ excludeFromPlaygroundsSearchDefault =
+ (await vscode.workspace
+ .getConfiguration('mdb')
+ .get('excludeFromPlaygroundsSearch')) || [];
+ });
+
+ afterEach(async () => {
+ await vscode.workspace
+ .getConfiguration('mdb')
+ .update(
+ 'excludeFromPlaygroundsSearch',
+ excludeFromPlaygroundsSearchDefault
+ );
+ });
+
+ test('should show a welcome view if playgrounds list is empty', async () => {
+ const treeController = testPlaygroundsExplorer.getTreeController();
+
+ assert(!!treeController, 'Tree controller should not be undefined');
+
+ try {
+ const treeControllerChildren = await treeController.getChildren();
+
+ assert(
+ treeControllerChildren.length === 0,
+ `Tree controller should have 0 child, found ${treeControllerChildren.length}`
+ );
+ } catch (error) {
+ assert(false, error);
+ }
+ });
+
+ test('should search for playground in the workspace', async () => {
+ const workspaceFolders = (vscode.workspace.workspaceFolders || []).filter(
+ (folder) => folder.uri.scheme === 'file'
+ );
+ const rootPath = workspaceFolders[0]?.uri.path.replace('/out/test', '');
+ const rootUri = vscode.Uri.parse(rootPath);
+ const treeController = testPlaygroundsExplorer.getTreeController();
+
+ try {
+ const children = await treeController.getPlaygrounds(rootUri);
+
+ assert(
+ Object.keys(children).length === 4,
+ `Tree playgrounds should have 4 child, found ${children.length}`
+ );
+
+ const playgrounds = Object.values(children).filter(
+ (item: any) => item.label && item.label.split('.').pop() === 'mongodb'
+ );
+
+ assert(
+ Object.keys(playgrounds).length === 4,
+ `Tree playgrounds should have 4 playgrounds with mongodb extension, found ${children.length}`
+ );
+ } catch (error) {
+ assert(false, error);
+ }
+ });
+
+ test('should exclude folders and files specified in extension settings', async () => {
+ const workspaceFolders = (vscode.workspace.workspaceFolders || []).filter(
+ (folder) => folder.uri.scheme === 'file'
+ );
+ const rootPath = workspaceFolders[0]?.uri.path.replace('/out/test', '');
+ const rootUri = vscode.Uri.parse(rootPath);
+
+ try {
+ await vscode.workspace
+ .getConfiguration('mdb')
+ .update(
+ 'excludeFromPlaygroundsSearch',
+ excludeFromPlaygroundsSearchDefault.concat(['**/playgrounds/**'])
+ );
+
+ const treeController = new PlaygroundsTree();
+ const children = await treeController.getPlaygrounds(rootUri);
+
+ assert(
+ Object.keys(children).length === 3,
+ `Tree playgrounds should have 3 child, found ${children.length}`
+ );
+ } catch (error) {
+ assert(false, error);
+ }
+ });
+
+ test('should fetch new folders and files to exclude after refreshing', async () => {
+ const workspaceFolders = (vscode.workspace.workspaceFolders || []).filter(
+ (folder) => folder.uri.scheme === 'file'
+ );
+ const rootPath = workspaceFolders[0]?.uri.path.replace('/out/test', '');
+ const rootUri = vscode.Uri.parse(rootPath);
+
+ try {
+ const treeController = new PlaygroundsTree();
+
+ assert(
+ !treeController.excludeFromPlaygroundsSearch.includes(
+ '**/playgrounds/**'
+ )
+ );
+
+ await vscode.workspace
+ .getConfiguration('mdb')
+ .update(
+ 'excludeFromPlaygroundsSearch',
+ excludeFromPlaygroundsSearchDefault.concat(['**/playgrounds/**'])
+ );
+ await treeController.refresh();
+
+ assert(
+ treeController.excludeFromPlaygroundsSearch.includes(
+ '**/playgrounds/**'
+ )
+ );
+ } catch (error) {
+ assert(false, error);
+ }
+ });
+});
diff --git a/src/test/suite/extension.test.ts b/src/test/suite/extension.test.ts
index 5400849c6..f5f013e78 100644
--- a/src/test/suite/extension.test.ts
+++ b/src/test/suite/extension.test.ts
@@ -40,6 +40,7 @@ suite('Extension Test Suite', () => {
'mdb.openMongoDBShell',
'mdb.createPlayground',
'mdb.createNewPlaygroundFromViewAction',
+ 'mdb.createNewPlaygroundFromPlaygroundExplorer',
// Tree view commands.
'mdb.addConnection',
diff --git a/src/test/suite/language/languageServerController.test.ts b/src/test/suite/language/languageServerController.test.ts
index 79b0c9f31..95ac0fd6e 100644
--- a/src/test/suite/language/languageServerController.test.ts
+++ b/src/test/suite/language/languageServerController.test.ts
@@ -37,7 +37,7 @@ suite('Language Server Controller Test Suite', () => {
mockExtensionContext
);
- testLanguageServerController.activate();
+ testLanguageServerController.startLanguageServer();
const testConnectionController = new ConnectionController(
new StatusView(mockExtensionContext),
diff --git a/src/test/suite/mdbExtensionController.test.ts b/src/test/suite/mdbExtensionController.test.ts
index b101a39f7..5a75f1400 100644
--- a/src/test/suite/mdbExtensionController.test.ts
+++ b/src/test/suite/mdbExtensionController.test.ts
@@ -1264,13 +1264,10 @@ suite('MDBExtensionController Test Suite', () => {
const mockShowTextDocument = sinon.fake.resolves();
sinon.replace(vscode.window, 'showTextDocument', mockShowTextDocument);
- await vscode.commands.executeCommand(
- 'mdb.searchForDocuments',
- {
- databaseName: 'dbbbbbName',
- collectionName: 'colllllllllName'
- }
- );
+ await vscode.commands.executeCommand('mdb.searchForDocuments', {
+ databaseName: 'dbbbbbName',
+ collectionName: 'colllllllllName'
+ });
assert(mockOpenTextDocument.firstArg.language === 'mongodb');
assert(
@@ -1278,16 +1275,8 @@ suite('MDBExtensionController Test Suite', () => {
'Search for documents in the current collection.'
)
);
- assert(
- mockOpenTextDocument.firstArg.content.includes(
- 'dbbbbbName'
- )
- );
- assert(
- mockOpenTextDocument.firstArg.content.includes(
- 'colllllllllName'
- )
- );
+ assert(mockOpenTextDocument.firstArg.content.includes('dbbbbbName'));
+ assert(mockOpenTextDocument.firstArg.content.includes('colllllllllName'));
assert(
mockShowTextDocument.firstArg === 'untitled',
'Expected it to call vscode to show the playground'
@@ -1301,25 +1290,14 @@ suite('MDBExtensionController Test Suite', () => {
const mockShowTextDocument = sinon.fake.resolves();
sinon.replace(vscode.window, 'showTextDocument', mockShowTextDocument);
- await vscode.commands.executeCommand(
- 'mdb.createIndexFromTreeView',
- {
- databaseName: 'dbbbbbName',
- collectionName: 'colllllllllName'
- }
- );
+ await vscode.commands.executeCommand('mdb.createIndexFromTreeView', {
+ databaseName: 'dbbbbbName',
+ collectionName: 'colllllllllName'
+ });
assert(mockOpenTextDocument.firstArg.language === 'mongodb');
- assert(
- mockOpenTextDocument.firstArg.content.includes(
- 'dbbbbbName'
- )
- );
- assert(
- mockOpenTextDocument.firstArg.content.includes(
- 'colllllllllName'
- )
- );
+ assert(mockOpenTextDocument.firstArg.content.includes('dbbbbbName'));
+ assert(mockOpenTextDocument.firstArg.content.includes('colllllllllName'));
assert(
mockOpenTextDocument.firstArg.content.includes(
'Create a new index in the collection.'
@@ -1448,4 +1426,23 @@ suite('MDBExtensionController Test Suite', () => {
})
.then(done, done);
});
+
+ test('mdb.refreshPlaygrounds command should call refreshPlaygrounds on the playgrounds explorer controller', (done) => {
+ const mockRefreshPlaygrounds = sinon.fake.resolves();
+ sinon.replace(
+ mdbTestExtension.testExtensionController._playgroundsExplorer,
+ 'refresh',
+ mockRefreshPlaygrounds
+ );
+
+ vscode.commands
+ .executeCommand('mdb.refreshPlaygrounds')
+ .then(() => {
+ assert(
+ mockRefreshPlaygrounds.called,
+ 'Expected "refreshPlaygrounds" to be called on the playground controller.'
+ );
+ })
+ .then(done, done);
+ });
});
diff --git a/src/test/suite/stubs.ts b/src/test/suite/stubs.ts
index 22053c0cd..79e42d1fc 100644
--- a/src/test/suite/stubs.ts
+++ b/src/test/suite/stubs.ts
@@ -176,9 +176,13 @@ class MockLanguageServerController {
this.client = null;
}
- activate(): void { /* */ }
+ startLanguageServer(): void {
+ /* */
+ }
- deactivate(): void { /* */ }
+ deactivate(): void {
+ /* */
+ }
executeAll(codeToEvaluate: string): Promise {
return Promise.resolve('Result');