"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const lifecycle = require("../../../../base/common/lifecycle.js");
const strings = require("../../../../base/common/strings.js");
const editorExtensions = require("../../../browser/editorExtensions.js");
const shiftCommand = require("../../../common/commands/shiftCommand.js");
const editOperation = require("../../../common/core/editOperation.js");
const range = require("../../../common/core/range.js");
const selection = require("../../../common/core/selection.js");
const editorContextKeys = require("../../../common/editorContextKeys.js");
const languageConfigurationRegistry = require("../../../common/languages/languageConfigurationRegistry.js");
const model = require("../../../common/services/model.js");
const indentUtils = require("./indentUtils.js");
const nls = require("../../../../nls.js");
const quickInput = require("../../../../platform/quickinput/common/quickInput.js");
const indentation = require("../../../common/core/indentation.js");
function getReindentEditOperations(model2, languageConfigurationService, startLineNumber, endLineNumber, inheritedIndent) {
  if (model2.getLineCount() === 1 && model2.getLineMaxColumn(1) === 1) {
    return [];
  }
  const indentationRules = languageConfigurationService.getLanguageConfiguration(model2.getLanguageId()).indentationRules;
  if (!indentationRules) {
    return [];
  }
  endLineNumber = Math.min(endLineNumber, model2.getLineCount());
  while (startLineNumber <= endLineNumber) {
    if (!indentationRules.unIndentedLinePattern) {
      break;
    }
    let text = model2.getLineContent(startLineNumber);
    if (!indentationRules.unIndentedLinePattern.test(text)) {
      break;
    }
    startLineNumber++;
  }
  if (startLineNumber > endLineNumber - 1) {
    return [];
  }
  const { tabSize, indentSize, insertSpaces } = model2.getOptions();
  const shiftIndent = (indentation2, count) => {
    count = count || 1;
    return shiftCommand.ShiftCommand.shiftIndent(indentation2, indentation2.length + count, tabSize, indentSize, insertSpaces);
  };
  const unshiftIndent = (indentation2, count) => {
    count = count || 1;
    return shiftCommand.ShiftCommand.unshiftIndent(indentation2, indentation2.length + count, tabSize, indentSize, insertSpaces);
  };
  let indentEdits = [];
  let globalIndent;
  let currentLineText = model2.getLineContent(startLineNumber);
  let adjustedLineContent = currentLineText;
  if (inheritedIndent !== void 0 && inheritedIndent !== null) {
    globalIndent = inheritedIndent;
    let oldIndentation = strings.getLeadingWhitespace(currentLineText);
    adjustedLineContent = globalIndent + currentLineText.substring(oldIndentation.length);
    if (indentationRules.decreaseIndentPattern && indentationRules.decreaseIndentPattern.test(adjustedLineContent)) {
      globalIndent = unshiftIndent(globalIndent);
      adjustedLineContent = globalIndent + currentLineText.substring(oldIndentation.length);
    }
    if (currentLineText !== adjustedLineContent) {
      indentEdits.push(editOperation.EditOperation.replaceMove(new selection.Selection(startLineNumber, 1, startLineNumber, oldIndentation.length + 1), indentation.normalizeIndentation(globalIndent, indentSize, insertSpaces)));
    }
  } else {
    globalIndent = strings.getLeadingWhitespace(currentLineText);
  }
  let idealIndentForNextLine = globalIndent;
  if (indentationRules.increaseIndentPattern && indentationRules.increaseIndentPattern.test(adjustedLineContent)) {
    idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
    globalIndent = shiftIndent(globalIndent);
  } else if (indentationRules.indentNextLinePattern && indentationRules.indentNextLinePattern.test(adjustedLineContent)) {
    idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
  }
  startLineNumber++;
  for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
    let text = model2.getLineContent(lineNumber);
    let oldIndentation = strings.getLeadingWhitespace(text);
    let adjustedLineContent2 = idealIndentForNextLine + text.substring(oldIndentation.length);
    if (indentationRules.decreaseIndentPattern && indentationRules.decreaseIndentPattern.test(adjustedLineContent2)) {
      idealIndentForNextLine = unshiftIndent(idealIndentForNextLine);
      globalIndent = unshiftIndent(globalIndent);
    }
    if (oldIndentation !== idealIndentForNextLine) {
      indentEdits.push(editOperation.EditOperation.replaceMove(new selection.Selection(lineNumber, 1, lineNumber, oldIndentation.length + 1), indentation.normalizeIndentation(idealIndentForNextLine, indentSize, insertSpaces)));
    }
    if (indentationRules.unIndentedLinePattern && indentationRules.unIndentedLinePattern.test(text)) {
      continue;
    } else if (indentationRules.increaseIndentPattern && indentationRules.increaseIndentPattern.test(adjustedLineContent2)) {
      globalIndent = shiftIndent(globalIndent);
      idealIndentForNextLine = globalIndent;
    } else if (indentationRules.indentNextLinePattern && indentationRules.indentNextLinePattern.test(adjustedLineContent2)) {
      idealIndentForNextLine = shiftIndent(idealIndentForNextLine);
    } else {
      idealIndentForNextLine = globalIndent;
    }
  }
  return indentEdits;
}
class IndentationToSpacesAction extends editorExtensions.EditorAction {
  constructor() {
    super({
      id: IndentationToSpacesAction.ID,
      label: nls.localize("indentationToSpaces", "Convert Indentation to Spaces"),
      alias: "Convert Indentation to Spaces",
      precondition: editorContextKeys.EditorContextKeys.writable
    });
  }
  run(accessor, editor) {
    let model2 = editor.getModel();
    if (!model2) {
      return;
    }
    let modelOpts = model2.getOptions();
    let selection2 = editor.getSelection();
    if (!selection2) {
      return;
    }
    const command = new IndentationToSpacesCommand(selection2, modelOpts.tabSize);
    editor.pushUndoStop();
    editor.executeCommands(this.id, [command]);
    editor.pushUndoStop();
    model2.updateOptions({
      insertSpaces: true
    });
  }
}
IndentationToSpacesAction.ID = "editor.action.indentationToSpaces";
class IndentationToTabsAction extends editorExtensions.EditorAction {
  constructor() {
    super({
      id: IndentationToTabsAction.ID,
      label: nls.localize("indentationToTabs", "Convert Indentation to Tabs"),
      alias: "Convert Indentation to Tabs",
      precondition: editorContextKeys.EditorContextKeys.writable
    });
  }
  run(accessor, editor) {
    let model2 = editor.getModel();
    if (!model2) {
      return;
    }
    let modelOpts = model2.getOptions();
    let selection2 = editor.getSelection();
    if (!selection2) {
      return;
    }
    const command = new IndentationToTabsCommand(selection2, modelOpts.tabSize);
    editor.pushUndoStop();
    editor.executeCommands(this.id, [command]);
    editor.pushUndoStop();
    model2.updateOptions({
      insertSpaces: false
    });
  }
}
IndentationToTabsAction.ID = "editor.action.indentationToTabs";
class ChangeIndentationSizeAction extends editorExtensions.EditorAction {
  constructor(insertSpaces, opts) {
    super(opts);
    this.insertSpaces = insertSpaces;
  }
  run(accessor, editor) {
    const quickInputService = accessor.get(quickInput.IQuickInputService);
    const modelService = accessor.get(model.IModelService);
    let model$1 = editor.getModel();
    if (!model$1) {
      return;
    }
    const creationOpts = modelService.getCreationOptions(model$1.getLanguageId(), model$1.uri, model$1.isForSimpleWidget);
    const picks = [1, 2, 3, 4, 5, 6, 7, 8].map((n) => ({
      id: n.toString(),
      label: n.toString(),
      // add description for tabSize value set in the configuration
      description: n === creationOpts.tabSize ? nls.localize("configuredTabSize", "Configured Tab Size") : void 0
    }));
    const autoFocusIndex = Math.min(model$1.getOptions().tabSize - 1, 7);
    setTimeout(
      () => {
        quickInputService.pick(picks, { placeHolder: nls.localize({ key: "selectTabWidth", comment: ["Tab corresponds to the tab key"] }, "Select Tab Size for Current File"), activeItem: picks[autoFocusIndex] }).then((pick) => {
          if (pick) {
            if (model$1 && !model$1.isDisposed()) {
              model$1.updateOptions({
                tabSize: parseInt(pick.label, 10),
                insertSpaces: this.insertSpaces
              });
            }
          }
        });
      },
      50
      /* quick input is sensitive to being opened so soon after another */
    );
  }
}
class IndentUsingTabs extends ChangeIndentationSizeAction {
  constructor() {
    super(false, {
      id: IndentUsingTabs.ID,
      label: nls.localize("indentUsingTabs", "Indent Using Tabs"),
      alias: "Indent Using Tabs",
      precondition: void 0
    });
  }
}
IndentUsingTabs.ID = "editor.action.indentUsingTabs";
class IndentUsingSpaces extends ChangeIndentationSizeAction {
  constructor() {
    super(true, {
      id: IndentUsingSpaces.ID,
      label: nls.localize("indentUsingSpaces", "Indent Using Spaces"),
      alias: "Indent Using Spaces",
      precondition: void 0
    });
  }
}
IndentUsingSpaces.ID = "editor.action.indentUsingSpaces";
class DetectIndentation extends editorExtensions.EditorAction {
  constructor() {
    super({
      id: DetectIndentation.ID,
      label: nls.localize("detectIndentation", "Detect Indentation from Content"),
      alias: "Detect Indentation from Content",
      precondition: void 0
    });
  }
  run(accessor, editor) {
    const modelService = accessor.get(model.IModelService);
    let model$1 = editor.getModel();
    if (!model$1) {
      return;
    }
    const creationOpts = modelService.getCreationOptions(model$1.getLanguageId(), model$1.uri, model$1.isForSimpleWidget);
    model$1.detectIndentation(creationOpts.insertSpaces, creationOpts.tabSize);
  }
}
DetectIndentation.ID = "editor.action.detectIndentation";
class ReindentLinesAction extends editorExtensions.EditorAction {
  constructor() {
    super({
      id: "editor.action.reindentlines",
      label: nls.localize("editor.reindentlines", "Reindent Lines"),
      alias: "Reindent Lines",
      precondition: editorContextKeys.EditorContextKeys.writable
    });
  }
  run(accessor, editor) {
    const languageConfigurationService = accessor.get(languageConfigurationRegistry.ILanguageConfigurationService);
    let model2 = editor.getModel();
    if (!model2) {
      return;
    }
    let edits = getReindentEditOperations(model2, languageConfigurationService, 1, model2.getLineCount());
    if (edits.length > 0) {
      editor.pushUndoStop();
      editor.executeEdits(this.id, edits);
      editor.pushUndoStop();
    }
  }
}
class ReindentSelectedLinesAction extends editorExtensions.EditorAction {
  constructor() {
    super({
      id: "editor.action.reindentselectedlines",
      label: nls.localize("editor.reindentselectedlines", "Reindent Selected Lines"),
      alias: "Reindent Selected Lines",
      precondition: editorContextKeys.EditorContextKeys.writable
    });
  }
  run(accessor, editor) {
    const languageConfigurationService = accessor.get(languageConfigurationRegistry.ILanguageConfigurationService);
    let model2 = editor.getModel();
    if (!model2) {
      return;
    }
    let selections = editor.getSelections();
    if (selections === null) {
      return;
    }
    let edits = [];
    for (let selection2 of selections) {
      let startLineNumber = selection2.startLineNumber;
      let endLineNumber = selection2.endLineNumber;
      if (startLineNumber !== endLineNumber && selection2.endColumn === 1) {
        endLineNumber--;
      }
      if (startLineNumber === 1) {
        if (startLineNumber === endLineNumber) {
          continue;
        }
      } else {
        startLineNumber--;
      }
      let editOperations = getReindentEditOperations(model2, languageConfigurationService, startLineNumber, endLineNumber);
      edits.push(...editOperations);
    }
    if (edits.length > 0) {
      editor.pushUndoStop();
      editor.executeEdits(this.id, edits);
      editor.pushUndoStop();
    }
  }
}
class AutoIndentOnPasteCommand {
  constructor(edits, initialSelection) {
    this._initialSelection = initialSelection;
    this._edits = [];
    this._selectionId = null;
    for (let edit of edits) {
      if (edit.range && typeof edit.text === "string") {
        this._edits.push(edit);
      }
    }
  }
  getEditOperations(model2, builder) {
    for (let edit of this._edits) {
      builder.addEditOperation(range.Range.lift(edit.range), edit.text);
    }
    let selectionIsSet = false;
    if (Array.isArray(this._edits) && this._edits.length === 1 && this._initialSelection.isEmpty()) {
      if (this._edits[0].range.startColumn === this._initialSelection.endColumn && this._edits[0].range.startLineNumber === this._initialSelection.endLineNumber) {
        selectionIsSet = true;
        this._selectionId = builder.trackSelection(this._initialSelection, true);
      } else if (this._edits[0].range.endColumn === this._initialSelection.startColumn && this._edits[0].range.endLineNumber === this._initialSelection.startLineNumber) {
        selectionIsSet = true;
        this._selectionId = builder.trackSelection(this._initialSelection, false);
      }
    }
    if (!selectionIsSet) {
      this._selectionId = builder.trackSelection(this._initialSelection);
    }
  }
  computeCursorState(model2, helper) {
    return helper.getTrackedSelection(this._selectionId);
  }
}
class AutoIndentOnPaste {
  constructor(editor) {
    this.callOnDispose = new lifecycle.DisposableStore();
    this.callOnModel = new lifecycle.DisposableStore();
    this.editor = editor;
    this.callOnDispose.add(editor.onDidChangeConfiguration(() => this.update()));
    this.callOnDispose.add(editor.onDidChangeModel(() => this.update()));
    this.callOnDispose.add(editor.onDidChangeModelLanguage(() => this.update()));
  }
  update() {
    this.callOnModel.clear();
    if (this.editor.getOption(
      9
      /* autoIndent */
    ) < 4 || this.editor.getOption(
      48
      /* formatOnPaste */
    )) {
      return;
    }
    if (!this.editor.hasModel()) {
      return;
    }
    this.callOnModel.add(this.editor.onDidPaste(({ range: range2 }) => {
      this.trigger(range2);
    }));
  }
  trigger(range$1) {
    let selections = this.editor.getSelections();
    if (selections === null || selections.length > 1) {
      return;
    }
    const model2 = this.editor.getModel();
    if (!model2) {
      return;
    }
    if (!model2.isCheapToTokenize(range$1.getStartPosition().lineNumber)) {
      return;
    }
    const autoIndent = this.editor.getOption(
      9
      /* autoIndent */
    );
    const { tabSize, indentSize, insertSpaces } = model2.getOptions();
    let textEdits = [];
    let indentConverter = {
      shiftIndent: (indentation2) => {
        return shiftCommand.ShiftCommand.shiftIndent(indentation2, indentation2.length + 1, tabSize, indentSize, insertSpaces);
      },
      unshiftIndent: (indentation2) => {
        return shiftCommand.ShiftCommand.unshiftIndent(indentation2, indentation2.length + 1, tabSize, indentSize, insertSpaces);
      }
    };
    let startLineNumber = range$1.startLineNumber;
    while (startLineNumber <= range$1.endLineNumber) {
      if (this.shouldIgnoreLine(model2, startLineNumber)) {
        startLineNumber++;
        continue;
      }
      break;
    }
    if (startLineNumber > range$1.endLineNumber) {
      return;
    }
    let firstLineText = model2.getLineContent(startLineNumber);
    if (!/\S/.test(firstLineText.substring(0, range$1.startColumn - 1))) {
      const indentOfFirstLine = languageConfigurationRegistry.LanguageConfigurationRegistry.getGoodIndentForLine(autoIndent, model2, model2.getLanguageId(), startLineNumber, indentConverter);
      if (indentOfFirstLine !== null) {
        let oldIndentation = strings.getLeadingWhitespace(firstLineText);
        let newSpaceCnt = indentUtils.getSpaceCnt(indentOfFirstLine, tabSize);
        let oldSpaceCnt = indentUtils.getSpaceCnt(oldIndentation, tabSize);
        if (newSpaceCnt !== oldSpaceCnt) {
          let newIndent = indentUtils.generateIndent(newSpaceCnt, tabSize, insertSpaces);
          textEdits.push({
            range: new range.Range(startLineNumber, 1, startLineNumber, oldIndentation.length + 1),
            text: newIndent
          });
          firstLineText = newIndent + firstLineText.substr(oldIndentation.length);
        } else {
          let indentMetadata = languageConfigurationRegistry.LanguageConfigurationRegistry.getIndentMetadata(model2, startLineNumber);
          if (indentMetadata === 0 || indentMetadata === 8) {
            return;
          }
        }
      }
    }
    const firstLineNumber = startLineNumber;
    while (startLineNumber < range$1.endLineNumber) {
      if (!/\S/.test(model2.getLineContent(startLineNumber + 1))) {
        startLineNumber++;
        continue;
      }
      break;
    }
    if (startLineNumber !== range$1.endLineNumber) {
      let virtualModel = {
        getLineTokens: (lineNumber) => {
          return model2.getLineTokens(lineNumber);
        },
        getLanguageId: () => {
          return model2.getLanguageId();
        },
        getLanguageIdAtPosition: (lineNumber, column) => {
          return model2.getLanguageIdAtPosition(lineNumber, column);
        },
        getLineContent: (lineNumber) => {
          if (lineNumber === firstLineNumber) {
            return firstLineText;
          } else {
            return model2.getLineContent(lineNumber);
          }
        }
      };
      let indentOfSecondLine = languageConfigurationRegistry.LanguageConfigurationRegistry.getGoodIndentForLine(autoIndent, virtualModel, model2.getLanguageId(), startLineNumber + 1, indentConverter);
      if (indentOfSecondLine !== null) {
        let newSpaceCntOfSecondLine = indentUtils.getSpaceCnt(indentOfSecondLine, tabSize);
        let oldSpaceCntOfSecondLine = indentUtils.getSpaceCnt(strings.getLeadingWhitespace(model2.getLineContent(startLineNumber + 1)), tabSize);
        if (newSpaceCntOfSecondLine !== oldSpaceCntOfSecondLine) {
          let spaceCntOffset = newSpaceCntOfSecondLine - oldSpaceCntOfSecondLine;
          for (let i = startLineNumber + 1; i <= range$1.endLineNumber; i++) {
            let lineContent = model2.getLineContent(i);
            let originalIndent = strings.getLeadingWhitespace(lineContent);
            let originalSpacesCnt = indentUtils.getSpaceCnt(originalIndent, tabSize);
            let newSpacesCnt = originalSpacesCnt + spaceCntOffset;
            let newIndent = indentUtils.generateIndent(newSpacesCnt, tabSize, insertSpaces);
            if (newIndent !== originalIndent) {
              textEdits.push({
                range: new range.Range(i, 1, i, originalIndent.length + 1),
                text: newIndent
              });
            }
          }
        }
      }
    }
    if (textEdits.length > 0) {
      this.editor.pushUndoStop();
      let cmd = new AutoIndentOnPasteCommand(textEdits, this.editor.getSelection());
      this.editor.executeCommand("autoIndentOnPaste", cmd);
      this.editor.pushUndoStop();
    }
  }
  shouldIgnoreLine(model2, lineNumber) {
    model2.forceTokenization(lineNumber);
    let nonWhitespaceColumn = model2.getLineFirstNonWhitespaceColumn(lineNumber);
    if (nonWhitespaceColumn === 0) {
      return true;
    }
    let tokens = model2.getLineTokens(lineNumber);
    if (tokens.getCount() > 0) {
      let firstNonWhitespaceTokenIndex = tokens.findTokenIndexAtOffset(nonWhitespaceColumn);
      if (firstNonWhitespaceTokenIndex >= 0 && tokens.getStandardTokenType(firstNonWhitespaceTokenIndex) === 1) {
        return true;
      }
    }
    return false;
  }
  dispose() {
    this.callOnDispose.dispose();
    this.callOnModel.dispose();
  }
}
AutoIndentOnPaste.ID = "editor.contrib.autoIndentOnPaste";
function getIndentationEditOperations(model2, builder, tabSize, tabsToSpaces) {
  if (model2.getLineCount() === 1 && model2.getLineMaxColumn(1) === 1) {
    return;
  }
  let spaces = "";
  for (let i = 0; i < tabSize; i++) {
    spaces += " ";
  }
  let spacesRegExp = new RegExp(spaces, "gi");
  for (let lineNumber = 1, lineCount = model2.getLineCount(); lineNumber <= lineCount; lineNumber++) {
    let lastIndentationColumn = model2.getLineFirstNonWhitespaceColumn(lineNumber);
    if (lastIndentationColumn === 0) {
      lastIndentationColumn = model2.getLineMaxColumn(lineNumber);
    }
    if (lastIndentationColumn === 1) {
      continue;
    }
    const originalIndentationRange = new range.Range(lineNumber, 1, lineNumber, lastIndentationColumn);
    const originalIndentation = model2.getValueInRange(originalIndentationRange);
    const newIndentation = tabsToSpaces ? originalIndentation.replace(/\t/ig, spaces) : originalIndentation.replace(spacesRegExp, "	");
    builder.addEditOperation(originalIndentationRange, newIndentation);
  }
}
class IndentationToSpacesCommand {
  constructor(selection2, tabSize) {
    this.selection = selection2;
    this.tabSize = tabSize;
    this.selectionId = null;
  }
  getEditOperations(model2, builder) {
    this.selectionId = builder.trackSelection(this.selection);
    getIndentationEditOperations(model2, builder, this.tabSize, true);
  }
  computeCursorState(model2, helper) {
    return helper.getTrackedSelection(this.selectionId);
  }
}
class IndentationToTabsCommand {
  constructor(selection2, tabSize) {
    this.selection = selection2;
    this.tabSize = tabSize;
    this.selectionId = null;
  }
  getEditOperations(model2, builder) {
    this.selectionId = builder.trackSelection(this.selection);
    getIndentationEditOperations(model2, builder, this.tabSize, false);
  }
  computeCursorState(model2, helper) {
    return helper.getTrackedSelection(this.selectionId);
  }
}
editorExtensions.registerEditorContribution(AutoIndentOnPaste.ID, AutoIndentOnPaste);
editorExtensions.registerEditorAction(IndentationToSpacesAction);
editorExtensions.registerEditorAction(IndentationToTabsAction);
editorExtensions.registerEditorAction(IndentUsingTabs);
editorExtensions.registerEditorAction(IndentUsingSpaces);
editorExtensions.registerEditorAction(DetectIndentation);
editorExtensions.registerEditorAction(ReindentLinesAction);
editorExtensions.registerEditorAction(ReindentSelectedLinesAction);
exports.AutoIndentOnPaste = AutoIndentOnPaste;
exports.AutoIndentOnPasteCommand = AutoIndentOnPasteCommand;
exports.ChangeIndentationSizeAction = ChangeIndentationSizeAction;
exports.DetectIndentation = DetectIndentation;
exports.IndentUsingSpaces = IndentUsingSpaces;
exports.IndentUsingTabs = IndentUsingTabs;
exports.IndentationToSpacesAction = IndentationToSpacesAction;
exports.IndentationToSpacesCommand = IndentationToSpacesCommand;
exports.IndentationToTabsAction = IndentationToTabsAction;
exports.IndentationToTabsCommand = IndentationToTabsCommand;
exports.ReindentLinesAction = ReindentLinesAction;
exports.ReindentSelectedLinesAction = ReindentSelectedLinesAction;
exports.getReindentEditOperations = getReindentEditOperations;
