提交 0b21524f 编写于 作者: J Johannes Rieken

prep for #41882

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