3
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
4
*--------------------------------------------------------------------------------------------*/
5
5
6
- import { Action } from 'vs/base/common/actions' ;
7
6
import { coalesce , isNonEmptyArray } from 'vs/base/common/arrays' ;
8
7
import { Codicon } from 'vs/base/common/codicons' ;
9
- import { Disposable , DisposableMap , DisposableStore , IDisposable , toDisposable } from 'vs/base/common/lifecycle' ;
8
+ import { DisposableMap , DisposableStore , IDisposable , toDisposable } from 'vs/base/common/lifecycle' ;
10
9
import * as strings from 'vs/base/common/strings' ;
11
10
import { localize , localize2 } from 'vs/nls' ;
12
- import { ICommandService } from 'vs/platform/commands /common/commands ' ;
11
+ import { ContextKeyExpr , IContextKeyService } from 'vs/platform/contextkey /common/contextkey ' ;
13
12
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions' ;
14
13
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors' ;
15
14
import { ILogService } from 'vs/platform/log/common/log' ;
16
- import { INotificationService , Severity } from 'vs/platform/notification/common/notification' ;
15
+ import { Severity } from 'vs/platform/notification/common/notification' ;
17
16
import { Registry } from 'vs/platform/registry/common/platform' ;
18
17
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer' ;
19
18
import { IWorkbenchContribution } from 'vs/workbench/common/contributions' ;
20
19
import { IViewContainersRegistry , IViewDescriptor , IViewsRegistry , ViewContainer , ViewContainerLocation , Extensions as ViewExtensions } from 'vs/workbench/common/views' ;
21
20
import { CHAT_VIEW_ID } from 'vs/workbench/contrib/chat/browser/chat' ;
22
21
import { CHAT_SIDEBAR_PANEL_ID , ChatViewPane } from 'vs/workbench/contrib/chat/browser/chatViewPane' ;
23
22
import { ChatAgentLocation , IChatAgentData , IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents' ;
23
+ import { CONTEXT_CHAT_EXTENSION_INVALID , CONTEXT_CHAT_PANEL_PARTICIPANT_REGISTERED } from 'vs/workbench/contrib/chat/common/chatContextKeys' ;
24
24
import { IRawChatParticipantContribution } from 'vs/workbench/contrib/chat/common/chatParticipantContribTypes' ;
25
+ import { showExtensionsWithIdsCommandId } from 'vs/workbench/contrib/extensions/browser/extensionsActions' ;
25
26
import { IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions' ;
26
27
import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions' ;
27
28
import * as extensionsRegistry from 'vs/workbench/services/extensions/common/extensionsRegistry' ;
@@ -160,35 +161,6 @@ const chatParticipantExtensionPoint = extensionsRegistry.ExtensionsRegistry.regi
160
161
} ,
161
162
} ) ;
162
163
163
- export class ChatCompatibilityNotifier implements IWorkbenchContribution {
164
- static readonly ID = 'workbench.contrib.chatCompatNotifier' ;
165
-
166
- constructor (
167
- @IExtensionsWorkbenchService extensionsWorkbenchService : IExtensionsWorkbenchService ,
168
- @INotificationService notificationService : INotificationService ,
169
- @ICommandService commandService : ICommandService
170
- ) {
171
- // It may be better to have some generic UI for this, for any extension that is incompatible,
172
- // but this is only enabled for Copilot Chat now and it needs to be obvious.
173
- extensionsWorkbenchService . queryLocal ( ) . then ( exts => {
174
- const chat = exts . find ( ext => ext . identifier . id === 'github.copilot-chat' ) ;
175
- if ( chat ?. local ?. validations . some ( v => v [ 0 ] === Severity . Error ) ) {
176
- notificationService . notify ( {
177
- severity : Severity . Error ,
178
- message : localize ( 'chatFailErrorMessage' , "Chat failed to load. Please ensure that the GitHub Copilot Chat extension is up to date." ) ,
179
- actions : {
180
- primary : [
181
- new Action ( 'showExtension' , localize ( 'action.showExtension' , "Show Extension" ) , undefined , true , ( ) => {
182
- return commandService . executeCommand ( 'workbench.extensions.action.showExtensionsWithIds' , [ 'GitHub.copilot-chat' ] ) ;
183
- } )
184
- ]
185
- }
186
- } ) ;
187
- }
188
- } ) ;
189
- }
190
- }
191
-
192
164
export class ChatExtensionPointHandler implements IWorkbenchContribution {
193
165
194
166
static readonly ID = 'workbench.contrib.chatExtensionPointHandler' ;
@@ -198,9 +170,10 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
198
170
199
171
constructor (
200
172
@IChatAgentService private readonly _chatAgentService : IChatAgentService ,
201
- @ILogService private readonly logService : ILogService ,
173
+ @ILogService private readonly logService : ILogService
202
174
) {
203
175
this . _viewContainer = this . registerViewContainer ( ) ;
176
+ this . registerDefaultParticipantView ( ) ;
204
177
this . handleAndRegisterChatExtensions ( ) ;
205
178
}
206
179
@@ -239,11 +212,6 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
239
212
continue ;
240
213
}
241
214
242
- const store = new DisposableStore ( ) ;
243
- if ( providerDescriptor . isDefault && ( ! providerDescriptor . locations || providerDescriptor . locations ?. includes ( ChatAgentLocation . Panel ) ) ) {
244
- store . add ( this . registerDefaultParticipantView ( providerDescriptor ) ) ;
245
- }
246
-
247
215
const participantsAndCommandsDisambiguation : {
248
216
categoryName : string ;
249
217
description : string ;
@@ -260,6 +228,7 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
260
228
}
261
229
}
262
230
231
+ const store = new DisposableStore ( ) ;
263
232
store . add ( this . _chatAgentService . registerAgent (
264
233
providerDescriptor . id ,
265
234
{
@@ -318,15 +287,9 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
318
287
return viewContainer ;
319
288
}
320
289
321
- private hasRegisteredDefaultParticipantView = false ;
322
- private registerDefaultParticipantView ( defaultParticipantDescriptor : IRawChatParticipantContribution ) : IDisposable {
323
- if ( this . hasRegisteredDefaultParticipantView ) {
324
- this . logService . warn ( `Tried to register a second default chat participant view for "${ defaultParticipantDescriptor . id } "` ) ;
325
- return Disposable . None ;
326
- }
327
-
328
- // Register View
329
- const name = defaultParticipantDescriptor . fullName ?? defaultParticipantDescriptor . name ;
290
+ private registerDefaultParticipantView ( ) : IDisposable {
291
+ // Register View. Name must be hardcoded because we want to show it even when the extension fails to load due to an API version incompatibility.
292
+ const name = 'GitHub Copilot' ;
330
293
const viewDescriptor : IViewDescriptor [ ] = [ {
331
294
id : CHAT_VIEW_ID ,
332
295
containerIcon : this . _viewContainer . icon ,
@@ -336,12 +299,11 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
336
299
canToggleVisibility : false ,
337
300
canMoveView : true ,
338
301
ctorDescriptor : new SyncDescriptor ( ChatViewPane ) ,
302
+ when : ContextKeyExpr . or ( CONTEXT_CHAT_PANEL_PARTICIPANT_REGISTERED , CONTEXT_CHAT_EXTENSION_INVALID )
339
303
} ] ;
340
- this . hasRegisteredDefaultParticipantView = true ;
341
304
Registry . as < IViewsRegistry > ( ViewExtensions . ViewsRegistry ) . registerViews ( viewDescriptor , this . _viewContainer ) ;
342
305
343
306
return toDisposable ( ( ) => {
344
- this . hasRegisteredDefaultParticipantView = false ;
345
307
Registry . as < IViewsRegistry > ( ViewExtensions . ViewsRegistry ) . deregisterViews ( viewDescriptor , this . _viewContainer ) ;
346
308
} ) ;
347
309
}
@@ -350,3 +312,39 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution {
350
312
function getParticipantKey ( extensionId : ExtensionIdentifier , participantName : string ) : string {
351
313
return `${ extensionId . value } _${ participantName } ` ;
352
314
}
315
+
316
+ export class ChatCompatibilityNotifier implements IWorkbenchContribution {
317
+ static readonly ID = 'workbench.contrib.chatCompatNotifier' ;
318
+
319
+ constructor (
320
+ @IExtensionsWorkbenchService extensionsWorkbenchService : IExtensionsWorkbenchService ,
321
+ @IContextKeyService contextKeyService : IContextKeyService ,
322
+ @IChatAgentService chatAgentService : IChatAgentService ,
323
+ ) {
324
+ // It may be better to have some generic UI for this, for any extension that is incompatible,
325
+ // but this is only enabled for Copilot Chat now and it needs to be obvious.
326
+
327
+ const showExtensionLabel = localize ( 'showExtension' , "Show Extension" ) ;
328
+ const viewsRegistry = Registry . as < IViewsRegistry > ( ViewExtensions . ViewsRegistry ) ;
329
+ viewsRegistry . registerViewWelcomeContent ( CHAT_VIEW_ID , {
330
+ content : localize ( 'chatFailErrorMessage' , "Chat failed to load. Please ensure that the GitHub Copilot Chat extension is up to date." ) + `\n\n[${ showExtensionLabel } ](command:${ showExtensionsWithIdsCommandId } ?${ encodeURIComponent ( JSON . stringify ( [ [ 'GitHub.copilot-chat' ] ] ) ) } )` ,
331
+ when : CONTEXT_CHAT_EXTENSION_INVALID ,
332
+ } ) ;
333
+
334
+ const isInvalid = CONTEXT_CHAT_EXTENSION_INVALID . bindTo ( contextKeyService ) ;
335
+ extensionsWorkbenchService . queryLocal ( ) . then ( exts => {
336
+ const chat = exts . find ( ext => ext . identifier . id === 'github.copilot-chat' ) ;
337
+ if ( chat ?. local ?. validations . some ( v => v [ 0 ] === Severity . Error ) ) {
338
+ // This catches vscode starting up with the invalid extension, but the extension may still get updated by vscode after this.
339
+ isInvalid . set ( true ) ;
340
+ }
341
+ } ) ;
342
+
343
+ const listener = chatAgentService . onDidChangeAgents ( ( ) => {
344
+ if ( chatAgentService . getDefaultAgent ( ChatAgentLocation . Panel ) ) {
345
+ isInvalid . set ( false ) ;
346
+ listener . dispose ( ) ;
347
+ }
348
+ } ) ;
349
+ }
350
+ }
0 commit comments