Skip to content

Commit a4b4916

Browse files
committed
1 parent 4bfec9c commit a4b4916

10 files changed

Lines changed: 917 additions & 819 deletions

File tree

src/tsconfig.strictNullChecks.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"./vs/workbench/services/files/node/watcher/**/*",
2828
"./vs/workbench/services/themes/**/*.ts",
2929
"./vs/workbench/services/bulkEdit/**/*.ts",
30+
"./vs/workbench/services/output/**/*.ts",
3031
"./vs/workbench/services/progress/**/*.ts",
3132
"./vs/workbench/services/preferences/**/*.ts",
3233
"./vs/workbench/services/timer/**/*.ts",
@@ -313,9 +314,8 @@
313314
"./vs/workbench/contrib/output/common/output.ts",
314315
"./vs/workbench/contrib/output/common/outputLinkComputer.ts",
315316
"./vs/workbench/contrib/output/common/outputLinkProvider.ts",
316-
"./vs/workbench/contrib/output/electron-browser/output.contribution.ts",
317-
"./vs/workbench/contrib/output/electron-browser/outputServices.ts",
318-
"./vs/workbench/contrib/output/node/outputAppender.ts",
317+
"./vs/workbench/contrib/output/browser/output.contribution.ts",
318+
"./vs/workbench/contrib/output/browser/outputServices.ts",
319319
"./vs/workbench/contrib/preferences/browser/preferencesActions.ts",
320320
"./vs/workbench/contrib/preferences/browser/preferencesWidgets.ts",
321321
"./vs/workbench/contrib/preferences/browser/settingsLayout.ts",

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { MainContext, MainThreadOutputServiceShape, IMainContext, ExtHostOutputS
77
import * as vscode from 'vscode';
88
import { URI } from 'vs/base/common/uri';
99
import { join } from 'vs/base/common/path';
10-
import { OutputAppender } from 'vs/workbench/contrib/output/node/outputAppender';
10+
import { OutputAppender } from 'vs/workbench/services/output/node/outputAppender';
1111
import { toLocalISOString } from 'vs/base/common/date';
1212
import { Event, Emitter } from 'vs/base/common/event';
1313
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';

src/vs/workbench/contrib/output/electron-browser/output.contribution.ts renamed to src/vs/workbench/contrib/output/browser/output.contribution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
1010
import { MenuId, MenuRegistry, SyncActionDescriptor, registerAction } from 'vs/platform/actions/common/actions';
1111
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
1212
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
13-
import { OutputService, LogContentProvider } from 'vs/workbench/contrib/output/electron-browser/outputServices';
13+
import { OutputService, LogContentProvider } from 'vs/workbench/contrib/output/browser/outputServices';
1414
import { ToggleOutputAction, ClearOutputAction, OpenLogOutputFile, ShowLogsOutputChannelAction, OpenOutputLogFileAction } from 'vs/workbench/contrib/output/browser/outputActions';
1515
import { OUTPUT_MODE_ID, OUTPUT_MIME, OUTPUT_PANEL_ID, IOutputService, CONTEXT_IN_OUTPUT, LOG_SCHEME, LOG_MODE_ID, LOG_MIME, CONTEXT_ACTIVE_LOG_OUTPUT } from 'vs/workbench/contrib/output/common/output';
1616
import { PanelRegistry, Extensions, PanelDescriptor } from 'vs/workbench/browser/panel';
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (c) Microsoft Corporation. All rights reserved.
3+
* Licensed under the MIT License. See License.txt in the project root for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as nls from 'vs/nls';
7+
import { Event, Emitter } from 'vs/base/common/event';
8+
import { URI } from 'vs/base/common/uri';
9+
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
10+
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
11+
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
12+
import { Registry } from 'vs/platform/registry/common/platform';
13+
import { EditorOptions } from 'vs/workbench/common/editor';
14+
import { IOutputChannelDescriptor, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, OUTPUT_SCHEME, LOG_SCHEME, CONTEXT_ACTIVE_LOG_OUTPUT, LOG_MIME, OUTPUT_MIME } from 'vs/workbench/contrib/output/common/output';
15+
import { OutputPanel } from 'vs/workbench/contrib/output/browser/outputPanel';
16+
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
17+
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
18+
import { OutputLinkProvider } from 'vs/workbench/contrib/output/common/outputLinkProvider';
19+
import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService';
20+
import { ITextModel } from 'vs/editor/common/model';
21+
import { IPanel } from 'vs/workbench/common/panel';
22+
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
23+
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
24+
import { IWindowService } from 'vs/platform/windows/common/windows';
25+
import { ILogService } from 'vs/platform/log/common/log';
26+
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
27+
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
28+
import { CancellationToken } from 'vs/base/common/cancellation';
29+
import { IOutputChannelModel, IOutputChannelModelService } from 'vs/workbench/services/output/common/outputChannelModel';
30+
31+
const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel';
32+
33+
class OutputChannel extends Disposable implements IOutputChannel {
34+
35+
scrollLock: boolean = false;
36+
readonly model: IOutputChannelModel;
37+
readonly id: string;
38+
readonly label: string;
39+
40+
constructor(
41+
readonly outputChannelDescriptor: IOutputChannelDescriptor,
42+
@IOutputChannelModelService outputChannelModelService: IOutputChannelModelService
43+
) {
44+
super();
45+
this.id = outputChannelDescriptor.id;
46+
this.label = outputChannelDescriptor.label;
47+
this.model = this._register(outputChannelModelService.createOutputChannelModel(this.id, URI.from({ scheme: OUTPUT_SCHEME, path: this.id }), outputChannelDescriptor.log ? LOG_MIME : OUTPUT_MIME, outputChannelDescriptor.file));
48+
}
49+
50+
append(output: string): void {
51+
this.model.append(output);
52+
}
53+
54+
update(): void {
55+
this.model.update();
56+
}
57+
58+
clear(till?: number): void {
59+
this.model.clear(till);
60+
}
61+
}
62+
63+
export class OutputService extends Disposable implements IOutputService, ITextModelContentProvider {
64+
65+
public _serviceBrand: any;
66+
67+
private channels: Map<string, OutputChannel> = new Map<string, OutputChannel>();
68+
private activeChannelIdInStorage: string;
69+
private activeChannel: OutputChannel | null;
70+
71+
private readonly _onActiveOutputChannel = new Emitter<string>();
72+
readonly onActiveOutputChannel: Event<string> = this._onActiveOutputChannel.event;
73+
74+
private _outputPanel: OutputPanel;
75+
76+
constructor(
77+
@IStorageService private readonly storageService: IStorageService,
78+
@IInstantiationService private readonly instantiationService: IInstantiationService,
79+
@IPanelService private readonly panelService: IPanelService,
80+
@IWorkspaceContextService contextService: IWorkspaceContextService,
81+
@ITextModelService textModelResolverService: ITextModelService,
82+
@IEnvironmentService environmentService: IEnvironmentService,
83+
@IWindowService windowService: IWindowService,
84+
@ILogService private readonly logService: ILogService,
85+
@ILifecycleService private readonly lifecycleService: ILifecycleService,
86+
@IContextKeyService private readonly contextKeyService: IContextKeyService,
87+
) {
88+
super();
89+
this.activeChannelIdInStorage = this.storageService.get(OUTPUT_ACTIVE_CHANNEL_KEY, StorageScope.WORKSPACE, '');
90+
91+
// Register as text model content provider for output
92+
textModelResolverService.registerTextModelContentProvider(OUTPUT_SCHEME, this);
93+
instantiationService.createInstance(OutputLinkProvider);
94+
95+
// Create output channels for already registered channels
96+
const registry = Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels);
97+
for (const channelIdentifier of registry.getChannels()) {
98+
this.onDidRegisterChannel(channelIdentifier.id);
99+
}
100+
this._register(registry.onDidRegisterChannel(this.onDidRegisterChannel, this));
101+
102+
this._register(panelService.onDidPanelOpen(({ panel, focus }) => this.onDidPanelOpen(panel, !focus), this));
103+
this._register(panelService.onDidPanelClose(this.onDidPanelClose, this));
104+
105+
// Set active channel to first channel if not set
106+
if (!this.activeChannel) {
107+
const channels = this.getChannelDescriptors();
108+
this.activeChannel = channels && channels.length > 0 ? this.getChannel(channels[0].id) : null;
109+
}
110+
111+
this._register(this.lifecycleService.onShutdown(() => this.dispose()));
112+
this._register(this.storageService.onWillSaveState(() => this.saveState()));
113+
}
114+
115+
provideTextContent(resource: URI): Promise<ITextModel> | null {
116+
const channel = <OutputChannel>this.getChannel(resource.path);
117+
if (channel) {
118+
return channel.model.loadModel();
119+
}
120+
return null;
121+
}
122+
123+
showChannel(id: string, preserveFocus?: boolean): Promise<void> {
124+
const channel = this.getChannel(id);
125+
if (!channel || this.isChannelShown(channel)) {
126+
if (this._outputPanel && !preserveFocus) {
127+
this._outputPanel.focus();
128+
}
129+
return Promise.resolve(undefined);
130+
}
131+
132+
this.activeChannel = channel;
133+
let promise: Promise<void>;
134+
if (this.isPanelShown()) {
135+
promise = this.doShowChannel(channel, !!preserveFocus);
136+
} else {
137+
this.panelService.openPanel(OUTPUT_PANEL_ID);
138+
promise = this.doShowChannel(this.activeChannel, !!preserveFocus);
139+
}
140+
return promise.then(() => this._onActiveOutputChannel.fire(id));
141+
}
142+
143+
getChannel(id: string): OutputChannel | null {
144+
return this.channels.get(id) || null;
145+
}
146+
147+
getChannelDescriptors(): IOutputChannelDescriptor[] {
148+
return Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).getChannels();
149+
}
150+
151+
getActiveChannel(): IOutputChannel | null {
152+
return this.activeChannel;
153+
}
154+
155+
private onDidRegisterChannel(channelId: string): void {
156+
const channel = this.createChannel(channelId);
157+
this.channels.set(channelId, channel);
158+
if (this.activeChannelIdInStorage === channelId) {
159+
this.activeChannel = channel;
160+
this.onDidPanelOpen(this.panelService.getActivePanel(), true)
161+
.then(() => this._onActiveOutputChannel.fire(channelId));
162+
}
163+
}
164+
165+
private onDidPanelOpen(panel: IPanel | null, preserveFocus: boolean): Promise<void> {
166+
if (panel && panel.getId() === OUTPUT_PANEL_ID) {
167+
this._outputPanel = <OutputPanel>this.panelService.getActivePanel();
168+
if (this.activeChannel) {
169+
return this.doShowChannel(this.activeChannel, preserveFocus);
170+
}
171+
}
172+
return Promise.resolve(undefined);
173+
}
174+
175+
private onDidPanelClose(panel: IPanel): void {
176+
if (this._outputPanel && panel.getId() === OUTPUT_PANEL_ID) {
177+
CONTEXT_ACTIVE_LOG_OUTPUT.bindTo(this.contextKeyService).set(false);
178+
this._outputPanel.clearInput();
179+
}
180+
}
181+
182+
private createChannel(id: string): OutputChannel {
183+
const channelDisposables: IDisposable[] = [];
184+
const channel = this.instantiateChannel(id);
185+
channel.model.onDidAppendedContent(() => {
186+
if (!channel.scrollLock) {
187+
const panel = this.panelService.getActivePanel();
188+
if (panel && panel.getId() === OUTPUT_PANEL_ID && this.isChannelShown(channel)) {
189+
let outputPanel = <OutputPanel>panel;
190+
outputPanel.revealLastLine();
191+
}
192+
}
193+
}, channelDisposables);
194+
channel.model.onDispose(() => {
195+
if (this.activeChannel === channel) {
196+
const channels = this.getChannelDescriptors();
197+
const channel = channels.length ? this.getChannel(channels[0].id) : null;
198+
if (channel && this.isPanelShown()) {
199+
this.showChannel(channel.id, true);
200+
} else {
201+
this.activeChannel = channel;
202+
if (this.activeChannel) {
203+
this._onActiveOutputChannel.fire(this.activeChannel.id);
204+
}
205+
}
206+
}
207+
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).removeChannel(id);
208+
dispose(channelDisposables);
209+
}, channelDisposables);
210+
211+
return channel;
212+
}
213+
214+
private instantiateChannel(id: string): OutputChannel {
215+
const channelData = Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).getChannel(id);
216+
if (!channelData) {
217+
this.logService.error(`Channel '${id}' is not registered yet`);
218+
throw new Error(`Channel '${id}' is not registered yet`);
219+
}
220+
return this.instantiationService.createInstance(OutputChannel, channelData);
221+
}
222+
223+
private doShowChannel(channel: OutputChannel, preserveFocus: boolean): Promise<void> {
224+
if (this._outputPanel) {
225+
CONTEXT_ACTIVE_LOG_OUTPUT.bindTo(this.contextKeyService).set(!!channel.outputChannelDescriptor.file && channel.outputChannelDescriptor.log);
226+
return this._outputPanel.setInput(this.createInput(channel), EditorOptions.create({ preserveFocus }), CancellationToken.None)
227+
.then(() => {
228+
if (!preserveFocus) {
229+
this._outputPanel.focus();
230+
}
231+
});
232+
}
233+
return Promise.resolve(undefined);
234+
}
235+
236+
private isChannelShown(channel: IOutputChannel): boolean {
237+
return this.isPanelShown() && this.activeChannel === channel;
238+
}
239+
240+
private isPanelShown(): boolean {
241+
const panel = this.panelService.getActivePanel();
242+
return !!panel && panel.getId() === OUTPUT_PANEL_ID;
243+
}
244+
245+
private createInput(channel: IOutputChannel): ResourceEditorInput {
246+
const resource = URI.from({ scheme: OUTPUT_SCHEME, path: channel.id });
247+
return this.instantiationService.createInstance(ResourceEditorInput, nls.localize('output', "{0} - Output", channel.label), nls.localize('channel', "Output channel for '{0}'", channel.label), resource);
248+
}
249+
250+
private saveState(): void {
251+
if (this.activeChannel) {
252+
this.storageService.store(OUTPUT_ACTIVE_CHANNEL_KEY, this.activeChannel.id, StorageScope.WORKSPACE);
253+
}
254+
}
255+
}
256+
257+
export class LogContentProvider {
258+
259+
private channelModels: Map<string, IOutputChannelModel> = new Map<string, IOutputChannelModel>();
260+
261+
constructor(
262+
@IOutputService private readonly outputService: IOutputService,
263+
@IOutputChannelModelService private readonly outputChannelModelService: IOutputChannelModelService
264+
) {
265+
}
266+
267+
provideTextContent(resource: URI): Promise<ITextModel> | null {
268+
if (resource.scheme === LOG_SCHEME) {
269+
let channelModel = this.getChannelModel(resource);
270+
if (channelModel) {
271+
return channelModel.loadModel();
272+
}
273+
}
274+
return null;
275+
}
276+
277+
private getChannelModel(resource: URI): IOutputChannelModel | undefined {
278+
const channelId = resource.path;
279+
let channelModel = this.channelModels.get(channelId);
280+
if (!channelModel) {
281+
const channelDisposables: IDisposable[] = [];
282+
const outputChannelDescriptor = this.outputService.getChannelDescriptors().filter(({ id }) => id === channelId)[0];
283+
if (outputChannelDescriptor && outputChannelDescriptor.file) {
284+
channelModel = this.outputChannelModelService.createOutputChannelModel(channelId, resource, outputChannelDescriptor.log ? LOG_MIME : OUTPUT_MIME, outputChannelDescriptor.file);
285+
channelModel.onDispose(() => dispose(channelDisposables), channelDisposables);
286+
this.channelModels.set(channelId, channelModel);
287+
}
288+
}
289+
return channelModel;
290+
}
291+
}

0 commit comments

Comments
 (0)