private registerListeners()

in patched-vscode/src/vs/workbench/contrib/debug/browser/debugSession.ts [1005:1277]


	private registerListeners(): void {
		if (!this.raw) {
			return;
		}

		this.rawListeners.add(this.raw.onDidInitialize(async () => {
			aria.status(
				this.configuration.noDebug
					? localize('debuggingStartedNoDebug', "Started running without debugging.")
					: localize('debuggingStarted', "Debugging started.")
			);

			const sendConfigurationDone = async () => {
				if (this.raw && this.raw.capabilities.supportsConfigurationDoneRequest) {
					try {
						await this.raw.configurationDone();
					} catch (e) {
						// Disconnect the debug session on configuration done error #10596
						this.notificationService.error(e);
						this.raw?.disconnect({});
					}
				}

				return undefined;
			};

			// Send all breakpoints
			try {
				await this.debugService.sendAllBreakpoints(this);
			} finally {
				await sendConfigurationDone();
				await this.fetchThreads();
			}
		}));


		const statusQueue = this.statusQueue;
		this.rawListeners.add(this.raw.onDidStop(event => this.handleStop(event.body)));

		this.rawListeners.add(this.raw.onDidThread(event => {
			statusQueue.cancel([event.body.threadId]);
			if (event.body.reason === 'started') {
				// debounce to reduce threadsRequest frequency and improve performance
				if (!this.fetchThreadsScheduler) {
					this.fetchThreadsScheduler = new RunOnceScheduler(() => {
						this.fetchThreads();
					}, 100);
					this.rawListeners.add(this.fetchThreadsScheduler);
				}
				if (!this.fetchThreadsScheduler.isScheduled()) {
					this.fetchThreadsScheduler.schedule();
				}
			} else if (event.body.reason === 'exited') {
				this.model.clearThreads(this.getId(), true, event.body.threadId);
				const viewModel = this.debugService.getViewModel();
				const focusedThread = viewModel.focusedThread;
				this.passFocusScheduler.cancel();
				if (focusedThread && event.body.threadId === focusedThread.threadId) {
					// De-focus the thread in case it was focused
					this.debugService.focusStackFrame(undefined, undefined, viewModel.focusedSession, { explicit: false });
				}
			}
		}));

		this.rawListeners.add(this.raw.onDidTerminateDebugee(async event => {
			aria.status(localize('debuggingStopped', "Debugging stopped."));
			if (event.body && event.body.restart) {
				await this.debugService.restartSession(this, event.body.restart);
			} else if (this.raw) {
				await this.raw.disconnect({ terminateDebuggee: false });
			}
		}));

		this.rawListeners.add(this.raw.onDidContinued(event => {
			const allThreads = event.body.allThreadsContinued !== false;

			statusQueue.cancel(allThreads ? undefined : [event.body.threadId]);

			const threadId = allThreads ? undefined : event.body.threadId;
			if (typeof threadId === 'number') {
				this.stoppedDetails = this.stoppedDetails.filter(sd => sd.threadId !== threadId);
				const tokens = this.cancellationMap.get(threadId);
				this.cancellationMap.delete(threadId);
				tokens?.forEach(t => t.dispose(true));
			} else {
				this.stoppedDetails = [];
				this.cancelAllRequests();
			}
			this.lastContinuedThreadId = threadId;
			// We need to pass focus to other sessions / threads with a timeout in case a quick stop event occurs #130321
			this.passFocusScheduler.schedule();
			this.model.clearThreads(this.getId(), false, threadId);
			this._onDidChangeState.fire();
		}));

		const outputQueue = new Queue<void>();
		this.rawListeners.add(this.raw.onDidOutput(async event => {
			const outputSeverity = event.body.category === 'stderr' ? Severity.Error : event.body.category === 'console' ? Severity.Warning : Severity.Info;

			// When a variables event is received, execute immediately to obtain the variables value #126967
			if (event.body.variablesReference) {
				const source = event.body.source && event.body.line ? {
					lineNumber: event.body.line,
					column: event.body.column ? event.body.column : 1,
					source: this.getSource(event.body.source)
				} : undefined;
				const container = new ExpressionContainer(this, undefined, event.body.variablesReference, generateUuid());
				const children = container.getChildren();
				// we should put appendToRepl into queue to make sure the logs to be displayed in correct order
				// see https://github.com/microsoft/vscode/issues/126967#issuecomment-874954269
				outputQueue.queue(async () => {
					const resolved = await children;
					// For single logged variables, try to use the output if we can so
					// present a better (i.e. ANSI-aware) representation of the output
					if (resolved.length === 1) {
						this.appendToRepl({ output: event.body.output, expression: resolved[0], sev: outputSeverity, source }, event.body.category === 'important');
						return;
					}

					resolved.forEach((child) => {
						// Since we can not display multiple trees in a row, we are displaying these variables one after the other (ignoring their names)
						(<any>child).name = null;
						this.appendToRepl({ output: '', expression: child, sev: outputSeverity, source }, event.body.category === 'important');
					});
				});
				return;
			}
			outputQueue.queue(async () => {
				if (!event.body || !this.raw) {
					return;
				}

				if (event.body.category === 'telemetry') {
					// only log telemetry events from debug adapter if the debug extension provided the telemetry key
					// and the user opted in telemetry
					const telemetryEndpoint = this.raw.dbgr.getCustomTelemetryEndpoint();
					if (telemetryEndpoint && this.telemetryService.telemetryLevel !== TelemetryLevel.NONE) {
						// __GDPR__TODO__ We're sending events in the name of the debug extension and we can not ensure that those are declared correctly.
						let data = event.body.data;
						if (!telemetryEndpoint.sendErrorTelemetry && event.body.data) {
							data = filterExceptionsFromTelemetry(event.body.data);
						}

						this.customEndpointTelemetryService.publicLog(telemetryEndpoint, event.body.output, data);
					}

					return;
				}

				// Make sure to append output in the correct order by properly waiting on preivous promises #33822
				const source = event.body.source && event.body.line ? {
					lineNumber: event.body.line,
					column: event.body.column ? event.body.column : 1,
					source: this.getSource(event.body.source)
				} : undefined;

				if (event.body.group === 'start' || event.body.group === 'startCollapsed') {
					const expanded = event.body.group === 'start';
					this.repl.startGroup(event.body.output || '', expanded, source);
					return;
				}
				if (event.body.group === 'end') {
					this.repl.endGroup();
					if (!event.body.output) {
						// Only return if the end event does not have additional output in it
						return;
					}
				}

				if (typeof event.body.output === 'string') {
					this.appendToRepl({ output: event.body.output, sev: outputSeverity, source }, event.body.category === 'important');
				}
			});
		}));

		this.rawListeners.add(this.raw.onDidBreakpoint(event => {
			const id = event.body && event.body.breakpoint ? event.body.breakpoint.id : undefined;
			const breakpoint = this.model.getBreakpoints().find(bp => bp.getIdFromAdapter(this.getId()) === id);
			const functionBreakpoint = this.model.getFunctionBreakpoints().find(bp => bp.getIdFromAdapter(this.getId()) === id);
			const dataBreakpoint = this.model.getDataBreakpoints().find(dbp => dbp.getIdFromAdapter(this.getId()) === id);
			const exceptionBreakpoint = this.model.getExceptionBreakpoints().find(excbp => excbp.getIdFromAdapter(this.getId()) === id);

			if (event.body.reason === 'new' && event.body.breakpoint.source && event.body.breakpoint.line) {
				const source = this.getSource(event.body.breakpoint.source);
				const bps = this.model.addBreakpoints(source.uri, [{
					column: event.body.breakpoint.column,
					enabled: true,
					lineNumber: event.body.breakpoint.line,
				}], false);
				if (bps.length === 1) {
					const data = new Map<string, DebugProtocol.Breakpoint>([[bps[0].getId(), event.body.breakpoint]]);
					this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
				}
			}

			if (event.body.reason === 'removed') {
				if (breakpoint) {
					this.model.removeBreakpoints([breakpoint]);
				}
				if (functionBreakpoint) {
					this.model.removeFunctionBreakpoints(functionBreakpoint.getId());
				}
				if (dataBreakpoint) {
					this.model.removeDataBreakpoints(dataBreakpoint.getId());
				}
			}

			if (event.body.reason === 'changed') {
				if (breakpoint) {
					if (!breakpoint.column) {
						event.body.breakpoint.column = undefined;
					}
					const data = new Map<string, DebugProtocol.Breakpoint>([[breakpoint.getId(), event.body.breakpoint]]);
					this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
				}
				if (functionBreakpoint) {
					const data = new Map<string, DebugProtocol.Breakpoint>([[functionBreakpoint.getId(), event.body.breakpoint]]);
					this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
				}
				if (dataBreakpoint) {
					const data = new Map<string, DebugProtocol.Breakpoint>([[dataBreakpoint.getId(), event.body.breakpoint]]);
					this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
				}
				if (exceptionBreakpoint) {
					const data = new Map<string, DebugProtocol.Breakpoint>([[exceptionBreakpoint.getId(), event.body.breakpoint]]);
					this.model.setBreakpointSessionData(this.getId(), this.capabilities, data);
				}
			}
		}));

		this.rawListeners.add(this.raw.onDidLoadedSource(event => {
			this._onDidLoadedSource.fire({
				reason: event.body.reason,
				source: this.getSource(event.body.source)
			});
		}));

		this.rawListeners.add(this.raw.onDidCustomEvent(event => {
			this._onDidCustomEvent.fire(event);
		}));

		this.rawListeners.add(this.raw.onDidProgressStart(event => {
			this._onDidProgressStart.fire(event);
		}));
		this.rawListeners.add(this.raw.onDidProgressUpdate(event => {
			this._onDidProgressUpdate.fire(event);
		}));
		this.rawListeners.add(this.raw.onDidProgressEnd(event => {
			this._onDidProgressEnd.fire(event);
		}));
		this.rawListeners.add(this.raw.onDidInvalidateMemory(event => {
			this._onDidInvalidMemory.fire(event);
		}));
		this.rawListeners.add(this.raw.onDidInvalidated(async event => {
			const areas = event.body.areas || ['all'];
			// If invalidated event only requires to update variables or watch, do that, otherwise refetch threads https://github.com/microsoft/vscode/issues/106745
			if (areas.includes('threads') || areas.includes('stacks') || areas.includes('all')) {
				this.cancelAllRequests();
				this.model.clearThreads(this.getId(), true);

				const details = this.stoppedDetails;
				this.stoppedDetails.length = 1;
				await Promise.all(details.map(d => this.handleStop(d)));
			}

			const viewModel = this.debugService.getViewModel();
			if (viewModel.focusedSession === this) {
				viewModel.updateViews();
			}
		}));

		this.rawListeners.add(this.raw.onDidExitAdapter(event => this.onDidExitAdapter(event)));
	}