|
6 | 6 | import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; |
7 | 7 | import * as extfs from 'vs/base/node/extfs'; |
8 | 8 | import { dirname, join } from 'vs/base/common/path'; |
| 9 | +import * as resources from 'vs/base/common/resources'; |
9 | 10 | import { ITextModel } from 'vs/editor/common/model'; |
10 | 11 | import { URI } from 'vs/base/common/uri'; |
11 | 12 | import { ThrottledDelayer } from 'vs/base/common/async'; |
12 | 13 | import { IFileService } from 'vs/platform/files/common/files'; |
13 | 14 | import { IModelService } from 'vs/editor/common/services/modelService'; |
14 | 15 | import { IModeService } from 'vs/editor/common/services/modeService'; |
15 | | -import { toDisposable, IDisposable } from 'vs/base/common/lifecycle'; |
| 16 | +import { toDisposable, IDisposable, Disposable } from 'vs/base/common/lifecycle'; |
16 | 17 | import { ILogService } from 'vs/platform/log/common/log'; |
17 | | -import { IOutputChannelModel, AbstractFileOutputChannelModel, IOutputChannelModelService, AsbtractOutputChannelModelService } from 'vs/workbench/services/output/common/outputChannelModel'; |
| 18 | +import { IOutputChannelModel, AbstractFileOutputChannelModel, IOutputChannelModelService, AsbtractOutputChannelModelService, BufferredOutputChannel } from 'vs/workbench/services/output/common/outputChannelModel'; |
18 | 19 | import { OutputAppender } from 'vs/workbench/services/output/node/outputAppender'; |
19 | 20 | import { IEnvironmentService } from 'vs/platform/environment/common/environment'; |
20 | 21 | import { IWindowService } from 'vs/platform/windows/common/windows'; |
21 | 22 | import { toLocalISOString } from 'vs/base/common/date'; |
22 | 23 | import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; |
23 | 24 | import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; |
| 25 | +import { Emitter, Event } from 'vs/base/common/event'; |
24 | 26 |
|
25 | 27 | let watchingOutputDir = false; |
26 | 28 | let callbacks: ((eventType: string, fileName?: string) => void)[] = []; |
@@ -55,15 +57,13 @@ class OutputChannelBackedByFile extends AbstractFileOutputChannelModel implement |
55 | 57 | id: string, |
56 | 58 | modelUri: URI, |
57 | 59 | mimeType: string, |
58 | | - @IWindowService windowService: IWindowService, |
59 | | - @IEnvironmentService environmentService: IEnvironmentService, |
| 60 | + file: URI, |
60 | 61 | @IFileService fileService: IFileService, |
61 | 62 | @IModelService modelService: IModelService, |
62 | 63 | @IModeService modeService: IModeService, |
63 | 64 | @ILogService logService: ILogService |
64 | 65 | ) { |
65 | | - const outputDir = join(environmentService.logsPath, `output_${windowService.getCurrentWindowId()}_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`); |
66 | | - super(modelUri, mimeType, URI.file(join(outputDir, `${id}.log`)), fileService, modelService, modeService); |
| 66 | + super(modelUri, mimeType, file, fileService, modelService, modeService); |
67 | 67 | this.appendedMessage = ''; |
68 | 68 | this.loadingFromFileInProgress = false; |
69 | 69 |
|
@@ -159,32 +159,93 @@ class OutputChannelBackedByFile extends AbstractFileOutputChannelModel implement |
159 | 159 | } |
160 | 160 | } |
161 | 161 |
|
| 162 | +class DelegatedOutputChannelModel extends Disposable implements IOutputChannelModel { |
| 163 | + |
| 164 | + private readonly _onDidAppendedContent: Emitter<void> = this._register(new Emitter<void>()); |
| 165 | + readonly onDidAppendedContent: Event<void> = this._onDidAppendedContent.event; |
| 166 | + |
| 167 | + private readonly _onDispose: Emitter<void> = this._register(new Emitter<void>()); |
| 168 | + readonly onDispose: Event<void> = this._onDispose.event; |
| 169 | + |
| 170 | + private readonly outputChannelModel: Promise<IOutputChannelModel>; |
| 171 | + |
| 172 | + constructor( |
| 173 | + id: string, |
| 174 | + modelUri: URI, |
| 175 | + mimeType: string, |
| 176 | + outputDir: Promise<URI>, |
| 177 | + @IInstantiationService private readonly instantiationService: IInstantiationService, |
| 178 | + @ILogService private readonly logService: ILogService, |
| 179 | + @ITelemetryService private readonly telemetryService: ITelemetryService, |
| 180 | + ) { |
| 181 | + super(); |
| 182 | + this.outputChannelModel = this.createOutputChannelModel(id, modelUri, mimeType, outputDir); |
| 183 | + } |
| 184 | + |
| 185 | + private async createOutputChannelModel(id: string, modelUri: URI, mimeType: string, outputDirPromise: Promise<URI>): Promise<IOutputChannelModel> { |
| 186 | + let outputChannelModel: IOutputChannelModel; |
| 187 | + try { |
| 188 | + const outputDir = await outputDirPromise; |
| 189 | + const file = resources.joinPath(outputDir, `${id}.log`); |
| 190 | + outputChannelModel = this.instantiationService.createInstance(OutputChannelBackedByFile, id, modelUri, mimeType, file); |
| 191 | + } catch (e) { |
| 192 | + // Do not crash if spdlog rotating logger cannot be loaded (workaround for https://github.com/Microsoft/vscode/issues/47883) |
| 193 | + this.logService.error(e); |
| 194 | + /* __GDPR__ |
| 195 | + "output.channel.creation.error" : {} |
| 196 | + */ |
| 197 | + this.telemetryService.publicLog('output.channel.creation.error'); |
| 198 | + outputChannelModel = this.instantiationService.createInstance(BufferredOutputChannel, modelUri, mimeType); |
| 199 | + } |
| 200 | + this._register(outputChannelModel); |
| 201 | + outputChannelModel.onDidAppendedContent(() => this._onDidAppendedContent.fire()); |
| 202 | + outputChannelModel.onDispose(() => this._onDispose.fire()); |
| 203 | + return outputChannelModel; |
| 204 | + } |
| 205 | + |
| 206 | + append(output: string): void { |
| 207 | + this.outputChannelModel.then(outputChannelModel => outputChannelModel.append(output)); |
| 208 | + } |
| 209 | + |
| 210 | + update(): void { |
| 211 | + this.outputChannelModel.then(outputChannelModel => outputChannelModel.update()); |
| 212 | + } |
| 213 | + |
| 214 | + loadModel(): Promise<ITextModel> { |
| 215 | + return this.outputChannelModel.then(outputChannelModel => outputChannelModel.loadModel()); |
| 216 | + } |
| 217 | + |
| 218 | + clear(till?: number): void { |
| 219 | + this.outputChannelModel.then(outputChannelModel => outputChannelModel.clear(till)); |
| 220 | + } |
| 221 | + |
| 222 | +} |
| 223 | + |
162 | 224 | export class OutputChannelModelService extends AsbtractOutputChannelModelService implements IOutputChannelModelService { |
163 | 225 |
|
164 | 226 | _serviceBrand: any; |
165 | 227 |
|
166 | 228 | constructor( |
167 | 229 | @IInstantiationService instantiationService: IInstantiationService, |
168 | | - @ILogService private readonly logService: ILogService, |
169 | | - @ITelemetryService private readonly telemetryService: ITelemetryService |
| 230 | + @IEnvironmentService private readonly environmentService: IEnvironmentService, |
| 231 | + @IWindowService private readonly windowService: IWindowService, |
| 232 | + @IFileService private readonly fileService: IFileService |
170 | 233 | ) { |
171 | 234 | super(instantiationService); |
172 | 235 | } |
173 | 236 |
|
174 | 237 | createOutputChannelModel(id: string, modelUri: URI, mimeType: string, file?: URI): IOutputChannelModel { |
175 | | - if (!file) { |
176 | | - try { |
177 | | - return this.instantiationService.createInstance(OutputChannelBackedByFile, id, modelUri, mimeType); |
178 | | - } catch (e) { |
179 | | - // Do not crash if spdlog rotating logger cannot be loaded (workaround for https://github.com/Microsoft/vscode/issues/47883) |
180 | | - this.logService.error(e); |
181 | | - /* __GDPR__ |
182 | | - "output.channel.creation.error" : {} |
183 | | - */ |
184 | | - this.telemetryService.publicLog('output.channel.creation.error'); |
185 | | - } |
| 238 | + return file ? super.createOutputChannelModel(id, modelUri, mimeType, file) : |
| 239 | + this.instantiationService.createInstance(DelegatedOutputChannelModel, id, modelUri, mimeType, this.outputDir); |
| 240 | + } |
| 241 | + |
| 242 | + private _outputDir: Promise<URI> | null; |
| 243 | + private get outputDir(): Promise<URI> { |
| 244 | + if (!this._outputDir) { |
| 245 | + const outputDir = URI.file(join(this.environmentService.logsPath, `output_${this.windowService.getCurrentWindowId()}_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`)); |
| 246 | + this._outputDir = this.fileService.createFolder(outputDir).then(() => outputDir); |
186 | 247 | } |
187 | | - return super.createOutputChannelModel(id, modelUri, mimeType, file); |
| 248 | + return this._outputDir; |
188 | 249 | } |
189 | 250 |
|
190 | 251 | } |
|
0 commit comments