From 86d25b74531104a448086bbb46181d63dbecb5e1 Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 21 Nov 2024 14:02:51 +0100 Subject: [PATCH 01/32] WIP --- .vscode/settings.json | 1 - .../activeConnectionCodeLensProvider.ts | 4 +- src/editors/editorsController.ts | 5 +++ .../queryWithCopilotCodeLensProvider.ts | 39 +++++++++++++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 src/editors/queryWithCopilotCodeLensProvider.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 732bb7406..32baf6163 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,5 @@ // Place your settings in this file to overwrite default and user settings. { - "editor.formatOnSave": false, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, diff --git a/src/editors/activeConnectionCodeLensProvider.ts b/src/editors/activeConnectionCodeLensProvider.ts index 046b335eb..fee92432e 100644 --- a/src/editors/activeConnectionCodeLensProvider.ts +++ b/src/editors/activeConnectionCodeLensProvider.ts @@ -24,7 +24,7 @@ export default class ActiveConnectionCodeLensProvider this._onDidChangeCodeLenses.fire(); }); - this._activeConnectionChangedHandler = () => { + this._activeConnectionChangedHandler = (): void => { this._onDidChangeCodeLenses.fire(); }; this._connectionController.addEventListener( @@ -65,7 +65,7 @@ export default class ActiveConnectionCodeLensProvider return [codeLens]; } - deactivate() { + deactivate(): void { this._connectionController.removeEventListener( DataServiceEventTypes.ACTIVE_CONNECTION_CHANGED, this._activeConnectionChangedHandler diff --git a/src/editors/editorsController.ts b/src/editors/editorsController.ts index 51c111f82..0e3ede7e4 100644 --- a/src/editors/editorsController.ts +++ b/src/editors/editorsController.ts @@ -32,6 +32,7 @@ import type PlaygroundResultProvider from './playgroundResultProvider'; import { PLAYGROUND_RESULT_SCHEME } from './playgroundResultProvider'; import { StatusView } from '../views'; import type TelemetryService from '../telemetry/telemetryService'; +import type { QueryWithCopilotCodeLensProvider } from './queryWithCopilotCodeLensProvider'; const log = createLogger('editors controller'); @@ -102,6 +103,7 @@ export default class EditorsController { _exportToLanguageCodeLensProvider: ExportToLanguageCodeLensProvider; _editDocumentCodeLensProvider: EditDocumentCodeLensProvider; _collectionDocumentsCodeLensProvider: CollectionDocumentsCodeLensProvider; + _queryWithCopilotCodeLensProvider: QueryWithCopilotCodeLensProvider; constructor({ context, @@ -115,6 +117,7 @@ export default class EditorsController { playgroundSelectedCodeActionProvider, playgroundDiagnosticsCodeActionProvider, editDocumentCodeLensProvider, + queryWithCopilotCodeLensProvider, }: { context: vscode.ExtensionContext; connectionController: ConnectionController; @@ -127,6 +130,7 @@ export default class EditorsController { playgroundSelectedCodeActionProvider: PlaygroundSelectedCodeActionProvider; playgroundDiagnosticsCodeActionProvider: PlaygroundDiagnosticsCodeActionProvider; editDocumentCodeLensProvider: EditDocumentCodeLensProvider; + queryWithCopilotCodeLensProvider: QueryWithCopilotCodeLensProvider; }) { this._connectionController = connectionController; this._playgroundController = playgroundController; @@ -160,6 +164,7 @@ export default class EditorsController { playgroundSelectedCodeActionProvider; this._playgroundDiagnosticsCodeActionProvider = playgroundDiagnosticsCodeActionProvider; + this._queryWithCopilotCodeLensProvider = queryWithCopilotCodeLensProvider; vscode.workspace.onDidCloseTextDocument((e) => { const uriParams = new URLSearchParams(e.uri.query); diff --git a/src/editors/queryWithCopilotCodeLensProvider.ts b/src/editors/queryWithCopilotCodeLensProvider.ts new file mode 100644 index 000000000..29afd3369 --- /dev/null +++ b/src/editors/queryWithCopilotCodeLensProvider.ts @@ -0,0 +1,39 @@ +import * as vscode from 'vscode'; +import EXTENSION_COMMANDS from '../commands'; + +const COPILOT_CHAT_EXTENSION_ID = 'GitHub.copilot-chat'; + +export class QueryWithCopilotCodeLensProvider + implements vscode.CodeLensProvider +{ + constructor() {} + + readonly onDidChangeCodeLenses: vscode.Event = + vscode.extensions.onDidChange; + + provideCodeLenses(): vscode.CodeLens[] { + // We can only detect whether a user has the Copilot extension active + // but not whether it has an active subscription. + const hasCopilotInstalled = + vscode.extensions.getExtension(COPILOT_CHAT_EXTENSION_ID)?.isActive === + true; + + if (!hasCopilotInstalled) { + return []; + } + + return [ + new vscode.CodeLens(new vscode.Range(0, 0, 0, 0), { + title: '✨ Generate query with MongoDB Copilot', + command: EXTENSION_COMMANDS.SEND_PARTICIPANT_MESSAGE, + arguments: [ + { + message: '/query ', + isNewChat: true, + isPartialQuery: true, + }, + ], + }), + ]; + } +} From 6feac32331b212d0055992d43c536656641a9deb Mon Sep 17 00:00:00 2001 From: gagik Date: Fri, 22 Nov 2024 14:43:03 +0100 Subject: [PATCH 02/32] add query codelens and shorten wording --- src/commands/index.ts | 2 + .../activeConnectionCodeLensProvider.ts | 6 +- src/editors/editorsController.ts | 4 + .../queryWithCopilotCodeLensProvider.ts | 26 ++-- src/mdbExtensionController.ts | 25 ++++ src/participant/participant.ts | 65 +++++++-- src/participant/participantTypes.ts | 12 ++ .../activeConnectionCodeLensProvider.test.ts | 10 +- .../queryWithCopilotCodeLensProvider.test.ts | 133 ++++++++++++++++++ 9 files changed, 252 insertions(+), 31 deletions(-) create mode 100644 src/participant/participantTypes.ts create mode 100644 src/test/suite/editors/queryWithCopilotCodeLensProvider.test.ts diff --git a/src/commands/index.ts b/src/commands/index.ts index 209990b10..21096c2e5 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -79,6 +79,8 @@ enum EXTENSION_COMMANDS { // Chat participant. OPEN_PARTICIPANT_CODE_IN_PLAYGROUND = 'mdb.openParticipantCodeInPlayground', + SEND_MESSAGE_TO_PARTICIPANT = 'mdb.sendMessageToParticipant', + SEND_MESSAGE_TO_PARTICIPANT_FROM_INPUT = 'mdb.sendMessageToParticipantFromInput', RUN_PARTICIPANT_CODE = 'mdb.runParticipantCode', CONNECT_WITH_PARTICIPANT = 'mdb.connectWithParticipant', SELECT_DATABASE_WITH_PARTICIPANT = 'mdb.selectDatabaseWithParticipant', diff --git a/src/editors/activeConnectionCodeLensProvider.ts b/src/editors/activeConnectionCodeLensProvider.ts index fee92432e..c4c4cc7c9 100644 --- a/src/editors/activeConnectionCodeLensProvider.ts +++ b/src/editors/activeConnectionCodeLensProvider.ts @@ -50,10 +50,10 @@ export default class ActiveConnectionCodeLensProvider ? getDBFromConnectionString(connectionString) : null; message = defaultDB - ? `Currently connected to ${this._connectionController.getActiveConnectionName()} with default database ${defaultDB}. Click here to change connection.` - : `Currently connected to ${this._connectionController.getActiveConnectionName()}. Click here to change connection.`; + ? `Connected to ${this._connectionController.getActiveConnectionName()} with default database ${defaultDB}` + : `Connected to ${this._connectionController.getActiveConnectionName()}`; } else { - message = 'Disconnected. Click here to connect.'; + message = 'Connect'; } codeLens.command = { diff --git a/src/editors/editorsController.ts b/src/editors/editorsController.ts index 0e3ede7e4..eee7366e2 100644 --- a/src/editors/editorsController.ts +++ b/src/editors/editorsController.ts @@ -418,6 +418,10 @@ export default class EditorsController { ) ); this._context.subscriptions.push( + vscode.languages.registerCodeLensProvider( + { language: 'javascript' }, + this._queryWithCopilotCodeLensProvider + ), vscode.languages.registerCodeLensProvider( { language: 'javascript' }, this._activeConnectionCodeLensProvider diff --git a/src/editors/queryWithCopilotCodeLensProvider.ts b/src/editors/queryWithCopilotCodeLensProvider.ts index 29afd3369..72caec139 100644 --- a/src/editors/queryWithCopilotCodeLensProvider.ts +++ b/src/editors/queryWithCopilotCodeLensProvider.ts @@ -1,5 +1,7 @@ import * as vscode from 'vscode'; import EXTENSION_COMMANDS from '../commands'; +import type { SendMessageToParticipantFromInputOptions } from '../participant/participantTypes'; +import { isPlayground } from '../utils/playground'; const COPILOT_CHAT_EXTENSION_ID = 'GitHub.copilot-chat'; @@ -11,7 +13,11 @@ export class QueryWithCopilotCodeLensProvider readonly onDidChangeCodeLenses: vscode.Event = vscode.extensions.onDidChange; - provideCodeLenses(): vscode.CodeLens[] { + provideCodeLenses(document: vscode.TextDocument): vscode.CodeLens[] { + if (!isPlayground(document.uri)) { + return []; + } + // We can only detect whether a user has the Copilot extension active // but not whether it has an active subscription. const hasCopilotInstalled = @@ -22,17 +28,19 @@ export class QueryWithCopilotCodeLensProvider return []; } + const options: SendMessageToParticipantFromInputOptions = { + prompt: 'Describe the query you would like to generate.', + placeHolder: + 'e.g. Find the document in sample_mflix.users with the name of Kayden Washington', + messagePrefix: '/query', + isNewChat: true, + }; + return [ new vscode.CodeLens(new vscode.Range(0, 0, 0, 0), { title: '✨ Generate query with MongoDB Copilot', - command: EXTENSION_COMMANDS.SEND_PARTICIPANT_MESSAGE, - arguments: [ - { - message: '/query ', - isNewChat: true, - isPartialQuery: true, - }, - ], + command: EXTENSION_COMMANDS.SEND_MESSAGE_TO_PARTICIPANT_FROM_INPUT, + arguments: [options], }), ]; } diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index 0f8e6bdf6..b6f24d21d 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -48,6 +48,11 @@ import type { } from './participant/participant'; import ParticipantController from './participant/participant'; import type { OpenSchemaCommandArgs } from './participant/prompts/schema'; +import { QueryWithCopilotCodeLensProvider } from './editors/queryWithCopilotCodeLensProvider'; +import type { + SendMessageToParticipantOptions, + SendMessageToParticipantFromInputOptions, +} from './participant/participantTypes'; // This class is the top-level controller for our extension. // Commands which the extensions handles are defined in the function `activate`. @@ -68,6 +73,7 @@ export default class MDBExtensionController implements vscode.Disposable { _languageServerController: LanguageServerController; _webviewController: WebviewController; _playgroundResultViewProvider: PlaygroundResultProvider; + _queryWithCopilotCodeLensProvider: QueryWithCopilotCodeLensProvider; _activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider; _editDocumentCodeLensProvider: EditDocumentCodeLensProvider; _exportToLanguageCodeLensProvider: ExportToLanguageCodeLensProvider; @@ -106,6 +112,8 @@ export default class MDBExtensionController implements vscode.Disposable { this._connectionController, this._editDocumentCodeLensProvider ); + this._queryWithCopilotCodeLensProvider = + new QueryWithCopilotCodeLensProvider(); this._activeConnectionCodeLensProvider = new ActiveConnectionCodeLensProvider(this._connectionController); this._exportToLanguageCodeLensProvider = @@ -143,6 +151,7 @@ export default class MDBExtensionController implements vscode.Disposable { playgroundDiagnosticsCodeActionProvider: this._playgroundDiagnosticsCodeActionProvider, editDocumentCodeLensProvider: this._editDocumentCodeLensProvider, + queryWithCopilotCodeLensProvider: this._queryWithCopilotCodeLensProvider, }); this._webviewController = new WebviewController({ connectionController: this._connectionController, @@ -306,6 +315,22 @@ export default class MDBExtensionController implements vscode.Disposable { }); } ); + this.registerParticipantCommand( + EXTENSION_COMMANDS.SEND_MESSAGE_TO_PARTICIPANT, + async (options: SendMessageToParticipantOptions) => { + await this._participantController.sendMessageToParticipant(options); + return true; + } + ); + this.registerParticipantCommand( + EXTENSION_COMMANDS.SEND_MESSAGE_TO_PARTICIPANT_FROM_INPUT, + async (options: SendMessageToParticipantFromInputOptions) => { + await this._participantController.sendMessageToParticipantFromInput( + options + ); + return true; + } + ); this.registerParticipantCommand( EXTENSION_COMMANDS.RUN_PARTICIPANT_CODE, ({ runnableContent }: RunParticipantCodeCommandArgs) => { diff --git a/src/participant/participant.ts b/src/participant/participant.ts index c8ee509c3..a0463490d 100644 --- a/src/participant/participant.ts +++ b/src/participant/participant.ts @@ -46,6 +46,10 @@ import type { PromptIntent } from './prompts/intent'; import type { DataService } from 'mongodb-data-service'; import { ParticipantErrorTypes } from './participantErrorTypes'; import { PromptHistory } from './prompts/promptHistory'; +import type { + SendMessageToParticipantOptions, + SendMessageToParticipantFromInputOptions, +} from './participantTypes'; const log = createLogger('participant'); @@ -121,9 +125,46 @@ export default class ParticipantController { * in the chat. To work around this, we can write a message as the user, which will * trigger the chat handler and give us access to the model. */ - writeChatMessageAsUser(message: string): Thenable { + async sendMessageToParticipant( + options: SendMessageToParticipantOptions + ): Promise { + const { message, isNewChat = false, isPartialQuery = false } = options; + + if (isNewChat) { + await vscode.commands.executeCommand('workbench.action.chat.newChat'); + await vscode.commands.executeCommand( + 'workbench.action.chat.clearHistory' + ); + } + return vscode.commands.executeCommand('workbench.action.chat.open', { query: `@MongoDB ${message}`, + isPartialQuery, + }); + } + + async sendMessageToParticipantFromInput( + options: SendMessageToParticipantFromInputOptions + ): Promise { + const { + messagePrefix = '', + isNewChat = false, + isPartialQuery = false, + ...inputBoxOptions + } = options; + + const message = await vscode.window.showInputBox({ + ...inputBoxOptions, + }); + + if (message === undefined || message.trim() === '') { + return Promise.resolve(); + } + + return this.sendMessageToParticipant({ + message: `${messagePrefix ? `${messagePrefix} ` : ''}${message}`, + isNewChat, + isPartialQuery, }); } @@ -444,9 +485,9 @@ export default class ParticipantController { const connectionName = this._connectionController.getActiveConnectionName(); - return this.writeChatMessageAsUser( - `${command ? `${command} ` : ''}${connectionName}` - ) as Promise; + return this.sendMessageToParticipant({ + message: `${command ? `${command} ` : ''}${connectionName}`, + }) as Promise; } getConnectionsTree(command: ParticipantCommand): vscode.MarkdownString[] { @@ -485,7 +526,7 @@ export default class ParticipantController { const dataService = this._connectionController.getActiveDataService(); if (!dataService) { // Run a blank command to get the user to connect first. - void this.writeChatMessageAsUser(command); + void this.sendMessageToParticipant({ message: command }); return []; } @@ -533,9 +574,9 @@ export default class ParticipantController { databaseName: databaseName, }); - return this.writeChatMessageAsUser( - `${command} ${databaseName}` - ) as Promise; + return this.sendMessageToParticipant({ + message: `${command} ${databaseName}`, + }) as Promise; } async getCollectionQuickPicks({ @@ -548,7 +589,7 @@ export default class ParticipantController { const dataService = this._connectionController.getActiveDataService(); if (!dataService) { // Run a blank command to get the user to connect first. - void this.writeChatMessageAsUser(command); + void this.sendMessageToParticipant({ message: command }); return []; } @@ -609,9 +650,9 @@ export default class ParticipantController { databaseName: databaseName, collectionName: collectionName, }); - return this.writeChatMessageAsUser( - `${command} ${collectionName}` - ) as Promise; + return this.sendMessageToParticipant({ + message: `${command} ${collectionName}`, + }) as Promise; } renderDatabasesTree({ diff --git a/src/participant/participantTypes.ts b/src/participant/participantTypes.ts new file mode 100644 index 000000000..d28b5f1b3 --- /dev/null +++ b/src/participant/participantTypes.ts @@ -0,0 +1,12 @@ +import type * as vscode from 'vscode'; + +export type SendMessageToParticipantOptions = { + message: string; + isNewChat?: boolean; + isPartialQuery?: boolean; +}; + +export type SendMessageToParticipantFromInputOptions = { + messagePrefix?: string; +} & Omit & + vscode.InputBoxOptions; diff --git a/src/test/suite/editors/activeConnectionCodeLensProvider.test.ts b/src/test/suite/editors/activeConnectionCodeLensProvider.test.ts index 0af23e134..4e2de73b2 100644 --- a/src/test/suite/editors/activeConnectionCodeLensProvider.test.ts +++ b/src/test/suite/editors/activeConnectionCodeLensProvider.test.ts @@ -61,9 +61,7 @@ suite('Active Connection CodeLens Provider Test Suite', () => { expect(codeLens).to.be.an('array'); expect(codeLens.length).to.be.equal(1); - expect(codeLens[0].command?.title).to.be.equal( - 'Disconnected. Click here to connect.' - ); + expect(codeLens[0].command?.title).to.be.equal('Connect'); expect(codeLens[0].range.start.line).to.be.equal(0); expect(codeLens[0].range.end.line).to.be.equal(0); }); @@ -111,9 +109,7 @@ suite('Active Connection CodeLens Provider Test Suite', () => { expect(codeLens).to.be.an('array'); expect(codeLens.length).to.be.equal(1); - expect(codeLens[0].command?.title).to.be.equal( - 'Currently connected to fakeName. Click here to change connection.' - ); + expect(codeLens[0].command?.title).to.be.equal('Connected to fakeName'); expect(codeLens[0].range.start.line).to.be.equal(0); expect(codeLens[0].range.end.line).to.be.equal(0); expect(codeLens[0].command?.command).to.be.equal( @@ -133,7 +129,7 @@ suite('Active Connection CodeLens Provider Test Suite', () => { expect(codeLens).to.be.an('array'); expect(codeLens.length).to.be.equal(1); expect(codeLens[0].command?.title).to.be.equal( - 'Currently connected to fakeName with default database fakeDBName. Click here to change connection.' + 'Connected to fakeName with default database fakeDBName' ); expect(codeLens[0].range.start.line).to.be.equal(0); expect(codeLens[0].range.end.line).to.be.equal(0); diff --git a/src/test/suite/editors/queryWithCopilotCodeLensProvider.test.ts b/src/test/suite/editors/queryWithCopilotCodeLensProvider.test.ts new file mode 100644 index 000000000..4435dd685 --- /dev/null +++ b/src/test/suite/editors/queryWithCopilotCodeLensProvider.test.ts @@ -0,0 +1,133 @@ +import * as vscode from 'vscode'; +import { beforeEach, afterEach } from 'mocha'; +import { expect } from 'chai'; +import sinon from 'sinon'; +import path from 'path'; +import { QueryWithCopilotCodeLensProvider } from '../../../editors/queryWithCopilotCodeLensProvider'; +import EXTENSION_COMMANDS from '../../../commands'; + +suite('Query with Copilot CodeLens Provider Test Suite', () => { + let testCodeLensProvider: QueryWithCopilotCodeLensProvider; + const sandbox = sinon.createSandbox(); + + const mockExtensionChangeEmitter: vscode.EventEmitter = + new vscode.EventEmitter(); + + beforeEach(() => { + sandbox.replaceGetter( + vscode.extensions, + 'onDidChange', + () => mockExtensionChangeEmitter.event + ); + + testCodeLensProvider = new QueryWithCopilotCodeLensProvider(); + }); + + afterEach(() => { + sandbox.restore(); + }); + + suite('the MongoDB playground in JS', () => { + const mockFileName = path.join('nonexistent', 'playground-test.mongodb.js'); + const mockDocumentUri = vscode.Uri.from({ + path: mockFileName, + scheme: 'untitled', + }); + const mockTextDoc: vscode.TextDocument = { + uri: mockDocumentUri, + } as Pick as vscode.TextDocument; + + suite('does not have the copilot extension', () => { + beforeEach(() => { + sandbox.stub(vscode.extensions, 'getExtension').returns(undefined); + }); + + test('should not show the codelens', () => { + const codeLens = testCodeLensProvider.provideCodeLenses(mockTextDoc); + + expect(codeLens).to.be.an('array'); + expect(codeLens.length).to.be.equal(0); + }); + }); + + suite('has the extension but it is not active', () => { + test('should not show the codelens', () => { + const codeLens = testCodeLensProvider.provideCodeLenses(mockTextDoc); + + expect(codeLens).to.be.an('array'); + expect(codeLens.length).to.be.equal(0); + }); + }); + + suite('has the copilot extension active', () => { + beforeEach(() => { + sandbox.stub(vscode.extensions, 'getExtension').returns({ + isActive: true, + } as vscode.Extension); + }); + + test('should show the codelens', () => { + const codeLens = testCodeLensProvider.provideCodeLenses(mockTextDoc); + + expect(codeLens).to.be.an('array'); + expect(codeLens.length).to.be.equal(1); + expect(codeLens[0].command?.title).to.be.equal( + '✨ Generate query with MongoDB Copilot' + ); + expect(codeLens[0].range.start.line).to.be.equal(0); + expect(codeLens[0].range.end.line).to.be.equal(0); + expect(codeLens[0].command?.command).to.be.equal( + EXTENSION_COMMANDS.SEND_MESSAGE_TO_PARTICIPANT_FROM_INPUT + ); + }); + }); + + suite('on extensions list changes', function () { + test('calls onDidChangeCodeLenses', function () { + const extensionListChanged = sinon.stub(); + testCodeLensProvider.onDidChangeCodeLenses(extensionListChanged); + + mockExtensionChangeEmitter.fire(); + + expect(extensionListChanged).calledOnce; + }); + }); + }); + + suite('the regular JS file', () => { + const mockFileName = path.join('nonexistent', 'playground-test.js'); + const mockDocumentUri = vscode.Uri.from({ + path: mockFileName, + scheme: 'untitled', + }); + const mockTextDoc: vscode.TextDocument = { + uri: mockDocumentUri, + } as Pick as vscode.TextDocument; + + suite('does not have the copilot extension', () => { + beforeEach(() => { + sandbox.stub(vscode.extensions, 'getExtension').returns(undefined); + }); + + test('should not show the codelens', () => { + const codeLens = testCodeLensProvider.provideCodeLenses(mockTextDoc); + + expect(codeLens).to.be.an('array'); + expect(codeLens.length).to.be.equal(0); + }); + }); + + suite('has the copilot extension active', () => { + beforeEach(() => { + sandbox.stub(vscode.extensions, 'getExtension').returns(undefined); + }); + + test('should not show the codelens', () => { + const codeLens = testCodeLensProvider.provideCodeLenses(mockTextDoc); + + expect(codeLens).to.be.an('array'); + expect(codeLens.length).to.be.equal(0); + }); + }); + }); +}); From 474beb3d2110766fb7ec2da64e224e0905c3c0e2 Mon Sep 17 00:00:00 2001 From: gagik Date: Fri, 22 Nov 2024 14:46:23 +0100 Subject: [PATCH 03/32] revert vscode change --- .vscode/settings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 32baf6163..732bb7406 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ // Place your settings in this file to overwrite default and user settings. { + "editor.formatOnSave": false, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, From e915b3fcd8c8d218b9e74b90e68d7480892a2106 Mon Sep 17 00:00:00 2001 From: gagik Date: Fri, 22 Nov 2024 14:47:27 +0100 Subject: [PATCH 04/32] wording change --- src/editors/queryWithCopilotCodeLensProvider.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/editors/queryWithCopilotCodeLensProvider.ts b/src/editors/queryWithCopilotCodeLensProvider.ts index 72caec139..e5a47dae0 100644 --- a/src/editors/queryWithCopilotCodeLensProvider.ts +++ b/src/editors/queryWithCopilotCodeLensProvider.ts @@ -20,11 +20,11 @@ export class QueryWithCopilotCodeLensProvider // We can only detect whether a user has the Copilot extension active // but not whether it has an active subscription. - const hasCopilotInstalled = + const hasCopilotChatActive = vscode.extensions.getExtension(COPILOT_CHAT_EXTENSION_ID)?.isActive === true; - if (!hasCopilotInstalled) { + if (!hasCopilotChatActive) { return []; } From e17008f0acd41285c82898350f9e85bd7c6f41ac Mon Sep 17 00:00:00 2001 From: gagik Date: Mon, 25 Nov 2024 11:29:32 +0100 Subject: [PATCH 05/32] Add icons to connect to MongoDB --- fonts/mongodb-icons.woff | Bin 0 -> 1204 bytes package-lock.json | 754 ++++++++++++++++++ package.json | 20 +- scripts/generate-icon-font.ts | 63 ++ .../activeConnectionCodeLensProvider.ts | 6 +- 5 files changed, 839 insertions(+), 4 deletions(-) create mode 100644 fonts/mongodb-icons.woff create mode 100644 scripts/generate-icon-font.ts diff --git a/fonts/mongodb-icons.woff b/fonts/mongodb-icons.woff new file mode 100644 index 0000000000000000000000000000000000000000..fdac68fa6458958744bae19eefbbb54f286493c7 GIT binary patch literal 1204 zcmXT-cXMN4WB>w|EezZsn*9re4`QPN_ux<`1_nkBplAvZuTo4^GxZPFHv)>~0Qsgs zoZy-4;FFx2SO64T0_1OEU|_QOrn;FwJ*P4a$c_Mt`GVA4R4xk3NKFKq!?Xg(R|R6T zcqLob44@!T>;jN42E-~1oD6~)xg`}qu@^wT01z8609}xipA1s>11cuP$j0y?H?aa} zFEhw}@<6P>D9Ip`mzbLh6w?5@#|Eh0KlSGdrC;MXD{mXqLY(WzeW^yUtheVWN0J>KH`o@HOUzRdWt<_5;1 zLQ%m(lN(+tnyc8S&;N5`#n+jW=XkiD?QNSjud(pdv&h}6Z>yTCd=GZqzUJ{Hwl^Z` z%M(`5QBq}}d%9%DkD|l*U;BEJyk}=@R{daZwe06RZSOTjWsAzLsPFyP^mR+&noZX> za#mLdJxhs)e z@xS}#S#ge^6^#jt4Bl8Y2EKZG@%3dd-o&KUKcj_D=U5cz-u`_$@KAA^_RMV@Q?p)w zW_)49vI`sr;P_#9#AMp`{No?9c*z}-5fU31#Wq;5GYfYxGqkQ@c0i~DhO>i`q+|di zKLg_g25FFck`ox1#5B~HfGMGZffK?vWMlT>U=Ufs_#GDii2?};X$*{N3eT9F*xLRl z*fBG3Co`G>_18`~>vzaO#BIORL64&|GI|P4^X|-;GJmcPr&Gj+#Dn{$WHxRwK3k%Y z__p{5i%t8!HvfnBPFDyscb-!zKWF>-Ze|ce_>YG6!=EmceBGdBvG`s4Y8Ue}b|0CG zc^RFaS?PT^bLW6?+zjTJ#S6r3{?9pM8#hyX#}Vn`sek6(RIf{4_fTy9nbJECyLUFu zyq?1M;l)wmwL!Kox(nBYT26hb)_rE8bynJ?DcWmY)N?kMSE)=jnK+f(@P22}?6S4K zE=eVNZBdakQ=>%O)~DtiUb@wF{Z+3U?HkV-Hnur=l{VP19P5~?Y1pc=-?5G3+59{A z&5f%LUf5Z**P*?x`nSse*>ZtVJ3Be1on!SqvvF}$lJ`%&$nKV3Ek;t77cZQZyCj+G zyR4u*+sN>zfVX+*f_AaDPgLZ0t$%+1V^C%gFzilP1Ji;4!^OEju19#BdpJ|2MI^Iq z_Jq&cHzF+09uQXlUnwJN%d9MH%&jdwFSajbmF3l>s$=Q8?foLPy)6q=10.0.0" + } + }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -6649,6 +6675,13 @@ "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", "dev": true }, + "node_modules/a-sync-waterfall": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", + "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==", + "dev": true, + "license": "MIT" + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -6939,6 +6972,13 @@ "node": ">=8" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, "node_modules/asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", @@ -7650,6 +7690,42 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/caller-callsite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-callsite/-/caller-callsite-2.0.0.tgz", + "integrity": "sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-callsite/node_modules/callsites": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-2.0.0.tgz", + "integrity": "sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/caller-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-2.0.0.tgz", + "integrity": "sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "caller-callsite": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/callsite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", @@ -7679,6 +7755,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys/node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001669", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz", @@ -8436,6 +8550,13 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" }, + "node_modules/cubic2quad": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/cubic2quad/-/cubic2quad-1.2.1.tgz", + "integrity": "sha512-wT5Y7mO8abrV16gnssKdmIhIbA9wSkeMzhh27jAguKrV82i24wER0vL5TGhUJ9dbJNDcigoRZ0IAHFEEEI4THQ==", + "dev": true, + "license": "MIT" + }, "node_modules/data-uri-to-buffer": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", @@ -8492,6 +8613,43 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/decimal.js": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", @@ -11510,6 +11668,13 @@ "node": ">=6.9.0" } }, + "node_modules/geometry-interfaces": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/geometry-interfaces/-/geometry-interfaces-1.1.4.tgz", + "integrity": "sha512-qD6OdkT6NcES9l4Xx3auTpwraQruU7dARbQPVO71MKvkGYw5/z/oIiGymuFXrRaEQa5Y67EIojUpaLeGEa5hGA==", + "dev": true, + "license": "MIT" + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -11938,6 +12103,16 @@ "lodash": "^4.17.21" } }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -12403,6 +12578,16 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -12813,6 +12998,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-directory": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/is-directory/-/is-directory-0.3.1.tgz", + "integrity": "sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", @@ -12968,6 +13163,16 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -13887,6 +14092,19 @@ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/markdown-it": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", @@ -13999,6 +14217,72 @@ "node": ">= 0.10.0" } }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/meow/node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -14026,6 +14310,13 @@ "node": ">= 0.6" } }, + "node_modules/microbuffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/microbuffer/-/microbuffer-1.0.0.tgz", + "integrity": "sha512-O/SUXauVN4x6RaEJFqSPcXNtLFL+QzJHKZlyDVYFwcDDRVca3Fa/37QXXC+4zAGGa4YhHrHxKXuuHvLDIQECtA==", + "dev": true, + "license": "MIT" + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -14103,6 +14394,16 @@ "node": ">=4" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -14135,6 +14436,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minimist-options/node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", @@ -14945,6 +15271,16 @@ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, + "node_modules/neatequal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/neatequal/-/neatequal-1.0.0.tgz", + "integrity": "sha512-sVt5awO4a4w24QmAthdrCPiVRW3naB8FGLdyadin01BH+6BzNPEBwGrpwCczQvPlULS6uXTItTe1PJ5P0kYm7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "varstream": "^0.3.2" + } + }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -15438,6 +15774,42 @@ "node": "*" } }, + "node_modules/nunjucks": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.4.tgz", + "integrity": "sha512-26XRV6BhkgK0VOxfbU5cQI+ICFUtMLixv1noZn1tGU38kQH5A5nmmbk/O45xdyBhD1esk47nKrY0mvQpZIhRjQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + }, + "bin": { + "nunjucks-precompile": "bin/precompile" + }, + "engines": { + "node": ">= 6.9.0" + }, + "peerDependencies": { + "chokidar": "^3.3.0" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/nunjucks/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -16875,6 +17247,60 @@ "node": ">=4" } }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, "node_modules/read-pkg/node_modules/path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -16931,6 +17357,20 @@ "node": ">= 10.13.0" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/redux": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", @@ -18368,6 +18808,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -18429,6 +18882,108 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/svg-pathdata": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", + "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/svg2ttf": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/svg2ttf/-/svg2ttf-6.0.3.tgz", + "integrity": "sha512-CgqMyZrbOPpc+WqH7aga4JWkDPso23EgypLsbQ6gN3uoPWwwiLjXvzgrwGADBExvCRJrWFzAeK1bSoSpE7ixSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.7.2", + "argparse": "^2.0.1", + "cubic2quad": "^1.2.1", + "lodash": "^4.17.10", + "microbuffer": "^1.0.0", + "svgpath": "^2.1.5" + }, + "bin": { + "svg2ttf": "svg2ttf.js" + } + }, + "node_modules/svg2ttf/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/svgicons2svgfont": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/svgicons2svgfont/-/svgicons2svgfont-10.0.6.tgz", + "integrity": "sha512-fUgQEVg3XwTbOHvlXahHGqCet5Wvfo1bV4DCvbSRvjsOCPCRunYbG4dUJCPegps37BMph3eOrfoobhH5AWuC6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^7.2.0", + "geometry-interfaces": "^1.1.4", + "glob": "^7.1.6", + "neatequal": "^1.0.0", + "readable-stream": "^3.4.0", + "sax": "^1.2.4", + "svg-pathdata": "^6.0.0" + }, + "bin": { + "svgicons2svgfont": "bin/svgicons2svgfont.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/svgicons2svgfont/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/svgicons2svgfont/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/svgicons2svgfont/node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/svgpath": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/svgpath/-/svgpath-2.6.0.tgz", + "integrity": "sha512-OIWR6bKzXvdXYyO4DK/UWa1VA1JeKq8E+0ug2DG98Y/vOmMpfZNj+TIG988HjfYSqtcy/hFOtZq/n/j5GSESNg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/fontello/svg2ttf?sponsor=1" + } + }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -18952,6 +19507,16 @@ "node": ">=18" } }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/triple-beam": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", @@ -19082,6 +19647,35 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true }, + "node_modules/ttf2eot": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ttf2eot/-/ttf2eot-2.0.0.tgz", + "integrity": "sha512-U56aG2Ylw7psLOmakjemAzmpqVgeadwENg9oaDjaZG5NYX4WB6+7h74bNPcc+0BXsoU5A/XWiHabDXyzFOmsxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.6", + "microbuffer": "^1.0.0" + }, + "bin": { + "ttf2eot": "ttf2eot.js" + } + }, + "node_modules/ttf2woff": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ttf2woff/-/ttf2woff-2.0.2.tgz", + "integrity": "sha512-X68badwBjAy/+itU49scLjXUL094up+rHuYk+YAOTTBYSUMOmLZ7VyhZJuqQESj1gnyLAC2/5V8Euv+mExmyPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.6", + "microbuffer": "^1.0.0", + "pako": "^1.0.0" + }, + "bin": { + "ttf2woff": "ttf2woff.js" + } + }, "node_modules/tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", @@ -19475,6 +20069,22 @@ "spdx-expression-parse": "^3.0.0" } }, + "node_modules/varstream": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/varstream/-/varstream-0.3.2.tgz", + "integrity": "sha512-OpR3Usr9dGZZbDttlTxdviGdxiURI0prX68+DuaN/JfIDbK9ZOmREKM6PgmelsejMnhgjXmEEEgf+E4NbsSqMg==", + "dev": true, + "dependencies": { + "readable-stream": "^1.0.33" + }, + "bin": { + "json2varstream": "cli/json2varstream.js", + "varstream2json": "cli/varstream2json.js" + }, + "engines": { + "node": ">=0.10.*" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -19590,6 +20200,27 @@ "node": ">=10.13.0" } }, + "node_modules/wawoff2": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wawoff2/-/wawoff2-2.0.1.tgz", + "integrity": "sha512-r0CEmvpH63r4T15ebFqeOjGqU4+EgTx4I510NtK35EMciSdcTxCw3Byy3JnBonz7iyIFZ0AbVo0bbFpEVuhCYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "woff2_compress.js": "bin/woff2_compress.js", + "woff2_decompress.js": "bin/woff2_decompress.js" + } + }, + "node_modules/wawoff2/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", @@ -19607,6 +20238,129 @@ "node": ">= 8" } }, + "node_modules/webfont": { + "version": "11.2.26", + "resolved": "https://registry.npmjs.org/webfont/-/webfont-11.2.26.tgz", + "integrity": "sha512-ms9abzcJGMBj5yVTpNfAcyQB0SNzmi10aBlKLC7kAt1TQ5eZqieRhvhxN1BrXaizWV0nksp6vLqBfaJmBWmF7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^5.2.0", + "deepmerge": "^4.2.2", + "globby": "^11.0.0", + "meow": "^9.0.0", + "nunjucks": "^3.2.3", + "p-limit": "^3.1.0", + "parse-json": "^5.2.0", + "resolve-from": "^5.0.0", + "svg2ttf": "^6.0.2", + "svgicons2svgfont": "^10.0.3", + "ttf2eot": "^2.0.0", + "ttf2woff": "^2.0.2", + "wawoff2": "^2.0.0", + "xml2js": "^0.4.23" + }, + "bin": { + "webfont": "dist/cli.js" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/webfont/node_modules/cosmiconfig": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz", + "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==", + "dev": true, + "license": "MIT", + "dependencies": { + "import-fresh": "^2.0.0", + "is-directory": "^0.3.1", + "js-yaml": "^3.13.1", + "parse-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webfont/node_modules/cosmiconfig/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webfont/node_modules/import-fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz", + "integrity": "sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg==", + "dev": true, + "license": "MIT", + "dependencies": { + "caller-path": "^2.0.0", + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/webfont/node_modules/import-fresh/node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/webfont/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/webfont/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/webfont/node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/package.json b/package.json index aa670b06c..e677313e1 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "release-draft": "node ./scripts/release-draft.js", "reformat": "prettier --write .", "snyk-test": "node scripts/snyk-test.js", + "generate-icon-font": "ts-node ./scripts/generate-icon-font.ts", "generate-vulnerability-report": "mongodb-sbom-tools generate-vulnerability-report --snyk-reports=.sbom/snyk-test-result.json --dependencies=.sbom/dependencies.json --fail-on=high", "create-vulnerability-tickets": "mongodb-sbom-tools generate-vulnerability-report --snyk-reports=.sbom/snyk-test-result.json --dependencies=.sbom/dependencies.json --create-jira-issues", "prepare": "husky", @@ -1154,6 +1155,22 @@ "description": "Specify a shell command that is run to start the browser for authenticating with the OIDC identity provider for the server connection. Leave this empty for default browser." } } + }, + "icons": { + "connection-active": { + "description": "MongoDB Icon", + "default": { + "fontPath": "./fonts/mongodb-icons.woff", + "fontCharacter": "\\ea01" + } + }, + "connection-inactive": { + "description": "MongoDB Icon", + "default": { + "fontPath": "./fonts/mongodb-icons.woff", + "fontCharacter": "\\ea02" + } + } } }, "dependencies": { @@ -1198,7 +1215,6 @@ "@mongodb-js/oidc-mock-provider": "^0.10.1", "@mongodb-js/oidc-plugin": "^0.4.0", "@mongodb-js/prettier-config-devtools": "^1.0.1", - "mongodb-rag-core": "^0.4.1", "@mongodb-js/sbom-tools": "^0.7.2", "@mongodb-js/signing-utils": "^0.3.6", "@mongosh/service-provider-core": "^2.3.3", @@ -1241,6 +1257,7 @@ "mocha-junit-reporter": "^2.2.1", "mocha-multi": "^1.1.7", "mongodb-client-encryption": "^6.1.0", + "mongodb-rag-core": "^0.4.1", "mongodb-runner": "^5.7.0", "node-fetch": "^2.7.0", "node-loader": "^0.6.0", @@ -1260,6 +1277,7 @@ "ts-loader": "^9.5.1", "ts-node": "^10.9.2", "typescript": "^4.9.5", + "webfont": "^11.2.26", "webpack": "^5.95.0", "webpack-bundle-analyzer": "^4.10.2", "webpack-cli": "^5.1.4", diff --git a/scripts/generate-icon-font.ts b/scripts/generate-icon-font.ts new file mode 100644 index 000000000..9f9ce3d8f --- /dev/null +++ b/scripts/generate-icon-font.ts @@ -0,0 +1,63 @@ +import webfont from 'webfont'; +import fs from 'fs/promises'; +import { GlyphData } from 'webfont/dist/src/types'; + +/** Icons to include in the generated icon font */ +const INCLUDED_ICONS = ['connection-active', 'connection-inactive']; + +/** + * Generates an icon font from the included icons and outputs package.json + * configuration field for those icons as specified in + * https://code.visualstudio.com/api/references/icons-in-labels + */ +async function main(): Promise { + const font = await webfont({ + files: INCLUDED_ICONS.map((icon) => `./images/light/${icon}.svg`), + fontName: 'MongoDB Icons', + formats: ['woff'], + normalize: true, + centerHorizontally: true, + }); + + if (!font.woff) { + throw new Error('Error occurred generating template'); + } + + await fs.writeFile('./fonts/mongodb-icons.woff', font.woff); + + const iconsConfig = {}; + font.glyphsData?.forEach((glyph) => { + if (!glyph.metadata?.name) { + throw new Error('There is a glyph without a name'); + } + iconsConfig[glyph.metadata.name] = { + description: 'MongoDB Icon', + default: { + fontPath: './fonts/mongodb-icons.woff', + fontCharacter: getUnicodeHex(glyph), + }, + }; + }); + + // Prints out the VSCode configuration package.json + console.info( + JSON.stringify( + { + icons: iconsConfig, + }, + undefined, + 2 + ) + ); +} + +function getUnicodeHex(glyph: GlyphData): string { + if (glyph.metadata?.unicode == undefined) { + throw new Error('No unicode defined'); + } + const hex = glyph.metadata?.unicode[0].codePointAt(0)!.toString(16); + + return `\\${hex}`; +} + +main(); diff --git a/src/editors/activeConnectionCodeLensProvider.ts b/src/editors/activeConnectionCodeLensProvider.ts index c4c4cc7c9..efd240c2c 100644 --- a/src/editors/activeConnectionCodeLensProvider.ts +++ b/src/editors/activeConnectionCodeLensProvider.ts @@ -50,10 +50,10 @@ export default class ActiveConnectionCodeLensProvider ? getDBFromConnectionString(connectionString) : null; message = defaultDB - ? `Connected to ${this._connectionController.getActiveConnectionName()} with default database ${defaultDB}` - : `Connected to ${this._connectionController.getActiveConnectionName()}`; + ? `$(connection-active)Connected to ${this._connectionController.getActiveConnectionName()} with default database ${defaultDB}` + : `$(connection-active)Connected to ${this._connectionController.getActiveConnectionName()}`; } else { - message = 'Connect'; + message = '$(connection-inactive)Connect'; } codeLens.command = { From 27f0aad56abe9eda9367363303b09ee0e8631cd9 Mon Sep 17 00:00:00 2001 From: gagik Date: Mon, 25 Nov 2024 11:37:48 +0100 Subject: [PATCH 06/32] Fix tests --- .../editors/activeConnectionCodeLensProvider.test.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/suite/editors/activeConnectionCodeLensProvider.test.ts b/src/test/suite/editors/activeConnectionCodeLensProvider.test.ts index 4e2de73b2..b74bb83e7 100644 --- a/src/test/suite/editors/activeConnectionCodeLensProvider.test.ts +++ b/src/test/suite/editors/activeConnectionCodeLensProvider.test.ts @@ -61,7 +61,9 @@ suite('Active Connection CodeLens Provider Test Suite', () => { expect(codeLens).to.be.an('array'); expect(codeLens.length).to.be.equal(1); - expect(codeLens[0].command?.title).to.be.equal('Connect'); + expect(codeLens[0].command?.title).to.be.equal( + '$(connection-inactive)Connect' + ); expect(codeLens[0].range.start.line).to.be.equal(0); expect(codeLens[0].range.end.line).to.be.equal(0); }); @@ -109,7 +111,9 @@ suite('Active Connection CodeLens Provider Test Suite', () => { expect(codeLens).to.be.an('array'); expect(codeLens.length).to.be.equal(1); - expect(codeLens[0].command?.title).to.be.equal('Connected to fakeName'); + expect(codeLens[0].command?.title).to.be.equal( + '$(connection-active)Connected to fakeName' + ); expect(codeLens[0].range.start.line).to.be.equal(0); expect(codeLens[0].range.end.line).to.be.equal(0); expect(codeLens[0].command?.command).to.be.equal( @@ -129,7 +133,7 @@ suite('Active Connection CodeLens Provider Test Suite', () => { expect(codeLens).to.be.an('array'); expect(codeLens.length).to.be.equal(1); expect(codeLens[0].command?.title).to.be.equal( - 'Connected to fakeName with default database fakeDBName' + '$(connection-active)Connected to fakeName with default database fakeDBName' ); expect(codeLens[0].range.start.line).to.be.equal(0); expect(codeLens[0].range.end.line).to.be.equal(0); From bce4de9bf3d872d5c2e11f97cef310af39957e25 Mon Sep 17 00:00:00 2001 From: gagik Date: Mon, 25 Nov 2024 14:29:13 +0100 Subject: [PATCH 07/32] check for copilot extension, not chat --- src/editors/queryWithCopilotCodeLensProvider.ts | 6 ++---- src/participant/constants.ts | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/editors/queryWithCopilotCodeLensProvider.ts b/src/editors/queryWithCopilotCodeLensProvider.ts index e5a47dae0..c7093db03 100644 --- a/src/editors/queryWithCopilotCodeLensProvider.ts +++ b/src/editors/queryWithCopilotCodeLensProvider.ts @@ -2,8 +2,7 @@ import * as vscode from 'vscode'; import EXTENSION_COMMANDS from '../commands'; import type { SendMessageToParticipantFromInputOptions } from '../participant/participantTypes'; import { isPlayground } from '../utils/playground'; - -const COPILOT_CHAT_EXTENSION_ID = 'GitHub.copilot-chat'; +import { COPILOT_EXTENSION_ID } from '../participant/constants'; export class QueryWithCopilotCodeLensProvider implements vscode.CodeLensProvider @@ -21,8 +20,7 @@ export class QueryWithCopilotCodeLensProvider // We can only detect whether a user has the Copilot extension active // but not whether it has an active subscription. const hasCopilotChatActive = - vscode.extensions.getExtension(COPILOT_CHAT_EXTENSION_ID)?.isActive === - true; + vscode.extensions.getExtension(COPILOT_EXTENSION_ID)?.isActive === true; if (!hasCopilotChatActive) { return []; diff --git a/src/participant/constants.ts b/src/participant/constants.ts index 90fd81490..1aeec079e 100644 --- a/src/participant/constants.ts +++ b/src/participant/constants.ts @@ -3,6 +3,7 @@ import { ChatMetadataStore } from './chatMetadata'; export const CHAT_PARTICIPANT_ID = 'mongodb.participant'; export const CHAT_PARTICIPANT_MODEL = 'gpt-4o'; +export const COPILOT_EXTENSION_ID = 'GitHub.copilot'; export type ParticipantResponseType = | 'query' From 99128f5a4cf96992522471070430a97a86461fbf Mon Sep 17 00:00:00 2001 From: gagik Date: Tue, 26 Nov 2024 13:41:49 +0100 Subject: [PATCH 08/32] Add namespacing --- fonts/mongodb-icons.woff | Bin 1204 -> 1244 bytes package.json | 4 ++-- scripts/generate-icon-font.ts | 2 +- .../activeConnectionCodeLensProvider.ts | 6 +++--- .../activeConnectionCodeLensProvider.test.ts | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/fonts/mongodb-icons.woff b/fonts/mongodb-icons.woff index fdac68fa6458958744bae19eefbbb54f286493c7..c12f6799b9969a9997c3059adb93b3cd4cb1513a 100644 GIT binary patch delta 529 zcmdnOd52S^+~3WOfsp|SSne=zgJ=$oi6Z9p)<9N*=Ol;xj8M1}2+t zs+;-Kb1Kt->=>X}2@p@Y?sdR5BQ-GvD0TtJR|jIVcwJkC44@!T><5r90mLc{N(}ND zxg`}049o&RJ_s8CMHzDPlY#1(6`*2LjMI!5i*geyfEF_w00rfNSb;Yz#nF~zV1vWSASaXIa!qH(? zHrHXswjbd>JWdXJ%FPB5GXz~H8ZaEHVdm~nf(?3sgH%?1K27v@GLb!_Wez$;g! zbtReCB=*mL`PauJXWYFk>~%UeEiy%Tyx7!eNw@3eWY~R;xa}&9_*K_b*GU>apV#y!I+DRNr6?2jk zQW6pv9vzw8XGCt86AM;)=oIj8M1}2+t zs+;-Kb1Kt->11cuP$Y#XwAvduCXfZQTnn50j6&NKMWbzVoQ-NX{ zKnK|XHTb7KpSz|YzqkbG`jm-($_!KH95`_5z<~oSEj$IY1e<0`M9h@j(9~dZU8Ief z=i~Yr5)lWO1SSc(PBdUxe}Kh|fnoA;#_jd0-GN*U20YG_w?zmZT>nV$&s~Y+ivQg= z&x&*WtY}PFWbnqKG4R#fi?1(x@g^p%{uwQNI>(|w_xA78frpCQv}bPPn40zaGvf;* zmR&%HR?JCGNJ&Uwc*JDd_Wa`?vv|oJk`WRc7{xYNurmvHFf+8SVRk^MOGxxfa8Qzz z3}EDEV4T1p4KaX$NlZhH3FyrV22PNH3`~Y>%sw0pA}bibLoIlaD3Fkl#=xki@Qlfc Ut?hq;9Ww)WGNajMf976B0GnTnIRF3v diff --git a/package.json b/package.json index e677313e1..86a2ce518 100644 --- a/package.json +++ b/package.json @@ -1157,14 +1157,14 @@ } }, "icons": { - "connection-active": { + "mdb-connection-active": { "description": "MongoDB Icon", "default": { "fontPath": "./fonts/mongodb-icons.woff", "fontCharacter": "\\ea01" } }, - "connection-inactive": { + "mdb-connection-inactive": { "description": "MongoDB Icon", "default": { "fontPath": "./fonts/mongodb-icons.woff", diff --git a/scripts/generate-icon-font.ts b/scripts/generate-icon-font.ts index 9f9ce3d8f..da65d96b2 100644 --- a/scripts/generate-icon-font.ts +++ b/scripts/generate-icon-font.ts @@ -30,7 +30,7 @@ async function main(): Promise { if (!glyph.metadata?.name) { throw new Error('There is a glyph without a name'); } - iconsConfig[glyph.metadata.name] = { + iconsConfig[`mdb-${glyph.metadata.name}`] = { description: 'MongoDB Icon', default: { fontPath: './fonts/mongodb-icons.woff', diff --git a/src/editors/activeConnectionCodeLensProvider.ts b/src/editors/activeConnectionCodeLensProvider.ts index efd240c2c..cb34e90fd 100644 --- a/src/editors/activeConnectionCodeLensProvider.ts +++ b/src/editors/activeConnectionCodeLensProvider.ts @@ -50,10 +50,10 @@ export default class ActiveConnectionCodeLensProvider ? getDBFromConnectionString(connectionString) : null; message = defaultDB - ? `$(connection-active)Connected to ${this._connectionController.getActiveConnectionName()} with default database ${defaultDB}` - : `$(connection-active)Connected to ${this._connectionController.getActiveConnectionName()}`; + ? `$(mdb-connection-active)Connected to ${this._connectionController.getActiveConnectionName()} with default database ${defaultDB}` + : `$(mdb-connection-active)Connected to ${this._connectionController.getActiveConnectionName()}`; } else { - message = '$(connection-inactive)Connect'; + message = '$(mdb-connection-inactive)Connect'; } codeLens.command = { diff --git a/src/test/suite/editors/activeConnectionCodeLensProvider.test.ts b/src/test/suite/editors/activeConnectionCodeLensProvider.test.ts index b74bb83e7..05a65ca53 100644 --- a/src/test/suite/editors/activeConnectionCodeLensProvider.test.ts +++ b/src/test/suite/editors/activeConnectionCodeLensProvider.test.ts @@ -62,7 +62,7 @@ suite('Active Connection CodeLens Provider Test Suite', () => { expect(codeLens).to.be.an('array'); expect(codeLens.length).to.be.equal(1); expect(codeLens[0].command?.title).to.be.equal( - '$(connection-inactive)Connect' + '$(mdb-connection-inactive)Connect' ); expect(codeLens[0].range.start.line).to.be.equal(0); expect(codeLens[0].range.end.line).to.be.equal(0); @@ -112,7 +112,7 @@ suite('Active Connection CodeLens Provider Test Suite', () => { expect(codeLens).to.be.an('array'); expect(codeLens.length).to.be.equal(1); expect(codeLens[0].command?.title).to.be.equal( - '$(connection-active)Connected to fakeName' + '$(mdb-connection-active)Connected to fakeName' ); expect(codeLens[0].range.start.line).to.be.equal(0); expect(codeLens[0].range.end.line).to.be.equal(0); @@ -133,7 +133,7 @@ suite('Active Connection CodeLens Provider Test Suite', () => { expect(codeLens).to.be.an('array'); expect(codeLens.length).to.be.equal(1); expect(codeLens[0].command?.title).to.be.equal( - '$(connection-active)Connected to fakeName with default database fakeDBName' + '$(mdb-connection-active)Connected to fakeName with default database fakeDBName' ); expect(codeLens[0].range.start.line).to.be.equal(0); expect(codeLens[0].range.end.line).to.be.equal(0); From b7f961898bddfc5e4c4076d1bdfe7b22c4f3db44 Mon Sep 17 00:00:00 2001 From: gagik Date: Wed, 27 Nov 2024 09:47:10 +0100 Subject: [PATCH 09/32] add codelens telemetry --- src/editors/queryWithCopilotCodeLensProvider.ts | 1 + src/participant/participant.ts | 5 +++++ src/participant/participantTypes.ts | 1 + src/telemetry/telemetryService.ts | 12 ++++++++++++ 4 files changed, 19 insertions(+) diff --git a/src/editors/queryWithCopilotCodeLensProvider.ts b/src/editors/queryWithCopilotCodeLensProvider.ts index c7093db03..2df01871d 100644 --- a/src/editors/queryWithCopilotCodeLensProvider.ts +++ b/src/editors/queryWithCopilotCodeLensProvider.ts @@ -32,6 +32,7 @@ export class QueryWithCopilotCodeLensProvider 'e.g. Find the document in sample_mflix.users with the name of Kayden Washington', messagePrefix: '/query', isNewChat: true, + source: 'query with copilot codelens', }; return [ diff --git a/src/participant/participant.ts b/src/participant/participant.ts index b59139c95..f39cec2bc 100644 --- a/src/participant/participant.ts +++ b/src/participant/participant.ts @@ -159,9 +159,14 @@ export default class ParticipantController { messagePrefix = '', isNewChat = false, isPartialQuery = false, + source, ...inputBoxOptions } = options; + this._telemetryService.trackCopilotParticipantSubmittedFromInputBox({ + source, + }); + const message = await vscode.window.showInputBox({ ...inputBoxOptions, }); diff --git a/src/participant/participantTypes.ts b/src/participant/participantTypes.ts index d28b5f1b3..dbf6787c8 100644 --- a/src/participant/participantTypes.ts +++ b/src/participant/participantTypes.ts @@ -8,5 +8,6 @@ export type SendMessageToParticipantOptions = { export type SendMessageToParticipantFromInputOptions = { messagePrefix?: string; + source?: 'query with copilot codelens'; } & Omit & vscode.InputBoxOptions; diff --git a/src/telemetry/telemetryService.ts b/src/telemetry/telemetryService.ts index 1d4238e66..1b1e92869 100644 --- a/src/telemetry/telemetryService.ts +++ b/src/telemetry/telemetryService.ts @@ -130,6 +130,10 @@ export type CopilotIntroductionProperties = { is_copilot_active: boolean; }; +export type ParticipantOpenedFromInputBoxProperties = { + source?: 'query with copilot codelens'; +}; + export function chatResultFeedbackKindToTelemetryValue( kind: vscode.ChatResultFeedbackKind ): TelemetryFeedbackKind { @@ -161,6 +165,7 @@ type TelemetryEventProperties = | ParticipantFeedbackProperties | ParticipantResponseFailedProperties | ParticipantPromptProperties + | ParticipantOpenedFromInputBoxProperties | ParticipantResponseProperties | CopilotIntroductionProperties; @@ -186,6 +191,7 @@ export enum TelemetryEventTypes { PARTICIPANT_RESPONSE_FAILED = 'Participant Response Failed', PARTICIPANT_PROMPT_SUBMITTED = 'Participant Prompt Submitted', PARTICIPANT_RESPONSE_GENERATED = 'Participant Response Generated', + PARTICIPANT_SUBMITTED_FROM_INPUT_BOX = 'Participant Submitted From Input Box', COPILOT_INTRODUCTION_CLICKED = 'Copilot Introduction Clicked', COPILOT_INTRODUCTION_DISMISSED = 'Copilot Introduction Dismissed', } @@ -440,6 +446,12 @@ export default class TelemetryService { this.track(TelemetryEventTypes.PARTICIPANT_FEEDBACK, props); } + trackCopilotParticipantSubmittedFromInputBox( + props: ParticipantOpenedFromInputBoxProperties + ): void { + this.track(TelemetryEventTypes.PARTICIPANT_SUBMITTED_FROM_INPUT_BOX, props); + } + trackCopilotParticipantError(err: any, command: string): void { let errorCode: string | undefined; let errorName: ParticipantErrorTypes; From 245121f7083f513686b80227fe35bc21c9172324 Mon Sep 17 00:00:00 2001 From: gagik Date: Wed, 27 Nov 2024 15:24:56 +0100 Subject: [PATCH 10/32] WIP demo icons --- fonts/mongodb-icons.woff | Bin 1204 -> 1908 bytes images/icons/playground.svg | 1 + images/{dark => icons}/plus-circle.svg | 0 package.json | 72 +++++++++++++----- scripts/generate-icon-font.ts | 17 ++++- src/commands/index.ts | 2 + src/editors/playgroundController.ts | 25 +++++- src/mdbExtensionController.ts | 12 +++ src/participant/participant.ts | 48 +++++++++++- src/participant/participantTypes.ts | 9 +++ .../playgroundWithNameSpaceTemplate.ts | 21 +++++ 11 files changed, 178 insertions(+), 29 deletions(-) create mode 100644 images/icons/playground.svg rename images/{dark => icons}/plus-circle.svg (100%) create mode 100644 src/templates/playgroundWithNameSpaceTemplate.ts diff --git a/fonts/mongodb-icons.woff b/fonts/mongodb-icons.woff index fdac68fa6458958744bae19eefbbb54f286493c7..45d6e125479765cb70c11cb4a09c3ea3c01bf018 100644 GIT binary patch delta 1580 zcmXYxeK^x=7{`AzW0;YbD61Jlaxxs`oD@2Zyvvc~?e{aIn2oI2(K4~ZEGc9%IwwuU zk@ti&tPZ6zlDCLd*4t&hxK3y}zq-zSU7zc@zu)VAp8I+JxStla&se=we-a5m0C1=> z0FCb}Cg7Z&DTfDV&rQ88T>XP=>;M2!1Zy`KFwlh*OiqN7U~e4Op8-H=NM)|XF^>92 zESwL*!~p=34N0w%4^yBR06_f=*IB|iLOyPJhhY=;!eMO)W2T&~L_eI!V8I>(*6Uyn zs(~B;pwh|T3v*yY4`%y1iFQ{a#DW{6gs{fL7$Z!9QW}&`@y*;Tu1YQM0&*THnn0su=$ z2BCh6P%OSD79Z?u=bk)CP9_*pFBgJEx4FH!H*+%w6`;sQB(6JrSSm<~E+>F$ZvZtsu3 z*m3V0vL;t_mPyHeRHT-x`E?xvdtC!F$f0G44~3l#cSw(^^D1##Uw5z>x_{E1Q(``| zm_}O?jius(wGtI^8W-=c6#g3*`B?hZ#k^?HkT_aGdg0}K<#HA17e!|l^$asP<;yl< zWmP_*);{%?!4ZRj`D;@3PHwMmt9!qR(7}7rxw$A?hNS}eyH!JjU7)Spz;I$5 zBt=7%=odB(r0{?K=1Kxqzbr{d6W%&6-pVmPF^5}_txLwC_!)JsJ*&ft;O0j2ph zesBUlB6R6^I>lwjh(LcETY{P-=}x06iS6A}CwFb$YGA6kVl|cRL%CMhb^-yJu$0ra zW7U}%$tq0?cd@6&7qg&DH>!K`a9KcF=afN_F2-?eDlnrcb25-lm%g64U2C$(Y#&>r zW1`REQGRni#7XB6h2A$cuSdqW1YeMQd^&c{eN`WlX`HBd<2Aaux{exRT=7s7*Z&t6 z74vP$`1!^SyTEbzN>Or;WxYHTj8FM^25MsaZ_?0Zxs~J745?aOHk#p;eL>GULuhxn zwof8b9cnwfWeH+?%jyRuXPpI(*^h84OVokcv4;&6oR>EC=`q|Na~cDJz;jf#$*Vh` zrr4C(SRN==5_}d6X6UIRb#qjKu2E&__j!ivcp#Wjn1*+R-Yj05hw7Na$)9k)JZ(dK0=qzEm9v8kbEU}VVHQaN&(!z*c+g<9bNU4j$Be8ef Y{mlyG41SmNAV#;jU)6B|zXSmP0iZswxc~qF delta 880 zcmeyuw}n%r+~3WOfsp|SShg^5gJ||I6GfEkQ-G{him7U*{=xc23=E7pK)xvuCwL}1 z_$22h768ST0Qs937?^Cnscz;^oUvLk?Ez94lMm5ahMQWJsZFs%UcRe{(nUdfg< z11Ja-y8z^i0kH}LCxc){Zb=1D>;;f70K^6i9Q6zgIr+&Tg+HKzQjBa2A952bfEF`z z0LA2iSb8#4J6`j{^J@Z|7R zIRr^)9Oi1t;W@+e#-UK4U)WCGK1`!th1dc4K=JAqom3{_jJjQA4P}rzxMScdC$(+top&+YT3_s+TLr5$`+MfQQ!NozUk|h!Zn+& zZRD~(UUlC(?DK;uNhKoLT-@K}m*q6RVHJ0hIRbLEYIh)4g8`58=Eoh-DWz6u?o$@QA72wC(xFKW6cgJ0v3{HZY29uwZ8v?qFtUUBm2vPzVfa2PH|# z07iZW#t974AO|HUFffT}s4)RkLj?mTgm1{k?8CtzvV!qDEDa|@JK?O~AqSDoM_El7{c2BG1AQmJaB=RB>k%I39?n#05y>o@J>j$V zjR?!L2ZYuCSIWrRGAj!kb8Ab_i|tEUWqCEJ>R7sNd%s9+Z_C0It=4W2;hl`_!9tH2 F7yybCHt+xd diff --git a/images/icons/playground.svg b/images/icons/playground.svg new file mode 100644 index 000000000..f38f28a70 --- /dev/null +++ b/images/icons/playground.svg @@ -0,0 +1 @@ + diff --git a/images/dark/plus-circle.svg b/images/icons/plus-circle.svg similarity index 100% rename from images/dark/plus-circle.svg rename to images/icons/plus-circle.svg diff --git a/package.json b/package.json index e677313e1..5d948af5d 100644 --- a/package.json +++ b/package.json @@ -255,6 +255,11 @@ "dark": "images/dark/add.svg" } }, + { + "command": "mdb.createNewPlaygroundFromConnectionTreeView", + "title": "Create MongoDB Playground", + "icon": "$(mdb-playground)" + }, { "command": "mdb.changeActiveConnection", "title": "MongoDB: Change Active Connection" @@ -330,10 +335,7 @@ { "command": "mdb.addDatabase", "title": "Add Database...", - "icon": { - "light": "images/light/plus-circle.svg", - "dark": "images/dark/plus-circle.svg" - } + "icon": "$(mdb-plus-circle)" }, { "command": "mdb.searchForDocuments", @@ -371,13 +373,15 @@ "command": "mdb.refreshDatabase", "title": "Refresh" }, + { + "command": "mdb.askCopilotFromTreeItem", + "title": "Ask MongoDB Copilot", + "icon": "$(copilot)" + }, { "command": "mdb.addCollection", "title": "Add Collection...", - "icon": { - "light": "images/light/plus-circle.svg", - "dark": "images/dark/plus-circle.svg" - } + "icon": "$(mdb-plus-circle)" }, { "command": "mdb.viewCollectionDocuments", @@ -422,10 +426,7 @@ { "command": "mdb.createIndexFromTreeView", "title": "Create New Index...", - "icon": { - "light": "images/light/plus-circle.svg", - "dark": "images/dark/plus-circle.svg" - } + "icon": "$(mdb-plus-circle)" }, { "command": "mdb.insertObjectIdToEditor", @@ -454,10 +455,7 @@ { "command": "mdb.addStreamProcessor", "title": "Add StreamProcessor...", - "icon": { - "light": "images/light/plus-circle.svg", - "dark": "images/dark/plus-circle.svg" - } + "icon": "$(mdb-plus-circle)" }, { "command": "mdb.startStreamProcessor", @@ -587,7 +585,7 @@ { "command": "mdb.addCollection", "when": "view == mongoDBConnectionExplorer && viewItem == databaseTreeItem", - "group": "inline" + "group": "inline@3" }, { "command": "mdb.addCollection", @@ -604,10 +602,30 @@ "when": "view == mongoDBConnectionExplorer && viewItem == databaseTreeItem", "group": "2@1" }, + { + "command": "mdb.askCopilotFromTreeItem", + "when": "view == mongoDBConnectionExplorer && (viewItem == databaseTreeItem || viewItem == collectionTreeItem)", + "group": "inline@1" + }, + { + "command": "mdb.askCopilotFromTreeItem", + "when": "view == mongoDBConnectionExplorer && (viewItem == databaseTreeItem || viewItem == collectionTreeItem)", + "group": "3@1" + }, + { + "command": "mdb.createNewPlaygroundFromConnectionTreeView", + "when": "view == mongoDBConnectionExplorer && (viewItem == databaseTreeItem || viewItem == collectionTreeItem)", + "group": "inline@2" + }, + { + "command": "mdb.createNewPlaygroundFromConnectionTreeView", + "when": "view == mongoDBConnectionExplorer && (viewItem == databaseTreeItem || viewItem == collectionTreeItem)", + "group": "3@2" + }, { "command": "mdb.dropDatabase", "when": "view == mongoDBConnectionExplorer && viewItem == databaseTreeItem", - "group": "3@1" + "group": "4@1" }, { "command": "mdb.viewCollectionDocuments", @@ -1157,19 +1175,33 @@ } }, "icons": { - "connection-active": { + "mdb-playground": { "description": "MongoDB Icon", "default": { "fontPath": "./fonts/mongodb-icons.woff", "fontCharacter": "\\ea01" } }, - "connection-inactive": { + "mdb-plus-circle": { "description": "MongoDB Icon", "default": { "fontPath": "./fonts/mongodb-icons.woff", "fontCharacter": "\\ea02" } + }, + "mdb-connection-active": { + "description": "MongoDB Icon", + "default": { + "fontPath": "./fonts/mongodb-icons.woff", + "fontCharacter": "\\ea03" + } + }, + "mdb-connection-inactive": { + "description": "MongoDB Icon", + "default": { + "fontPath": "./fonts/mongodb-icons.woff", + "fontCharacter": "\\ea04" + } } } }, diff --git a/scripts/generate-icon-font.ts b/scripts/generate-icon-font.ts index 9f9ce3d8f..30b5be0aa 100644 --- a/scripts/generate-icon-font.ts +++ b/scripts/generate-icon-font.ts @@ -3,7 +3,12 @@ import fs from 'fs/promises'; import { GlyphData } from 'webfont/dist/src/types'; /** Icons to include in the generated icon font */ -const INCLUDED_ICONS = ['connection-active', 'connection-inactive']; +const INCLUDED_ICONS = [ + 'light/connection-active', + 'light/connection-inactive', + 'playground', + 'plus-circle', +]; /** * Generates an icon font from the included icons and outputs package.json @@ -12,7 +17,13 @@ const INCLUDED_ICONS = ['connection-active', 'connection-inactive']; */ async function main(): Promise { const font = await webfont({ - files: INCLUDED_ICONS.map((icon) => `./images/light/${icon}.svg`), + files: INCLUDED_ICONS.map((icon) => { + // Legacy support for icons inside light and dark folders. + if (icon.startsWith('light/')) { + return `./images/${icon}.svg`; + } + return `./images/icons/${icon}.svg`; + }), fontName: 'MongoDB Icons', formats: ['woff'], normalize: true, @@ -30,7 +41,7 @@ async function main(): Promise { if (!glyph.metadata?.name) { throw new Error('There is a glyph without a name'); } - iconsConfig[glyph.metadata.name] = { + iconsConfig[`mdb-${glyph.metadata.name}`] = { description: 'MongoDB Icon', default: { fontPath: './fonts/mongodb-icons.woff', diff --git a/src/commands/index.ts b/src/commands/index.ts index a5fe02aa5..d6c25ab63 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -39,6 +39,7 @@ enum EXTENSION_COMMANDS { MDB_OPEN_PLAYGROUND_FROM_TREE_VIEW = 'mdb.openPlaygroundFromTreeView', MDB_CONNECT_TO_CONNECTION_TREE_VIEW = 'mdb.connectToConnectionTreeItem', MDB_CREATE_PLAYGROUND_FROM_TREE_VIEW = 'mdb.createNewPlaygroundFromTreeView', + MDB_CREATE_PLAYGROUND_FROM_CONNECTION_TREE_VIEW = 'mdb.createNewPlaygroundFromConnectionTreeView', MDB_DISCONNECT_FROM_CONNECTION_TREE_VIEW = 'mdb.disconnectFromConnectionTreeItem', MDB_EDIT_CONNECTION = 'mdb.editConnection', MDB_REFRESH_CONNECTION = 'mdb.refreshConnection', @@ -75,6 +76,7 @@ enum EXTENSION_COMMANDS { OPEN_PARTICIPANT_CODE_IN_PLAYGROUND = 'mdb.openParticipantCodeInPlayground', SEND_MESSAGE_TO_PARTICIPANT = 'mdb.sendMessageToParticipant', SEND_MESSAGE_TO_PARTICIPANT_FROM_INPUT = 'mdb.sendMessageToParticipantFromInput', + ASK_COPILOT_FROM_TREE_ITEM = 'mdb.askCopilotFromTreeItem', RUN_PARTICIPANT_CODE = 'mdb.runParticipantCode', CONNECT_WITH_PARTICIPANT = 'mdb.connectWithParticipant', SELECT_DATABASE_WITH_PARTICIPANT = 'mdb.selectDatabaseWithParticipant', diff --git a/src/editors/playgroundController.ts b/src/editors/playgroundController.ts index da4632294..8122c2130 100644 --- a/src/editors/playgroundController.ts +++ b/src/editors/playgroundController.ts @@ -8,6 +8,7 @@ import type ConnectionController from '../connectionController'; import { DataServiceEventTypes } from '../connectionController'; import { createLogger } from '../logging'; import type { ConnectionTreeItem } from '../explorer'; +import { CollectionTreeItem } from '../explorer'; import { DatabaseTreeItem } from '../explorer'; import formatError from '../utils/formatError'; import type { LanguageServerController } from '../language'; @@ -41,6 +42,7 @@ import { getPlaygroundExtensionForTelemetry, } from '../utils/playground'; import type ExportToLanguageCodeLensProvider from './exportToLanguageCodeLensProvider'; +import { playgroundWithNamespaceTemplate } from '../templates/playgroundWithNameSpaceTemplate'; const log = createLogger('playground controller'); @@ -316,13 +318,28 @@ export default class PlaygroundController { return this._createPlaygroundFileWithContent(content); } - async createPlayground(): Promise { + async createPlayground( + treeItem?: DatabaseTreeItem | CollectionTreeItem + ): Promise { const useDefaultTemplate = !!vscode.workspace .getConfiguration('mdb') .get('useDefaultTemplateForPlayground'); - const isStreams = this._connectionController.isConnectedToAtlasStreams(); - const template = isStreams ? playgroundStreamsTemplate : playgroundTemplate; - const content = useDefaultTemplate ? template : ''; + let content = ''; + + if (treeItem instanceof DatabaseTreeItem) { + content = playgroundWithNamespaceTemplate(treeItem.databaseName); + } else if (treeItem instanceof CollectionTreeItem) { + content = playgroundWithNamespaceTemplate( + treeItem.databaseName, + treeItem.collectionName + ); + } else if (useDefaultTemplate) { + const isStreams = this._connectionController.isConnectedToAtlasStreams(); + const template = isStreams + ? playgroundStreamsTemplate + : playgroundTemplate; + content = template; + } this._telemetryService.trackPlaygroundCreated('crud'); return this._createPlaygroundFileWithContent(content); diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index ed5c3a802..2bb025de9 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -324,6 +324,13 @@ export default class MDBExtensionController implements vscode.Disposable { return true; } ); + this.registerParticipantCommand( + EXTENSION_COMMANDS.ASK_COPILOT_FROM_TREE_ITEM, + async (treeItem: DatabaseTreeItem) => { + await this._participantController.askCopilotFromTreeItem(treeItem); + return true; + } + ); this.registerParticipantCommand( EXTENSION_COMMANDS.RUN_PARTICIPANT_CODE, ({ runnableContent }: RunParticipantCodeCommandArgs) => { @@ -736,6 +743,11 @@ export default class MDBExtensionController implements vscode.Disposable { EXTENSION_COMMANDS.MDB_CREATE_PLAYGROUND_FROM_TREE_VIEW, () => this._playgroundController.createPlayground() ); + this.registerCommand( + EXTENSION_COMMANDS.MDB_CREATE_PLAYGROUND_FROM_CONNECTION_TREE_VIEW, + (treeItem: DatabaseTreeItem | CollectionTreeItem) => + this._playgroundController.createPlayground(treeItem) + ); this.registerCommand( EXTENSION_COMMANDS.MDB_REFRESH_PLAYGROUNDS_FROM_TREE_VIEW, () => this._playgroundsExplorer.refresh() diff --git a/src/participant/participant.ts b/src/participant/participant.ts index b59139c95..7b3a5f28b 100644 --- a/src/participant/participant.ts +++ b/src/participant/participant.ts @@ -55,6 +55,7 @@ import type { } from './participantTypes'; import { DEFAULT_EXPORT_TO_LANGUAGE_DRIVER_SYNTAX } from '../editors/exportToLanguageCodeLensProvider'; import { EXPORT_TO_LANGUAGE_ALIASES } from '../editors/playgroundSelectionCodeActionProvider'; +import { CollectionTreeItem, DatabaseTreeItem } from '../explorer'; const log = createLogger('participant'); @@ -137,7 +138,13 @@ export default class ParticipantController { async sendMessageToParticipant( options: SendMessageToParticipantOptions ): Promise { - const { message, isNewChat = false, isPartialQuery = false } = options; + log.info(`STUFF: ${util.inspect(options)}`); + const { + message, + isNewChat = false, + isPartialQuery = false, + ...otherOptions + } = options; if (isNewChat) { await vscode.commands.executeCommand('workbench.action.chat.newChat'); @@ -146,7 +153,8 @@ export default class ParticipantController { ); } - return vscode.commands.executeCommand('workbench.action.chat.open', { + return await vscode.commands.executeCommand('workbench.action.chat.open', { + ...otherOptions, query: `@MongoDB ${message}`, isPartialQuery, }); @@ -177,6 +185,34 @@ export default class ParticipantController { }); } + async askCopilotFromTreeItem( + treeItem: DatabaseTreeItem | CollectionTreeItem + ): Promise { + if (treeItem instanceof DatabaseTreeItem) { + const { databaseName } = treeItem; + + await this.sendMessageToParticipant({ + message: `I want to ask questions about the \`${databaseName}\` database.`, + isNewChat: true, + }); + } + if (treeItem instanceof CollectionTreeItem) { + const { databaseName, collectionName } = treeItem; + + await this.sendMessageToParticipant({ + message: `I want to ask questions about the \`${databaseName}\` database's \`${collectionName}\` collection.`, + isNewChat: true, + }); + } + + await this.sendMessageToParticipant({ + message: '', + isPartialQuery: true, + }); + log.info(`after: ${util.inspect(this._chatMetadataStore._chats)}`); + return undefined; + } + async _getChatResponse({ modelInput, token, @@ -792,6 +828,13 @@ export default class ParticipantController { collectionName: undefined, }; + log.info( + `messages: ${util.inspect( + messagesWithNamespace.messages.map((c) => getContent(c)) + )}` + ); + log.info(`context.history: ${util.inspect(context.history, true, 10)}`); + // When there's no user message content we can // skip the request to the model. This would happen with /schema. if (Prompts.doMessagesContainUserInput(messagesWithNamespace.messages)) { @@ -815,6 +858,7 @@ export default class ParticipantController { modelInput: messagesWithNamespace, token, }); + log.info(`with namespace: ${responseContentWithNamespace}`); ({ databaseName, collectionName } = Prompts.namespace.extractDatabaseAndCollectionNameFromResponse( responseContentWithNamespace diff --git a/src/participant/participantTypes.ts b/src/participant/participantTypes.ts index d28b5f1b3..ad1670a99 100644 --- a/src/participant/participantTypes.ts +++ b/src/participant/participantTypes.ts @@ -1,9 +1,18 @@ import type * as vscode from 'vscode'; +/** Based on options from Copilot's chat open command IChatViewOpenOptions */ export type SendMessageToParticipantOptions = { message: string; isNewChat?: boolean; isPartialQuery?: boolean; + /** + * Any previous chat requests and responses that should be shown in the chat view. + * Note that currently these requests do not end up included in vscode's context.history. + */ + previousRequests?: { + request: string; + response: string; + }[]; }; export type SendMessageToParticipantFromInputOptions = { diff --git a/src/templates/playgroundWithNameSpaceTemplate.ts b/src/templates/playgroundWithNameSpaceTemplate.ts new file mode 100644 index 000000000..6f4280ce4 --- /dev/null +++ b/src/templates/playgroundWithNameSpaceTemplate.ts @@ -0,0 +1,21 @@ +import template from './playgroundCloneDocumentTemplate'; + +export function playgroundWithNamespaceTemplate( + databaseName?: string, + collectionName?: string +): string { + let adjustedTemplate = template; + if (databaseName) { + adjustedTemplate = adjustedTemplate.replaceAll( + 'CURRENT_DATABASE', + databaseName + ); + } + if (collectionName) { + adjustedTemplate = adjustedTemplate.replaceAll( + 'CURRENT_COLLECTION', + collectionName + ); + } + return adjustedTemplate; +} From a9c2836f3be67be2cababf768d8fd72611f02fb1 Mon Sep 17 00:00:00 2001 From: Gagik Amaryan Date: Wed, 27 Nov 2024 16:31:58 +0100 Subject: [PATCH 11/32] Update src/editors/queryWithCopilotCodeLensProvider.ts Co-authored-by: Alena Khineika --- src/editors/queryWithCopilotCodeLensProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/editors/queryWithCopilotCodeLensProvider.ts b/src/editors/queryWithCopilotCodeLensProvider.ts index 2df01871d..5e68f830c 100644 --- a/src/editors/queryWithCopilotCodeLensProvider.ts +++ b/src/editors/queryWithCopilotCodeLensProvider.ts @@ -27,7 +27,7 @@ export class QueryWithCopilotCodeLensProvider } const options: SendMessageToParticipantFromInputOptions = { - prompt: 'Describe the query you would like to generate.', + prompt: 'Describe the query you would like to generate', placeHolder: 'e.g. Find the document in sample_mflix.users with the name of Kayden Washington', messagePrefix: '/query', From d4c9d6d47040980b55d2b50975b58c3164f8e89b Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 28 Nov 2024 13:12:54 +0100 Subject: [PATCH 12/32] WIP --- .vscode/settings.json | 1 - package.json | 4 +-- src/editors/playgroundController.ts | 35 ++++++++++++------- src/mdbExtensionController.ts | 24 ++++++++++++- .../playgroundCreateCollectionTemplate.ts | 7 ++-- .../playgroundWithNameSpaceTemplate.ts | 21 ----------- src/test/suite/mdbExtensionController.test.ts | 6 ++-- 7 files changed, 52 insertions(+), 46 deletions(-) delete mode 100644 src/templates/playgroundWithNameSpaceTemplate.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 732bb7406..32baf6163 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,5 @@ // Place your settings in this file to overwrite default and user settings. { - "editor.formatOnSave": false, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, diff --git a/package.json b/package.json index 5d948af5d..82aa724a4 100644 --- a/package.json +++ b/package.json @@ -604,12 +604,12 @@ }, { "command": "mdb.askCopilotFromTreeItem", - "when": "view == mongoDBConnectionExplorer && (viewItem == databaseTreeItem || viewItem == collectionTreeItem)", + "when": "mdb.isCopilotActive == true && view == mongoDBConnectionExplorer && (viewItem == databaseTreeItem || viewItem == collectionTreeItem)", "group": "inline@1" }, { "command": "mdb.askCopilotFromTreeItem", - "when": "view == mongoDBConnectionExplorer && (viewItem == databaseTreeItem || viewItem == collectionTreeItem)", + "when": "mdb.isCopilotActive == true && view == mongoDBConnectionExplorer && (viewItem == databaseTreeItem || viewItem == collectionTreeItem)", "group": "3@1" }, { diff --git a/src/editors/playgroundController.ts b/src/editors/playgroundController.ts index 8122c2130..608ca95dc 100644 --- a/src/editors/playgroundController.ts +++ b/src/editors/playgroundController.ts @@ -42,7 +42,6 @@ import { getPlaygroundExtensionForTelemetry, } from '../utils/playground'; import type ExportToLanguageCodeLensProvider from './exportToLanguageCodeLensProvider'; -import { playgroundWithNamespaceTemplate } from '../templates/playgroundWithNameSpaceTemplate'; const log = createLogger('playground controller'); @@ -243,7 +242,7 @@ export default class PlaygroundController { if (element instanceof DatabaseTreeItem) { content = content - .replace('NEW_DATABASE_NAME', element.databaseName) + .replace('CURRENT_DATABASE', element.databaseName) .replace('Create a new database', 'The current database to use'); this._telemetryService.trackPlaygroundCreated('createCollection'); } else { @@ -318,22 +317,32 @@ export default class PlaygroundController { return this._createPlaygroundFileWithContent(content); } - async createPlayground( - treeItem?: DatabaseTreeItem | CollectionTreeItem + async createPlaygroundFromTreeView( + treeItem: DatabaseTreeItem | CollectionTreeItem ): Promise { + let content = ''; + if (treeItem instanceof DatabaseTreeItem) { + content = playgroundInsertDocumentTemplate.replace( + 'CURRENT_DATABASE', + treeItem.databaseName + ); + this._telemetryService.trackPlaygroundCreated('fromDatabaseTreeItem'); + } else if (treeItem instanceof CollectionTreeItem) { + content = playgroundInsertDocumentTemplate + .replace('CURRENT_DATABASE', treeItem.databaseName) + .replace('CURRENT_COLLECTION', treeItem.collectionName); + this._telemetryService.trackPlaygroundCreated('fromCollectionTreeItem'); + } + + return this._createPlaygroundFileWithContent(content); + } + + async createPlayground(): Promise { const useDefaultTemplate = !!vscode.workspace .getConfiguration('mdb') .get('useDefaultTemplateForPlayground'); let content = ''; - - if (treeItem instanceof DatabaseTreeItem) { - content = playgroundWithNamespaceTemplate(treeItem.databaseName); - } else if (treeItem instanceof CollectionTreeItem) { - content = playgroundWithNamespaceTemplate( - treeItem.databaseName, - treeItem.collectionName - ); - } else if (useDefaultTemplate) { + if (useDefaultTemplate) { const isStreams = this._connectionController.isConnectedToAtlasStreams(); const template = isStreams ? playgroundStreamsTemplate diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index 2bb025de9..f4938d4f6 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -53,6 +53,7 @@ import type { SendMessageToParticipantOptions, SendMessageToParticipantFromInputOptions, } from './participant/participantTypes'; +import { logger } from 'mongodb-rag-core'; // This class is the top-level controller for our extension. // Commands which the extensions handles are defined in the function `activate`. @@ -174,7 +175,10 @@ export default class MDBExtensionController implements vscode.Disposable { await this._languageServerController.startLanguageServer(); this.registerCommands(); + logger.info('this is running'); + this.updateCopilotStatusContext(); this.showOverviewPageIfRecentlyInstalled(); + void this.showSurveyForEstablishedUsers(); void this.showCopilotIntroductionForEstablishedUsers(); } @@ -746,7 +750,7 @@ export default class MDBExtensionController implements vscode.Disposable { this.registerCommand( EXTENSION_COMMANDS.MDB_CREATE_PLAYGROUND_FROM_CONNECTION_TREE_VIEW, (treeItem: DatabaseTreeItem | CollectionTreeItem) => - this._playgroundController.createPlayground(treeItem) + this._playgroundController.createPlaygroundFromTreeView(treeItem) ); this.registerCommand( EXTENSION_COMMANDS.MDB_REFRESH_PLAYGROUNDS_FROM_TREE_VIEW, @@ -948,6 +952,24 @@ export default class MDBExtensionController implements vscode.Disposable { } } + updateCopilotStatusContext(): void { + const copilot = vscode.extensions.getExtension('github.copilot-chat'); + void vscode.commands.executeCommand( + 'setContext', + 'mdb.isCopilotActive', + copilot?.isActive === true + ); + logger.info(`coplit is ${copilot?.isActive}`); + vscode.extensions.onDidChange(() => { + const copilot = vscode.extensions.getExtension('github.copilot-chat'); + void vscode.commands.executeCommand( + 'setContext', + 'mdb.isCopilotActive', + copilot?.isActive === true + ); + }); + } + async showCopilotIntroductionForEstablishedUsers(): Promise { const copilotIntroductionShown = this._storageController.get( diff --git a/src/templates/playgroundCreateCollectionTemplate.ts b/src/templates/playgroundCreateCollectionTemplate.ts index a24eba944..1a506f8f1 100644 --- a/src/templates/playgroundCreateCollectionTemplate.ts +++ b/src/templates/playgroundCreateCollectionTemplate.ts @@ -2,14 +2,11 @@ const template = `/* global use, db */ // MongoDB Playground // Use Ctrl+Space inside a snippet or a string literal to trigger completions. -const database = 'NEW_DATABASE_NAME'; -const collection = 'NEW_COLLECTION_NAME'; - // Create a new database. -use(database); +use('CURRENT_DATABASE'); // Create a new collection. -db.createCollection(collection); +db.createCollection('CURRENT_COLLECTION'); // The prototype form to create a collection: /* db.createCollection( , diff --git a/src/templates/playgroundWithNameSpaceTemplate.ts b/src/templates/playgroundWithNameSpaceTemplate.ts deleted file mode 100644 index 6f4280ce4..000000000 --- a/src/templates/playgroundWithNameSpaceTemplate.ts +++ /dev/null @@ -1,21 +0,0 @@ -import template from './playgroundCloneDocumentTemplate'; - -export function playgroundWithNamespaceTemplate( - databaseName?: string, - collectionName?: string -): string { - let adjustedTemplate = template; - if (databaseName) { - adjustedTemplate = adjustedTemplate.replaceAll( - 'CURRENT_DATABASE', - databaseName - ); - } - if (collectionName) { - adjustedTemplate = adjustedTemplate.replaceAll( - 'CURRENT_COLLECTION', - collectionName - ); - } - return adjustedTemplate; -} diff --git a/src/test/suite/mdbExtensionController.test.ts b/src/test/suite/mdbExtensionController.test.ts index dac0bb16a..54719580d 100644 --- a/src/test/suite/mdbExtensionController.test.ts +++ b/src/test/suite/mdbExtensionController.test.ts @@ -524,8 +524,8 @@ suite('MDBExtensionController Test Suite', function () { const content = fakeCreatePlaygroundFileWithContent.firstCall.args[0]; assert(content.includes('// Create a new database.')); - assert(content.includes('NEW_DATABASE_NAME')); - assert(content.includes('NEW_COLLECTION_NAME')); + assert(content.includes('CURRENT_DATABASE')); + assert(content.includes('CURRENT_COLLECTION')); }); test('mdb.addCollection should create a MongoDB playground with create collection template', async () => { @@ -535,7 +535,7 @@ suite('MDBExtensionController Test Suite', function () { const content = fakeCreatePlaygroundFileWithContent.firstCall.args[0]; assert(content.includes('// The current database to use.')); assert(content.includes('zebra')); - assert(content.includes('NEW_COLLECTION_NAME')); + assert(content.includes('CURRENT_COLLECTION')); assert(!content.includes('time-series')); }); From b0ca54309ed5eac593d87f37671acf6068ea8423 Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 28 Nov 2024 13:14:16 +0100 Subject: [PATCH 13/32] use DocumentSource --- src/participant/participantTypes.ts | 3 ++- src/telemetry/telemetryService.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/participant/participantTypes.ts b/src/participant/participantTypes.ts index dbf6787c8..3d271b2a1 100644 --- a/src/participant/participantTypes.ts +++ b/src/participant/participantTypes.ts @@ -1,4 +1,5 @@ import type * as vscode from 'vscode'; +import type { DocumentSource } from '../documentSource'; export type SendMessageToParticipantOptions = { message: string; @@ -8,6 +9,6 @@ export type SendMessageToParticipantOptions = { export type SendMessageToParticipantFromInputOptions = { messagePrefix?: string; - source?: 'query with copilot codelens'; + source?: DocumentSource; } & Omit & vscode.InputBoxOptions; diff --git a/src/telemetry/telemetryService.ts b/src/telemetry/telemetryService.ts index 1b1e92869..d78c1f0d1 100644 --- a/src/telemetry/telemetryService.ts +++ b/src/telemetry/telemetryService.ts @@ -131,7 +131,7 @@ export type CopilotIntroductionProperties = { }; export type ParticipantOpenedFromInputBoxProperties = { - source?: 'query with copilot codelens'; + source?: DocumentSource; }; export function chatResultFeedbackKindToTelemetryValue( From c87078682c6ef67cf93b063aede4179097e93bb5 Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 28 Nov 2024 13:17:38 +0100 Subject: [PATCH 14/32] use document source --- src/documentSource.ts | 1 + src/editors/queryWithCopilotCodeLensProvider.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/documentSource.ts b/src/documentSource.ts index 171e3e48b..4c110ca04 100644 --- a/src/documentSource.ts +++ b/src/documentSource.ts @@ -2,4 +2,5 @@ export enum DocumentSource { DOCUMENT_SOURCE_TREEVIEW = 'treeview', DOCUMENT_SOURCE_PLAYGROUND = 'playground', DOCUMENT_SOURCE_COLLECTIONVIEW = 'collectionview', + DOCUMENT_SOURCE_QUERY_WITH_COPILOT_CODELENS = 'query with copilot codelens', } diff --git a/src/editors/queryWithCopilotCodeLensProvider.ts b/src/editors/queryWithCopilotCodeLensProvider.ts index 5e68f830c..23b3958a4 100644 --- a/src/editors/queryWithCopilotCodeLensProvider.ts +++ b/src/editors/queryWithCopilotCodeLensProvider.ts @@ -3,6 +3,7 @@ import EXTENSION_COMMANDS from '../commands'; import type { SendMessageToParticipantFromInputOptions } from '../participant/participantTypes'; import { isPlayground } from '../utils/playground'; import { COPILOT_EXTENSION_ID } from '../participant/constants'; +import { DocumentSource } from '../documentSource'; export class QueryWithCopilotCodeLensProvider implements vscode.CodeLensProvider @@ -32,7 +33,7 @@ export class QueryWithCopilotCodeLensProvider 'e.g. Find the document in sample_mflix.users with the name of Kayden Washington', messagePrefix: '/query', isNewChat: true, - source: 'query with copilot codelens', + source: DocumentSource.DOCUMENT_SOURCE_QUERY_WITH_COPILOT_CODELENS, }; return [ From 145af8d0dace7802f08cc19c67f09cb31757e087 Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 28 Nov 2024 13:23:30 +0100 Subject: [PATCH 15/32] use sendMessage in extensionController --- src/mdbExtensionController.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index 6890be53c..ee3b9a2b7 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -975,12 +975,9 @@ export default class MDBExtensionController implements vscode.Disposable { const copilot = vscode.extensions.getExtension('github.copilot-chat'); if (result?.title === action) { - await vscode.commands.executeCommand('workbench.action.chat.newChat'); - await vscode.commands.executeCommand( - 'workbench.action.chat.clearHistory' - ); - await vscode.commands.executeCommand('workbench.action.chat.open', { - query: '@MongoDB ', + await this._participantController.sendMessageToParticipant({ + message: '', + isNewChat: true, isPartialQuery: true, }); this._telemetryService.trackCopilotIntroductionClicked({ From 692dc1cc6eb1e30551a5ef00bd452f363a34a8f7 Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 28 Nov 2024 13:24:28 +0100 Subject: [PATCH 16/32] change srv text --- src/connectionController.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/connectionController.ts b/src/connectionController.ts index 301db9e97..4bffaa094 100644 --- a/src/connectionController.ts +++ b/src/connectionController.ts @@ -201,7 +201,7 @@ export default class ConnectionController { ignoreFocusOut: true, placeHolder: 'e.g. mongodb+srv://username:password@cluster0.mongodb.net/admin', - prompt: 'Enter your connection string (SRV or standard)', + prompt: 'Enter your SRV or standard connection string', validateInput: (uri: string) => { if ( !uri.startsWith('mongodb://') && From 3e8bd0ea668045ac963ae12997387c5552a44f0f Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 28 Nov 2024 14:07:29 +0100 Subject: [PATCH 17/32] WIP --- src/editors/playgroundSelectionCodeActionProvider.ts | 2 +- src/extension.ts | 2 +- src/mdbExtensionController.ts | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/editors/playgroundSelectionCodeActionProvider.ts b/src/editors/playgroundSelectionCodeActionProvider.ts index 578e68ce4..8fcd80910 100644 --- a/src/editors/playgroundSelectionCodeActionProvider.ts +++ b/src/editors/playgroundSelectionCodeActionProvider.ts @@ -42,7 +42,7 @@ export default class PlaygroundSelectionCodeActionProvider provideCodeActions(): vscode.CodeAction[] | undefined { const editor = vscode.window.activeTextEditor; - const copilot = vscode.extensions.getExtension('github.copilot-chat'); + const copilot = vscode.extensions.getExtension('GitHub.copilot-chat'); let codeActions: vscode.CodeAction[] = [ this.createCodeAction({ title: 'Run selected playground blocks', diff --git a/src/extension.ts b/src/extension.ts index a4d690889..2540ecf03 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -34,7 +34,7 @@ export async function activate( .getConfiguration('mdb.connectionSaving') .get('defaultConnectionSavingLocation'); - log.info('Activating extension...', { + log.info('123Activating extension...', { id: context.extension.id, version: version, mode: vscode.ExtensionMode[context.extensionMode], diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index 71697b02e..328fd17ff 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -53,8 +53,8 @@ import type { SendMessageToParticipantOptions, SendMessageToParticipantFromInputOptions, } from './participant/participantTypes'; -import { logger } from 'mongodb-rag-core'; - +import { createLogger } from './logging'; +const log = createLogger('connection controller'); // This class is the top-level controller for our extension. // Commands which the extensions handles are defined in the function `activate`. export default class MDBExtensionController implements vscode.Disposable { @@ -175,7 +175,7 @@ export default class MDBExtensionController implements vscode.Disposable { await this._languageServerController.startLanguageServer(); this.registerCommands(); - logger.info('this is running'); + log.info('this is running'); this.updateCopilotStatusContext(); this.showOverviewPageIfRecentlyInstalled(); @@ -966,9 +966,10 @@ export default class MDBExtensionController implements vscode.Disposable { 'mdb.isCopilotActive', copilot?.isActive === true ); - logger.info(`coplit is ${copilot?.isActive}`); + log.info(`coplit is ${copilot?.isActive}`); vscode.extensions.onDidChange(() => { const copilot = vscode.extensions.getExtension('github.copilot-chat'); + log.info(`coplit is ${copilot?.isActive}`); void vscode.commands.executeCommand( 'setContext', 'mdb.isCopilotActive', From f22248fbe0ee6d1410fc0195e5c155a804ee946c Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 28 Nov 2024 21:16:22 +0100 Subject: [PATCH 18/32] move to participant --- src/mdbExtensionController.ts | 21 ------------------- src/participant/participant.ts | 37 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index 707f5089c..9148f7189 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -173,8 +173,6 @@ export default class MDBExtensionController implements vscode.Disposable { await this._languageServerController.startLanguageServer(); this.registerCommands(); - log.info('this is running'); - this.updateCopilotStatusContext(); this.showOverviewPageIfRecentlyInstalled(); // ------ In-app notifications ------ // @@ -957,25 +955,6 @@ export default class MDBExtensionController implements vscode.Disposable { } } - updateCopilotStatusContext(): void { - const copilot = vscode.extensions.getExtension('github.copilot-chat'); - void vscode.commands.executeCommand( - 'setContext', - 'mdb.isCopilotActive', - copilot?.isActive === true - ); - log.info(`coplit is ${copilot?.isActive}`); - vscode.extensions.onDidChange(() => { - const copilot = vscode.extensions.getExtension('github.copilot-chat'); - log.info(`coplit is ${copilot?.isActive}`); - void vscode.commands.executeCommand( - 'setContext', - 'mdb.isCopilotActive', - copilot?.isActive === true - ); - }); - } - async showCopilotIntroductionForEstablishedUsers(): Promise { const copilotIntroductionShown = this._storageController.get( diff --git a/src/participant/participant.ts b/src/participant/participant.ts index fd60a75d9..f1e7cd4d2 100644 --- a/src/participant/participant.ts +++ b/src/participant/participant.ts @@ -23,6 +23,7 @@ import { schemaRequestChatResult, createCancelledRequestChatResult, codeBlockIdentifier, + COPILOT_EXTENSION_ID, } from './constants'; import { SchemaFormatter } from './schema'; import { getSimplifiedSampleDocuments } from './sampleDocuments'; @@ -84,6 +85,7 @@ export default class ParticipantController { _docsChatbotAIService: DocsChatbotAIService; _telemetryService: TelemetryService; _playgroundResultProvider: PlaygroundResultProvider; + _copilotStatusSubscription?: vscode.Disposable; constructor({ connectionController, @@ -121,9 +123,44 @@ export default class ParticipantController { participantId: this._participant?.id, }); this._participant.onDidReceiveFeedback(this.handleUserFeedback.bind(this)); + + this.subscribeToCopilotStatus(); + return this._participant; } + dispose(): void { + this._copilotStatusSubscription?.dispose(); + } + + subscribeToCopilotStatus(): void { + const updateCopilotStatusContext = (): void => { + const copilot = vscode.extensions.getExtension(COPILOT_EXTENSION_ID); + void vscode.commands.executeCommand( + 'setContext', + 'mdb.isCopilotActive', + copilot?.isActive === true + ); + if (copilot !== undefined && !copilot.isActive) { + // TODO: This is a workaround for https://github.com/microsoft/vscode/issues/234426 + // The onDidChange event only gets fired if extensions get added or removed but + // not when they are activated; so retry after a delay and check again. + setTimeout(() => { + void vscode.commands.executeCommand( + 'setContext', + 'mdb.isCopilotActive', + copilot?.isActive === true + ); + }, 3000); + } + }; + + this._copilotStatusSubscription = vscode.extensions.onDidChange(() => + updateCopilotStatusContext() + ); + updateCopilotStatusContext(); + } + getParticipant(): vscode.ChatParticipant | undefined { return this._participant; } From 44a7eeaa3f67b4134c0ac907bae9e6b38ed79255 Mon Sep 17 00:00:00 2001 From: gagik Date: Thu, 28 Nov 2024 21:21:11 +0100 Subject: [PATCH 19/32] remove test related changes --- .vscode/settings.json | 1 + src/editors/playgroundSelectionCodeActionProvider.ts | 3 ++- src/extension.ts | 2 +- src/mdbExtensionController.ts | 3 ++- src/participant/constants.ts | 1 + src/participant/participant.ts | 8 -------- 6 files changed, 7 insertions(+), 11 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 32baf6163..732bb7406 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,6 @@ // Place your settings in this file to overwrite default and user settings. { + "editor.formatOnSave": false, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, diff --git a/src/editors/playgroundSelectionCodeActionProvider.ts b/src/editors/playgroundSelectionCodeActionProvider.ts index 8fcd80910..d943ddaba 100644 --- a/src/editors/playgroundSelectionCodeActionProvider.ts +++ b/src/editors/playgroundSelectionCodeActionProvider.ts @@ -2,6 +2,7 @@ import * as vscode from 'vscode'; import EXTENSION_COMMANDS from '../commands'; import { isPlayground, getSelectedText } from '../utils/playground'; +import { COPILOT_CHAT_EXTENSION_ID } from '../participant/constants'; export const EXPORT_TO_LANGUAGE_ALIASES = [ { id: 'csharp', alias: 'C#' }, @@ -42,7 +43,7 @@ export default class PlaygroundSelectionCodeActionProvider provideCodeActions(): vscode.CodeAction[] | undefined { const editor = vscode.window.activeTextEditor; - const copilot = vscode.extensions.getExtension('GitHub.copilot-chat'); + const copilot = vscode.extensions.getExtension(COPILOT_CHAT_EXTENSION_ID); let codeActions: vscode.CodeAction[] = [ this.createCodeAction({ title: 'Run selected playground blocks', diff --git a/src/extension.ts b/src/extension.ts index 2540ecf03..a4d690889 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -34,7 +34,7 @@ export async function activate( .getConfiguration('mdb.connectionSaving') .get('defaultConnectionSavingLocation'); - log.info('123Activating extension...', { + log.info('Activating extension...', { id: context.extension.id, version: version, mode: vscode.ExtensionMode[context.extensionMode], diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index 9148f7189..590b22bc9 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -52,6 +52,7 @@ import type { SendMessageToParticipantFromInputOptions, } from './participant/participantTypes'; import { createLogger } from './logging'; +import { COPILOT_CHAT_EXTENSION_ID } from './participant/constants'; const log = createLogger('connection controller'); // This class is the top-level controller for our extension. // Commands which the extensions handles are defined in the function `activate`. @@ -985,7 +986,7 @@ export default class MDBExtensionController implements vscode.Disposable { } ); - const copilot = vscode.extensions.getExtension('github.copilot-chat'); + const copilot = vscode.extensions.getExtension(COPILOT_CHAT_EXTENSION_ID); if (result?.title === action) { await this._participantController.sendMessageToParticipant({ message: '', diff --git a/src/participant/constants.ts b/src/participant/constants.ts index 1aeec079e..86511f823 100644 --- a/src/participant/constants.ts +++ b/src/participant/constants.ts @@ -4,6 +4,7 @@ import { ChatMetadataStore } from './chatMetadata'; export const CHAT_PARTICIPANT_ID = 'mongodb.participant'; export const CHAT_PARTICIPANT_MODEL = 'gpt-4o'; export const COPILOT_EXTENSION_ID = 'GitHub.copilot'; +export const COPILOT_CHAT_EXTENSION_ID = 'GitHub.copilot-chat'; export type ParticipantResponseType = | 'query' diff --git a/src/participant/participant.ts b/src/participant/participant.ts index f1e7cd4d2..56a348821 100644 --- a/src/participant/participant.ts +++ b/src/participant/participant.ts @@ -869,13 +869,6 @@ export default class ParticipantController { collectionName: undefined, }; - log.info( - `messages: ${util.inspect( - messagesWithNamespace.messages.map((c) => getContent(c)) - )}` - ); - log.info(`context.history: ${util.inspect(context.history, true, 10)}`); - // When there's no user message content we can // skip the request to the model. This would happen with /schema. if (Prompts.doMessagesContainUserInput(messagesWithNamespace.messages)) { @@ -899,7 +892,6 @@ export default class ParticipantController { modelInput: messagesWithNamespace, token, }); - log.info(`with namespace: ${responseContentWithNamespace}`); ({ databaseName, collectionName } = Prompts.namespace.extractDatabaseAndCollectionNameFromResponse( responseContentWithNamespace From 68c448216839eb8556316571832ec3e4d183417d Mon Sep 17 00:00:00 2001 From: gagik Date: Fri, 29 Nov 2024 10:12:11 +0100 Subject: [PATCH 20/32] remove more test code --- src/mdbExtensionController.ts | 3 +-- src/participant/participant.ts | 1 - src/templates/playgroundCreateCollectionTemplate.ts | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index 590b22bc9..973c2345b 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -51,9 +51,8 @@ import type { SendMessageToParticipantOptions, SendMessageToParticipantFromInputOptions, } from './participant/participantTypes'; -import { createLogger } from './logging'; import { COPILOT_CHAT_EXTENSION_ID } from './participant/constants'; -const log = createLogger('connection controller'); + // This class is the top-level controller for our extension. // Commands which the extensions handles are defined in the function `activate`. export default class MDBExtensionController implements vscode.Disposable { diff --git a/src/participant/participant.ts b/src/participant/participant.ts index 56a348821..02ebb395e 100644 --- a/src/participant/participant.ts +++ b/src/participant/participant.ts @@ -250,7 +250,6 @@ export default class ParticipantController { message: '', isPartialQuery: true, }); - log.info(`after: ${util.inspect(this._chatMetadataStore._chats)}`); return undefined; } diff --git a/src/templates/playgroundCreateCollectionTemplate.ts b/src/templates/playgroundCreateCollectionTemplate.ts index 1a506f8f1..a306a3d7b 100644 --- a/src/templates/playgroundCreateCollectionTemplate.ts +++ b/src/templates/playgroundCreateCollectionTemplate.ts @@ -6,7 +6,7 @@ const template = `/* global use, db */ use('CURRENT_DATABASE'); // Create a new collection. -db.createCollection('CURRENT_COLLECTION'); +db.createCollection('NEW_COLLECTION_NAME'); // The prototype form to create a collection: /* db.createCollection( , From efb14323209ca9e62979cb1a39c8f2ede33b42da Mon Sep 17 00:00:00 2001 From: gagik Date: Fri, 29 Nov 2024 10:18:45 +0100 Subject: [PATCH 21/32] fix test --- src/test/suite/mdbExtensionController.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/suite/mdbExtensionController.test.ts b/src/test/suite/mdbExtensionController.test.ts index dec058ba4..7ecf54b4a 100644 --- a/src/test/suite/mdbExtensionController.test.ts +++ b/src/test/suite/mdbExtensionController.test.ts @@ -523,7 +523,7 @@ suite('MDBExtensionController Test Suite', function () { const content = fakeCreatePlaygroundFileWithContent.firstCall.args[0]; assert(content.includes('// Create a new database.')); assert(content.includes('CURRENT_DATABASE')); - assert(content.includes('CURRENT_COLLECTION')); + assert(content.includes('NEW_COLLECTION_NAME')); }); test('mdb.addCollection should create a MongoDB playground with create collection template', async () => { @@ -533,7 +533,7 @@ suite('MDBExtensionController Test Suite', function () { const content = fakeCreatePlaygroundFileWithContent.firstCall.args[0]; assert(content.includes('// The current database to use.')); assert(content.includes('zebra')); - assert(content.includes('CURRENT_COLLECTION')); + assert(content.includes('NEW_COLLECTION_NAME')); assert(!content.includes('time-series')); }); From 93fb8f81812c5aa5814a12e62d336681b32d80ef Mon Sep 17 00:00:00 2001 From: gagik Date: Fri, 29 Nov 2024 13:26:31 +0100 Subject: [PATCH 22/32] add tests --- .../suite/participant/participant.test.ts | 83 +++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/src/test/suite/participant/participant.test.ts b/src/test/suite/participant/participant.test.ts index ce89bf105..5e4722c2f 100644 --- a/src/test/suite/participant/participant.test.ts +++ b/src/test/suite/participant/participant.test.ts @@ -42,6 +42,8 @@ import { } from './participantHelpers'; import EditDocumentCodeLensProvider from '../../../editors/editDocumentCodeLensProvider'; import PlaygroundResultProvider from '../../../editors/playgroundResultProvider'; +import { CollectionTreeItem, DatabaseTreeItem } from '../../../explorer'; +import type { SendMessageToParticipantOptions } from '../../../participant/participantTypes'; // The Copilot's model in not available in tests, // therefore we need to mock its methods and returning values. @@ -1802,6 +1804,87 @@ Schema: }); }); + suite('opened from tree view', function () { + let sendMessageToParticipantStub: SinonStub< + [options: SendMessageToParticipantOptions], + Promise + >; + + beforeEach(function () { + sendMessageToParticipantStub = sinon.stub( + testParticipantController, + 'sendMessageToParticipant' + ); + }); + + suite('with a database item', function () { + const mockDatabaseItem = Object.assign( + Object.create(DatabaseTreeItem.prototype), + { + databaseName: 'testDb', + } as DatabaseTreeItem + ); + + test('opens the chat and sends a message to set database context', async function () { + expect(sendMessageToParticipantStub).not.called; + + await testParticipantController.askCopilotFromTreeItem( + mockDatabaseItem + ); + + expect(sendMessageToParticipantStub).has.callCount(2); + + expect(sendMessageToParticipantStub.getCall(0).args).deep.equals([ + { + message: `I want to ask questions about the \`${mockDatabaseItem.databaseName}\` database.`, + isNewChat: true, + }, + ]); + + expect(sendMessageToParticipantStub.getCall(1).args).deep.equals([ + { + message: '', + isPartialQuery: true, + }, + ]); + }); + }); + + suite('with a collection item', function () { + const mockCollectionItem = Object.assign( + Object.create(CollectionTreeItem.prototype), + { + databaseName: 'testDb', + collectionName: 'testColl', + } as CollectionTreeItem + ); + + test('opens the chat and sends a message to set database and collection context', async function () { + expect(sendMessageToParticipantStub).not.called; + + await testParticipantController.askCopilotFromTreeItem( + mockCollectionItem + ); + + expect(sendMessageToParticipantStub).has.callCount(2); + + expect(sendMessageToParticipantStub.getCall(0).args).deep.equals([ + { + message: `I want to ask questions about the \`${mockCollectionItem.databaseName}\` database's \`${mockCollectionItem.collectionName}\` collection.`, + isNewChat: true, + }, + ]); + + expect(sendMessageToParticipantStub.getCall(1).args).deep.equals([ + { + message: '', + isPartialQuery: true, + }, + ]); + }); + }); + }); + suite('determining the namespace', function () { ['query', 'schema'].forEach(function (command) { suite(`${command} command`, function () { From aeee8284919fe8ce52abcc72873aa6a5830f0f07 Mon Sep 17 00:00:00 2001 From: gagik Date: Fri, 29 Nov 2024 13:34:32 +0100 Subject: [PATCH 23/32] only range --- src/participant/participant.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/participant/participant.ts b/src/participant/participant.ts index 02ebb395e..670636fae 100644 --- a/src/participant/participant.ts +++ b/src/participant/participant.ts @@ -236,14 +236,15 @@ export default class ParticipantController { message: `I want to ask questions about the \`${databaseName}\` database.`, isNewChat: true, }); - } - if (treeItem instanceof CollectionTreeItem) { + } else if (treeItem instanceof CollectionTreeItem) { const { databaseName, collectionName } = treeItem; await this.sendMessageToParticipant({ message: `I want to ask questions about the \`${databaseName}\` database's \`${collectionName}\` collection.`, isNewChat: true, }); + } else { + throw new Error('Unsupported tree item type'); } await this.sendMessageToParticipant({ From f30c1f4da65375f468047c23d777a1a1346f2f30 Mon Sep 17 00:00:00 2001 From: gagik Date: Mon, 2 Dec 2024 10:03:51 +0100 Subject: [PATCH 24/32] use Promise --- src/participant/participant.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/participant/participant.ts b/src/participant/participant.ts index 670636fae..fef4db158 100644 --- a/src/participant/participant.ts +++ b/src/participant/participant.ts @@ -228,7 +228,7 @@ export default class ParticipantController { async askCopilotFromTreeItem( treeItem: DatabaseTreeItem | CollectionTreeItem - ): Promise { + ): Promise { if (treeItem instanceof DatabaseTreeItem) { const { databaseName } = treeItem; @@ -251,7 +251,6 @@ export default class ParticipantController { message: '', isPartialQuery: true, }); - return undefined; } async _getChatResponse({ From e7a1d084043a6f0babec2cd9e99f6cd464cfe025 Mon Sep 17 00:00:00 2001 From: gagik Date: Tue, 3 Dec 2024 16:14:38 +0100 Subject: [PATCH 25/32] Apply changes from feedback --- src/editors/playgroundController.ts | 14 ++++----- src/mdbExtensionController.ts | 13 ++++++++ src/participant/participant.ts | 31 ------------------- src/participant/participantTypes.ts | 8 ----- .../playgroundCreateCollectionTemplate.ts | 7 +++-- ...laygroundFromCollectionTreeItemTemplate.ts | 16 ++++++++++ .../playgroundFromDatabaseTreeItemTemplate.ts | 12 +++++++ src/templates/templateHelpers.ts | 10 ++++++ src/test/suite/mdbExtensionController.test.ts | 2 +- 9 files changed, 64 insertions(+), 49 deletions(-) create mode 100644 src/templates/playgroundFromCollectionTreeItemTemplate.ts create mode 100644 src/templates/playgroundFromDatabaseTreeItemTemplate.ts create mode 100644 src/templates/templateHelpers.ts diff --git a/src/editors/playgroundController.ts b/src/editors/playgroundController.ts index 608ca95dc..57610e6f2 100644 --- a/src/editors/playgroundController.ts +++ b/src/editors/playgroundController.ts @@ -42,6 +42,8 @@ import { getPlaygroundExtensionForTelemetry, } from '../utils/playground'; import type ExportToLanguageCodeLensProvider from './exportToLanguageCodeLensProvider'; +import { playgroundFromDatabaseTreeItemTemplate } from '../templates/playgroundFromDatabaseTreeItemTemplate'; +import { playgroundFromCollectionTreeItemTemplate } from '../templates/playgroundFromCollectionTreeItemTemplate'; const log = createLogger('playground controller'); @@ -322,15 +324,13 @@ export default class PlaygroundController { ): Promise { let content = ''; if (treeItem instanceof DatabaseTreeItem) { - content = playgroundInsertDocumentTemplate.replace( - 'CURRENT_DATABASE', - treeItem.databaseName - ); + content = playgroundFromDatabaseTreeItemTemplate(treeItem.databaseName); this._telemetryService.trackPlaygroundCreated('fromDatabaseTreeItem'); } else if (treeItem instanceof CollectionTreeItem) { - content = playgroundInsertDocumentTemplate - .replace('CURRENT_DATABASE', treeItem.databaseName) - .replace('CURRENT_COLLECTION', treeItem.collectionName); + content = playgroundFromCollectionTreeItemTemplate( + treeItem.databaseName, + treeItem.collectionName + ); this._telemetryService.trackPlaygroundCreated('fromCollectionTreeItem'); } diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index 973c2345b..ce83d381e 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -184,6 +184,19 @@ export default class MDBExtensionController implements vscode.Disposable { 'mdb.isCopilotActive', copilot?.isActive ); + + // TODO: This is a workaround related to https://github.com/microsoft/vscode/issues/234426 + // If the extension was found but is not activated, there is a chance that the MongoDB extension + // was activated before the Copilot one, so we check again after a delay. + if (copilot && !copilot?.isActive) { + setTimeout(() => { + void vscode.commands.executeCommand( + 'setContext', + 'mdb.isCopilotActive', + copilot?.isActive === true + ); + }, 3000); + } } registerCommands = (): void => { diff --git a/src/participant/participant.ts b/src/participant/participant.ts index fef4db158..325d2406f 100644 --- a/src/participant/participant.ts +++ b/src/participant/participant.ts @@ -23,7 +23,6 @@ import { schemaRequestChatResult, createCancelledRequestChatResult, codeBlockIdentifier, - COPILOT_EXTENSION_ID, } from './constants'; import { SchemaFormatter } from './schema'; import { getSimplifiedSampleDocuments } from './sampleDocuments'; @@ -124,8 +123,6 @@ export default class ParticipantController { }); this._participant.onDidReceiveFeedback(this.handleUserFeedback.bind(this)); - this.subscribeToCopilotStatus(); - return this._participant; } @@ -133,34 +130,6 @@ export default class ParticipantController { this._copilotStatusSubscription?.dispose(); } - subscribeToCopilotStatus(): void { - const updateCopilotStatusContext = (): void => { - const copilot = vscode.extensions.getExtension(COPILOT_EXTENSION_ID); - void vscode.commands.executeCommand( - 'setContext', - 'mdb.isCopilotActive', - copilot?.isActive === true - ); - if (copilot !== undefined && !copilot.isActive) { - // TODO: This is a workaround for https://github.com/microsoft/vscode/issues/234426 - // The onDidChange event only gets fired if extensions get added or removed but - // not when they are activated; so retry after a delay and check again. - setTimeout(() => { - void vscode.commands.executeCommand( - 'setContext', - 'mdb.isCopilotActive', - copilot?.isActive === true - ); - }, 3000); - } - }; - - this._copilotStatusSubscription = vscode.extensions.onDidChange(() => - updateCopilotStatusContext() - ); - updateCopilotStatusContext(); - } - getParticipant(): vscode.ChatParticipant | undefined { return this._participant; } diff --git a/src/participant/participantTypes.ts b/src/participant/participantTypes.ts index b9e258385..7c1eb76ed 100644 --- a/src/participant/participantTypes.ts +++ b/src/participant/participantTypes.ts @@ -6,14 +6,6 @@ export type SendMessageToParticipantOptions = { message: string; isNewChat?: boolean; isPartialQuery?: boolean; - /** - * Any previous chat requests and responses that should be shown in the chat view. - * Note that currently these requests do not end up included in vscode's context.history. - */ - previousRequests?: { - request: string; - response: string; - }[]; }; export type SendMessageToParticipantFromInputOptions = { diff --git a/src/templates/playgroundCreateCollectionTemplate.ts b/src/templates/playgroundCreateCollectionTemplate.ts index a306a3d7b..a24eba944 100644 --- a/src/templates/playgroundCreateCollectionTemplate.ts +++ b/src/templates/playgroundCreateCollectionTemplate.ts @@ -2,11 +2,14 @@ const template = `/* global use, db */ // MongoDB Playground // Use Ctrl+Space inside a snippet or a string literal to trigger completions. +const database = 'NEW_DATABASE_NAME'; +const collection = 'NEW_COLLECTION_NAME'; + // Create a new database. -use('CURRENT_DATABASE'); +use(database); // Create a new collection. -db.createCollection('NEW_COLLECTION_NAME'); +db.createCollection(collection); // The prototype form to create a collection: /* db.createCollection( , diff --git a/src/templates/playgroundFromCollectionTreeItemTemplate.ts b/src/templates/playgroundFromCollectionTreeItemTemplate.ts new file mode 100644 index 000000000..1113642da --- /dev/null +++ b/src/templates/playgroundFromCollectionTreeItemTemplate.ts @@ -0,0 +1,16 @@ +import { createTemplate } from './templateHelpers'; + +export const playgroundFromCollectionTreeItemTemplate = createTemplate( + (databaseName, collectionName) => `// MongoDB Playground +// Use Ctrl+Space inside a snippet or a string literal to trigger completions. +// Use PLAYGROUND_SHORTCUT to run the playground + +// The current database to use. +use(${databaseName}); + +// Find a document in a collection. +db.getCollection(${collectionName}).findOne({ + +}); +` +); diff --git a/src/templates/playgroundFromDatabaseTreeItemTemplate.ts b/src/templates/playgroundFromDatabaseTreeItemTemplate.ts new file mode 100644 index 000000000..09dbac99d --- /dev/null +++ b/src/templates/playgroundFromDatabaseTreeItemTemplate.ts @@ -0,0 +1,12 @@ +import { createTemplate } from './templateHelpers'; + +export const playgroundFromDatabaseTreeItemTemplate = createTemplate( + (currentDatabase) => `// MongoDB Playground +// Use Ctrl+Space inside a snippet or a string literal to trigger completions. +// Use PLAYGROUND_SHORTCUT to run the playground + +// The current database to use. +use(${currentDatabase}); + +` +); diff --git a/src/templates/templateHelpers.ts b/src/templates/templateHelpers.ts new file mode 100644 index 000000000..185464a32 --- /dev/null +++ b/src/templates/templateHelpers.ts @@ -0,0 +1,10 @@ +/** Wraps a template function and escapes given string arguments. */ +export function createTemplate string>( + templateBuilder: T +): (...args: Parameters) => string { + return (...args: Parameters) => { + const escapedArgs = args.map((arg) => JSON.stringify(arg)); + + return templateBuilder(...escapedArgs); + }; +} diff --git a/src/test/suite/mdbExtensionController.test.ts b/src/test/suite/mdbExtensionController.test.ts index 7ecf54b4a..9642055e8 100644 --- a/src/test/suite/mdbExtensionController.test.ts +++ b/src/test/suite/mdbExtensionController.test.ts @@ -522,7 +522,7 @@ suite('MDBExtensionController Test Suite', function () { const content = fakeCreatePlaygroundFileWithContent.firstCall.args[0]; assert(content.includes('// Create a new database.')); - assert(content.includes('CURRENT_DATABASE')); + assert(content.includes('NEW_DATABASE_NAME')); assert(content.includes('NEW_COLLECTION_NAME')); }); From 8c9347efae79c4113de114cf5b48714061f900ec Mon Sep 17 00:00:00 2001 From: gagik Date: Tue, 3 Dec 2024 16:26:19 +0100 Subject: [PATCH 26/32] remove playground shortcut --- src/templates/playgroundFromCollectionTreeItemTemplate.ts | 1 - src/templates/playgroundFromDatabaseTreeItemTemplate.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/templates/playgroundFromCollectionTreeItemTemplate.ts b/src/templates/playgroundFromCollectionTreeItemTemplate.ts index 1113642da..35c335887 100644 --- a/src/templates/playgroundFromCollectionTreeItemTemplate.ts +++ b/src/templates/playgroundFromCollectionTreeItemTemplate.ts @@ -3,7 +3,6 @@ import { createTemplate } from './templateHelpers'; export const playgroundFromCollectionTreeItemTemplate = createTemplate( (databaseName, collectionName) => `// MongoDB Playground // Use Ctrl+Space inside a snippet or a string literal to trigger completions. -// Use PLAYGROUND_SHORTCUT to run the playground // The current database to use. use(${databaseName}); diff --git a/src/templates/playgroundFromDatabaseTreeItemTemplate.ts b/src/templates/playgroundFromDatabaseTreeItemTemplate.ts index 09dbac99d..0a5c57941 100644 --- a/src/templates/playgroundFromDatabaseTreeItemTemplate.ts +++ b/src/templates/playgroundFromDatabaseTreeItemTemplate.ts @@ -3,7 +3,6 @@ import { createTemplate } from './templateHelpers'; export const playgroundFromDatabaseTreeItemTemplate = createTemplate( (currentDatabase) => `// MongoDB Playground // Use Ctrl+Space inside a snippet or a string literal to trigger completions. -// Use PLAYGROUND_SHORTCUT to run the playground // The current database to use. use(${currentDatabase}); From 7fa46fe29ed2071325fc5185b064555b36cf41ba Mon Sep 17 00:00:00 2001 From: gagik Date: Tue, 3 Dec 2024 16:55:38 +0100 Subject: [PATCH 27/32] use new database name --- .vscode/settings.json | 1 - src/editors/playgroundController.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 732bb7406..32baf6163 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,5 @@ // Place your settings in this file to overwrite default and user settings. { - "editor.formatOnSave": false, "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" }, diff --git a/src/editors/playgroundController.ts b/src/editors/playgroundController.ts index 57610e6f2..f8697d5d6 100644 --- a/src/editors/playgroundController.ts +++ b/src/editors/playgroundController.ts @@ -244,7 +244,7 @@ export default class PlaygroundController { if (element instanceof DatabaseTreeItem) { content = content - .replace('CURRENT_DATABASE', element.databaseName) + .replace('NEW_DATABASE_NAME', element.databaseName) .replace('Create a new database', 'The current database to use'); this._telemetryService.trackPlaygroundCreated('createCollection'); } else { From 4a6df5a5b1195eb6cbfe91a6901cd9cc8ab1be86 Mon Sep 17 00:00:00 2001 From: gagik Date: Tue, 3 Dec 2024 16:56:26 +0100 Subject: [PATCH 28/32] Remove redundant changes --- src/participant/participantTypes.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/participant/participantTypes.ts b/src/participant/participantTypes.ts index 7c1eb76ed..3d271b2a1 100644 --- a/src/participant/participantTypes.ts +++ b/src/participant/participantTypes.ts @@ -1,7 +1,6 @@ import type * as vscode from 'vscode'; import type { DocumentSource } from '../documentSource'; -/** Based on options from Copilot's chat open command IChatViewOpenOptions */ export type SendMessageToParticipantOptions = { message: string; isNewChat?: boolean; From 09c68eef19384807041ed30436e01315f28f2f97 Mon Sep 17 00:00:00 2001 From: gagik Date: Wed, 4 Dec 2024 10:30:49 +0100 Subject: [PATCH 29/32] changes from feedback --- package.json | 6 +++--- src/commands/index.ts | 2 +- src/editors/playgroundController.ts | 2 +- src/mdbExtensionController.ts | 7 ++++--- src/participant/participant.ts | 10 ---------- 5 files changed, 9 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 3906dbf26..cb99fa7dd 100644 --- a/package.json +++ b/package.json @@ -256,7 +256,7 @@ } }, { - "command": "mdb.createNewPlaygroundFromConnectionTreeView", + "command": "mdb.createNewPlaygroundFromTreeItem", "title": "Create MongoDB Playground", "icon": "$(mdb-playground)" }, @@ -613,12 +613,12 @@ "group": "3@1" }, { - "command": "mdb.createNewPlaygroundFromConnectionTreeView", + "command": "mdb.createNewPlaygroundFromTreeItem", "when": "view == mongoDBConnectionExplorer && (viewItem == databaseTreeItem || viewItem == collectionTreeItem)", "group": "inline@2" }, { - "command": "mdb.createNewPlaygroundFromConnectionTreeView", + "command": "mdb.createNewPlaygroundFromTreeItem", "when": "view == mongoDBConnectionExplorer && (viewItem == databaseTreeItem || viewItem == collectionTreeItem)", "group": "3@2" }, diff --git a/src/commands/index.ts b/src/commands/index.ts index d6c25ab63..deca325c6 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -39,7 +39,7 @@ enum EXTENSION_COMMANDS { MDB_OPEN_PLAYGROUND_FROM_TREE_VIEW = 'mdb.openPlaygroundFromTreeView', MDB_CONNECT_TO_CONNECTION_TREE_VIEW = 'mdb.connectToConnectionTreeItem', MDB_CREATE_PLAYGROUND_FROM_TREE_VIEW = 'mdb.createNewPlaygroundFromTreeView', - MDB_CREATE_PLAYGROUND_FROM_CONNECTION_TREE_VIEW = 'mdb.createNewPlaygroundFromConnectionTreeView', + MDB_CREATE_PLAYGROUND_FROM_TREE_ITEM = 'mdb.createNewPlaygroundFromTreeItem', MDB_DISCONNECT_FROM_CONNECTION_TREE_VIEW = 'mdb.disconnectFromConnectionTreeItem', MDB_EDIT_CONNECTION = 'mdb.editConnection', MDB_REFRESH_CONNECTION = 'mdb.refreshConnection', diff --git a/src/editors/playgroundController.ts b/src/editors/playgroundController.ts index f8697d5d6..7f2c2969d 100644 --- a/src/editors/playgroundController.ts +++ b/src/editors/playgroundController.ts @@ -319,7 +319,7 @@ export default class PlaygroundController { return this._createPlaygroundFileWithContent(content); } - async createPlaygroundFromTreeView( + async createPlaygroundFromTreeItem( treeItem: DatabaseTreeItem | CollectionTreeItem ): Promise { let content = ''; diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index ce83d381e..d63a8ea9a 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -190,6 +190,7 @@ export default class MDBExtensionController implements vscode.Disposable { // was activated before the Copilot one, so we check again after a delay. if (copilot && !copilot?.isActive) { setTimeout(() => { + const copilot = vscode.extensions.getExtension('GitHub.copilot'); void vscode.commands.executeCommand( 'setContext', 'mdb.isCopilotActive', @@ -346,7 +347,7 @@ export default class MDBExtensionController implements vscode.Disposable { ); this.registerParticipantCommand( EXTENSION_COMMANDS.ASK_COPILOT_FROM_TREE_ITEM, - async (treeItem: DatabaseTreeItem) => { + async (treeItem: DatabaseTreeItem | CollectionTreeItem) => { await this._participantController.askCopilotFromTreeItem(treeItem); return true; } @@ -764,9 +765,9 @@ export default class MDBExtensionController implements vscode.Disposable { () => this._playgroundController.createPlayground() ); this.registerCommand( - EXTENSION_COMMANDS.MDB_CREATE_PLAYGROUND_FROM_CONNECTION_TREE_VIEW, + EXTENSION_COMMANDS.MDB_CREATE_PLAYGROUND_FROM_TREE_ITEM, (treeItem: DatabaseTreeItem | CollectionTreeItem) => - this._playgroundController.createPlaygroundFromTreeView(treeItem) + this._playgroundController.createPlaygroundFromTreeItem(treeItem) ); this.registerCommand( EXTENSION_COMMANDS.MDB_REFRESH_PLAYGROUNDS_FROM_TREE_VIEW, diff --git a/src/participant/participant.ts b/src/participant/participant.ts index 325d2406f..af61e0a33 100644 --- a/src/participant/participant.ts +++ b/src/participant/participant.ts @@ -84,7 +84,6 @@ export default class ParticipantController { _docsChatbotAIService: DocsChatbotAIService; _telemetryService: TelemetryService; _playgroundResultProvider: PlaygroundResultProvider; - _copilotStatusSubscription?: vscode.Disposable; constructor({ connectionController, @@ -126,10 +125,6 @@ export default class ParticipantController { return this._participant; } - dispose(): void { - this._copilotStatusSubscription?.dispose(); - } - getParticipant(): vscode.ChatParticipant | undefined { return this._participant; } @@ -215,11 +210,6 @@ export default class ParticipantController { } else { throw new Error('Unsupported tree item type'); } - - await this.sendMessageToParticipant({ - message: '', - isPartialQuery: true, - }); } async _getChatResponse({ From 9c9ffc0e074077d9dd367e5ba37430f9db9a8867 Mon Sep 17 00:00:00 2001 From: gagik Date: Wed, 4 Dec 2024 10:49:35 +0100 Subject: [PATCH 30/32] expect one call --- src/test/suite/participant/participant.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/suite/participant/participant.test.ts b/src/test/suite/participant/participant.test.ts index 5e4722c2f..1472fa1cb 100644 --- a/src/test/suite/participant/participant.test.ts +++ b/src/test/suite/participant/participant.test.ts @@ -1832,7 +1832,7 @@ Schema: mockDatabaseItem ); - expect(sendMessageToParticipantStub).has.callCount(2); + expect(sendMessageToParticipantStub).has.callCount(1); expect(sendMessageToParticipantStub.getCall(0).args).deep.equals([ { @@ -1866,7 +1866,7 @@ Schema: mockCollectionItem ); - expect(sendMessageToParticipantStub).has.callCount(2); + expect(sendMessageToParticipantStub).has.callCount(1); expect(sendMessageToParticipantStub.getCall(0).args).deep.equals([ { From 11cf2343ece862e5200bff81c1934b4d56808ebb Mon Sep 17 00:00:00 2001 From: gagik Date: Wed, 4 Dec 2024 10:57:56 +0100 Subject: [PATCH 31/32] fix tests --- src/test/suite/participant/participant.test.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/test/suite/participant/participant.test.ts b/src/test/suite/participant/participant.test.ts index 1472fa1cb..1263f0e6e 100644 --- a/src/test/suite/participant/participant.test.ts +++ b/src/test/suite/participant/participant.test.ts @@ -1840,13 +1840,6 @@ Schema: isNewChat: true, }, ]); - - expect(sendMessageToParticipantStub.getCall(1).args).deep.equals([ - { - message: '', - isPartialQuery: true, - }, - ]); }); }); @@ -1874,13 +1867,6 @@ Schema: isNewChat: true, }, ]); - - expect(sendMessageToParticipantStub.getCall(1).args).deep.equals([ - { - message: '', - isPartialQuery: true, - }, - ]); }); }); }); From a805fd269b694d4474ad8f0ad4a75ad0e4521d04 Mon Sep 17 00:00:00 2001 From: gagik Date: Fri, 6 Dec 2024 16:18:43 +0100 Subject: [PATCH 32/32] remove line and use COPILOT_EXTENSION_ID --- src/mdbExtensionController.ts | 9 ++++++--- src/participant/participant.ts | 1 - 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/mdbExtensionController.ts b/src/mdbExtensionController.ts index d63a8ea9a..c7c5f139e 100644 --- a/src/mdbExtensionController.ts +++ b/src/mdbExtensionController.ts @@ -51,7 +51,10 @@ import type { SendMessageToParticipantOptions, SendMessageToParticipantFromInputOptions, } from './participant/participantTypes'; -import { COPILOT_CHAT_EXTENSION_ID } from './participant/constants'; +import { + COPILOT_CHAT_EXTENSION_ID, + COPILOT_EXTENSION_ID, +} from './participant/constants'; // This class is the top-level controller for our extension. // Commands which the extensions handles are defined in the function `activate`. @@ -178,7 +181,7 @@ export default class MDBExtensionController implements vscode.Disposable { // ------ In-app notifications ------ // void this.showCopilotIntroductionForEstablishedUsers(); - const copilot = vscode.extensions.getExtension('GitHub.copilot'); + const copilot = vscode.extensions.getExtension(COPILOT_EXTENSION_ID); void vscode.commands.executeCommand( 'setContext', 'mdb.isCopilotActive', @@ -190,7 +193,7 @@ export default class MDBExtensionController implements vscode.Disposable { // was activated before the Copilot one, so we check again after a delay. if (copilot && !copilot?.isActive) { setTimeout(() => { - const copilot = vscode.extensions.getExtension('GitHub.copilot'); + const copilot = vscode.extensions.getExtension(COPILOT_EXTENSION_ID); void vscode.commands.executeCommand( 'setContext', 'mdb.isCopilotActive', diff --git a/src/participant/participant.ts b/src/participant/participant.ts index af61e0a33..5d98dd543 100644 --- a/src/participant/participant.ts +++ b/src/participant/participant.ts @@ -121,7 +121,6 @@ export default class ParticipantController { participantId: this._participant?.id, }); this._participant.onDidReceiveFeedback(this.handleUserFeedback.bind(this)); - return this._participant; }