From c175c387742c41cac1b83a3de03677f226519548 Mon Sep 17 00:00:00 2001 From: Pradeep Ramola Date: Wed, 2 Sep 2026 14:39:47 -0400 Subject: [PATCH] Preserve output channel visibility on restart --- client-node-tests/src/integration.test.ts | 46 +++++++++++++++++++++++ client/src/common/client.ts | 20 ++++++++++ client/src/node/main.ts | 2 + 3 files changed, 68 insertions(+) diff --git a/client-node-tests/src/integration.test.ts b/client-node-tests/src/integration.test.ts index c4b4bfbe2..113d826bb 100644 --- a/client-node-tests/src/integration.test.ts +++ b/client-node-tests/src/integration.test.ts @@ -242,6 +242,52 @@ suite('Server output', () => { }); }); +suite('Client restart', () => { + + test('Preserves output channel visibility after restart', async () => { + const client = new RestartTestLanguageClient(true); + + await client.restart(); + + assert.deepStrictEqual(client.events, ['isOutputChannelVisible', 'stop', 'start', 'restoreOutputChannelVisibility:true']); + }); + + test('Does not restore hidden output channel after restart', async () => { + const client = new RestartTestLanguageClient(false); + + await client.restart(); + + assert.deepStrictEqual(client.events, ['isOutputChannelVisible', 'stop', 'start', 'restoreOutputChannelVisibility:false']); + }); +}); + +class RestartTestLanguageClient extends lsclient.LanguageClient { + + public readonly events: string[] = []; + + public constructor(private readonly outputChannelVisible: boolean) { + super('test restart', { module: 'unused', transport: lsclient.TransportKind.ipc }, {}); + } + + public override async start(): Promise { + this.events.push('start'); + } + + public override stop(): Promise { + this.events.push('stop'); + return Promise.resolve(); + } + + protected override isOutputChannelVisible(): boolean { + this.events.push('isOutputChannelVisible'); + return this.outputChannelVisible; + } + + protected override restoreOutputChannelVisibility(wasVisible: boolean): void { + this.events.push(`restoreOutputChannelVisibility:${wasVisible}`); + } +} + suite('Client integration', () => { let client!: lsclient.LanguageClient; diff --git a/client/src/common/client.ts b/client/src/common/client.ts index a5953c6ab..ed6dafd3a 100644 --- a/client/src/common/client.ts +++ b/client/src/common/client.ts @@ -858,6 +858,26 @@ export abstract class BaseLanguageClient implements FeatureClient { + if (editor.document.uri.scheme !== 'output') { + return false; + } + const outputResource = `${editor.document.uri.toString(true)}\n${editor.document.fileName}`.toLowerCase(); + return outputResource.includes(outputChannelName); + }); + } + + protected restoreOutputChannelVisibility(wasVisible: boolean): void { + if (wasVisible) { + this.outputChannel.show(true); + } + } + public get traceOutputChannel(): LogOutputChannel { return this._traceOutputChannel ? this._traceOutputChannel : this.outputChannel; } diff --git a/client/src/node/main.ts b/client/src/node/main.ts index 5f91530c4..7f128c5cf 100644 --- a/client/src/node/main.ts +++ b/client/src/node/main.ts @@ -236,6 +236,7 @@ export class LanguageClient extends BaseLanguageClient { } public async restart(): Promise { + const outputChannelVisible = this.isOutputChannelVisible(); await this.stop(); // We are in debug mode. Wait a little before we restart // so that the debug port can be freed. We can safely ignore @@ -247,6 +248,7 @@ export class LanguageClient extends BaseLanguageClient { } else { await this.start(); } + this.restoreOutputChannelVisibility(outputChannelVisible); } protected shutdown(mode: ShutdownMode, timeout: number = 2000): Promise {