| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
- return new (P || (P = Promise))(function (resolve, reject) {
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
- step((generator = generator.apply(thisArg, _arguments || [])).next());
- });
- };
- import { alert } from '../../../../base/browser/ui/aria/aria.js';
- import { asArray, isNonEmptyArray } from '../../../../base/common/arrays.js';
- import { CancellationToken } from '../../../../base/common/cancellation.js';
- import { onUnexpectedExternalError } from '../../../../base/common/errors.js';
- import { Iterable } from '../../../../base/common/iterator.js';
- import { LinkedList } from '../../../../base/common/linkedList.js';
- import { assertType } from '../../../../base/common/types.js';
- import { URI } from '../../../../base/common/uri.js';
- import { EditorStateCancellationTokenSource, TextModelCancellationTokenSource } from '../../editorState/browser/editorState.js';
- import { isCodeEditor } from '../../../browser/editorBrowser.js';
- import { Position } from '../../../common/core/position.js';
- import { Range } from '../../../common/core/range.js';
- import { Selection } from '../../../common/core/selection.js';
- import { IEditorWorkerService } from '../../../common/services/editorWorker.js';
- import { ITextModelService } from '../../../common/services/resolverService.js';
- import { FormattingEdit } from './formattingEdit.js';
- import * as nls from '../../../../nls.js';
- import { CommandsRegistry } from '../../../../platform/commands/common/commands.js';
- import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js';
- import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
- import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
- export function alertFormattingEdits(edits) {
- edits = edits.filter(edit => edit.range);
- if (!edits.length) {
- return;
- }
- let { range } = edits[0];
- for (let i = 1; i < edits.length; i++) {
- range = Range.plusRange(range, edits[i].range);
- }
- const { startLineNumber, endLineNumber } = range;
- if (startLineNumber === endLineNumber) {
- if (edits.length === 1) {
- alert(nls.localize('hint11', "Made 1 formatting edit on line {0}", startLineNumber));
- }
- else {
- alert(nls.localize('hintn1', "Made {0} formatting edits on line {1}", edits.length, startLineNumber));
- }
- }
- else {
- if (edits.length === 1) {
- alert(nls.localize('hint1n', "Made 1 formatting edit between lines {0} and {1}", startLineNumber, endLineNumber));
- }
- else {
- alert(nls.localize('hintnn', "Made {0} formatting edits between lines {1} and {2}", edits.length, startLineNumber, endLineNumber));
- }
- }
- }
- export function getRealAndSyntheticDocumentFormattersOrdered(documentFormattingEditProvider, documentRangeFormattingEditProvider, model) {
- const result = [];
- const seen = new Set();
- // (1) add all document formatter
- const docFormatter = documentFormattingEditProvider.ordered(model);
- for (const formatter of docFormatter) {
- result.push(formatter);
- if (formatter.extensionId) {
- seen.add(ExtensionIdentifier.toKey(formatter.extensionId));
- }
- }
- // (2) add all range formatter as document formatter (unless the same extension already did that)
- const rangeFormatter = documentRangeFormattingEditProvider.ordered(model);
- for (const formatter of rangeFormatter) {
- if (formatter.extensionId) {
- if (seen.has(ExtensionIdentifier.toKey(formatter.extensionId))) {
- continue;
- }
- seen.add(ExtensionIdentifier.toKey(formatter.extensionId));
- }
- result.push({
- displayName: formatter.displayName,
- extensionId: formatter.extensionId,
- provideDocumentFormattingEdits(model, options, token) {
- return formatter.provideDocumentRangeFormattingEdits(model, model.getFullModelRange(), options, token);
- }
- });
- }
- return result;
- }
- export class FormattingConflicts {
- static setFormatterSelector(selector) {
- const remove = FormattingConflicts._selectors.unshift(selector);
- return { dispose: remove };
- }
- static select(formatter, document, mode) {
- return __awaiter(this, void 0, void 0, function* () {
- if (formatter.length === 0) {
- return undefined;
- }
- const selector = Iterable.first(FormattingConflicts._selectors);
- if (selector) {
- return yield selector(formatter, document, mode);
- }
- return undefined;
- });
- }
- }
- FormattingConflicts._selectors = new LinkedList();
- export function formatDocumentRangesWithSelectedProvider(accessor, editorOrModel, rangeOrRanges, mode, progress, token) {
- return __awaiter(this, void 0, void 0, function* () {
- const instaService = accessor.get(IInstantiationService);
- const { documentRangeFormattingEditProvider: documentRangeFormattingEditProviderRegistry } = accessor.get(ILanguageFeaturesService);
- const model = isCodeEditor(editorOrModel) ? editorOrModel.getModel() : editorOrModel;
- const provider = documentRangeFormattingEditProviderRegistry.ordered(model);
- const selected = yield FormattingConflicts.select(provider, model, mode);
- if (selected) {
- progress.report(selected);
- yield instaService.invokeFunction(formatDocumentRangesWithProvider, selected, editorOrModel, rangeOrRanges, token);
- }
- });
- }
- export function formatDocumentRangesWithProvider(accessor, provider, editorOrModel, rangeOrRanges, token) {
- return __awaiter(this, void 0, void 0, function* () {
- const workerService = accessor.get(IEditorWorkerService);
- let model;
- let cts;
- if (isCodeEditor(editorOrModel)) {
- model = editorOrModel.getModel();
- cts = new EditorStateCancellationTokenSource(editorOrModel, 1 /* CodeEditorStateFlag.Value */ | 4 /* CodeEditorStateFlag.Position */, undefined, token);
- }
- else {
- model = editorOrModel;
- cts = new TextModelCancellationTokenSource(editorOrModel, token);
- }
- // make sure that ranges don't overlap nor touch each other
- const ranges = [];
- let len = 0;
- for (const range of asArray(rangeOrRanges).sort(Range.compareRangesUsingStarts)) {
- if (len > 0 && Range.areIntersectingOrTouching(ranges[len - 1], range)) {
- ranges[len - 1] = Range.fromPositions(ranges[len - 1].getStartPosition(), range.getEndPosition());
- }
- else {
- len = ranges.push(range);
- }
- }
- const computeEdits = (range) => __awaiter(this, void 0, void 0, function* () {
- return (yield provider.provideDocumentRangeFormattingEdits(model, range, model.getFormattingOptions(), cts.token)) || [];
- });
- const hasIntersectingEdit = (a, b) => {
- if (!a.length || !b.length) {
- return false;
- }
- // quick exit if the list of ranges are completely unrelated [O(n)]
- const mergedA = a.reduce((acc, val) => { return Range.plusRange(acc, val.range); }, a[0].range);
- if (!b.some(x => { return Range.intersectRanges(mergedA, x.range); })) {
- return false;
- }
- // fallback to a complete check [O(n^2)]
- for (const edit of a) {
- for (const otherEdit of b) {
- if (Range.intersectRanges(edit.range, otherEdit.range)) {
- return true;
- }
- }
- }
- return false;
- };
- const allEdits = [];
- const rawEditsList = [];
- try {
- for (const range of ranges) {
- if (cts.token.isCancellationRequested) {
- return true;
- }
- rawEditsList.push(yield computeEdits(range));
- }
- for (let i = 0; i < ranges.length; ++i) {
- for (let j = i + 1; j < ranges.length; ++j) {
- if (cts.token.isCancellationRequested) {
- return true;
- }
- if (hasIntersectingEdit(rawEditsList[i], rawEditsList[j])) {
- // Merge ranges i and j into a single range, recompute the associated edits
- const mergedRange = Range.plusRange(ranges[i], ranges[j]);
- const edits = yield computeEdits(mergedRange);
- ranges.splice(j, 1);
- ranges.splice(i, 1);
- ranges.push(mergedRange);
- rawEditsList.splice(j, 1);
- rawEditsList.splice(i, 1);
- rawEditsList.push(edits);
- // Restart scanning
- i = 0;
- j = 0;
- }
- }
- }
- for (const rawEdits of rawEditsList) {
- if (cts.token.isCancellationRequested) {
- return true;
- }
- const minimalEdits = yield workerService.computeMoreMinimalEdits(model.uri, rawEdits);
- if (minimalEdits) {
- allEdits.push(...minimalEdits);
- }
- }
- }
- finally {
- cts.dispose();
- }
- if (allEdits.length === 0) {
- return false;
- }
- if (isCodeEditor(editorOrModel)) {
- // use editor to apply edits
- FormattingEdit.execute(editorOrModel, allEdits, true);
- alertFormattingEdits(allEdits);
- editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), 1 /* ScrollType.Immediate */);
- }
- else {
- // use model to apply edits
- const [{ range }] = allEdits;
- const initialSelection = new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn);
- model.pushEditOperations([initialSelection], allEdits.map(edit => {
- return {
- text: edit.text,
- range: Range.lift(edit.range),
- forceMoveMarkers: true
- };
- }), undoEdits => {
- for (const { range } of undoEdits) {
- if (Range.areIntersectingOrTouching(range, initialSelection)) {
- return [new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)];
- }
- }
- return null;
- });
- }
- return true;
- });
- }
- export function formatDocumentWithSelectedProvider(accessor, editorOrModel, mode, progress, token) {
- return __awaiter(this, void 0, void 0, function* () {
- const instaService = accessor.get(IInstantiationService);
- const languageFeaturesService = accessor.get(ILanguageFeaturesService);
- const model = isCodeEditor(editorOrModel) ? editorOrModel.getModel() : editorOrModel;
- const provider = getRealAndSyntheticDocumentFormattersOrdered(languageFeaturesService.documentFormattingEditProvider, languageFeaturesService.documentRangeFormattingEditProvider, model);
- const selected = yield FormattingConflicts.select(provider, model, mode);
- if (selected) {
- progress.report(selected);
- yield instaService.invokeFunction(formatDocumentWithProvider, selected, editorOrModel, mode, token);
- }
- });
- }
- export function formatDocumentWithProvider(accessor, provider, editorOrModel, mode, token) {
- return __awaiter(this, void 0, void 0, function* () {
- const workerService = accessor.get(IEditorWorkerService);
- let model;
- let cts;
- if (isCodeEditor(editorOrModel)) {
- model = editorOrModel.getModel();
- cts = new EditorStateCancellationTokenSource(editorOrModel, 1 /* CodeEditorStateFlag.Value */ | 4 /* CodeEditorStateFlag.Position */, undefined, token);
- }
- else {
- model = editorOrModel;
- cts = new TextModelCancellationTokenSource(editorOrModel, token);
- }
- let edits;
- try {
- const rawEdits = yield provider.provideDocumentFormattingEdits(model, model.getFormattingOptions(), cts.token);
- edits = yield workerService.computeMoreMinimalEdits(model.uri, rawEdits);
- if (cts.token.isCancellationRequested) {
- return true;
- }
- }
- finally {
- cts.dispose();
- }
- if (!edits || edits.length === 0) {
- return false;
- }
- if (isCodeEditor(editorOrModel)) {
- // use editor to apply edits
- FormattingEdit.execute(editorOrModel, edits, mode !== 2 /* FormattingMode.Silent */);
- if (mode !== 2 /* FormattingMode.Silent */) {
- alertFormattingEdits(edits);
- editorOrModel.revealPositionInCenterIfOutsideViewport(editorOrModel.getPosition(), 1 /* ScrollType.Immediate */);
- }
- }
- else {
- // use model to apply edits
- const [{ range }] = edits;
- const initialSelection = new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn);
- model.pushEditOperations([initialSelection], edits.map(edit => {
- return {
- text: edit.text,
- range: Range.lift(edit.range),
- forceMoveMarkers: true
- };
- }), undoEdits => {
- for (const { range } of undoEdits) {
- if (Range.areIntersectingOrTouching(range, initialSelection)) {
- return [new Selection(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn)];
- }
- }
- return null;
- });
- }
- return true;
- });
- }
- export function getDocumentRangeFormattingEditsUntilResult(workerService, languageFeaturesService, model, range, options, token) {
- return __awaiter(this, void 0, void 0, function* () {
- const providers = languageFeaturesService.documentRangeFormattingEditProvider.ordered(model);
- for (const provider of providers) {
- const rawEdits = yield Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range, options, token)).catch(onUnexpectedExternalError);
- if (isNonEmptyArray(rawEdits)) {
- return yield workerService.computeMoreMinimalEdits(model.uri, rawEdits);
- }
- }
- return undefined;
- });
- }
- export function getDocumentFormattingEditsUntilResult(workerService, languageFeaturesService, model, options, token) {
- return __awaiter(this, void 0, void 0, function* () {
- const providers = getRealAndSyntheticDocumentFormattersOrdered(languageFeaturesService.documentFormattingEditProvider, languageFeaturesService.documentRangeFormattingEditProvider, model);
- for (const provider of providers) {
- const rawEdits = yield Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token)).catch(onUnexpectedExternalError);
- if (isNonEmptyArray(rawEdits)) {
- return yield workerService.computeMoreMinimalEdits(model.uri, rawEdits);
- }
- }
- return undefined;
- });
- }
- export function getOnTypeFormattingEdits(workerService, languageFeaturesService, model, position, ch, options, token) {
- const providers = languageFeaturesService.onTypeFormattingEditProvider.ordered(model);
- if (providers.length === 0) {
- return Promise.resolve(undefined);
- }
- if (providers[0].autoFormatTriggerCharacters.indexOf(ch) < 0) {
- return Promise.resolve(undefined);
- }
- return Promise.resolve(providers[0].provideOnTypeFormattingEdits(model, position, ch, options, token)).catch(onUnexpectedExternalError).then(edits => {
- return workerService.computeMoreMinimalEdits(model.uri, edits);
- });
- }
- CommandsRegistry.registerCommand('_executeFormatRangeProvider', function (accessor, ...args) {
- return __awaiter(this, void 0, void 0, function* () {
- const [resource, range, options] = args;
- assertType(URI.isUri(resource));
- assertType(Range.isIRange(range));
- const resolverService = accessor.get(ITextModelService);
- const workerService = accessor.get(IEditorWorkerService);
- const languageFeaturesService = accessor.get(ILanguageFeaturesService);
- const reference = yield resolverService.createModelReference(resource);
- try {
- return getDocumentRangeFormattingEditsUntilResult(workerService, languageFeaturesService, reference.object.textEditorModel, Range.lift(range), options, CancellationToken.None);
- }
- finally {
- reference.dispose();
- }
- });
- });
- CommandsRegistry.registerCommand('_executeFormatDocumentProvider', function (accessor, ...args) {
- return __awaiter(this, void 0, void 0, function* () {
- const [resource, options] = args;
- assertType(URI.isUri(resource));
- const resolverService = accessor.get(ITextModelService);
- const workerService = accessor.get(IEditorWorkerService);
- const languageFeaturesService = accessor.get(ILanguageFeaturesService);
- const reference = yield resolverService.createModelReference(resource);
- try {
- return getDocumentFormattingEditsUntilResult(workerService, languageFeaturesService, reference.object.textEditorModel, options, CancellationToken.None);
- }
- finally {
- reference.dispose();
- }
- });
- });
- CommandsRegistry.registerCommand('_executeFormatOnTypeProvider', function (accessor, ...args) {
- return __awaiter(this, void 0, void 0, function* () {
- const [resource, position, ch, options] = args;
- assertType(URI.isUri(resource));
- assertType(Position.isIPosition(position));
- assertType(typeof ch === 'string');
- const resolverService = accessor.get(ITextModelService);
- const workerService = accessor.get(IEditorWorkerService);
- const languageFeaturesService = accessor.get(ILanguageFeaturesService);
- const reference = yield resolverService.createModelReference(resource);
- try {
- return getOnTypeFormattingEdits(workerService, languageFeaturesService, reference.object.textEditorModel, Position.lift(position), ch, options, CancellationToken.None);
- }
- finally {
- reference.dispose();
- }
- });
- });
|