diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 7124a18d986..ba2cc271a3e 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -653,7 +653,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I if (languageId) { checkProposedApiEnabled(extension, 'outputChannelLanguage'); } - return extHostOutputService.createOutputChannel(name, languageId || '', extension); + return extHostOutputService.createOutputChannel(name, languageId, extension); }, createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn; preserveFocus?: boolean }, options?: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel { return extHostWebviewPanels.createWebviewPanel(extension, viewType, title, showOptions, options); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 7b3d8080281..352e2b8c95a 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -410,7 +410,7 @@ export interface MainThreadMessageServiceShape extends IDisposable { } export interface MainThreadOutputServiceShape extends IDisposable { - $register(label: string, log: boolean, file: UriComponents, languageId: string, extensionId: string): Promise; + $register(label: string, log: boolean, file: UriComponents, languageId: string | undefined, extensionId: string): Promise; $update(channelId: string, mode: OutputChannelUpdateMode, till?: number): Promise; $reveal(channelId: string, preserveFocus: boolean): Promise; $close(channelId: string): Promise; diff --git a/src/vs/workbench/api/common/extHostOutput.ts b/src/vs/workbench/api/common/extHostOutput.ts index 26df54d4607..12e6570161e 100644 --- a/src/vs/workbench/api/common/extHostOutput.ts +++ b/src/vs/workbench/api/common/extHostOutput.ts @@ -17,6 +17,7 @@ import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitData import { IExtHostFileSystemInfo } from 'vs/workbench/api/common/extHostFileSystemInfo'; import { toLocalISOString } from 'vs/base/common/date'; import { VSBuffer } from 'vs/base/common/buffer'; +import { isString } from 'vs/base/common/types'; export class ExtHostOutputChannel extends Disposable implements vscode.OutputChannel { @@ -117,11 +118,14 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { } } - createOutputChannel(name: string, languageId: string, extension: IExtensionDescription): vscode.OutputChannel { + createOutputChannel(name: string, languageId: string | undefined, extension: IExtensionDescription): vscode.OutputChannel { name = name.trim(); if (!name) { throw new Error('illegal argument `name`. must not be falsy'); } + if (isString(languageId) && !languageId.trim()) { + throw new Error('illegal argument `languageId`. must not be empty'); + } const extHostOutputChannel = this.doCreateOutputChannel(name, languageId, extension); extHostOutputChannel.then(channel => { this.channels.set(channel.id, channel); @@ -130,7 +134,7 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape { return this.createExtHostOutputChannel(name, extHostOutputChannel); } - private async doCreateOutputChannel(name: string, languageId: string, extension: IExtensionDescription): Promise { + private async doCreateOutputChannel(name: string, languageId: string | undefined, extension: IExtensionDescription): Promise { const outputDir = await this.createOutputDirectory(); const file = this.extHostFileSystemInfo.extUri.joinPath(outputDir, `${this.namePool++}-${name.replace(/[\\/:\*\?"<>\|]/g, '')}.log`); const logger = this.loggerService.createLogger(file, { always: true, donotRotate: true, donotUseFormatters: true }); diff --git a/src/vs/workbench/contrib/output/browser/outputServices.ts b/src/vs/workbench/contrib/output/browser/outputServices.ts index afd393c9a34..2002fd64025 100644 --- a/src/vs/workbench/contrib/output/browser/outputServices.ts +++ b/src/vs/workbench/contrib/output/browser/outputServices.ts @@ -20,6 +20,7 @@ import { IOutputChannelModel } from 'vs/workbench/contrib/output/common/outputCh import { IViewsService } from 'vs/workbench/common/views'; import { OutputViewPane } from 'vs/workbench/contrib/output/browser/outputView'; import { IOutputChannelModelService } from 'vs/workbench/contrib/output/common/outputChannelModelService'; +import { ILanguageService } from 'vs/editor/common/languages/language'; const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel'; @@ -33,13 +34,14 @@ class OutputChannel extends Disposable implements IOutputChannel { constructor( readonly outputChannelDescriptor: IOutputChannelDescriptor, - @IOutputChannelModelService outputChannelModelService: IOutputChannelModelService + @IOutputChannelModelService outputChannelModelService: IOutputChannelModelService, + @ILanguageService languageService: ILanguageService, ) { super(); this.id = outputChannelDescriptor.id; this.label = outputChannelDescriptor.label; this.uri = URI.from({ scheme: OUTPUT_SCHEME, path: this.id }); - this.model = this._register(outputChannelModelService.createOutputChannelModel(this.id, this.uri, outputChannelDescriptor.log ? LOG_MIME : OUTPUT_MIME, outputChannelDescriptor.file, outputChannelDescriptor.languageId)); + this.model = this._register(outputChannelModelService.createOutputChannelModel(this.id, this.uri, outputChannelDescriptor.languageId ? languageService.createById(outputChannelDescriptor.languageId) : languageService.createByMimeType(outputChannelDescriptor.log ? LOG_MIME : OUTPUT_MIME), outputChannelDescriptor.file)); } append(output: string): void { @@ -196,7 +198,8 @@ export class LogContentProvider { constructor( @IOutputService private readonly outputService: IOutputService, - @IOutputChannelModelService private readonly outputChannelModelService: IOutputChannelModelService + @IOutputChannelModelService private readonly outputChannelModelService: IOutputChannelModelService, + @ILanguageService private readonly languageService: ILanguageService ) { } @@ -217,7 +220,7 @@ export class LogContentProvider { const channelDisposables: IDisposable[] = []; const outputChannelDescriptor = this.outputService.getChannelDescriptors().filter(({ id }) => id === channelId)[0]; if (outputChannelDescriptor && outputChannelDescriptor.file) { - channelModel = this.outputChannelModelService.createOutputChannelModel(channelId, resource, outputChannelDescriptor.log ? LOG_MIME : OUTPUT_MIME, outputChannelDescriptor.file, outputChannelDescriptor.languageId); + channelModel = this.outputChannelModelService.createOutputChannelModel(channelId, resource, outputChannelDescriptor.languageId ? this.languageService.createById(outputChannelDescriptor.languageId) : this.languageService.createByMimeType(outputChannelDescriptor.log ? LOG_MIME : OUTPUT_MIME), outputChannelDescriptor.file); channelModel.onDispose(() => dispose(channelDisposables), channelDisposables); this.channelModels.set(channelId, channelModel); } diff --git a/src/vs/workbench/contrib/output/common/outputChannelModel.ts b/src/vs/workbench/contrib/output/common/outputChannelModel.ts index cf4772e2dff..daedaaaff83 100644 --- a/src/vs/workbench/contrib/output/common/outputChannelModel.ts +++ b/src/vs/workbench/contrib/output/common/outputChannelModel.ts @@ -12,7 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { Promises, ThrottledDelayer } from 'vs/base/common/async'; import { IFileService } from 'vs/platform/files/common/files'; import { IModelService } from 'vs/editor/common/services/model'; -import { ILanguageService } from 'vs/editor/common/languages/language'; +import { ILanguageSelection } from 'vs/editor/common/languages/language'; import { Disposable, toDisposable, IDisposable, dispose, MutableDisposable } from 'vs/base/common/lifecycle'; import { isNumber } from 'vs/base/common/types'; import { EditOperation, ISingleEditOperation } from 'vs/editor/common/core/editOperation'; @@ -106,12 +106,10 @@ export class FileOutputChannelModel extends Disposable implements IOutputChannel constructor( private readonly modelUri: URI, - private readonly mimeType: 'text/x-code-log-output' | 'text/x-code-output', + private readonly language: ILanguageSelection, private readonly file: URI, - private readonly languageId: string, @IFileService private readonly fileService: IFileService, @IModelService private readonly modelService: IModelService, - @ILanguageService private readonly languageService: ILanguageService, @ILogService logService: ILogService, @IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService, ) { @@ -164,7 +162,7 @@ export class FileOutputChannelModel extends Disposable implements IOutputChannel if (this.model) { this.model.setValue(content); } else { - this.model = this.modelService.createModel(content, this.languageId ? this.languageService.createById(this.languageId) : this.languageService.createByMimeType(this.mimeType), this.modelUri); + this.model = this.modelService.createModel(content, this.language, this.modelUri); this.fileHandler.watch(this.etag); const disposable = this.model.onWillDispose(() => { this.cancelModelUpdate(); @@ -325,17 +323,15 @@ class OutputChannelBackedByFile extends FileOutputChannelModel implements IOutpu constructor( id: string, modelUri: URI, - mimeType: 'text/x-code-log-output' | 'text/x-code-output', + language: ILanguageSelection, file: URI, - languageId: string, @IFileService fileService: IFileService, @IModelService modelService: IModelService, - @ILanguageService languageService: ILanguageService, @ILoggerService loggerService: ILoggerService, @ILogService logService: ILogService, @IEditorWorkerService editorWorkerService: IEditorWorkerService ) { - super(modelUri, mimeType, file, languageId, fileService, modelService, languageService, logService, editorWorkerService); + super(modelUri, language, file, fileService, modelService, logService, editorWorkerService); // Donot rotate to check for the file reset this.logger = loggerService.createLogger(file, { always: true, donotRotate: true, donotUseFormatters: true }); @@ -373,21 +369,20 @@ export class DelegatedOutputChannelModel extends Disposable implements IOutputCh constructor( id: string, modelUri: URI, - mimeType: 'text/x-code-log-output' | 'text/x-code-output', + language: ILanguageSelection, outputDir: Promise, - languageId: string, @IInstantiationService private readonly instantiationService: IInstantiationService, @IFileService private readonly fileService: IFileService, ) { super(); - this.outputChannelModel = this.createOutputChannelModel(id, modelUri, mimeType, outputDir, languageId); + this.outputChannelModel = this.createOutputChannelModel(id, modelUri, language, outputDir); } - private async createOutputChannelModel(id: string, modelUri: URI, mimeType: 'text/x-code-log-output' | 'text/x-code-output', outputDirPromise: Promise, languageId: string): Promise { + private async createOutputChannelModel(id: string, modelUri: URI, language: ILanguageSelection, outputDirPromise: Promise): Promise { const outputDir = await outputDirPromise; const file = resources.joinPath(outputDir, `${id.replace(/[\\/:\*\?"<>\|]/g, '')}.log`); await this.fileService.createFile(file); - const outputChannelModel = this._register(this.instantiationService.createInstance(OutputChannelBackedByFile, id, modelUri, mimeType, file, languageId)); + const outputChannelModel = this._register(this.instantiationService.createInstance(OutputChannelBackedByFile, id, modelUri, language, file)); this._register(outputChannelModel.onDispose(() => this._onDispose.fire())); return outputChannelModel; } diff --git a/src/vs/workbench/contrib/output/common/outputChannelModelService.ts b/src/vs/workbench/contrib/output/common/outputChannelModelService.ts index c52f15160d6..114b99fb652 100644 --- a/src/vs/workbench/contrib/output/common/outputChannelModelService.ts +++ b/src/vs/workbench/contrib/output/common/outputChannelModelService.ts @@ -11,13 +11,14 @@ import { toLocalISOString } from 'vs/base/common/date'; import { dirname, joinPath } from 'vs/base/common/resources'; import { DelegatedOutputChannelModel, FileOutputChannelModel, IOutputChannelModel } from 'vs/workbench/contrib/output/common/outputChannelModel'; import { URI } from 'vs/base/common/uri'; +import { ILanguageSelection } from 'vs/editor/common/languages/language'; export const IOutputChannelModelService = createDecorator('outputChannelModelService'); export interface IOutputChannelModelService { readonly _serviceBrand: undefined; - createOutputChannelModel(id: string, modelUri: URI, mimeType: 'text/x-code-log-output' | 'text/x-code-output', file?: URI, languageId?: string): IOutputChannelModel; + createOutputChannelModel(id: string, modelUri: URI, language: ILanguageSelection, file?: URI): IOutputChannelModel; } @@ -31,8 +32,8 @@ export abstract class AbstractOutputChannelModelService { @IInstantiationService protected readonly instantiationService: IInstantiationService ) { } - createOutputChannelModel(id: string, modelUri: URI, mimeType: 'text/x-code-log-output' | 'text/x-code-output', file?: URI, languageId?: string): IOutputChannelModel { - return file ? this.instantiationService.createInstance(FileOutputChannelModel, modelUri, mimeType, file, languageId || '') : this.instantiationService.createInstance(DelegatedOutputChannelModel, id, modelUri, mimeType, this.outputDir, languageId || ''); + createOutputChannelModel(id: string, modelUri: URI, language: ILanguageSelection, file?: URI): IOutputChannelModel { + return file ? this.instantiationService.createInstance(FileOutputChannelModel, modelUri, language, file) : this.instantiationService.createInstance(DelegatedOutputChannelModel, id, modelUri, language, this.outputDir); } private _outputDir: Promise | null = null; diff --git a/src/vs/workbench/services/output/common/output.ts b/src/vs/workbench/services/output/common/output.ts index 7f0eab939b2..f12fa430d7a 100644 --- a/src/vs/workbench/services/output/common/output.ts +++ b/src/vs/workbench/services/output/common/output.ts @@ -15,8 +15,8 @@ export interface IOutputChannelDescriptor { id: string; label: string; log: boolean; - file?: URI; languageId?: string; + file?: URI; } export interface IFileOutputChannelDescriptor extends IOutputChannelDescriptor {