Skip to content

Commit fabbc8c

Browse files
gagikalenakhineika
andauthored
feat(tree-explorer): add buttons to ask Copilot and create playgrounds from tree view VSCODE-651 (#890)
Co-authored-by: Alena Khineika <[email protected]>
1 parent 2dc7651 commit fabbc8c

15 files changed

+268
-30
lines changed

fonts/mongodb-icons.woff

664 Bytes
Binary file not shown.

images/icons/playground.svg

Lines changed: 1 addition & 0 deletions
Loading
File renamed without changes.

package.json

Lines changed: 52 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,11 @@
255255
"dark": "images/dark/add.svg"
256256
}
257257
},
258+
{
259+
"command": "mdb.createNewPlaygroundFromTreeItem",
260+
"title": "Create MongoDB Playground",
261+
"icon": "$(mdb-playground)"
262+
},
258263
{
259264
"command": "mdb.changeActiveConnection",
260265
"title": "MongoDB: Change Active Connection"
@@ -330,10 +335,7 @@
330335
{
331336
"command": "mdb.addDatabase",
332337
"title": "Add Database...",
333-
"icon": {
334-
"light": "images/light/plus-circle.svg",
335-
"dark": "images/dark/plus-circle.svg"
336-
}
338+
"icon": "$(mdb-plus-circle)"
337339
},
338340
{
339341
"command": "mdb.searchForDocuments",
@@ -371,13 +373,15 @@
371373
"command": "mdb.refreshDatabase",
372374
"title": "Refresh"
373375
},
376+
{
377+
"command": "mdb.askCopilotFromTreeItem",
378+
"title": "Ask MongoDB Copilot",
379+
"icon": "$(copilot)"
380+
},
374381
{
375382
"command": "mdb.addCollection",
376383
"title": "Add Collection...",
377-
"icon": {
378-
"light": "images/light/plus-circle.svg",
379-
"dark": "images/dark/plus-circle.svg"
380-
}
384+
"icon": "$(mdb-plus-circle)"
381385
},
382386
{
383387
"command": "mdb.viewCollectionDocuments",
@@ -422,10 +426,7 @@
422426
{
423427
"command": "mdb.createIndexFromTreeView",
424428
"title": "Create New Index...",
425-
"icon": {
426-
"light": "images/light/plus-circle.svg",
427-
"dark": "images/dark/plus-circle.svg"
428-
}
429+
"icon": "$(mdb-plus-circle)"
429430
},
430431
{
431432
"command": "mdb.insertObjectIdToEditor",
@@ -454,10 +455,7 @@
454455
{
455456
"command": "mdb.addStreamProcessor",
456457
"title": "Add StreamProcessor...",
457-
"icon": {
458-
"light": "images/light/plus-circle.svg",
459-
"dark": "images/dark/plus-circle.svg"
460-
}
458+
"icon": "$(mdb-plus-circle)"
461459
},
462460
{
463461
"command": "mdb.startStreamProcessor",
@@ -587,7 +585,7 @@
587585
{
588586
"command": "mdb.addCollection",
589587
"when": "view == mongoDBConnectionExplorer && viewItem == databaseTreeItem",
590-
"group": "inline"
588+
"group": "inline@3"
591589
},
592590
{
593591
"command": "mdb.addCollection",
@@ -604,10 +602,30 @@
604602
"when": "view == mongoDBConnectionExplorer && viewItem == databaseTreeItem",
605603
"group": "2@1"
606604
},
605+
{
606+
"command": "mdb.askCopilotFromTreeItem",
607+
"when": "mdb.isCopilotActive == true && view == mongoDBConnectionExplorer && (viewItem == databaseTreeItem || viewItem == collectionTreeItem)",
608+
"group": "inline@1"
609+
},
610+
{
611+
"command": "mdb.askCopilotFromTreeItem",
612+
"when": "mdb.isCopilotActive == true && view == mongoDBConnectionExplorer && (viewItem == databaseTreeItem || viewItem == collectionTreeItem)",
613+
"group": "3@1"
614+
},
615+
{
616+
"command": "mdb.createNewPlaygroundFromTreeItem",
617+
"when": "view == mongoDBConnectionExplorer && (viewItem == databaseTreeItem || viewItem == collectionTreeItem)",
618+
"group": "inline@2"
619+
},
620+
{
621+
"command": "mdb.createNewPlaygroundFromTreeItem",
622+
"when": "view == mongoDBConnectionExplorer && (viewItem == databaseTreeItem || viewItem == collectionTreeItem)",
623+
"group": "3@2"
624+
},
607625
{
608626
"command": "mdb.dropDatabase",
609627
"when": "view == mongoDBConnectionExplorer && viewItem == databaseTreeItem",
610-
"group": "3@1"
628+
"group": "4@1"
611629
},
612630
{
613631
"command": "mdb.viewCollectionDocuments",
@@ -1157,19 +1175,33 @@
11571175
}
11581176
},
11591177
"icons": {
1160-
"mdb-connection-active": {
1178+
"mdb-playground": {
11611179
"description": "MongoDB Icon",
11621180
"default": {
11631181
"fontPath": "./fonts/mongodb-icons.woff",
11641182
"fontCharacter": "\\ea01"
11651183
}
11661184
},
1167-
"mdb-connection-inactive": {
1185+
"mdb-plus-circle": {
11681186
"description": "MongoDB Icon",
11691187
"default": {
11701188
"fontPath": "./fonts/mongodb-icons.woff",
11711189
"fontCharacter": "\\ea02"
11721190
}
1191+
},
1192+
"mdb-connection-active": {
1193+
"description": "MongoDB Icon",
1194+
"default": {
1195+
"fontPath": "./fonts/mongodb-icons.woff",
1196+
"fontCharacter": "\\ea03"
1197+
}
1198+
},
1199+
"mdb-connection-inactive": {
1200+
"description": "MongoDB Icon",
1201+
"default": {
1202+
"fontPath": "./fonts/mongodb-icons.woff",
1203+
"fontCharacter": "\\ea04"
1204+
}
11731205
}
11741206
}
11751207
},

scripts/generate-icon-font.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@ import { GlyphData } from 'webfont/dist/src/types';
44
import prettier from 'prettier';
55

66
/** Icons to include in the generated icon font */
7-
const INCLUDED_ICONS = ['connection-active', 'connection-inactive'];
7+
const INCLUDED_ICONS = [
8+
'light/connection-active',
9+
'light/connection-inactive',
10+
'playground',
11+
'plus-circle',
12+
];
813

914
/**
1015
* Generates an icon font from the included icons and outputs package.json
@@ -13,7 +18,13 @@ const INCLUDED_ICONS = ['connection-active', 'connection-inactive'];
1318
*/
1419
async function main(): Promise<void> {
1520
const font = await webfont({
16-
files: INCLUDED_ICONS.map((icon) => `./images/light/${icon}.svg`),
21+
files: INCLUDED_ICONS.map((icon) => {
22+
// Legacy support for icons inside light and dark folders.
23+
if (icon.startsWith('light/')) {
24+
return `./images/${icon}.svg`;
25+
}
26+
return `./images/icons/${icon}.svg`;
27+
}),
1728
fontName: 'MongoDB Icons',
1829
formats: ['woff'],
1930
normalize: true,

src/commands/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ enum EXTENSION_COMMANDS {
3939
MDB_OPEN_PLAYGROUND_FROM_TREE_VIEW = 'mdb.openPlaygroundFromTreeView',
4040
MDB_CONNECT_TO_CONNECTION_TREE_VIEW = 'mdb.connectToConnectionTreeItem',
4141
MDB_CREATE_PLAYGROUND_FROM_TREE_VIEW = 'mdb.createNewPlaygroundFromTreeView',
42+
MDB_CREATE_PLAYGROUND_FROM_TREE_ITEM = 'mdb.createNewPlaygroundFromTreeItem',
4243
MDB_DISCONNECT_FROM_CONNECTION_TREE_VIEW = 'mdb.disconnectFromConnectionTreeItem',
4344
MDB_EDIT_CONNECTION = 'mdb.editConnection',
4445
MDB_REFRESH_CONNECTION = 'mdb.refreshConnection',
@@ -75,6 +76,7 @@ enum EXTENSION_COMMANDS {
7576
OPEN_PARTICIPANT_CODE_IN_PLAYGROUND = 'mdb.openParticipantCodeInPlayground',
7677
SEND_MESSAGE_TO_PARTICIPANT = 'mdb.sendMessageToParticipant',
7778
SEND_MESSAGE_TO_PARTICIPANT_FROM_INPUT = 'mdb.sendMessageToParticipantFromInput',
79+
ASK_COPILOT_FROM_TREE_ITEM = 'mdb.askCopilotFromTreeItem',
7880
RUN_PARTICIPANT_CODE = 'mdb.runParticipantCode',
7981
CONNECT_WITH_PARTICIPANT = 'mdb.connectWithParticipant',
8082
SELECT_DATABASE_WITH_PARTICIPANT = 'mdb.selectDatabaseWithParticipant',

src/editors/playgroundController.ts

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type ConnectionController from '../connectionController';
88
import { DataServiceEventTypes } from '../connectionController';
99
import { createLogger } from '../logging';
1010
import type { ConnectionTreeItem } from '../explorer';
11+
import { CollectionTreeItem } from '../explorer';
1112
import { DatabaseTreeItem } from '../explorer';
1213
import formatError from '../utils/formatError';
1314
import type { LanguageServerController } from '../language';
@@ -41,6 +42,8 @@ import {
4142
getPlaygroundExtensionForTelemetry,
4243
} from '../utils/playground';
4344
import type ExportToLanguageCodeLensProvider from './exportToLanguageCodeLensProvider';
45+
import { playgroundFromDatabaseTreeItemTemplate } from '../templates/playgroundFromDatabaseTreeItemTemplate';
46+
import { playgroundFromCollectionTreeItemTemplate } from '../templates/playgroundFromCollectionTreeItemTemplate';
4447

4548
const log = createLogger('playground controller');
4649

@@ -316,13 +319,36 @@ export default class PlaygroundController {
316319
return this._createPlaygroundFileWithContent(content);
317320
}
318321

322+
async createPlaygroundFromTreeItem(
323+
treeItem: DatabaseTreeItem | CollectionTreeItem
324+
): Promise<boolean> {
325+
let content = '';
326+
if (treeItem instanceof DatabaseTreeItem) {
327+
content = playgroundFromDatabaseTreeItemTemplate(treeItem.databaseName);
328+
this._telemetryService.trackPlaygroundCreated('fromDatabaseTreeItem');
329+
} else if (treeItem instanceof CollectionTreeItem) {
330+
content = playgroundFromCollectionTreeItemTemplate(
331+
treeItem.databaseName,
332+
treeItem.collectionName
333+
);
334+
this._telemetryService.trackPlaygroundCreated('fromCollectionTreeItem');
335+
}
336+
337+
return this._createPlaygroundFileWithContent(content);
338+
}
339+
319340
async createPlayground(): Promise<boolean> {
320341
const useDefaultTemplate = !!vscode.workspace
321342
.getConfiguration('mdb')
322343
.get('useDefaultTemplateForPlayground');
323-
const isStreams = this._connectionController.isConnectedToAtlasStreams();
324-
const template = isStreams ? playgroundStreamsTemplate : playgroundTemplate;
325-
const content = useDefaultTemplate ? template : '';
344+
let content = '';
345+
if (useDefaultTemplate) {
346+
const isStreams = this._connectionController.isConnectedToAtlasStreams();
347+
const template = isStreams
348+
? playgroundStreamsTemplate
349+
: playgroundTemplate;
350+
content = template;
351+
}
326352

327353
this._telemetryService.trackPlaygroundCreated('crud');
328354
return this._createPlaygroundFileWithContent(content);

src/editors/playgroundSelectionCodeActionProvider.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import * as vscode from 'vscode';
22

33
import EXTENSION_COMMANDS from '../commands';
44
import { isPlayground, getSelectedText } from '../utils/playground';
5+
import { COPILOT_CHAT_EXTENSION_ID } from '../participant/constants';
56

67
export const EXPORT_TO_LANGUAGE_ALIASES = [
78
{ id: 'csharp', alias: 'C#' },
@@ -42,7 +43,7 @@ export default class PlaygroundSelectionCodeActionProvider
4243

4344
provideCodeActions(): vscode.CodeAction[] | undefined {
4445
const editor = vscode.window.activeTextEditor;
45-
const copilot = vscode.extensions.getExtension('github.copilot-chat');
46+
const copilot = vscode.extensions.getExtension(COPILOT_CHAT_EXTENSION_ID);
4647
let codeActions: vscode.CodeAction[] = [
4748
this.createCodeAction({
4849
title: 'Run selected playground blocks',

src/mdbExtensionController.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ import type {
5151
SendMessageToParticipantOptions,
5252
SendMessageToParticipantFromInputOptions,
5353
} from './participant/participantTypes';
54+
import {
55+
COPILOT_CHAT_EXTENSION_ID,
56+
COPILOT_EXTENSION_ID,
57+
} from './participant/constants';
5458

5559
// This class is the top-level controller for our extension.
5660
// Commands which the extensions handles are defined in the function `activate`.
@@ -177,12 +181,26 @@ export default class MDBExtensionController implements vscode.Disposable {
177181
// ------ In-app notifications ------ //
178182
void this.showCopilotIntroductionForEstablishedUsers();
179183

180-
const copilot = vscode.extensions.getExtension('GitHub.copilot');
184+
const copilot = vscode.extensions.getExtension(COPILOT_EXTENSION_ID);
181185
void vscode.commands.executeCommand(
182186
'setContext',
183187
'mdb.isCopilotActive',
184188
copilot?.isActive
185189
);
190+
191+
// TODO: This is a workaround related to https://github.com/microsoft/vscode/issues/234426
192+
// If the extension was found but is not activated, there is a chance that the MongoDB extension
193+
// was activated before the Copilot one, so we check again after a delay.
194+
if (copilot && !copilot?.isActive) {
195+
setTimeout(() => {
196+
const copilot = vscode.extensions.getExtension(COPILOT_EXTENSION_ID);
197+
void vscode.commands.executeCommand(
198+
'setContext',
199+
'mdb.isCopilotActive',
200+
copilot?.isActive === true
201+
);
202+
}, 3000);
203+
}
186204
}
187205

188206
registerCommands = (): void => {
@@ -330,6 +348,13 @@ export default class MDBExtensionController implements vscode.Disposable {
330348
return true;
331349
}
332350
);
351+
this.registerParticipantCommand(
352+
EXTENSION_COMMANDS.ASK_COPILOT_FROM_TREE_ITEM,
353+
async (treeItem: DatabaseTreeItem | CollectionTreeItem) => {
354+
await this._participantController.askCopilotFromTreeItem(treeItem);
355+
return true;
356+
}
357+
);
333358
this.registerParticipantCommand(
334359
EXTENSION_COMMANDS.RUN_PARTICIPANT_CODE,
335360
({ runnableContent }: RunParticipantCodeCommandArgs) => {
@@ -742,6 +767,11 @@ export default class MDBExtensionController implements vscode.Disposable {
742767
EXTENSION_COMMANDS.MDB_CREATE_PLAYGROUND_FROM_TREE_VIEW,
743768
() => this._playgroundController.createPlayground()
744769
);
770+
this.registerCommand(
771+
EXTENSION_COMMANDS.MDB_CREATE_PLAYGROUND_FROM_TREE_ITEM,
772+
(treeItem: DatabaseTreeItem | CollectionTreeItem) =>
773+
this._playgroundController.createPlaygroundFromTreeItem(treeItem)
774+
);
745775
this.registerCommand(
746776
EXTENSION_COMMANDS.MDB_REFRESH_PLAYGROUNDS_FROM_TREE_VIEW,
747777
() => this._playgroundsExplorer.refresh()
@@ -972,7 +1002,7 @@ export default class MDBExtensionController implements vscode.Disposable {
9721002
}
9731003
);
9741004

975-
const copilot = vscode.extensions.getExtension('github.copilot-chat');
1005+
const copilot = vscode.extensions.getExtension(COPILOT_CHAT_EXTENSION_ID);
9761006
if (result?.title === action) {
9771007
await this._participantController.sendMessageToParticipant({
9781008
message: '',

src/participant/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { ChatMetadataStore } from './chatMetadata';
44
export const CHAT_PARTICIPANT_ID = 'mongodb.participant';
55
export const CHAT_PARTICIPANT_MODEL = 'gpt-4o';
66
export const COPILOT_EXTENSION_ID = 'GitHub.copilot';
7+
export const COPILOT_CHAT_EXTENSION_ID = 'GitHub.copilot-chat';
78

89
export type ParticipantResponseType =
910
| 'query'

0 commit comments

Comments
 (0)