Skip to content

Commit 89a07de

Browse files
authored
Merge pull request microsoft#59462 from Microsoft/sandy081/59209
Update output channel when visible
2 parents eabc203 + 0e0fde9 commit 89a07de

6 files changed

Lines changed: 94 additions & 27 deletions

File tree

src/vs/workbench/api/electron-browser/mainThreadOutputService.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,18 @@ import { Registry } from 'vs/platform/registry/common/platform';
99
import { IOutputService, IOutputChannel, OUTPUT_PANEL_ID, Extensions, IOutputChannelRegistry } from 'vs/workbench/parts/output/common/output';
1010
import { IPartService } from 'vs/workbench/services/part/common/partService';
1111
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
12-
import { MainThreadOutputServiceShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
12+
import { MainThreadOutputServiceShape, MainContext, IExtHostContext, ExtHostOutputServiceShape, ExtHostContext } from '../node/extHost.protocol';
1313
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
1414
import { UriComponents, URI } from 'vs/base/common/uri';
15+
import { Disposable } from 'vs/base/common/lifecycle';
16+
import { anyEvent } from 'vs/base/common/event';
1517

1618
@extHostNamedCustomer(MainContext.MainThreadOutputService)
17-
export class MainThreadOutputService implements MainThreadOutputServiceShape {
19+
export class MainThreadOutputService extends Disposable implements MainThreadOutputServiceShape {
1820

1921
private static _idPool = 1;
2022

23+
private _proxy: ExtHostOutputServiceShape;
2124
private readonly _outputService: IOutputService;
2225
private readonly _partService: IPartService;
2326
private readonly _panelService: IPanelService;
@@ -28,12 +31,22 @@ export class MainThreadOutputService implements MainThreadOutputServiceShape {
2831
@IPartService partService: IPartService,
2932
@IPanelService panelService: IPanelService
3033
) {
34+
super();
3135
this._outputService = outputService;
3236
this._partService = partService;
3337
this._panelService = panelService;
38+
39+
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostOutputService);
40+
41+
this._register(anyEvent<any>(this._outputService.onActiveOutputChannel, this._panelService.onDidPanelOpen, this._panelService.onDidPanelClose)(() => {
42+
const panel = this._panelService.getActivePanel();
43+
const visibleChannel: IOutputChannel = panel && panel.getId() === OUTPUT_PANEL_ID ? this._outputService.getActiveChannel() : null;
44+
this._proxy.$setVisibleChannel(visibleChannel ? visibleChannel.id : null);
45+
}));
3446
}
3547

3648
public dispose(): void {
49+
super.dispose();
3750
// Leave all the existing channels intact (e.g. might help with troubleshooting)
3851
}
3952

@@ -51,6 +64,14 @@ export class MainThreadOutputService implements MainThreadOutputServiceShape {
5164
return undefined;
5265
}
5366

67+
public $update(channelId: string): Thenable<void> {
68+
const channel = this._getChannel(channelId);
69+
if (channel) {
70+
channel.update();
71+
}
72+
return undefined;
73+
}
74+
5475
public $clear(channelId: string): Thenable<void> {
5576
const channel = this._getChannel(channelId);
5677
if (channel) {

src/vs/workbench/api/node/extHost.api.impl.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ export function createApiFactory(
128128
rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);
129129
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
130130
const exthostCommentProviders = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands.converter, extHostDocuments));
131+
const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, new ExtHostOutputService(initData.logsLocation, rpcProtocol));
131132

132133
// Check that no named customers are missing
133134
const expected: ProxyIdentifier<any>[] = Object.keys(ExtHostContext).map((key) => (<any>ExtHostContext)[key]);
@@ -137,7 +138,6 @@ export function createApiFactory(
137138
const extHostMessageService = new ExtHostMessageService(rpcProtocol);
138139
const extHostDialogs = new ExtHostDialogs(rpcProtocol);
139140
const extHostStatusBar = new ExtHostStatusBar(rpcProtocol);
140-
const extHostOutputService = new ExtHostOutputService(initData.logsLocation, rpcProtocol);
141141
const extHostLanguages = new ExtHostLanguages(rpcProtocol, extHostDocuments);
142142

143143
// Register an output channel for exthost log

src/vs/workbench/api/node/extHost.protocol.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ export interface MainThreadMessageServiceShape extends IDisposable {
304304
export interface MainThreadOutputServiceShape extends IDisposable {
305305
$register(label: string, log: boolean, file?: UriComponents): Thenable<string>;
306306
$append(channelId: string, value: string): Thenable<void>;
307+
$update(channelId: string): Thenable<void>;
307308
$clear(channelId: string): Thenable<void>;
308309
$reveal(channelId: string, preserveFocus: boolean): Thenable<void>;
309310
$close(channelId: string): Thenable<void>;
@@ -989,6 +990,10 @@ export interface ExtHostLogServiceShape {
989990
$setLevel(level: LogLevel): void;
990991
}
991992

993+
export interface ExtHostOutputServiceShape {
994+
$setVisibleChannel(channelId: string | null): void;
995+
}
996+
992997
export interface ExtHostProgressShape {
993998
$acceptProgressCanceled(handle: number): void;
994999
}
@@ -1066,5 +1071,6 @@ export const ExtHostContext = {
10661071
ExtHostWebviews: createExtId<ExtHostWebviewsShape>('ExtHostWebviews'),
10671072
ExtHostProgress: createMainId<ExtHostProgressShape>('ExtHostProgress'),
10681073
ExtHostComments: createMainId<ExtHostCommentsShape>('ExtHostComments'),
1069-
ExtHostUrls: createExtId<ExtHostUrlsShape>('ExtHostUrls')
1074+
ExtHostUrls: createExtId<ExtHostUrlsShape>('ExtHostUrls'),
1075+
ExtHostOutputService: createMainId<ExtHostOutputServiceShape>('ExtHostOutputService'),
10701076
};

src/vs/workbench/api/node/extHostOutputService.ts

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,28 @@
44
*--------------------------------------------------------------------------------------------*/
55
'use strict';
66

7-
import { MainContext, MainThreadOutputServiceShape, IMainContext } from './extHost.protocol';
7+
import { MainContext, MainThreadOutputServiceShape, IMainContext, ExtHostOutputServiceShape } from './extHost.protocol';
88
import * as vscode from 'vscode';
99
import { URI } from 'vs/base/common/uri';
1010
import { posix } from 'path';
1111
import { OutputAppender } from 'vs/platform/output/node/outputAppender';
1212
import { toLocalISOString } from 'vs/base/common/date';
13+
import { Event, Emitter } from 'vs/base/common/event';
14+
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
1315

14-
export abstract class AbstractExtHostOutputChannel implements vscode.OutputChannel {
16+
export abstract class AbstractExtHostOutputChannel extends Disposable implements vscode.OutputChannel {
1517

16-
protected readonly _id: Thenable<string>;
18+
readonly _id: Thenable<string>;
1719
private readonly _name: string;
1820
protected readonly _proxy: MainThreadOutputServiceShape;
1921
private _disposed: boolean;
2022

23+
protected _onDidAppend: Emitter<void> = this._register(new Emitter<void>());
24+
get onDidAppend(): Event<void> { return this._onDidAppend.event; }
25+
2126
constructor(name: string, log: boolean, file: URI, proxy: MainThreadOutputServiceShape) {
27+
super();
28+
2229
this._name = name;
2330
this._proxy = proxy;
2431
this._id = proxy.$register(this.name, log, file);
@@ -30,6 +37,10 @@ export abstract class AbstractExtHostOutputChannel implements vscode.OutputChann
3037

3138
abstract append(value: string): void;
3239

40+
update(): void {
41+
this._id.then(id => this._proxy.$update(id));
42+
}
43+
3344
appendLine(value: string): void {
3445
this.validate();
3546
this.append(value + '\n');
@@ -57,6 +68,8 @@ export abstract class AbstractExtHostOutputChannel implements vscode.OutputChann
5768
}
5869

5970
dispose(): void {
71+
super.dispose();
72+
6073
if (!this._disposed) {
6174
this._id
6275
.then(id => this._proxy.$dispose(id))
@@ -74,6 +87,7 @@ export class ExtHostPushOutputChannel extends AbstractExtHostOutputChannel {
7487
append(value: string): void {
7588
this.validate();
7689
this._id.then(id => this._proxy.$append(id, value));
90+
this._onDidAppend.fire();
7791
}
7892
}
7993

@@ -93,6 +107,12 @@ export class ExtHostOutputChannelBackedByFile extends AbstractExtHostOutputChann
93107
append(value: string): void {
94108
this.validate();
95109
this._appender.append(value);
110+
this._onDidAppend.fire();
111+
}
112+
113+
update(): void {
114+
this._appender.flush();
115+
super.update();
96116
}
97117

98118
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
@@ -117,17 +137,37 @@ export class ExtHostLogFileOutputChannel extends AbstractExtHostOutputChannel {
117137
}
118138
}
119139

120-
export class ExtHostOutputService {
140+
export class ExtHostOutputService implements ExtHostOutputServiceShape {
121141

122142
private _proxy: MainThreadOutputServiceShape;
123143
private _outputDir: string;
144+
private _channels: Map<string, AbstractExtHostOutputChannel> = new Map<string, AbstractExtHostOutputChannel>();
145+
private _visibleChannelDisposable: IDisposable;
124146

125147
constructor(logsLocation: URI, mainContext: IMainContext) {
126148
this._outputDir = posix.join(logsLocation.fsPath, `output_logging_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`);
127149
this._proxy = mainContext.getProxy(MainContext.MainThreadOutputService);
128150
}
129151

152+
$setVisibleChannel(channelId: string): void {
153+
if (this._visibleChannelDisposable) {
154+
this._visibleChannelDisposable = dispose(this._visibleChannelDisposable);
155+
}
156+
if (channelId) {
157+
const channel = this._channels.get(channelId);
158+
if (channel) {
159+
this._visibleChannelDisposable = channel.onDidAppend(() => channel.update());
160+
}
161+
}
162+
}
163+
130164
createOutputChannel(name: string): vscode.OutputChannel {
165+
const channel = this._createOutputChannel(name);
166+
channel._id.then(id => this._channels.set(id, channel));
167+
return channel;
168+
}
169+
170+
private _createOutputChannel(name: string): AbstractExtHostOutputChannel {
131171
name = name.trim();
132172
if (!name) {
133173
throw new Error('illegal argument `name`. must not be falsy');

src/vs/workbench/parts/output/common/output.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ export interface IOutputChannel {
116116
*/
117117
append(output: string): void;
118118

119+
/**
120+
* Update the channel.
121+
*/
122+
update(): void;
123+
119124
/**
120125
* Clears all received output for this channel.
121126
*/

src/vs/workbench/parts/output/electron-browser/outputServices.ts

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
4141
import { CancellationToken } from 'vs/base/common/cancellation';
4242
import { OutputAppender } from 'vs/platform/output/node/outputAppender';
4343
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
44+
import { isNumber } from 'vs/base/common/types';
4445

4546
const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel';
4647

@@ -123,6 +124,8 @@ abstract class AbstractFileOutputChannel extends Disposable {
123124
this.startOffset = this.endOffset;
124125
}
125126

127+
update(): void { }
128+
126129
protected createModel(content: string): ITextModel {
127130
if (this.model) {
128131
this.model.setValue(content);
@@ -346,18 +349,17 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann
346349
super(outputChannelDescriptor, modelUri, fileService, modelService, modeService);
347350

348351
this.fileHandler = this._register(new OutputFileListener(this.file, this.fileService));
349-
this._register(this.fileHandler.onDidContentChange(size => this.onDidContentChange(size)));
352+
this._register(this.fileHandler.onDidContentChange(size => this.update(size)));
350353
this._register(toDisposable(() => this.fileHandler.unwatch()));
351354
}
352355

353356
loadModel(): TPromise<ITextModel> {
354-
return this.readContent()
355-
.then(content => this.createModel(content));
356-
}
357-
358-
clear(): void {
359-
this.readContent() // Read content from the file before clearing
360-
.then(() => super.clear());
357+
return this.fileService.resolveContent(this.file, { position: this.startOffset, encoding: 'utf8' })
358+
.then(content => {
359+
this.endOffset = this.startOffset + Buffer.from(content.value).byteLength;
360+
this.etag = content.etag;
361+
return this.createModel(content.value);
362+
});
361363
}
362364

363365
append(message: string): void {
@@ -392,19 +394,10 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann
392394
this.updateInProgress = false;
393395
}
394396

395-
private readContent(): TPromise<string> {
396-
return this.fileService.resolveContent(this.file, { position: this.startOffset, encoding: 'utf8' })
397-
.then(content => {
398-
this.endOffset = this.startOffset + Buffer.from(content.value).byteLength;
399-
this.etag = content.etag;
400-
return content.value;
401-
});
402-
}
403-
404-
private onDidContentChange(size: number): void {
397+
update(size?: number): void {
405398
if (!this.updateInProgress) {
406399
this.updateInProgress = true;
407-
if (this.endOffset > size) { // Reset - Content is removed
400+
if (isNumber(size) && this.endOffset > size) { // Reset - Content is removed
408401
this.startOffset = this.endOffset = 0;
409402
this.model.setValue('');
410403
}
@@ -712,6 +705,8 @@ class BufferredOutputChannel extends Disposable implements OutputChannel {
712705
}
713706
}
714707

708+
update(): void { }
709+
715710
clear(): void {
716711
if (this.modelUpdater.isScheduled()) {
717712
this.modelUpdater.cancel();

0 commit comments

Comments
 (0)