提交 6438f683 编写于 作者: I isidor

output: better protect agains spammy output

fixes #3896
fixes #3897
fixes #3609
上级 e156e2bd
......@@ -36,6 +36,8 @@ export const Extensions = {
export const OUTPUT_SERVICE_ID = 'outputService';
export const MAX_OUTPUT_LENGTH = 10000 /* Max. number of output lines to show in output */ * 100 /* Guestimated chars per line */;
/**
* The output event informs when new output got received.
*/
......
......@@ -5,13 +5,16 @@
'use strict';
import nls = require('vs/nls');
import lifecycle = require('vs/base/common/lifecycle');
import {TPromise} from 'vs/base/common/winjs.base';
import {RunOnceScheduler} from 'vs/base/common/async';
import {EditorModel} from 'vs/workbench/common/editor';
import {StringEditorInput} from 'vs/workbench/common/editor/stringEditorInput';
import {OUTPUT_EDITOR_INPUT_ID, OUTPUT_PANEL_ID, IOutputEvent, OUTPUT_MIME, IOutputService} from 'vs/workbench/parts/output/common/output';
import {OUTPUT_EDITOR_INPUT_ID, OUTPUT_PANEL_ID, IOutputEvent, OUTPUT_MIME, IOutputService, MAX_OUTPUT_LENGTH} from 'vs/workbench/parts/output/common/output';
import {OutputPanel} from 'vs/workbench/parts/output/browser/outputPanel';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {IEventService} from 'vs/platform/event/common/event';
import {EventType, CompositeEvent} from 'vs/workbench/common/events';
import {IPanelService} from 'vs/workbench/services/panel/common/panelService';
/**
......@@ -21,12 +24,11 @@ export class OutputEditorInput extends StringEditorInput {
private static OUTPUT_DELAY = 300; // delay in ms to accumulate output before emitting an event about it
private static instances: { [channel: string]: OutputEditorInput; } = Object.create(null);
private static MAX_OUTPUT_LINES = 10000; // Max. number of output lines to show in output
private outputSet: boolean;
private channel: string;
private toUnbind: { (): void; }[];
private bufferedOutput: string;
private toDispose: lifecycle.IDisposable[];
private appendOutputScheduler: RunOnceScheduler;
public static getInstances(): OutputEditorInput[] {
......@@ -47,23 +49,32 @@ export class OutputEditorInput extends StringEditorInput {
channel: string,
@IInstantiationService instantiationService: IInstantiationService,
@IOutputService private outputService: IOutputService,
@IPanelService private panelService: IPanelService
@IPanelService private panelService: IPanelService,
@IEventService private eventService: IEventService
) {
super(nls.localize('output', "Output"), channel ? nls.localize('outputChannel', "for '{0}'", channel) : '', '', OUTPUT_MIME, true, instantiationService);
this.channel = channel;
this.bufferedOutput = '';
this.toUnbind = [];
const listenerUnbind = this.outputService.onOutput(this.onOutputReceived, this);
this.toUnbind.push(() => listenerUnbind.dispose());
this.toDispose = [];
this.toDispose.push(this.outputService.onOutput(this.onOutputReceived, this));
this.toDispose.push(this.outputService.onActiveOutputChannel(() => this.scheduleOutputAppend()));
this.toDispose.push(this.eventService.addListener2(EventType.COMPOSITE_OPENED, (e: CompositeEvent) => {
if (e.compositeId === OUTPUT_PANEL_ID) {
this.scheduleOutputAppend();
}
}));
this.appendOutputScheduler = new RunOnceScheduler(() => {
this.append(this.bufferedOutput);
this.trim(OutputEditorInput.MAX_OUTPUT_LINES);
if (this.value.length + this.bufferedOutput.length > MAX_OUTPUT_LENGTH) {
this.setValue(this.outputService.getOutput(this.channel));
} else {
this.append(this.bufferedOutput);
}
this.bufferedOutput = '';
const panel = this.panelService.getActivePanel();
if (panel && panel.getId() === OUTPUT_PANEL_ID && this.outputService.getActiveChannel() === this.channel) {
if (this.isVisible()) {
const panel = this.panelService.getActivePanel();
(<OutputPanel>panel).revealLastLine();
}
}, OutputEditorInput.OUTPUT_DELAY);
......@@ -73,22 +84,30 @@ export class OutputEditorInput extends StringEditorInput {
if (this.outputSet && e.channel === this.channel) {
if (e.output) {
this.bufferedOutput += e.output;
if (!this.appendOutputScheduler.isScheduled()) {
this.appendOutputScheduler.schedule();
}
this.scheduleOutputAppend();
} else if (e.output === null) {
this.clearValue(); // special output indicates we should clear
}
}
}
private isVisible(): boolean {
const panel = this.panelService.getActivePanel();
return panel && panel.getId() === OUTPUT_PANEL_ID && this.outputService.getActiveChannel() === this.channel;
}
private scheduleOutputAppend(): void {
if (this.isVisible() && this.bufferedOutput && !this.appendOutputScheduler.isScheduled()) {
this.appendOutputScheduler.schedule();
}
}
public getId(): string {
return OUTPUT_EDITOR_INPUT_ID;
}
public resolve(refresh?: boolean): TPromise<EditorModel> {
return super.resolve(refresh).then(model => {
// Just return model if output already set
if (this.outputSet) {
return model;
......@@ -117,10 +136,9 @@ export class OutputEditorInput extends StringEditorInput {
}
public dispose(): void {
while (this.toUnbind.length) {
this.toUnbind.pop()();
}
this.appendOutputScheduler.dispose();
this.toDispose = lifecycle.disposeAll(this.toDispose);
super.dispose();
}
}
\ No newline at end of file
}
......@@ -13,7 +13,7 @@ import {IInstantiationService} from 'vs/platform/instantiation/common/instantiat
import {IStorageService, StorageScope} from 'vs/platform/storage/common/storage';
import {Registry} from 'vs/platform/platform';
import {EditorOptions} from 'vs/workbench/common/editor';
import {IOutputEvent, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry} from 'vs/workbench/parts/output/common/output';
import {IOutputEvent, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, MAX_OUTPUT_LENGTH} from 'vs/workbench/parts/output/common/output';
import {OutputEditorInput} from 'vs/workbench/parts/output/common/outputEditorInput';
import {OutputPanel} from 'vs/workbench/parts/output/browser/outputPanel';
import {IPanelService} from 'vs/workbench/services/panel/common/panelService';
......@@ -23,7 +23,6 @@ const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel';
export class OutputService implements IOutputService {
public serviceId = IOutputService;
private static MAX_OUTPUT = 10000 /* Lines */ * 100 /* Guestimated chars per line */;
private receivedOutput: { [channel: string]: string; };
private activeChannel: string;
......@@ -79,18 +78,18 @@ export class OutputService implements IOutputService {
let addLength = output.length;
// Still below MAX_OUTPUT, so just add
if (addLength + curLength <= OutputService.MAX_OUTPUT) {
if (addLength + curLength <= MAX_OUTPUT_LENGTH) {
this.receivedOutput[channel] += output;
} else {
// New output exceeds MAX_OUTPUT, so trim beginning and use as received output
if (addLength > OutputService.MAX_OUTPUT) {
this.receivedOutput[channel] = '...' + output.substr(addLength - OutputService.MAX_OUTPUT);
if (addLength > MAX_OUTPUT_LENGTH) {
this.receivedOutput[channel] = '...' + output.substr(addLength - MAX_OUTPUT_LENGTH);
}
// New output + existing output exceeds MAX_OUTPUT, so trim existing output that it fits new output
else {
let diff = OutputService.MAX_OUTPUT - addLength;
let diff = MAX_OUTPUT_LENGTH - addLength;
this.receivedOutput[channel] = '...' + this.receivedOutput[channel].substr(curLength - diff) + output;
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册