Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update output channel when visible #59462

Merged
merged 2 commits into from
Sep 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 23 additions & 2 deletions src/vs/workbench/api/electron-browser/mainThreadOutputService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,18 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IOutputService, IOutputChannel, OUTPUT_PANEL_ID, Extensions, IOutputChannelRegistry } from 'vs/workbench/parts/output/common/output';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { MainThreadOutputServiceShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { MainThreadOutputServiceShape, MainContext, IExtHostContext, ExtHostOutputServiceShape, ExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { UriComponents, URI } from 'vs/base/common/uri';
import { Disposable } from 'vs/base/common/lifecycle';
import { anyEvent } from 'vs/base/common/event';

@extHostNamedCustomer(MainContext.MainThreadOutputService)
export class MainThreadOutputService implements MainThreadOutputServiceShape {
export class MainThreadOutputService extends Disposable implements MainThreadOutputServiceShape {

private static _idPool = 1;

private _proxy: ExtHostOutputServiceShape;
private readonly _outputService: IOutputService;
private readonly _partService: IPartService;
private readonly _panelService: IPanelService;
Expand All @@ -28,12 +31,22 @@ export class MainThreadOutputService implements MainThreadOutputServiceShape {
@IPartService partService: IPartService,
@IPanelService panelService: IPanelService
) {
super();
this._outputService = outputService;
this._partService = partService;
this._panelService = panelService;

this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostOutputService);

this._register(anyEvent<any>(this._outputService.onActiveOutputChannel, this._panelService.onDidPanelOpen, this._panelService.onDidPanelClose)(() => {
const panel = this._panelService.getActivePanel();
const visibleChannel: IOutputChannel = panel && panel.getId() === OUTPUT_PANEL_ID ? this._outputService.getActiveChannel() : null;
this._proxy.$setVisibleChannel(visibleChannel ? visibleChannel.id : null);
}));
}

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

Expand All @@ -51,6 +64,14 @@ export class MainThreadOutputService implements MainThreadOutputServiceShape {
return undefined;
}

public $update(channelId: string): Thenable<void> {
const channel = this._getChannel(channelId);
if (channel) {
channel.update();
}
return undefined;
}

public $clear(channelId: string): Thenable<void> {
const channel = this._getChannel(channelId);
if (channel) {
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/node/extHost.api.impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export function createApiFactory(
rpcProtocol.set(ExtHostContext.ExtHostExtensionService, extensionService);
const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress)));
const exthostCommentProviders = rpcProtocol.set(ExtHostContext.ExtHostComments, new ExtHostComments(rpcProtocol, extHostCommands.converter, extHostDocuments));
const extHostOutputService = rpcProtocol.set(ExtHostContext.ExtHostOutputService, new ExtHostOutputService(initData.logsLocation, rpcProtocol));

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

// Register an output channel for exthost log
Expand Down
8 changes: 7 additions & 1 deletion src/vs/workbench/api/node/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ export interface MainThreadMessageServiceShape extends IDisposable {
export interface MainThreadOutputServiceShape extends IDisposable {
$register(label: string, log: boolean, file?: UriComponents): Thenable<string>;
$append(channelId: string, value: string): Thenable<void>;
$update(channelId: string): Thenable<void>;
$clear(channelId: string): Thenable<void>;
$reveal(channelId: string, preserveFocus: boolean): Thenable<void>;
$close(channelId: string): Thenable<void>;
Expand Down Expand Up @@ -989,6 +990,10 @@ export interface ExtHostLogServiceShape {
$setLevel(level: LogLevel): void;
}

export interface ExtHostOutputServiceShape {
$setVisibleChannel(channelId: string | null): void;
}

export interface ExtHostProgressShape {
$acceptProgressCanceled(handle: number): void;
}
Expand Down Expand Up @@ -1066,5 +1071,6 @@ export const ExtHostContext = {
ExtHostWebviews: createExtId<ExtHostWebviewsShape>('ExtHostWebviews'),
ExtHostProgress: createMainId<ExtHostProgressShape>('ExtHostProgress'),
ExtHostComments: createMainId<ExtHostCommentsShape>('ExtHostComments'),
ExtHostUrls: createExtId<ExtHostUrlsShape>('ExtHostUrls')
ExtHostUrls: createExtId<ExtHostUrlsShape>('ExtHostUrls'),
ExtHostOutputService: createMainId<ExtHostOutputServiceShape>('ExtHostOutputService'),
};
48 changes: 44 additions & 4 deletions src/vs/workbench/api/node/extHostOutputService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,28 @@
*--------------------------------------------------------------------------------------------*/
'use strict';

import { MainContext, MainThreadOutputServiceShape, IMainContext } from './extHost.protocol';
import { MainContext, MainThreadOutputServiceShape, IMainContext, ExtHostOutputServiceShape } from './extHost.protocol';
import * as vscode from 'vscode';
import { URI } from 'vs/base/common/uri';
import { posix } from 'path';
import { OutputAppender } from 'vs/platform/output/node/outputAppender';
import { toLocalISOString } from 'vs/base/common/date';
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';

export abstract class AbstractExtHostOutputChannel implements vscode.OutputChannel {
export abstract class AbstractExtHostOutputChannel extends Disposable implements vscode.OutputChannel {

protected readonly _id: Thenable<string>;
readonly _id: Thenable<string>;
private readonly _name: string;
protected readonly _proxy: MainThreadOutputServiceShape;
private _disposed: boolean;

protected _onDidAppend: Emitter<void> = this._register(new Emitter<void>());
get onDidAppend(): Event<void> { return this._onDidAppend.event; }

constructor(name: string, log: boolean, file: URI, proxy: MainThreadOutputServiceShape) {
super();

this._name = name;
this._proxy = proxy;
this._id = proxy.$register(this.name, log, file);
Expand All @@ -30,6 +37,10 @@ export abstract class AbstractExtHostOutputChannel implements vscode.OutputChann

abstract append(value: string): void;

update(): void {
this._id.then(id => this._proxy.$update(id));
}

appendLine(value: string): void {
this.validate();
this.append(value + '\n');
Expand Down Expand Up @@ -57,6 +68,8 @@ export abstract class AbstractExtHostOutputChannel implements vscode.OutputChann
}

dispose(): void {
super.dispose();

if (!this._disposed) {
this._id
.then(id => this._proxy.$dispose(id))
Expand All @@ -74,6 +87,7 @@ export class ExtHostPushOutputChannel extends AbstractExtHostOutputChannel {
append(value: string): void {
this.validate();
this._id.then(id => this._proxy.$append(id, value));
this._onDidAppend.fire();
}
}

Expand All @@ -93,6 +107,12 @@ export class ExtHostOutputChannelBackedByFile extends AbstractExtHostOutputChann
append(value: string): void {
this.validate();
this._appender.append(value);
this._onDidAppend.fire();
}

update(): void {
this._appender.flush();
super.update();
}

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

export class ExtHostOutputService {
export class ExtHostOutputService implements ExtHostOutputServiceShape {

private _proxy: MainThreadOutputServiceShape;
private _outputDir: string;
private _channels: Map<string, AbstractExtHostOutputChannel> = new Map<string, AbstractExtHostOutputChannel>();
private _visibleChannelDisposable: IDisposable;

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

$setVisibleChannel(channelId: string): void {
if (this._visibleChannelDisposable) {
this._visibleChannelDisposable = dispose(this._visibleChannelDisposable);
}
if (channelId) {
const channel = this._channels.get(channelId);
if (channel) {
this._visibleChannelDisposable = channel.onDidAppend(() => channel.update());
}
}
}

createOutputChannel(name: string): vscode.OutputChannel {
const channel = this._createOutputChannel(name);
channel._id.then(id => this._channels.set(id, channel));
return channel;
}

private _createOutputChannel(name: string): AbstractExtHostOutputChannel {
name = name.trim();
if (!name) {
throw new Error('illegal argument `name`. must not be falsy');
Expand Down
5 changes: 5 additions & 0 deletions src/vs/workbench/parts/output/common/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ export interface IOutputChannel {
*/
append(output: string): void;

/**
* Update the channel.
*/
update(): void;

/**
* Clears all received output for this channel.
*/
Expand Down
33 changes: 14 additions & 19 deletions src/vs/workbench/parts/output/electron-browser/outputServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { CancellationToken } from 'vs/base/common/cancellation';
import { OutputAppender } from 'vs/platform/output/node/outputAppender';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { isNumber } from 'vs/base/common/types';

const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel';

Expand Down Expand Up @@ -123,6 +124,8 @@ abstract class AbstractFileOutputChannel extends Disposable {
this.startOffset = this.endOffset;
}

update(): void { }

protected createModel(content: string): ITextModel {
if (this.model) {
this.model.setValue(content);
Expand Down Expand Up @@ -346,18 +349,17 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann
super(outputChannelDescriptor, modelUri, fileService, modelService, modeService);

this.fileHandler = this._register(new OutputFileListener(this.file, this.fileService));
this._register(this.fileHandler.onDidContentChange(size => this.onDidContentChange(size)));
this._register(this.fileHandler.onDidContentChange(size => this.update(size)));
this._register(toDisposable(() => this.fileHandler.unwatch()));
}

loadModel(): TPromise<ITextModel> {
return this.readContent()
.then(content => this.createModel(content));
}

clear(): void {
this.readContent() // Read content from the file before clearing
.then(() => super.clear());
return this.fileService.resolveContent(this.file, { position: this.startOffset, encoding: 'utf8' })
.then(content => {
this.endOffset = this.startOffset + Buffer.from(content.value).byteLength;
this.etag = content.etag;
return this.createModel(content.value);
});
}

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

private readContent(): TPromise<string> {
return this.fileService.resolveContent(this.file, { position: this.startOffset, encoding: 'utf8' })
.then(content => {
this.endOffset = this.startOffset + Buffer.from(content.value).byteLength;
this.etag = content.etag;
return content.value;
});
}

private onDidContentChange(size: number): void {
update(size?: number): void {
if (!this.updateInProgress) {
this.updateInProgress = true;
if (this.endOffset > size) { // Reset - Content is removed
if (isNumber(size) && this.endOffset > size) { // Reset - Content is removed
this.startOffset = this.endOffset = 0;
this.model.setValue('');
}
Expand Down Expand Up @@ -712,6 +705,8 @@ class BufferredOutputChannel extends Disposable implements OutputChannel {
}
}

update(): void { }

clear(): void {
if (this.modelUpdater.isScheduled()) {
this.modelUpdater.cancel();
Expand Down