Path: blob/main/src/vs/workbench/api/browser/mainThreadComments.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { CancellationToken } from '../../../base/common/cancellation.js';6import { Emitter, Event } from '../../../base/common/event.js';7import { Disposable, DisposableMap, DisposableStore, IDisposable, MutableDisposable } from '../../../base/common/lifecycle.js';8import { URI, UriComponents } from '../../../base/common/uri.js';9import { IRange, Range } from '../../../editor/common/core/range.js';10import * as languages from '../../../editor/common/languages.js';11import { ExtensionIdentifier } from '../../../platform/extensions/common/extensions.js';12import { Registry } from '../../../platform/registry/common/platform.js';13import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js';14import { ICommentController, ICommentService } from '../../contrib/comments/browser/commentService.js';15import { CommentsPanel } from '../../contrib/comments/browser/commentsView.js';16import { CommentProviderFeatures, ExtHostCommentsShape, ExtHostContext, MainContext, MainThreadCommentsShape, CommentThreadChanges } from '../common/extHost.protocol.js';17import { COMMENTS_VIEW_ID, COMMENTS_VIEW_STORAGE_ID, COMMENTS_VIEW_TITLE } from '../../contrib/comments/browser/commentsTreeViewer.js';18import { ViewContainer, IViewContainersRegistry, Extensions as ViewExtensions, ViewContainerLocation, IViewsRegistry, IViewDescriptorService } from '../../common/views.js';19import { SyncDescriptor } from '../../../platform/instantiation/common/descriptors.js';20import { ViewPaneContainer } from '../../browser/parts/views/viewPaneContainer.js';21import { Codicon } from '../../../base/common/codicons.js';22import { registerIcon } from '../../../platform/theme/common/iconRegistry.js';23import { localize } from '../../../nls.js';24import { MarshalledId } from '../../../base/common/marshallingIds.js';25import { ICellRange } from '../../contrib/notebook/common/notebookRange.js';26import { Schemas } from '../../../base/common/network.js';27import { IViewsService } from '../../services/views/common/viewsService.js';28import { MarshalledCommentThread } from '../../common/comments.js';29import { revealCommentThread } from '../../contrib/comments/browser/commentsController.js';30import { IEditorService } from '../../services/editor/common/editorService.js';31import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIdentity.js';3233export class MainThreadCommentThread<T> implements languages.CommentThread<T> {34private _input?: languages.CommentInput;35get input(): languages.CommentInput | undefined {36return this._input;37}3839set input(value: languages.CommentInput | undefined) {40this._input = value;41this._onDidChangeInput.fire(value);42}4344private readonly _onDidChangeInput = new Emitter<languages.CommentInput | undefined>();45get onDidChangeInput(): Event<languages.CommentInput | undefined> { return this._onDidChangeInput.event; }4647private _label: string | undefined;4849get label(): string | undefined {50return this._label;51}5253set label(label: string | undefined) {54this._label = label;55this._onDidChangeLabel.fire(this._label);56}5758private _contextValue: string | undefined;5960get contextValue(): string | undefined {61return this._contextValue;62}6364set contextValue(context: string | undefined) {65this._contextValue = context;66}6768private readonly _onDidChangeLabel = new Emitter<string | undefined>();69readonly onDidChangeLabel: Event<string | undefined> = this._onDidChangeLabel.event;7071private _comments: ReadonlyArray<languages.Comment> | undefined;7273public get comments(): ReadonlyArray<languages.Comment> | undefined {74return this._comments;75}7677public set comments(newComments: ReadonlyArray<languages.Comment> | undefined) {78this._comments = newComments;79this._onDidChangeComments.fire(this._comments);80}8182private readonly _onDidChangeComments = new Emitter<readonly languages.Comment[] | undefined>();83get onDidChangeComments(): Event<readonly languages.Comment[] | undefined> { return this._onDidChangeComments.event; }8485set range(range: T | undefined) {86this._range = range;87}8889get range(): T | undefined {90return this._range;91}9293private readonly _onDidChangeCanReply = new Emitter<boolean>();94get onDidChangeCanReply(): Event<boolean> { return this._onDidChangeCanReply.event; }95set canReply(state: boolean | languages.CommentAuthorInformation) {96this._canReply = state;97this._onDidChangeCanReply.fire(!!this._canReply);98}99100get canReply() {101return this._canReply;102}103104private _collapsibleState: languages.CommentThreadCollapsibleState | undefined = languages.CommentThreadCollapsibleState.Collapsed;105get collapsibleState() {106return this._collapsibleState;107}108109set collapsibleState(newState: languages.CommentThreadCollapsibleState | undefined) {110if (this.initialCollapsibleState === undefined) {111this.initialCollapsibleState = newState;112}113114if (newState !== this._collapsibleState) {115this._collapsibleState = newState;116this._onDidChangeCollapsibleState.fire(this._collapsibleState);117}118}119120private _initialCollapsibleState: languages.CommentThreadCollapsibleState | undefined;121get initialCollapsibleState() {122return this._initialCollapsibleState;123}124125private set initialCollapsibleState(initialCollapsibleState: languages.CommentThreadCollapsibleState | undefined) {126this._initialCollapsibleState = initialCollapsibleState;127this._onDidChangeInitialCollapsibleState.fire(initialCollapsibleState);128}129130private readonly _onDidChangeCollapsibleState = new Emitter<languages.CommentThreadCollapsibleState | undefined>();131public onDidChangeCollapsibleState = this._onDidChangeCollapsibleState.event;132private readonly _onDidChangeInitialCollapsibleState = new Emitter<languages.CommentThreadCollapsibleState | undefined>();133public onDidChangeInitialCollapsibleState = this._onDidChangeInitialCollapsibleState.event;134135private _isDisposed: boolean;136137get isDisposed(): boolean {138return this._isDisposed;139}140141isDocumentCommentThread(): this is languages.CommentThread<IRange> {142return this._range === undefined || Range.isIRange(this._range);143}144145private _state: languages.CommentThreadState | undefined;146get state() {147return this._state;148}149150set state(newState: languages.CommentThreadState | undefined) {151this._state = newState;152this._onDidChangeState.fire(this._state);153}154155private _applicability: languages.CommentThreadApplicability | undefined;156157get applicability(): languages.CommentThreadApplicability | undefined {158return this._applicability;159}160161set applicability(value: languages.CommentThreadApplicability | undefined) {162this._applicability = value;163this._onDidChangeApplicability.fire(value);164}165166private readonly _onDidChangeApplicability = new Emitter<languages.CommentThreadApplicability | undefined>();167readonly onDidChangeApplicability: Event<languages.CommentThreadApplicability | undefined> = this._onDidChangeApplicability.event;168169public get isTemplate(): boolean {170return this._isTemplate;171}172173private readonly _onDidChangeState = new Emitter<languages.CommentThreadState | undefined>();174public onDidChangeState = this._onDidChangeState.event;175176constructor(177public commentThreadHandle: number,178public controllerHandle: number,179public extensionId: string,180public threadId: string,181public resource: string,182private _range: T | undefined,183comments: languages.Comment[] | undefined,184private _canReply: boolean | languages.CommentAuthorInformation,185private _isTemplate: boolean,186public editorId?: string187) {188this._isDisposed = false;189if (_isTemplate) {190this.comments = [];191} else if (comments) {192this._comments = comments;193}194}195196batchUpdate(changes: CommentThreadChanges<T>) {197const modified = (value: keyof CommentThreadChanges): boolean =>198Object.prototype.hasOwnProperty.call(changes, value);199200if (modified('range')) { this._range = changes.range!; }201if (modified('label')) { this._label = changes.label; }202if (modified('contextValue')) { this._contextValue = changes.contextValue === null ? undefined : changes.contextValue; }203if (modified('comments')) { this.comments = changes.comments; }204if (modified('collapseState')) { this.collapsibleState = changes.collapseState; }205if (modified('canReply')) { this.canReply = changes.canReply!; }206if (modified('state')) { this.state = changes.state!; }207if (modified('applicability')) { this.applicability = changes.applicability!; }208if (modified('isTemplate')) { this._isTemplate = changes.isTemplate!; }209}210211hasComments(): boolean {212return !!this.comments && this.comments.length > 0;213}214215dispose() {216this._isDisposed = true;217this._onDidChangeCollapsibleState.dispose();218this._onDidChangeComments.dispose();219this._onDidChangeInput.dispose();220this._onDidChangeLabel.dispose();221this._onDidChangeState.dispose();222}223224toJSON(): MarshalledCommentThread {225return {226$mid: MarshalledId.CommentThread,227commentControlHandle: this.controllerHandle,228commentThreadHandle: this.commentThreadHandle,229};230}231}232233class CommentThreadWithDisposable {234public readonly disposableStore: DisposableStore = new DisposableStore();235constructor(public readonly thread: MainThreadCommentThread<IRange | ICellRange>) { }236dispose() {237this.disposableStore.dispose();238}239}240241export class MainThreadCommentController extends Disposable implements ICommentController {242get handle(): number {243return this._handle;244}245246get id(): string {247return this._id;248}249250get contextValue(): string {251return this._id;252}253254get proxy(): ExtHostCommentsShape {255return this._proxy;256}257258get label(): string {259return this._label;260}261262private _reactions: languages.CommentReaction[] | undefined;263264get reactions() {265return this._reactions;266}267268set reactions(reactions: languages.CommentReaction[] | undefined) {269this._reactions = reactions;270}271272get options() {273return this._features.options;274}275276private readonly _threads: DisposableMap<number, CommentThreadWithDisposable> = this._register(new DisposableMap<number, CommentThreadWithDisposable>());277public activeEditingCommentThread?: MainThreadCommentThread<IRange | ICellRange>;278279get features(): CommentProviderFeatures {280return this._features;281}282283get owner() {284return this._id;285}286287constructor(288private readonly _proxy: ExtHostCommentsShape,289private readonly _commentService: ICommentService,290private readonly _handle: number,291private readonly _uniqueId: string,292private readonly _id: string,293private readonly _label: string,294private _features: CommentProviderFeatures295) {296super();297}298299get activeComment() {300return this._activeComment;301}302303private _activeComment: { thread: languages.CommentThread; comment?: languages.Comment } | undefined;304async setActiveCommentAndThread(commentInfo: { thread: languages.CommentThread; comment?: languages.Comment } | undefined) {305this._activeComment = commentInfo;306return this._proxy.$setActiveComment(this._handle, commentInfo ? { commentThreadHandle: commentInfo.thread.commentThreadHandle, uniqueIdInThread: commentInfo.comment?.uniqueIdInThread } : undefined);307}308309updateFeatures(features: CommentProviderFeatures) {310this._features = features;311}312313createCommentThread(extensionId: string,314commentThreadHandle: number,315threadId: string,316resource: UriComponents,317range: IRange | ICellRange | undefined,318comments: languages.Comment[],319isTemplate: boolean,320editorId?: string321): languages.CommentThread<IRange | ICellRange> {322const thread = new MainThreadCommentThread(323commentThreadHandle,324this.handle,325extensionId,326threadId,327URI.revive(resource).toString(),328range,329comments,330true,331isTemplate,332editorId333);334335const threadWithDisposable = new CommentThreadWithDisposable(thread);336this._threads.set(commentThreadHandle, threadWithDisposable);337threadWithDisposable.disposableStore.add(thread.onDidChangeCollapsibleState(() => {338this.proxy.$updateCommentThread(this.handle, thread.commentThreadHandle, { collapseState: thread.collapsibleState });339}));340341342if (thread.isDocumentCommentThread()) {343this._commentService.updateComments(this._uniqueId, {344added: [thread],345removed: [],346changed: [],347pending: []348});349} else {350this._commentService.updateNotebookComments(this._uniqueId, {351added: [thread as MainThreadCommentThread<ICellRange>],352removed: [],353changed: [],354pending: []355});356}357358return thread;359}360361updateCommentThread(commentThreadHandle: number,362threadId: string,363resource: UriComponents,364changes: CommentThreadChanges): void {365const thread = this.getKnownThread(commentThreadHandle);366thread.batchUpdate(changes);367368if (thread.isDocumentCommentThread()) {369this._commentService.updateComments(this._uniqueId, {370added: [],371removed: [],372changed: [thread],373pending: []374});375} else {376this._commentService.updateNotebookComments(this._uniqueId, {377added: [],378removed: [],379changed: [thread as MainThreadCommentThread<ICellRange>],380pending: []381});382}383384}385386deleteCommentThread(commentThreadHandle: number) {387const thread = this.getKnownThread(commentThreadHandle);388this._threads.deleteAndDispose(commentThreadHandle);389thread.dispose();390391if (thread.isDocumentCommentThread()) {392this._commentService.updateComments(this._uniqueId, {393added: [],394removed: [thread],395changed: [],396pending: []397});398} else {399this._commentService.updateNotebookComments(this._uniqueId, {400added: [],401removed: [thread as MainThreadCommentThread<ICellRange>],402changed: [],403pending: []404});405}406}407408deleteCommentThreadMain(commentThreadId: string) {409for (const { thread } of this._threads.values()) {410if (thread.threadId === commentThreadId) {411this._proxy.$deleteCommentThread(this._handle, thread.commentThreadHandle);412}413}414}415416updateInput(input: string) {417const thread = this.activeEditingCommentThread;418419if (thread && thread.input) {420const commentInput = thread.input;421commentInput.value = input;422thread.input = commentInput;423}424}425426updateCommentingRanges(resourceHints?: languages.CommentingRangeResourceHint) {427this._commentService.updateCommentingRanges(this._uniqueId, resourceHints);428}429430private getKnownThread(commentThreadHandle: number): MainThreadCommentThread<IRange | ICellRange> {431const thread = this._threads.get(commentThreadHandle);432if (!thread) {433throw new Error('unknown thread');434}435return thread.thread;436}437438async getDocumentComments(resource: URI, token: CancellationToken) {439if (resource.scheme === Schemas.vscodeNotebookCell) {440return {441uniqueOwner: this._uniqueId,442label: this.label,443threads: [],444commentingRanges: {445resource: resource,446ranges: [],447fileComments: false448}449};450}451452const ret: languages.CommentThread<IRange>[] = [];453for (const thread of [...this._threads.keys()]) {454const commentThread = this._threads.get(thread)!;455if (commentThread.thread.resource === resource.toString()) {456if (commentThread.thread.isDocumentCommentThread()) {457ret.push(commentThread.thread);458}459}460}461462const commentingRanges = await this._proxy.$provideCommentingRanges(this.handle, resource, token);463464return {465uniqueOwner: this._uniqueId,466label: this.label,467threads: ret,468commentingRanges: {469resource: resource,470ranges: commentingRanges?.ranges || [],471fileComments: !!commentingRanges?.fileComments472}473};474}475476async getNotebookComments(resource: URI, token: CancellationToken) {477if (resource.scheme !== Schemas.vscodeNotebookCell) {478return {479uniqueOwner: this._uniqueId,480label: this.label,481threads: []482};483}484485const ret: languages.CommentThread<ICellRange>[] = [];486for (const thread of [...this._threads.keys()]) {487const commentThread = this._threads.get(thread)!;488if (commentThread.thread.resource === resource.toString()) {489if (!commentThread.thread.isDocumentCommentThread()) {490ret.push(commentThread.thread as languages.CommentThread<ICellRange>);491}492}493}494495return {496uniqueOwner: this._uniqueId,497label: this.label,498threads: ret499};500}501502async toggleReaction(uri: URI, thread: languages.CommentThread, comment: languages.Comment, reaction: languages.CommentReaction, token: CancellationToken): Promise<void> {503return this._proxy.$toggleReaction(this._handle, thread.commentThreadHandle, uri, comment, reaction);504}505506getAllComments(): MainThreadCommentThread<IRange | ICellRange>[] {507const ret: MainThreadCommentThread<IRange | ICellRange>[] = [];508for (const thread of [...this._threads.keys()]) {509ret.push(this._threads.get(thread)!.thread);510}511512return ret;513}514515createCommentThreadTemplate(resource: UriComponents, range: IRange | undefined, editorId?: string): Promise<void> {516return this._proxy.$createCommentThreadTemplate(this.handle, resource, range, editorId);517}518519async updateCommentThreadTemplate(threadHandle: number, range: IRange) {520await this._proxy.$updateCommentThreadTemplate(this.handle, threadHandle, range);521}522523toJSON(): any {524return {525$mid: MarshalledId.CommentController,526handle: this.handle527};528}529}530531532const commentsViewIcon = registerIcon('comments-view-icon', Codicon.commentDiscussion, localize('commentsViewIcon', 'View icon of the comments view.'));533534@extHostNamedCustomer(MainContext.MainThreadComments)535export class MainThreadComments extends Disposable implements MainThreadCommentsShape {536private readonly _proxy: ExtHostCommentsShape;537538private _handlers = new Map<number, string>();539private _commentControllers = new Map<number, MainThreadCommentController>();540541private _activeEditingCommentThread?: MainThreadCommentThread<IRange | ICellRange>;542private readonly _activeEditingCommentThreadDisposables = this._register(new DisposableStore());543544private readonly _openViewListener: MutableDisposable<IDisposable> = this._register(new MutableDisposable());545private readonly _onChangeContainerListener: MutableDisposable<IDisposable> = this._register(new MutableDisposable());546private readonly _onChangeContainerLocationListener: MutableDisposable<IDisposable> = this._register(new MutableDisposable());547548constructor(549extHostContext: IExtHostContext,550@ICommentService private readonly _commentService: ICommentService,551@IViewsService private readonly _viewsService: IViewsService,552@IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService,553@IUriIdentityService private readonly _uriIdentityService: IUriIdentityService,554@IEditorService private readonly _editorService: IEditorService555) {556super();557this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostComments);558this._commentService.unregisterCommentController();559560this._register(this._commentService.onDidChangeActiveEditingCommentThread(async thread => {561const handle = (thread as MainThreadCommentThread<IRange | ICellRange>).controllerHandle;562const controller = this._commentControllers.get(handle);563564if (!controller) {565return;566}567568this._activeEditingCommentThreadDisposables.clear();569this._activeEditingCommentThread = thread as MainThreadCommentThread<IRange | ICellRange>;570controller.activeEditingCommentThread = this._activeEditingCommentThread;571}));572}573574$registerCommentController(handle: number, id: string, label: string, extensionId: string): void {575const providerId = `${id}-${extensionId}`;576this._handlers.set(handle, providerId);577578const provider = new MainThreadCommentController(this._proxy, this._commentService, handle, providerId, id, label, {});579this._commentService.registerCommentController(providerId, provider);580this._commentControllers.set(handle, provider);581582this._register(this._commentService.onResourceHasCommentingRanges(e => {583this.registerView();584}));585586this._register(this._commentService.onDidUpdateCommentThreads(e => {587this.registerView();588}));589590this._commentService.setWorkspaceComments(String(handle), []);591}592593$unregisterCommentController(handle: number): void {594const providerId = this._handlers.get(handle);595this._handlers.delete(handle);596this._commentControllers.get(handle)?.dispose();597this._commentControllers.delete(handle);598599if (typeof providerId !== 'string') {600return;601// throw new Error('unknown handler');602} else {603this._commentService.unregisterCommentController(providerId);604}605}606607$updateCommentControllerFeatures(handle: number, features: CommentProviderFeatures): void {608const provider = this._commentControllers.get(handle);609610if (!provider) {611return undefined;612}613614provider.updateFeatures(features);615}616617$createCommentThread(handle: number,618commentThreadHandle: number,619threadId: string,620resource: UriComponents,621range: IRange | ICellRange | undefined,622comments: languages.Comment[],623extensionId: ExtensionIdentifier,624isTemplate: boolean,625editorId?: string626): languages.CommentThread<IRange | ICellRange> | undefined {627const provider = this._commentControllers.get(handle);628629if (!provider) {630return undefined;631}632633return provider.createCommentThread(extensionId.value, commentThreadHandle, threadId, resource, range, comments, isTemplate, editorId);634}635636$updateCommentThread(handle: number,637commentThreadHandle: number,638threadId: string,639resource: UriComponents,640changes: CommentThreadChanges): void {641const provider = this._commentControllers.get(handle);642643if (!provider) {644return undefined;645}646647return provider.updateCommentThread(commentThreadHandle, threadId, resource, changes);648}649650$deleteCommentThread(handle: number, commentThreadHandle: number) {651const provider = this._commentControllers.get(handle);652653if (!provider) {654return;655}656657return provider.deleteCommentThread(commentThreadHandle);658}659660$updateCommentingRanges(handle: number, resourceHints?: languages.CommentingRangeResourceHint) {661const provider = this._commentControllers.get(handle);662663if (!provider) {664return;665}666667provider.updateCommentingRanges(resourceHints);668}669670async $revealCommentThread(handle: number, commentThreadHandle: number, commentUniqueIdInThread: number, options: languages.CommentThreadRevealOptions): Promise<void> {671const provider = this._commentControllers.get(handle);672673if (!provider) {674return Promise.resolve();675}676677const thread = provider.getAllComments().find(thread => thread.commentThreadHandle === commentThreadHandle);678if (!thread || !thread.isDocumentCommentThread()) {679return Promise.resolve();680}681682const comment = thread.comments?.find(comment => comment.uniqueIdInThread === commentUniqueIdInThread);683684revealCommentThread(this._commentService, this._editorService, this._uriIdentityService, thread, comment, options.focusReply, undefined, options.preserveFocus);685}686687async $hideCommentThread(handle: number, commentThreadHandle: number): Promise<void> {688const provider = this._commentControllers.get(handle);689690if (!provider) {691return Promise.resolve();692}693694const thread = provider.getAllComments().find(thread => thread.commentThreadHandle === commentThreadHandle);695if (!thread || !thread.isDocumentCommentThread()) {696return Promise.resolve();697}698699thread.collapsibleState = languages.CommentThreadCollapsibleState.Collapsed;700}701702private registerView() {703const commentsPanelAlreadyConstructed = !!this._viewDescriptorService.getViewDescriptorById(COMMENTS_VIEW_ID);704if (!commentsPanelAlreadyConstructed) {705const VIEW_CONTAINER: ViewContainer = Registry.as<IViewContainersRegistry>(ViewExtensions.ViewContainersRegistry).registerViewContainer({706id: COMMENTS_VIEW_ID,707title: COMMENTS_VIEW_TITLE,708ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [COMMENTS_VIEW_ID, { mergeViewWithContainerWhenSingleView: true }]),709storageId: COMMENTS_VIEW_STORAGE_ID,710hideIfEmpty: true,711icon: commentsViewIcon,712order: 10,713}, ViewContainerLocation.Panel);714715Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry).registerViews([{716id: COMMENTS_VIEW_ID,717name: COMMENTS_VIEW_TITLE,718canToggleVisibility: false,719ctorDescriptor: new SyncDescriptor(CommentsPanel),720canMoveView: true,721containerIcon: commentsViewIcon,722focusCommand: {723id: 'workbench.action.focusCommentsPanel'724}725}], VIEW_CONTAINER);726}727this.registerViewListeners(commentsPanelAlreadyConstructed);728}729730private setComments() {731[...this._commentControllers.keys()].forEach(handle => {732const threads = this._commentControllers.get(handle)!.getAllComments();733734if (threads.length) {735const providerId = this.getHandler(handle);736this._commentService.setWorkspaceComments(providerId, threads);737}738});739}740741private registerViewOpenedListener() {742if (!this._openViewListener.value) {743this._openViewListener.value = this._viewsService.onDidChangeViewVisibility(e => {744if (e.id === COMMENTS_VIEW_ID && e.visible) {745this.setComments();746if (this._openViewListener) {747this._openViewListener.dispose();748}749}750});751}752}753754/**755* If the comments view has never been opened, the constructor for it has not yet run so it has756* no listeners for comment threads being set or updated. Listen for the view opening for the757* first time and send it comments then.758*/759private registerViewListeners(commentsPanelAlreadyConstructed: boolean) {760if (!commentsPanelAlreadyConstructed) {761this.registerViewOpenedListener();762}763764if (!this._onChangeContainerListener.value) {765this._onChangeContainerListener.value = this._viewDescriptorService.onDidChangeContainer(e => {766if (e.views.find(view => view.id === COMMENTS_VIEW_ID)) {767this.setComments();768this.registerViewOpenedListener();769}770});771}772773if (!this._onChangeContainerLocationListener.value) {774this._onChangeContainerLocationListener.value = this._viewDescriptorService.onDidChangeContainerLocation(e => {775const commentsContainer = this._viewDescriptorService.getViewContainerByViewId(COMMENTS_VIEW_ID);776if (e.viewContainer.id === commentsContainer?.id) {777this.setComments();778this.registerViewOpenedListener();779}780});781}782}783784private getHandler(handle: number) {785if (!this._handlers.has(handle)) {786throw new Error('Unknown handler');787}788return this._handlers.get(handle)!;789}790}791792793