"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const aria = require("../../../../base/browser/ui/aria/aria.js");
const arrays = require("../../../../base/common/arrays.js");
const cancellation = require("../../../../base/common/cancellation.js");
const errors = require("../../../../base/common/errors.js");
const iterator = require("../../../../base/common/iterator.js");
const linkedList = require("../../../../base/common/linkedList.js");
const types = require("../../../../base/common/types.js");
const uri = require("../../../../base/common/uri.js");
const editorState = require("../../editorState/browser/editorState.js");
const editorBrowser = require("../../../browser/editorBrowser.js");
const position = require("../../../common/core/position.js");
const range = require("../../../common/core/range.js");
const selection = require("../../../common/core/selection.js");
const editorWorker = require("../../../common/services/editorWorker.js");
const resolverService = require("../../../common/services/resolverService.js");
const formattingEdit = require("./formattingEdit.js");
const nls = require("../../../../nls.js");
const commands = require("../../../../platform/commands/common/commands.js");
const extensions = require("../../../../platform/extensions/common/extensions.js");
const instantiation = require("../../../../platform/instantiation/common/instantiation.js");
const languageFeatures = require("../../../common/services/languageFeatures.js");
var __awaiter = globalThis && globalThis.__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());
  });
};
function alertFormattingEdits(edits) {
  edits = edits.filter((edit) => edit.range);
  if (!edits.length) {
    return;
  }
  let { range: range$1 } = edits[0];
  for (let i = 1; i < edits.length; i++) {
    range$1 = range.Range.plusRange(range$1, edits[i].range);
  }
  const { startLineNumber, endLineNumber } = range$1;
  if (startLineNumber === endLineNumber) {
    if (edits.length === 1) {
      aria.alert(nls.localize("hint11", "Made 1 formatting edit on line {0}", startLineNumber));
    } else {
      aria.alert(nls.localize("hintn1", "Made {0} formatting edits on line {1}", edits.length, startLineNumber));
    }
  } else {
    if (edits.length === 1) {
      aria.alert(nls.localize("hint1n", "Made 1 formatting edit between lines {0} and {1}", startLineNumber, endLineNumber));
    } else {
      aria.alert(nls.localize("hintnn", "Made {0} formatting edits between lines {1} and {2}", edits.length, startLineNumber, endLineNumber));
    }
  }
}
function getRealAndSyntheticDocumentFormattersOrdered(documentFormattingEditProvider, documentRangeFormattingEditProvider, model) {
  const result = [];
  const seen = /* @__PURE__ */ new Set();
  const docFormatter = documentFormattingEditProvider.ordered(model);
  for (const formatter of docFormatter) {
    result.push(formatter);
    if (formatter.extensionId) {
      seen.add(extensions.ExtensionIdentifier.toKey(formatter.extensionId));
    }
  }
  const rangeFormatter = documentRangeFormattingEditProvider.ordered(model);
  for (const formatter of rangeFormatter) {
    if (formatter.extensionId) {
      if (seen.has(extensions.ExtensionIdentifier.toKey(formatter.extensionId))) {
        continue;
      }
      seen.add(extensions.ExtensionIdentifier.toKey(formatter.extensionId));
    }
    result.push({
      displayName: formatter.displayName,
      extensionId: formatter.extensionId,
      provideDocumentFormattingEdits(model2, options, token) {
        return formatter.provideDocumentRangeFormattingEdits(model2, model2.getFullModelRange(), options, token);
      }
    });
  }
  return result;
}
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 void 0;
      }
      const selector = iterator.Iterable.first(FormattingConflicts._selectors);
      if (selector) {
        return yield selector(formatter, document, mode);
      }
      return void 0;
    });
  }
}
FormattingConflicts._selectors = new linkedList.LinkedList();
function formatDocumentRangesWithSelectedProvider(accessor, editorOrModel, rangeOrRanges, mode, progress, token) {
  return __awaiter(this, void 0, void 0, function* () {
    const instaService = accessor.get(instantiation.IInstantiationService);
    const { documentRangeFormattingEditProvider: documentRangeFormattingEditProviderRegistry } = accessor.get(languageFeatures.ILanguageFeaturesService);
    const model = editorBrowser.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);
    }
  });
}
function formatDocumentRangesWithProvider(accessor, provider, editorOrModel, rangeOrRanges, token) {
  return __awaiter(this, void 0, void 0, function* () {
    const workerService = accessor.get(editorWorker.IEditorWorkerService);
    let model;
    let cts;
    if (editorBrowser.isCodeEditor(editorOrModel)) {
      model = editorOrModel.getModel();
      cts = new editorState.EditorStateCancellationTokenSource(editorOrModel, 1 | 4, void 0, token);
    } else {
      model = editorOrModel;
      cts = new editorState.TextModelCancellationTokenSource(editorOrModel, token);
    }
    let ranges = [];
    let len = 0;
    for (let range$1 of arrays.asArray(rangeOrRanges).sort(range.Range.compareRangesUsingStarts)) {
      if (len > 0 && range.Range.areIntersectingOrTouching(ranges[len - 1], range$1)) {
        ranges[len - 1] = range.Range.fromPositions(ranges[len - 1].getStartPosition(), range$1.getEndPosition());
      } else {
        len = ranges.push(range$1);
      }
    }
    const computeEdits = (range2) => __awaiter(this, void 0, void 0, function* () {
      return (yield provider.provideDocumentRangeFormattingEdits(model, range2, model.getFormattingOptions(), cts.token)) || [];
    });
    const hasIntersectingEdit = (a, b) => {
      if (!a.length || !b.length) {
        return false;
      }
      const mergedA = a.reduce((acc, val) => {
        return range.Range.plusRange(acc, val.range);
      }, a[0].range);
      if (!b.some((x) => {
        return range.Range.intersectRanges(mergedA, x.range);
      })) {
        return false;
      }
      for (let edit of a) {
        for (let otherEdit of b) {
          if (range.Range.intersectRanges(edit.range, otherEdit.range)) {
            return true;
          }
        }
      }
      return false;
    };
    const allEdits = [];
    const rawEditsList = [];
    try {
      for (let range2 of ranges) {
        if (cts.token.isCancellationRequested) {
          return true;
        }
        rawEditsList.push(yield computeEdits(range2));
      }
      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])) {
            const mergedRange = range.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);
            i = 0;
            j = 0;
          }
        }
      }
      for (let 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 (editorBrowser.isCodeEditor(editorOrModel)) {
      formattingEdit.FormattingEdit.execute(editorOrModel, allEdits, true);
      alertFormattingEdits(allEdits);
      editorOrModel.revealPositionInCenterIfOutsideViewport(
        editorOrModel.getPosition(),
        1
        /* Immediate */
      );
    } else {
      const [{ range: range$1 }] = allEdits;
      const initialSelection = new selection.Selection(range$1.startLineNumber, range$1.startColumn, range$1.endLineNumber, range$1.endColumn);
      model.pushEditOperations([initialSelection], allEdits.map((edit) => {
        return {
          text: edit.text,
          range: range.Range.lift(edit.range),
          forceMoveMarkers: true
        };
      }), (undoEdits) => {
        for (const { range: range$12 } of undoEdits) {
          if (range.Range.areIntersectingOrTouching(range$12, initialSelection)) {
            return [new selection.Selection(range$12.startLineNumber, range$12.startColumn, range$12.endLineNumber, range$12.endColumn)];
          }
        }
        return null;
      });
    }
    return true;
  });
}
function formatDocumentWithSelectedProvider(accessor, editorOrModel, mode, progress, token) {
  return __awaiter(this, void 0, void 0, function* () {
    const instaService = accessor.get(instantiation.IInstantiationService);
    const languageFeaturesService = accessor.get(languageFeatures.ILanguageFeaturesService);
    const model = editorBrowser.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);
    }
  });
}
function formatDocumentWithProvider(accessor, provider, editorOrModel, mode, token) {
  return __awaiter(this, void 0, void 0, function* () {
    const workerService = accessor.get(editorWorker.IEditorWorkerService);
    let model;
    let cts;
    if (editorBrowser.isCodeEditor(editorOrModel)) {
      model = editorOrModel.getModel();
      cts = new editorState.EditorStateCancellationTokenSource(editorOrModel, 1 | 4, void 0, token);
    } else {
      model = editorOrModel;
      cts = new editorState.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 (editorBrowser.isCodeEditor(editorOrModel)) {
      formattingEdit.FormattingEdit.execute(
        editorOrModel,
        edits,
        mode !== 2
        /* Silent */
      );
      if (mode !== 2) {
        alertFormattingEdits(edits);
        editorOrModel.revealPositionInCenterIfOutsideViewport(
          editorOrModel.getPosition(),
          1
          /* Immediate */
        );
      }
    } else {
      const [{ range: range$1 }] = edits;
      const initialSelection = new selection.Selection(range$1.startLineNumber, range$1.startColumn, range$1.endLineNumber, range$1.endColumn);
      model.pushEditOperations([initialSelection], edits.map((edit) => {
        return {
          text: edit.text,
          range: range.Range.lift(edit.range),
          forceMoveMarkers: true
        };
      }), (undoEdits) => {
        for (const { range: range$12 } of undoEdits) {
          if (range.Range.areIntersectingOrTouching(range$12, initialSelection)) {
            return [new selection.Selection(range$12.startLineNumber, range$12.startColumn, range$12.endLineNumber, range$12.endColumn)];
          }
        }
        return null;
      });
    }
    return true;
  });
}
function getDocumentRangeFormattingEditsUntilResult(workerService, languageFeaturesService, model, range2, options, token) {
  return __awaiter(this, void 0, void 0, function* () {
    const providers = languageFeaturesService.documentRangeFormattingEditProvider.ordered(model);
    for (const provider of providers) {
      let rawEdits = yield Promise.resolve(provider.provideDocumentRangeFormattingEdits(model, range2, options, token)).catch(errors.onUnexpectedExternalError);
      if (arrays.isNonEmptyArray(rawEdits)) {
        return yield workerService.computeMoreMinimalEdits(model.uri, rawEdits);
      }
    }
    return void 0;
  });
}
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) {
      let rawEdits = yield Promise.resolve(provider.provideDocumentFormattingEdits(model, options, token)).catch(errors.onUnexpectedExternalError);
      if (arrays.isNonEmptyArray(rawEdits)) {
        return yield workerService.computeMoreMinimalEdits(model.uri, rawEdits);
      }
    }
    return void 0;
  });
}
function getOnTypeFormattingEdits(workerService, languageFeaturesService, model, position2, ch, options, token) {
  const providers = languageFeaturesService.onTypeFormattingEditProvider.ordered(model);
  if (providers.length === 0) {
    return Promise.resolve(void 0);
  }
  if (providers[0].autoFormatTriggerCharacters.indexOf(ch) < 0) {
    return Promise.resolve(void 0);
  }
  return Promise.resolve(providers[0].provideOnTypeFormattingEdits(model, position2, ch, options, token)).catch(errors.onUnexpectedExternalError).then((edits) => {
    return workerService.computeMoreMinimalEdits(model.uri, edits);
  });
}
commands.CommandsRegistry.registerCommand("_executeFormatRangeProvider", function(accessor, ...args) {
  return __awaiter(this, void 0, void 0, function* () {
    const [resource, range$1, options] = args;
    types.assertType(uri.URI.isUri(resource));
    types.assertType(range.Range.isIRange(range$1));
    const resolverService$1 = accessor.get(resolverService.ITextModelService);
    const workerService = accessor.get(editorWorker.IEditorWorkerService);
    const languageFeaturesService = accessor.get(languageFeatures.ILanguageFeaturesService);
    const reference = yield resolverService$1.createModelReference(resource);
    try {
      return getDocumentRangeFormattingEditsUntilResult(workerService, languageFeaturesService, reference.object.textEditorModel, range.Range.lift(range$1), options, cancellation.CancellationToken.None);
    } finally {
      reference.dispose();
    }
  });
});
commands.CommandsRegistry.registerCommand("_executeFormatDocumentProvider", function(accessor, ...args) {
  return __awaiter(this, void 0, void 0, function* () {
    const [resource, options] = args;
    types.assertType(uri.URI.isUri(resource));
    const resolverService$1 = accessor.get(resolverService.ITextModelService);
    const workerService = accessor.get(editorWorker.IEditorWorkerService);
    const languageFeaturesService = accessor.get(languageFeatures.ILanguageFeaturesService);
    const reference = yield resolverService$1.createModelReference(resource);
    try {
      return getDocumentFormattingEditsUntilResult(workerService, languageFeaturesService, reference.object.textEditorModel, options, cancellation.CancellationToken.None);
    } finally {
      reference.dispose();
    }
  });
});
commands.CommandsRegistry.registerCommand("_executeFormatOnTypeProvider", function(accessor, ...args) {
  return __awaiter(this, void 0, void 0, function* () {
    const [resource, position$1, ch, options] = args;
    types.assertType(uri.URI.isUri(resource));
    types.assertType(position.Position.isIPosition(position$1));
    types.assertType(typeof ch === "string");
    const resolverService$1 = accessor.get(resolverService.ITextModelService);
    const workerService = accessor.get(editorWorker.IEditorWorkerService);
    const languageFeaturesService = accessor.get(languageFeatures.ILanguageFeaturesService);
    const reference = yield resolverService$1.createModelReference(resource);
    try {
      return getOnTypeFormattingEdits(workerService, languageFeaturesService, reference.object.textEditorModel, position.Position.lift(position$1), ch, options, cancellation.CancellationToken.None);
    } finally {
      reference.dispose();
    }
  });
});
exports.FormattingConflicts = FormattingConflicts;
exports.alertFormattingEdits = alertFormattingEdits;
exports.formatDocumentRangesWithProvider = formatDocumentRangesWithProvider;
exports.formatDocumentRangesWithSelectedProvider = formatDocumentRangesWithSelectedProvider;
exports.formatDocumentWithProvider = formatDocumentWithProvider;
exports.formatDocumentWithSelectedProvider = formatDocumentWithSelectedProvider;
exports.getDocumentFormattingEditsUntilResult = getDocumentFormattingEditsUntilResult;
exports.getDocumentRangeFormattingEditsUntilResult = getDocumentRangeFormattingEditsUntilResult;
exports.getOnTypeFormattingEdits = getOnTypeFormattingEdits;
exports.getRealAndSyntheticDocumentFormattersOrdered = getRealAndSyntheticDocumentFormattersOrdered;
