Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/api/browser/mainThreadComments.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { CancellationToken } from '../../../base/common/cancellation.js';
7
import { Emitter, Event } from '../../../base/common/event.js';
8
import { Disposable, DisposableMap, DisposableStore, IDisposable, MutableDisposable } from '../../../base/common/lifecycle.js';
9
import { URI, UriComponents } from '../../../base/common/uri.js';
10
import { IRange, Range } from '../../../editor/common/core/range.js';
11
import * as languages from '../../../editor/common/languages.js';
12
import { ExtensionIdentifier } from '../../../platform/extensions/common/extensions.js';
13
import { Registry } from '../../../platform/registry/common/platform.js';
14
import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js';
15
import { ICommentController, ICommentService } from '../../contrib/comments/browser/commentService.js';
16
import { CommentsPanel } from '../../contrib/comments/browser/commentsView.js';
17
import { CommentProviderFeatures, ExtHostCommentsShape, ExtHostContext, MainContext, MainThreadCommentsShape, CommentThreadChanges } from '../common/extHost.protocol.js';
18
import { COMMENTS_VIEW_ID, COMMENTS_VIEW_STORAGE_ID, COMMENTS_VIEW_TITLE } from '../../contrib/comments/browser/commentsTreeViewer.js';
19
import { ViewContainer, IViewContainersRegistry, Extensions as ViewExtensions, ViewContainerLocation, IViewsRegistry, IViewDescriptorService } from '../../common/views.js';
20
import { SyncDescriptor } from '../../../platform/instantiation/common/descriptors.js';
21
import { ViewPaneContainer } from '../../browser/parts/views/viewPaneContainer.js';
22
import { Codicon } from '../../../base/common/codicons.js';
23
import { registerIcon } from '../../../platform/theme/common/iconRegistry.js';
24
import { localize } from '../../../nls.js';
25
import { MarshalledId } from '../../../base/common/marshallingIds.js';
26
import { ICellRange } from '../../contrib/notebook/common/notebookRange.js';
27
import { Schemas } from '../../../base/common/network.js';
28
import { IViewsService } from '../../services/views/common/viewsService.js';
29
import { MarshalledCommentThread } from '../../common/comments.js';
30
import { revealCommentThread } from '../../contrib/comments/browser/commentsController.js';
31
import { IEditorService } from '../../services/editor/common/editorService.js';
32
import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIdentity.js';
33
34
export class MainThreadCommentThread<T> implements languages.CommentThread<T> {
35
private _input?: languages.CommentInput;
36
get input(): languages.CommentInput | undefined {
37
return this._input;
38
}
39
40
set input(value: languages.CommentInput | undefined) {
41
this._input = value;
42
this._onDidChangeInput.fire(value);
43
}
44
45
private readonly _onDidChangeInput = new Emitter<languages.CommentInput | undefined>();
46
get onDidChangeInput(): Event<languages.CommentInput | undefined> { return this._onDidChangeInput.event; }
47
48
private _label: string | undefined;
49
50
get label(): string | undefined {
51
return this._label;
52
}
53
54
set label(label: string | undefined) {
55
this._label = label;
56
this._onDidChangeLabel.fire(this._label);
57
}
58
59
private _contextValue: string | undefined;
60
61
get contextValue(): string | undefined {
62
return this._contextValue;
63
}
64
65
set contextValue(context: string | undefined) {
66
this._contextValue = context;
67
}
68
69
private readonly _onDidChangeLabel = new Emitter<string | undefined>();
70
readonly onDidChangeLabel: Event<string | undefined> = this._onDidChangeLabel.event;
71
72
private _comments: ReadonlyArray<languages.Comment> | undefined;
73
74
public get comments(): ReadonlyArray<languages.Comment> | undefined {
75
return this._comments;
76
}
77
78
public set comments(newComments: ReadonlyArray<languages.Comment> | undefined) {
79
this._comments = newComments;
80
this._onDidChangeComments.fire(this._comments);
81
}
82
83
private readonly _onDidChangeComments = new Emitter<readonly languages.Comment[] | undefined>();
84
get onDidChangeComments(): Event<readonly languages.Comment[] | undefined> { return this._onDidChangeComments.event; }
85
86
set range(range: T | undefined) {
87
this._range = range;
88
}
89
90
get range(): T | undefined {
91
return this._range;
92
}
93
94
private readonly _onDidChangeCanReply = new Emitter<boolean>();
95
get onDidChangeCanReply(): Event<boolean> { return this._onDidChangeCanReply.event; }
96
set canReply(state: boolean | languages.CommentAuthorInformation) {
97
this._canReply = state;
98
this._onDidChangeCanReply.fire(!!this._canReply);
99
}
100
101
get canReply() {
102
return this._canReply;
103
}
104
105
private _collapsibleState: languages.CommentThreadCollapsibleState | undefined = languages.CommentThreadCollapsibleState.Collapsed;
106
get collapsibleState() {
107
return this._collapsibleState;
108
}
109
110
set collapsibleState(newState: languages.CommentThreadCollapsibleState | undefined) {
111
if (this.initialCollapsibleState === undefined) {
112
this.initialCollapsibleState = newState;
113
}
114
115
if (newState !== this._collapsibleState) {
116
this._collapsibleState = newState;
117
this._onDidChangeCollapsibleState.fire(this._collapsibleState);
118
}
119
}
120
121
private _initialCollapsibleState: languages.CommentThreadCollapsibleState | undefined;
122
get initialCollapsibleState() {
123
return this._initialCollapsibleState;
124
}
125
126
private set initialCollapsibleState(initialCollapsibleState: languages.CommentThreadCollapsibleState | undefined) {
127
this._initialCollapsibleState = initialCollapsibleState;
128
this._onDidChangeInitialCollapsibleState.fire(initialCollapsibleState);
129
}
130
131
private readonly _onDidChangeCollapsibleState = new Emitter<languages.CommentThreadCollapsibleState | undefined>();
132
public onDidChangeCollapsibleState = this._onDidChangeCollapsibleState.event;
133
private readonly _onDidChangeInitialCollapsibleState = new Emitter<languages.CommentThreadCollapsibleState | undefined>();
134
public onDidChangeInitialCollapsibleState = this._onDidChangeInitialCollapsibleState.event;
135
136
private _isDisposed: boolean;
137
138
get isDisposed(): boolean {
139
return this._isDisposed;
140
}
141
142
isDocumentCommentThread(): this is languages.CommentThread<IRange> {
143
return this._range === undefined || Range.isIRange(this._range);
144
}
145
146
private _state: languages.CommentThreadState | undefined;
147
get state() {
148
return this._state;
149
}
150
151
set state(newState: languages.CommentThreadState | undefined) {
152
this._state = newState;
153
this._onDidChangeState.fire(this._state);
154
}
155
156
private _applicability: languages.CommentThreadApplicability | undefined;
157
158
get applicability(): languages.CommentThreadApplicability | undefined {
159
return this._applicability;
160
}
161
162
set applicability(value: languages.CommentThreadApplicability | undefined) {
163
this._applicability = value;
164
this._onDidChangeApplicability.fire(value);
165
}
166
167
private readonly _onDidChangeApplicability = new Emitter<languages.CommentThreadApplicability | undefined>();
168
readonly onDidChangeApplicability: Event<languages.CommentThreadApplicability | undefined> = this._onDidChangeApplicability.event;
169
170
public get isTemplate(): boolean {
171
return this._isTemplate;
172
}
173
174
private readonly _onDidChangeState = new Emitter<languages.CommentThreadState | undefined>();
175
public onDidChangeState = this._onDidChangeState.event;
176
177
constructor(
178
public commentThreadHandle: number,
179
public controllerHandle: number,
180
public extensionId: string,
181
public threadId: string,
182
public resource: string,
183
private _range: T | undefined,
184
comments: languages.Comment[] | undefined,
185
private _canReply: boolean | languages.CommentAuthorInformation,
186
private _isTemplate: boolean,
187
public editorId?: string
188
) {
189
this._isDisposed = false;
190
if (_isTemplate) {
191
this.comments = [];
192
} else if (comments) {
193
this._comments = comments;
194
}
195
}
196
197
batchUpdate(changes: CommentThreadChanges<T>) {
198
const modified = (value: keyof CommentThreadChanges): boolean =>
199
Object.prototype.hasOwnProperty.call(changes, value);
200
201
if (modified('range')) { this._range = changes.range!; }
202
if (modified('label')) { this._label = changes.label; }
203
if (modified('contextValue')) { this._contextValue = changes.contextValue === null ? undefined : changes.contextValue; }
204
if (modified('comments')) { this.comments = changes.comments; }
205
if (modified('collapseState')) { this.collapsibleState = changes.collapseState; }
206
if (modified('canReply')) { this.canReply = changes.canReply!; }
207
if (modified('state')) { this.state = changes.state!; }
208
if (modified('applicability')) { this.applicability = changes.applicability!; }
209
if (modified('isTemplate')) { this._isTemplate = changes.isTemplate!; }
210
}
211
212
hasComments(): boolean {
213
return !!this.comments && this.comments.length > 0;
214
}
215
216
dispose() {
217
this._isDisposed = true;
218
this._onDidChangeCollapsibleState.dispose();
219
this._onDidChangeComments.dispose();
220
this._onDidChangeInput.dispose();
221
this._onDidChangeLabel.dispose();
222
this._onDidChangeState.dispose();
223
}
224
225
toJSON(): MarshalledCommentThread {
226
return {
227
$mid: MarshalledId.CommentThread,
228
commentControlHandle: this.controllerHandle,
229
commentThreadHandle: this.commentThreadHandle,
230
};
231
}
232
}
233
234
class CommentThreadWithDisposable {
235
public readonly disposableStore: DisposableStore = new DisposableStore();
236
constructor(public readonly thread: MainThreadCommentThread<IRange | ICellRange>) { }
237
dispose() {
238
this.disposableStore.dispose();
239
}
240
}
241
242
export class MainThreadCommentController extends Disposable implements ICommentController {
243
get handle(): number {
244
return this._handle;
245
}
246
247
get id(): string {
248
return this._id;
249
}
250
251
get contextValue(): string {
252
return this._id;
253
}
254
255
get proxy(): ExtHostCommentsShape {
256
return this._proxy;
257
}
258
259
get label(): string {
260
return this._label;
261
}
262
263
private _reactions: languages.CommentReaction[] | undefined;
264
265
get reactions() {
266
return this._reactions;
267
}
268
269
set reactions(reactions: languages.CommentReaction[] | undefined) {
270
this._reactions = reactions;
271
}
272
273
get options() {
274
return this._features.options;
275
}
276
277
private readonly _threads: DisposableMap<number, CommentThreadWithDisposable> = this._register(new DisposableMap<number, CommentThreadWithDisposable>());
278
public activeEditingCommentThread?: MainThreadCommentThread<IRange | ICellRange>;
279
280
get features(): CommentProviderFeatures {
281
return this._features;
282
}
283
284
get owner() {
285
return this._id;
286
}
287
288
constructor(
289
private readonly _proxy: ExtHostCommentsShape,
290
private readonly _commentService: ICommentService,
291
private readonly _handle: number,
292
private readonly _uniqueId: string,
293
private readonly _id: string,
294
private readonly _label: string,
295
private _features: CommentProviderFeatures
296
) {
297
super();
298
}
299
300
get activeComment() {
301
return this._activeComment;
302
}
303
304
private _activeComment: { thread: languages.CommentThread; comment?: languages.Comment } | undefined;
305
async setActiveCommentAndThread(commentInfo: { thread: languages.CommentThread; comment?: languages.Comment } | undefined) {
306
this._activeComment = commentInfo;
307
return this._proxy.$setActiveComment(this._handle, commentInfo ? { commentThreadHandle: commentInfo.thread.commentThreadHandle, uniqueIdInThread: commentInfo.comment?.uniqueIdInThread } : undefined);
308
}
309
310
updateFeatures(features: CommentProviderFeatures) {
311
this._features = features;
312
}
313
314
createCommentThread(extensionId: string,
315
commentThreadHandle: number,
316
threadId: string,
317
resource: UriComponents,
318
range: IRange | ICellRange | undefined,
319
comments: languages.Comment[],
320
isTemplate: boolean,
321
editorId?: string
322
): languages.CommentThread<IRange | ICellRange> {
323
const thread = new MainThreadCommentThread(
324
commentThreadHandle,
325
this.handle,
326
extensionId,
327
threadId,
328
URI.revive(resource).toString(),
329
range,
330
comments,
331
true,
332
isTemplate,
333
editorId
334
);
335
336
const threadWithDisposable = new CommentThreadWithDisposable(thread);
337
this._threads.set(commentThreadHandle, threadWithDisposable);
338
threadWithDisposable.disposableStore.add(thread.onDidChangeCollapsibleState(() => {
339
this.proxy.$updateCommentThread(this.handle, thread.commentThreadHandle, { collapseState: thread.collapsibleState });
340
}));
341
342
343
if (thread.isDocumentCommentThread()) {
344
this._commentService.updateComments(this._uniqueId, {
345
added: [thread],
346
removed: [],
347
changed: [],
348
pending: []
349
});
350
} else {
351
this._commentService.updateNotebookComments(this._uniqueId, {
352
added: [thread as MainThreadCommentThread<ICellRange>],
353
removed: [],
354
changed: [],
355
pending: []
356
});
357
}
358
359
return thread;
360
}
361
362
updateCommentThread(commentThreadHandle: number,
363
threadId: string,
364
resource: UriComponents,
365
changes: CommentThreadChanges): void {
366
const thread = this.getKnownThread(commentThreadHandle);
367
thread.batchUpdate(changes);
368
369
if (thread.isDocumentCommentThread()) {
370
this._commentService.updateComments(this._uniqueId, {
371
added: [],
372
removed: [],
373
changed: [thread],
374
pending: []
375
});
376
} else {
377
this._commentService.updateNotebookComments(this._uniqueId, {
378
added: [],
379
removed: [],
380
changed: [thread as MainThreadCommentThread<ICellRange>],
381
pending: []
382
});
383
}
384
385
}
386
387
deleteCommentThread(commentThreadHandle: number) {
388
const thread = this.getKnownThread(commentThreadHandle);
389
this._threads.deleteAndDispose(commentThreadHandle);
390
thread.dispose();
391
392
if (thread.isDocumentCommentThread()) {
393
this._commentService.updateComments(this._uniqueId, {
394
added: [],
395
removed: [thread],
396
changed: [],
397
pending: []
398
});
399
} else {
400
this._commentService.updateNotebookComments(this._uniqueId, {
401
added: [],
402
removed: [thread as MainThreadCommentThread<ICellRange>],
403
changed: [],
404
pending: []
405
});
406
}
407
}
408
409
deleteCommentThreadMain(commentThreadId: string) {
410
for (const { thread } of this._threads.values()) {
411
if (thread.threadId === commentThreadId) {
412
this._proxy.$deleteCommentThread(this._handle, thread.commentThreadHandle);
413
}
414
}
415
}
416
417
updateInput(input: string) {
418
const thread = this.activeEditingCommentThread;
419
420
if (thread && thread.input) {
421
const commentInput = thread.input;
422
commentInput.value = input;
423
thread.input = commentInput;
424
}
425
}
426
427
updateCommentingRanges(resourceHints?: languages.CommentingRangeResourceHint) {
428
this._commentService.updateCommentingRanges(this._uniqueId, resourceHints);
429
}
430
431
private getKnownThread(commentThreadHandle: number): MainThreadCommentThread<IRange | ICellRange> {
432
const thread = this._threads.get(commentThreadHandle);
433
if (!thread) {
434
throw new Error('unknown thread');
435
}
436
return thread.thread;
437
}
438
439
async getDocumentComments(resource: URI, token: CancellationToken) {
440
if (resource.scheme === Schemas.vscodeNotebookCell) {
441
return {
442
uniqueOwner: this._uniqueId,
443
label: this.label,
444
threads: [],
445
commentingRanges: {
446
resource: resource,
447
ranges: [],
448
fileComments: false
449
}
450
};
451
}
452
453
const ret: languages.CommentThread<IRange>[] = [];
454
for (const thread of [...this._threads.keys()]) {
455
const commentThread = this._threads.get(thread)!;
456
if (commentThread.thread.resource === resource.toString()) {
457
if (commentThread.thread.isDocumentCommentThread()) {
458
ret.push(commentThread.thread);
459
}
460
}
461
}
462
463
const commentingRanges = await this._proxy.$provideCommentingRanges(this.handle, resource, token);
464
465
return {
466
uniqueOwner: this._uniqueId,
467
label: this.label,
468
threads: ret,
469
commentingRanges: {
470
resource: resource,
471
ranges: commentingRanges?.ranges || [],
472
fileComments: !!commentingRanges?.fileComments
473
}
474
};
475
}
476
477
async getNotebookComments(resource: URI, token: CancellationToken) {
478
if (resource.scheme !== Schemas.vscodeNotebookCell) {
479
return {
480
uniqueOwner: this._uniqueId,
481
label: this.label,
482
threads: []
483
};
484
}
485
486
const ret: languages.CommentThread<ICellRange>[] = [];
487
for (const thread of [...this._threads.keys()]) {
488
const commentThread = this._threads.get(thread)!;
489
if (commentThread.thread.resource === resource.toString()) {
490
if (!commentThread.thread.isDocumentCommentThread()) {
491
ret.push(commentThread.thread as languages.CommentThread<ICellRange>);
492
}
493
}
494
}
495
496
return {
497
uniqueOwner: this._uniqueId,
498
label: this.label,
499
threads: ret
500
};
501
}
502
503
async toggleReaction(uri: URI, thread: languages.CommentThread, comment: languages.Comment, reaction: languages.CommentReaction, token: CancellationToken): Promise<void> {
504
return this._proxy.$toggleReaction(this._handle, thread.commentThreadHandle, uri, comment, reaction);
505
}
506
507
getAllComments(): MainThreadCommentThread<IRange | ICellRange>[] {
508
const ret: MainThreadCommentThread<IRange | ICellRange>[] = [];
509
for (const thread of [...this._threads.keys()]) {
510
ret.push(this._threads.get(thread)!.thread);
511
}
512
513
return ret;
514
}
515
516
createCommentThreadTemplate(resource: UriComponents, range: IRange | undefined, editorId?: string): Promise<void> {
517
return this._proxy.$createCommentThreadTemplate(this.handle, resource, range, editorId);
518
}
519
520
async updateCommentThreadTemplate(threadHandle: number, range: IRange) {
521
await this._proxy.$updateCommentThreadTemplate(this.handle, threadHandle, range);
522
}
523
524
toJSON(): any {
525
return {
526
$mid: MarshalledId.CommentController,
527
handle: this.handle
528
};
529
}
530
}
531
532
533
const commentsViewIcon = registerIcon('comments-view-icon', Codicon.commentDiscussion, localize('commentsViewIcon', 'View icon of the comments view.'));
534
535
@extHostNamedCustomer(MainContext.MainThreadComments)
536
export class MainThreadComments extends Disposable implements MainThreadCommentsShape {
537
private readonly _proxy: ExtHostCommentsShape;
538
539
private _handlers = new Map<number, string>();
540
private _commentControllers = new Map<number, MainThreadCommentController>();
541
542
private _activeEditingCommentThread?: MainThreadCommentThread<IRange | ICellRange>;
543
private readonly _activeEditingCommentThreadDisposables = this._register(new DisposableStore());
544
545
private readonly _openViewListener: MutableDisposable<IDisposable> = this._register(new MutableDisposable());
546
private readonly _onChangeContainerListener: MutableDisposable<IDisposable> = this._register(new MutableDisposable());
547
private readonly _onChangeContainerLocationListener: MutableDisposable<IDisposable> = this._register(new MutableDisposable());
548
549
constructor(
550
extHostContext: IExtHostContext,
551
@ICommentService private readonly _commentService: ICommentService,
552
@IViewsService private readonly _viewsService: IViewsService,
553
@IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService,
554
@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService,
555
@IEditorService private readonly _editorService: IEditorService
556
) {
557
super();
558
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostComments);
559
this._commentService.unregisterCommentController();
560
561
this._register(this._commentService.onDidChangeActiveEditingCommentThread(async thread => {
562
const handle = (thread as MainThreadCommentThread<IRange | ICellRange>).controllerHandle;
563
const controller = this._commentControllers.get(handle);
564
565
if (!controller) {
566
return;
567
}
568
569
this._activeEditingCommentThreadDisposables.clear();
570
this._activeEditingCommentThread = thread as MainThreadCommentThread<IRange | ICellRange>;
571
controller.activeEditingCommentThread = this._activeEditingCommentThread;
572
}));
573
}
574
575
$registerCommentController(handle: number, id: string, label: string, extensionId: string): void {
576
const providerId = `${id}-${extensionId}`;
577
this._handlers.set(handle, providerId);
578
579
const provider = new MainThreadCommentController(this._proxy, this._commentService, handle, providerId, id, label, {});
580
this._commentService.registerCommentController(providerId, provider);
581
this._commentControllers.set(handle, provider);
582
583
this._register(this._commentService.onResourceHasCommentingRanges(e => {
584
this.registerView();
585
}));
586
587
this._register(this._commentService.onDidUpdateCommentThreads(e => {
588
this.registerView();
589
}));
590
591
this._commentService.setWorkspaceComments(String(handle), []);
592
}
593
594
$unregisterCommentController(handle: number): void {
595
const providerId = this._handlers.get(handle);
596
this._handlers.delete(handle);
597
this._commentControllers.get(handle)?.dispose();
598
this._commentControllers.delete(handle);
599
600
if (typeof providerId !== 'string') {
601
return;
602
// throw new Error('unknown handler');
603
} else {
604
this._commentService.unregisterCommentController(providerId);
605
}
606
}
607
608
$updateCommentControllerFeatures(handle: number, features: CommentProviderFeatures): void {
609
const provider = this._commentControllers.get(handle);
610
611
if (!provider) {
612
return undefined;
613
}
614
615
provider.updateFeatures(features);
616
}
617
618
$createCommentThread(handle: number,
619
commentThreadHandle: number,
620
threadId: string,
621
resource: UriComponents,
622
range: IRange | ICellRange | undefined,
623
comments: languages.Comment[],
624
extensionId: ExtensionIdentifier,
625
isTemplate: boolean,
626
editorId?: string
627
): languages.CommentThread<IRange | ICellRange> | undefined {
628
const provider = this._commentControllers.get(handle);
629
630
if (!provider) {
631
return undefined;
632
}
633
634
return provider.createCommentThread(extensionId.value, commentThreadHandle, threadId, resource, range, comments, isTemplate, editorId);
635
}
636
637
$updateCommentThread(handle: number,
638
commentThreadHandle: number,
639
threadId: string,
640
resource: UriComponents,
641
changes: CommentThreadChanges): void {
642
const provider = this._commentControllers.get(handle);
643
644
if (!provider) {
645
return undefined;
646
}
647
648
return provider.updateCommentThread(commentThreadHandle, threadId, resource, changes);
649
}
650
651
$deleteCommentThread(handle: number, commentThreadHandle: number) {
652
const provider = this._commentControllers.get(handle);
653
654
if (!provider) {
655
return;
656
}
657
658
return provider.deleteCommentThread(commentThreadHandle);
659
}
660
661
$updateCommentingRanges(handle: number, resourceHints?: languages.CommentingRangeResourceHint) {
662
const provider = this._commentControllers.get(handle);
663
664
if (!provider) {
665
return;
666
}
667
668
provider.updateCommentingRanges(resourceHints);
669
}
670
671
async $revealCommentThread(handle: number, commentThreadHandle: number, commentUniqueIdInThread: number, options: languages.CommentThreadRevealOptions): Promise<void> {
672
const provider = this._commentControllers.get(handle);
673
674
if (!provider) {
675
return Promise.resolve();
676
}
677
678
const thread = provider.getAllComments().find(thread => thread.commentThreadHandle === commentThreadHandle);
679
if (!thread || !thread.isDocumentCommentThread()) {
680
return Promise.resolve();
681
}
682
683
const comment = thread.comments?.find(comment => comment.uniqueIdInThread === commentUniqueIdInThread);
684
685
revealCommentThread(this._commentService, this._editorService, this._uriIdentityService, thread, comment, options.focusReply, undefined, options.preserveFocus);
686
}
687
688
async $hideCommentThread(handle: number, commentThreadHandle: number): Promise<void> {
689
const provider = this._commentControllers.get(handle);
690
691
if (!provider) {
692
return Promise.resolve();
693
}
694
695
const thread = provider.getAllComments().find(thread => thread.commentThreadHandle === commentThreadHandle);
696
if (!thread || !thread.isDocumentCommentThread()) {
697
return Promise.resolve();
698
}
699
700
thread.collapsibleState = languages.CommentThreadCollapsibleState.Collapsed;
701
}
702
703
private registerView() {
704
const commentsPanelAlreadyConstructed = !!this._viewDescriptorService.getViewDescriptorById(COMMENTS_VIEW_ID);
705
if (!commentsPanelAlreadyConstructed) {
706
const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).registerViewContainer({
707
id: COMMENTS_VIEW_ID,
708
title: COMMENTS_VIEW_TITLE,
709
ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, { mergeViewWithContainerWhenSingleView: true }]),
710
storageId: COMMENTS_VIEW_STORAGE_ID,
711
hideIfEmpty: true,
712
icon: commentsViewIcon,
713
order: 10,
714
}, ViewContainerLocation.Panel);
715
716
Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews([{
717
id: COMMENTS_VIEW_ID,
718
name: COMMENTS_VIEW_TITLE,
719
canToggleVisibility: false,
720
ctorDescriptor: new SyncDescriptor(CommentsPanel),
721
canMoveView: true,
722
containerIcon: commentsViewIcon,
723
focusCommand: {
724
id: 'workbench.action.focusCommentsPanel'
725
}
726
}], VIEW_CONTAINER);
727
}
728
this.registerViewListeners(commentsPanelAlreadyConstructed);
729
}
730
731
private setComments() {
732
[...this._commentControllers.keys()].forEach(handle => {
733
const threads = this._commentControllers.get(handle)!.getAllComments();
734
735
if (threads.length) {
736
const providerId = this.getHandler(handle);
737
this._commentService.setWorkspaceComments(providerId, threads);
738
}
739
});
740
}
741
742
private registerViewOpenedListener() {
743
if (!this._openViewListener.value) {
744
this._openViewListener.value = this._viewsService.onDidChangeViewVisibility(e => {
745
if (e.id === COMMENTS_VIEW_ID && e.visible) {
746
this.setComments();
747
if (this._openViewListener) {
748
this._openViewListener.dispose();
749
}
750
}
751
});
752
}
753
}
754
755
/**
756
* If the comments view has never been opened, the constructor for it has not yet run so it has
757
* no listeners for comment threads being set or updated. Listen for the view opening for the
758
* first time and send it comments then.
759
*/
760
private registerViewListeners(commentsPanelAlreadyConstructed: boolean) {
761
if (!commentsPanelAlreadyConstructed) {
762
this.registerViewOpenedListener();
763
}
764
765
if (!this._onChangeContainerListener.value) {
766
this._onChangeContainerListener.value = this._viewDescriptorService.onDidChangeContainer(e => {
767
if (e.views.find(view => view.id === COMMENTS_VIEW_ID)) {
768
this.setComments();
769
this.registerViewOpenedListener();
770
}
771
});
772
}
773
774
if (!this._onChangeContainerLocationListener.value) {
775
this._onChangeContainerLocationListener.value = this._viewDescriptorService.onDidChangeContainerLocation(e => {
776
const commentsContainer = this._viewDescriptorService.getViewContainerByViewId(COMMENTS_VIEW_ID);
777
if (e.viewContainer.id === commentsContainer?.id) {
778
this.setComments();
779
this.registerViewOpenedListener();
780
}
781
});
782
}
783
}
784
785
private getHandler(handle: number) {
786
if (!this._handlers.has(handle)) {
787
throw new Error('Unknown handler');
788
}
789
return this._handlers.get(handle)!;
790
}
791
}
792
793