diff --git a/src/vs/editor/contrib/format/format.ts b/src/vs/editor/contrib/format/format.ts index 401a6fc3fc0a376cd90af98a384146021a07d21f..ddda9baf4d5e1984ca54a78beafe906494058fa5 100644 --- a/src/vs/editor/contrib/format/format.ts +++ b/src/vs/editor/contrib/format/format.ts @@ -8,12 +8,14 @@ import { URI } from 'vs/base/common/uri'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { Range } from 'vs/editor/common/core/range'; import { ITextModel } from 'vs/editor/common/model'; -import { registerDefaultLanguageCommand, registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; +import { registerLanguageCommand } from 'vs/editor/browser/editorExtensions'; import { DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry, OnTypeFormattingEditProviderRegistry, FormattingOptions, TextEdit } from 'vs/editor/common/modes'; import { IModelService } from 'vs/editor/common/services/modelService'; import { first } from 'vs/base/common/async'; import { Position } from 'vs/editor/common/core/position'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export class NoProviderError extends Error { @@ -32,44 +34,114 @@ export class NoProviderError extends Error { } } -export function getDocumentRangeFormattingEdits(model: ITextModel, range: Range, options: FormattingOptions, token: CancellationToken): Promise { - - const providers = DocumentRangeFormattingEditProviderRegistry.ordered(model); - - if (providers.length === 0) { +export function getDocumentRangeFormattingEdits( + telemetryService: ITelemetryService, + workerService: IEditorWorkerService, + model: ITextModel, + range: Range, + options: FormattingOptions, + token: CancellationToken +): Promise { + + const allProvider = DocumentRangeFormattingEditProviderRegistry.ordered(model); + + /* __GDPR__ + "formatterInfo" : { + "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + telemetryService.publicLog('formatterInfo', { + type: 'range', + language: model.getLanguageIdentifier().language, + count: allProvider.length, + }); + + if (allProvider.length === 0) { return Promise.reject(new NoProviderError()); } - return first(providers.map(provider => () => { - return Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token)) - .then(undefined, onUnexpectedExternalError); - }), isNonEmptyArray); + return first(allProvider.map(provider => () => { + return Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token)).catch(onUnexpectedExternalError); + }), isNonEmptyArray).then(edits => { + // break edits into smaller edits + return workerService.computeMoreMinimalEdits(model.uri, edits); + }); } -export function getDocumentFormattingEdits(model: ITextModel, options: FormattingOptions, token: CancellationToken): Promise { +export function getDocumentFormattingEdits( + telemetryService: ITelemetryService, + workerService: IEditorWorkerService, + model: ITextModel, + options: FormattingOptions, + token: CancellationToken +): Promise { + const providers = DocumentFormattingEditProviderRegistry.ordered(model); + /* __GDPR__ + "formatterInfo" : { + "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + telemetryService.publicLog('formatterInfo', { + type: 'document', + language: model.getLanguageIdentifier().language, + count: providers.length, + }); + // try range formatters when no document formatter is registered if (providers.length === 0) { - return getDocumentRangeFormattingEdits(model, model.getFullModelRange(), options, token); + return getDocumentRangeFormattingEdits(telemetryService, workerService, model, model.getFullModelRange(), options, token); } return first(providers.map(provider => () => { - return Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token)) - .then(undefined, onUnexpectedExternalError); - }), isNonEmptyArray); + // first with result wins... + return Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token)).catch(onUnexpectedExternalError); + }), isNonEmptyArray).then(edits => { + // break edits into smaller edits + return workerService.computeMoreMinimalEdits(model.uri, edits); + }); } -export function getOnTypeFormattingEdits(model: ITextModel, position: Position, ch: string, options: FormattingOptions): Promise { - const [support] = OnTypeFormattingEditProviderRegistry.ordered(model); - if (!support) { +export function getOnTypeFormattingEdits( + telemetryService: ITelemetryService, + workerService: IEditorWorkerService, + model: ITextModel, + position: Position, + ch: string, + options: FormattingOptions +): Promise { + + const providers = OnTypeFormattingEditProviderRegistry.ordered(model); + + /* __GDPR__ + "formatterInfo" : { + "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } + } + */ + telemetryService.publicLog('formatterInfo', { + type: 'ontype', + language: model.getLanguageIdentifier().language, + count: providers.length, + }); + + if (providers.length === 0) { return Promise.resolve(undefined); } - if (support.autoFormatTriggerCharacters.indexOf(ch) < 0) { + + if (providers[0].autoFormatTriggerCharacters.indexOf(ch) < 0) { return Promise.resolve(undefined); } - return Promise.resolve(support.provideOnTypeFormattingEdits(model, position, ch, options, CancellationToken.None)).then(r => r, onUnexpectedExternalError); + return Promise.resolve(providers[0].provideOnTypeFormattingEdits(model, position, ch, options, CancellationToken.None)).catch(onUnexpectedExternalError).then(edits => { + return workerService.computeMoreMinimalEdits(model.uri, edits); + }); } registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args) { @@ -81,7 +153,7 @@ registerLanguageCommand('_executeFormatRangeProvider', function (accessor, args) if (!model) { throw illegalArgument('resource'); } - return getDocumentRangeFormattingEdits(model, Range.lift(range), options, CancellationToken.None); + return getDocumentRangeFormattingEdits(accessor.get(ITelemetryService), accessor.get(IEditorWorkerService), model, Range.lift(range), options, CancellationToken.None); }); registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, args) { @@ -94,13 +166,18 @@ registerLanguageCommand('_executeFormatDocumentProvider', function (accessor, ar throw illegalArgument('resource'); } - return getDocumentFormattingEdits(model, options, CancellationToken.None); + return getDocumentFormattingEdits(accessor.get(ITelemetryService), accessor.get(IEditorWorkerService), model, options, CancellationToken.None); }); -registerDefaultLanguageCommand('_executeFormatOnTypeProvider', function (model, position, args) { - const { ch, options } = args; - if (typeof ch !== 'string') { - throw illegalArgument('ch'); +registerLanguageCommand('_executeFormatOnTypeProvider', function (accessor, args) { + const { resource, position, ch, options } = args; + if (!(resource instanceof URI) || !Position.isIPosition(position) || typeof ch !== 'string') { + throw illegalArgument(); } - return getOnTypeFormattingEdits(model, position, ch, options); + const model = accessor.get(IModelService).getModel(resource); + if (!model) { + throw illegalArgument('resource'); + } + + return getOnTypeFormattingEdits(accessor.get(ITelemetryService), accessor.get(IEditorWorkerService), model, Position.lift(position), ch, options); }); diff --git a/src/vs/editor/contrib/format/formatActions.ts b/src/vs/editor/contrib/format/formatActions.ts index 429ea13fa61eb5b8cf4114814dbb658291ee6715..694f48e3f7ca5f46880137c3d471f9fd38967c19 100644 --- a/src/vs/editor/contrib/format/formatActions.ts +++ b/src/vs/editor/contrib/format/formatActions.ts @@ -5,7 +5,6 @@ import { alert } from 'vs/base/browser/ui/aria/aria'; import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { sequence } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; @@ -20,7 +19,7 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ISingleEditOperation } from 'vs/editor/common/model'; import { DocumentFormattingEditProviderRegistry, DocumentRangeFormattingEditProviderRegistry, FormattingOptions, OnTypeFormattingEditProviderRegistry } from 'vs/editor/common/modes'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; -import { getOnTypeFormattingEdits, NoProviderError } from 'vs/editor/contrib/format/format'; +import { getOnTypeFormattingEdits, NoProviderError, getDocumentFormattingEdits, getDocumentRangeFormattingEdits } from 'vs/editor/contrib/format/format'; import { FormattingEdit } from 'vs/editor/contrib/format/formattingEdit'; import * as nls from 'vs/nls'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -62,126 +61,64 @@ export const enum FormatRangeType { Selection, } -export function formatDocumentRange(telemetryService: ITelemetryService, workerService: IEditorWorkerService, editor: IActiveCodeEditor, rangeOrRangeType: Range | FormatRangeType, options: FormattingOptions, token: CancellationToken): Promise { - - const provider = DocumentRangeFormattingEditProviderRegistry.ordered(editor.getModel()); - if (provider.length === 0) { - return Promise.reject(new NoProviderError()); - } - - // Know how often multiple providers clash and (for now) - // continue picking the 'first' provider - if (provider.length !== 1) { - /* __GDPR__ - "manyformatters" : { - "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - telemetryService.publicLog('manyformatters', { - type: 'range', - language: editor.getModel().getLanguageIdentifier().language, - count: provider.length, - }); - provider.length = 1; +export function formatDocumentRange( + telemetryService: ITelemetryService, + workerService: IEditorWorkerService, + editor: IActiveCodeEditor, + rangeOrRangeType: Range | FormatRangeType, + options: FormattingOptions, + token: CancellationToken +): Promise { + + + const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); + const model = editor.getModel(); + + let range: Range; + if (rangeOrRangeType === FormatRangeType.Full) { + // full + range = model.getFullModelRange(); + + } else if (rangeOrRangeType === FormatRangeType.Selection) { + // selection or line (when empty) + range = editor.getSelection(); + if (range.isEmpty()) { + range = new Range(range.startLineNumber, 1, range.endLineNumber, model.getLineMaxColumn(range.endLineNumber)); + } + } else { + // as is + range = rangeOrRangeType; } - let allEdits: ISingleEditOperation[] = []; - - editor.pushUndoStop(); - return sequence(provider.map(provider => { - // create a formatting task per provider. they run sequentially, - // potentially undoing the working of a previous formatter - return () => { - const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); - const model = editor.getModel(); - - let range: Range; - if (rangeOrRangeType === FormatRangeType.Full) { - // full - range = model.getFullModelRange(); - - } else if (rangeOrRangeType === FormatRangeType.Selection) { - // selection or line (when empty) - range = editor.getSelection(); - if (range.isEmpty()) { - range = new Range(range.startLineNumber, 1, range.endLineNumber, model.getLineMaxColumn(range.endLineNumber)); - } - } else { - // as is - range = rangeOrRangeType; - } - return Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token)).then(edits => { - // break edits into smaller edits - return workerService.computeMoreMinimalEdits(editor.getModel().uri, edits); - }).then(edits => { - // make edit only when the editor didn't change while - // computing and only when there are edits - if (state.validate(editor) && isNonEmptyArray(edits)) { - FormattingEdit.execute(editor, edits); - allEdits = allEdits.concat(edits); - } - }); - }; - })).then(() => { - alertFormattingEdits(allEdits); - editor.pushUndoStop(); - editor.focus(); - editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate); + return getDocumentRangeFormattingEdits(telemetryService, workerService, model, range, options, token).then(edits => { + // make edit only when the editor didn't change while + // computing and only when there are edits + if (state.validate(editor) && isNonEmptyArray(edits)) { + FormattingEdit.execute(editor, edits); + alertFormattingEdits(edits); + editor.focus(); + editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate); + } }); + } export function formatDocument(telemetryService: ITelemetryService, workerService: IEditorWorkerService, editor: IActiveCodeEditor, options: FormattingOptions, token: CancellationToken): Promise { - const provider = DocumentFormattingEditProviderRegistry.ordered(editor.getModel()); - if (provider.length === 0) { - return formatDocumentRange(telemetryService, workerService, editor, FormatRangeType.Full, options, token); - } - // Know how often multiple providers clash and (for now) - // continue picking the 'first' provider - if (provider.length !== 1) { - /* __GDPR__ - "manyformatters" : { - "type" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "language" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - telemetryService.publicLog('manyformatters', { - type: 'document', - language: editor.getModel().getLanguageIdentifier().language, - count: provider.length, - }); - provider.length = 1; - } + const allEdits: ISingleEditOperation[] = []; + const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); - let allEdits: ISingleEditOperation[] = []; - - editor.pushUndoStop(); - return sequence(provider.map(provider => { - // create a formatting task per provider. they run sequentially, - // potentially undoing the working of a previous formatter - return () => { - const state = new EditorState(editor, CodeEditorStateFlag.Value | CodeEditorStateFlag.Position); - const model = editor.getModel(); - return Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token)).then(edits => { - // break edits into smaller edits - return workerService.computeMoreMinimalEdits(editor.getModel().uri, edits); - }).then(edits => { - // make edit only when the editor didn't change while - // computing and only when there are edits - if (state.validate(editor) && isNonEmptyArray(edits)) { - FormattingEdit.execute(editor, edits); - allEdits = allEdits.concat(edits); - } - }); - }; - })).then(() => { - alertFormattingEdits(allEdits); - editor.pushUndoStop(); - editor.focus(); - editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate); + return getDocumentFormattingEdits(telemetryService, workerService, editor.getModel(), options, token).then(edits => { + // make edit only when the editor didn't change while + // computing and only when there are edits + if (state.validate(editor) && isNonEmptyArray(edits)) { + FormattingEdit.execute(editor, edits); + + alertFormattingEdits(allEdits); + editor.pushUndoStop(); + editor.focus(); + editor.revealPositionInCenterIfOutsideViewport(editor.getPosition(), editorCommon.ScrollType.Immediate); + } }); } @@ -189,39 +126,38 @@ class FormatOnType implements editorCommon.IEditorContribution { private static readonly ID = 'editor.contrib.autoFormat'; - private editor: ICodeEditor; - private workerService: IEditorWorkerService; - private callOnDispose: IDisposable[]; - private callOnModel: IDisposable[]; - - constructor(editor: ICodeEditor, @IEditorWorkerService workerService: IEditorWorkerService) { - this.editor = editor; - this.workerService = workerService; - this.callOnDispose = []; - this.callOnModel = []; + private readonly _editor: ICodeEditor; + private _callOnDispose: IDisposable[] = []; + private _callOnModel: IDisposable[] = []; - this.callOnDispose.push(editor.onDidChangeConfiguration(() => this.update())); - this.callOnDispose.push(editor.onDidChangeModel(() => this.update())); - this.callOnDispose.push(editor.onDidChangeModelLanguage(() => this.update())); - this.callOnDispose.push(OnTypeFormattingEditProviderRegistry.onDidChange(this.update, this)); + constructor( + editor: ICodeEditor, + @ITelemetryService private readonly _telemetryService: ITelemetryService, + @IEditorWorkerService private readonly _workerService: IEditorWorkerService + ) { + this._editor = editor; + this._callOnDispose.push(editor.onDidChangeConfiguration(() => this.update())); + this._callOnDispose.push(editor.onDidChangeModel(() => this.update())); + this._callOnDispose.push(editor.onDidChangeModelLanguage(() => this.update())); + this._callOnDispose.push(OnTypeFormattingEditProviderRegistry.onDidChange(this.update, this)); } private update(): void { // clean up - this.callOnModel = dispose(this.callOnModel); + this._callOnModel = dispose(this._callOnModel); // we are disabled - if (!this.editor.getConfiguration().contribInfo.formatOnType) { + if (!this._editor.getConfiguration().contribInfo.formatOnType) { return; } // no model - if (!this.editor.hasModel()) { + if (!this._editor.hasModel()) { return; } - const model = this.editor.getModel(); + const model = this._editor.getModel(); // no support const [support] = OnTypeFormattingEditProviderRegistry.ordered(model); @@ -234,7 +170,7 @@ class FormatOnType implements editorCommon.IEditorContribution { for (let ch of support.autoFormatTriggerCharacters) { triggerChars.add(ch.charCodeAt(0)); } - this.callOnModel.push(this.editor.onDidType((text: string) => { + this._callOnModel.push(this._editor.onDidType((text: string) => { let lastCharCode = text.charCodeAt(text.length - 1); if (triggerChars.has(lastCharCode)) { this.trigger(String.fromCharCode(lastCharCode)); @@ -243,22 +179,22 @@ class FormatOnType implements editorCommon.IEditorContribution { } private trigger(ch: string): void { - if (!this.editor.hasModel()) { + if (!this._editor.hasModel()) { return; } - if (this.editor.getSelections().length > 1) { + if (this._editor.getSelections().length > 1) { return; } - const model = this.editor.getModel(); - const position = this.editor.getPosition(); + const model = this._editor.getModel(); + const position = this._editor.getPosition(); let canceled = false; // install a listener that checks if edits happens before the // position on which we format right now. If so, we won't // apply the format edits - const unbind = this.editor.onDidChangeModelContent((e) => { + const unbind = this._editor.onDidChangeModelContent((e) => { if (e.isFlush) { // a model.setValue() was called // cancel only once @@ -281,28 +217,32 @@ class FormatOnType implements editorCommon.IEditorContribution { let modelOpts = model.getOptions(); - getOnTypeFormattingEdits(model, position, ch, { - tabSize: modelOpts.tabSize, - insertSpaces: modelOpts.insertSpaces - }).then(edits => { - return this.workerService.computeMoreMinimalEdits(model.uri, edits); - }).then(edits => { + getOnTypeFormattingEdits( + this._telemetryService, + this._workerService, + model, + position, + ch, + { + tabSize: modelOpts.tabSize, + insertSpaces: modelOpts.insertSpaces + }).then(edits => { - unbind.dispose(); + unbind.dispose(); - if (canceled) { - return; - } + if (canceled) { + return; + } - if (isNonEmptyArray(edits)) { - FormattingEdit.execute(this.editor, edits); - alertFormattingEdits(edits); - } + if (isNonEmptyArray(edits)) { + FormattingEdit.execute(this._editor, edits); + alertFormattingEdits(edits); + } - }, (err) => { - unbind.dispose(); - throw err; - }); + }, (err) => { + unbind.dispose(); + throw err; + }); } public getId(): string { @@ -310,8 +250,8 @@ class FormatOnType implements editorCommon.IEditorContribution { } public dispose(): void { - this.callOnDispose = dispose(this.callOnDispose); - this.callOnModel = dispose(this.callOnModel); + this._callOnDispose = dispose(this._callOnDispose); + this._callOnModel = dispose(this._callOnModel); } } diff --git a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts index 7a7b720a77b2cae551f604bcaf5ff4a44eb48222..e9f5c349b5a74cb0201d8497f841459f60bda086 100644 --- a/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts +++ b/src/vs/workbench/api/electron-browser/mainThreadSaveParticipant.ts @@ -36,6 +36,7 @@ import { extHostCustomer } from 'vs/workbench/api/electron-browser/extHostCustom import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { ISaveParticipant, ITextFileEditorModel, SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../node/extHost.protocol'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; export interface ISaveParticipantParticipant extends ISaveParticipant { // progressMessage: string; @@ -215,7 +216,8 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { constructor( @ICodeEditorService private readonly _editorService: ICodeEditorService, @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, - @IConfigurationService private readonly _configurationService: IConfigurationService + @IConfigurationService private readonly _configurationService: IConfigurationService, + @ITelemetryService private readonly _telemetryService: ITelemetryService, ) { // Nothing } @@ -235,14 +237,14 @@ class FormatOnSaveParticipant implements ISaveParticipantParticipant { return new Promise((resolve, reject) => { let source = new CancellationTokenSource(); - let request = getDocumentFormattingEdits(model, { tabSize, insertSpaces }, source.token); + let request = getDocumentFormattingEdits(this._telemetryService, this._editorWorkerService, model, { tabSize, insertSpaces }, source.token); setTimeout(() => { reject(localize('timeout.formatOnSave', "Aborted format on save after {0}ms", timeout)); source.cancel(); }, timeout); - request.then(edits => this._editorWorkerService.computeMoreMinimalEdits(model.uri, edits)).then(resolve, err => { + request.then(resolve, err => { if (!NoProviderError.is(err)) { reject(err); } else { diff --git a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts index 81605448c0a352179ce7fa2a412c55983cb22d5a..1e45c161741476f7810a5c7ca874c8b58ba8c5b9 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostLanguageFeatures.test.ts @@ -46,6 +46,9 @@ import { getColors } from 'vs/editor/contrib/colorPicker/color'; import { CancellationToken } from 'vs/base/common/cancellation'; import { nullExtensionDescription as defaultExtension } from 'vs/workbench/services/extensions/common/extensions'; import { provideSelectionRanges } from 'vs/editor/contrib/smartSelect/smartSelect'; +import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; +import { mock } from 'vs/workbench/test/electron-browser/api/mock'; +import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService'; const defaultSelector = { scheme: 'far' }; const model: ITextModel = EditorModel.createFromString( @@ -64,6 +67,8 @@ let disposables: vscode.Disposable[] = []; let rpcProtocol: TestRPCProtocol; let originalErrorHandler: (e: any) => any; + + suite('ExtHostLanguageFeatures', function () { suiteSetup(() => { @@ -906,6 +911,12 @@ suite('ExtHostLanguageFeatures', function () { // --- format + const NullWorkerService = new class extends mock() { + computeMoreMinimalEdits(resource: URI, edits: modes.TextEdit[] | null | undefined): Promise { + return Promise.resolve(edits); + } + }; + test('Format Doc, data conversion', async () => { disposables.push(extHost.registerDocumentFormattingEditProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentFormattingEditProvider { provideDocumentFormattingEdits(): any { @@ -914,7 +925,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - let value = await getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None); + let value = await getDocumentFormattingEdits(NullTelemetryService, NullWorkerService, model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None); assert.equal(value.length, 2); let [first, second] = value; assert.equal(first.text, 'testing'); @@ -932,7 +943,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - return getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None); + return getDocumentFormattingEdits(NullTelemetryService, NullWorkerService, model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None); }); test('Format Doc, order', async () => { @@ -956,7 +967,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - let value = await getDocumentFormattingEdits(model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None); + let value = await getDocumentFormattingEdits(NullTelemetryService, NullWorkerService, model, { insertSpaces: true, tabSize: 4 }, CancellationToken.None); assert.equal(value.length, 1); let [first] = value; assert.equal(first.text, 'testing'); @@ -971,7 +982,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - let value = await getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None); + let value = await getDocumentRangeFormattingEdits(NullTelemetryService, NullWorkerService, model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None); assert.equal(value.length, 1); let [first] = value; assert.equal(first.text, 'testing'); @@ -995,7 +1006,7 @@ suite('ExtHostLanguageFeatures', function () { } })); await rpcProtocol.sync(); - let value = await getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None); + let value = await getDocumentRangeFormattingEdits(NullTelemetryService, NullWorkerService, model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None); assert.equal(value.length, 1); let [first] = value; assert.equal(first.text, 'range2'); @@ -1013,7 +1024,7 @@ suite('ExtHostLanguageFeatures', function () { })); await rpcProtocol.sync(); - return getDocumentRangeFormattingEdits(model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None); + return getDocumentRangeFormattingEdits(NullTelemetryService, NullWorkerService, model, new EditorRange(1, 1, 1, 1), { insertSpaces: true, tabSize: 4 }, CancellationToken.None); }); test('Format on Type, data conversion', async () => { @@ -1025,7 +1036,7 @@ suite('ExtHostLanguageFeatures', function () { }, [';'])); await rpcProtocol.sync(); - let value = await getOnTypeFormattingEdits(model, new EditorPosition(1, 1), ';', { insertSpaces: true, tabSize: 2 }); + let value = await getOnTypeFormattingEdits(NullTelemetryService, NullWorkerService, model, new EditorPosition(1, 1), ';', { insertSpaces: true, tabSize: 2 }); assert.equal(value.length, 1); let [first] = value; assert.equal(first.text, ';');