"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const async = require("../../../../base/common/async.js");
const cancellation = require("../../../../base/common/cancellation.js");
const errors = require("../../../../base/common/errors.js");
const event = require("../../../../base/common/event.js");
const lifecycle = require("../../../../base/common/lifecycle.js");
const strings = require("../../../../base/common/strings.js");
const coreCommands = require("../../../browser/coreCommands.js");
const editOperation = require("../../../common/core/editOperation.js");
const range = require("../../../common/core/range.js");
const languages = require("../../../common/languages.js");
const ghostText = require("./ghostText.js");
const commands = require("../../../../platform/commands/common/commands.js");
const consts = require("./consts.js");
const inlineCompletionToGhostText = require("./inlineCompletionToGhostText.js");
const languageConfigurationRegistry = require("../../../common/languages/languageConfigurationRegistry.js");
const fixBrackets = require("../../../common/model/bracketPairsTextModelPart/fixBrackets.js");
const languageFeatures = require("../../../common/services/languageFeatures.js");
const languageFeatureDebounce = require("../../../common/services/languageFeatureDebounce.js");
var __decorate = globalThis && globalThis.__decorate || function(decorators, target, key, desc) {
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function")
    r = Reflect.decorate(decorators, target, key, desc);
  else
    for (var i = decorators.length - 1; i >= 0; i--)
      if (d = decorators[i])
        r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
  return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param = globalThis && globalThis.__param || function(paramIndex, decorator) {
  return function(target, key) {
    decorator(target, key, paramIndex);
  };
};
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());
  });
};
exports.InlineCompletionsModel = class InlineCompletionsModel extends lifecycle.Disposable {
  constructor(editor, cache, commandService, languageConfigurationService, languageFeaturesService, debounceService) {
    super();
    this.editor = editor;
    this.cache = cache;
    this.commandService = commandService;
    this.languageConfigurationService = languageConfigurationService;
    this.languageFeaturesService = languageFeaturesService;
    this.debounceService = debounceService;
    this.onDidChangeEmitter = new event.Emitter();
    this.onDidChange = this.onDidChangeEmitter.event;
    this.completionSession = this._register(new lifecycle.MutableDisposable());
    this.active = false;
    this.disposed = false;
    this.debounceValue = this.debounceService.for(this.languageFeaturesService.inlineCompletionsProvider, "InlineCompletionsDebounce", { min: 50, max: 200 });
    this._register(commandService.onDidExecuteCommand((e) => {
      const commands2 = /* @__PURE__ */ new Set([
        coreCommands.CoreEditingCommands.Tab.id,
        coreCommands.CoreEditingCommands.DeleteLeft.id,
        coreCommands.CoreEditingCommands.DeleteRight.id,
        consts.inlineSuggestCommitId,
        "acceptSelectedSuggestion"
      ]);
      if (commands2.has(e.commandId) && editor.hasTextFocus()) {
        this.handleUserInput();
      }
    }));
    this._register(this.editor.onDidType((e) => {
      this.handleUserInput();
    }));
    this._register(this.editor.onDidChangeCursorPosition((e) => {
      if (this.session && !this.session.isValid) {
        this.hide();
      }
    }));
    this._register(lifecycle.toDisposable(() => {
      this.disposed = true;
    }));
    this._register(this.editor.onDidBlurEditorWidget(() => {
      this.hide();
    }));
  }
  handleUserInput() {
    if (this.session && !this.session.isValid) {
      this.hide();
    }
    setTimeout(() => {
      if (this.disposed) {
        return;
      }
      this.startSessionIfTriggered();
    }, 0);
  }
  get session() {
    return this.completionSession.value;
  }
  get ghostText() {
    var _a;
    return (_a = this.session) === null || _a === void 0 ? void 0 : _a.ghostText;
  }
  get minReservedLineCount() {
    return this.session ? this.session.minReservedLineCount : 0;
  }
  setExpanded(expanded) {
    var _a;
    (_a = this.session) === null || _a === void 0 ? void 0 : _a.setExpanded(expanded);
  }
  setActive(active) {
    var _a;
    this.active = active;
    if (active) {
      (_a = this.session) === null || _a === void 0 ? void 0 : _a.scheduleAutomaticUpdate();
    }
  }
  startSessionIfTriggered() {
    const suggestOptions = this.editor.getOption(
      55
      /* inlineSuggest */
    );
    if (!suggestOptions.enabled) {
      return;
    }
    if (this.session && this.session.isValid) {
      return;
    }
    this.trigger(languages.InlineCompletionTriggerKind.Automatic);
  }
  trigger(triggerKind) {
    if (this.completionSession.value) {
      if (triggerKind === languages.InlineCompletionTriggerKind.Explicit) {
        void this.completionSession.value.ensureUpdateWithExplicitContext();
      }
      return;
    }
    this.completionSession.value = new InlineCompletionsSession(this.editor, this.editor.getPosition(), () => this.active, this.commandService, this.cache, triggerKind, this.languageConfigurationService, this.languageFeaturesService.inlineCompletionsProvider, this.debounceValue);
    this.completionSession.value.takeOwnership(this.completionSession.value.onDidChange(() => {
      this.onDidChangeEmitter.fire();
    }));
  }
  hide() {
    this.completionSession.clear();
    this.onDidChangeEmitter.fire();
  }
  commitCurrentSuggestion() {
    var _a;
    (_a = this.session) === null || _a === void 0 ? void 0 : _a.commitCurrentCompletion();
  }
  showNext() {
    var _a;
    (_a = this.session) === null || _a === void 0 ? void 0 : _a.showNextInlineCompletion();
  }
  showPrevious() {
    var _a;
    (_a = this.session) === null || _a === void 0 ? void 0 : _a.showPreviousInlineCompletion();
  }
  hasMultipleInlineCompletions() {
    var _a;
    return __awaiter(this, void 0, void 0, function* () {
      const result = yield (_a = this.session) === null || _a === void 0 ? void 0 : _a.hasMultipleInlineCompletions();
      return result !== void 0 ? result : false;
    });
  }
};
exports.InlineCompletionsModel = __decorate([
  __param(2, commands.ICommandService),
  __param(3, languageConfigurationRegistry.ILanguageConfigurationService),
  __param(4, languageFeatures.ILanguageFeaturesService),
  __param(5, languageFeatureDebounce.ILanguageFeatureDebounceService)
], exports.InlineCompletionsModel);
class InlineCompletionsSession extends ghostText.BaseGhostTextWidgetModel {
  constructor(editor, triggerPosition, shouldUpdate, commandService, cache, initialTriggerKind, languageConfigurationService, registry, debounce) {
    super(editor);
    this.triggerPosition = triggerPosition;
    this.shouldUpdate = shouldUpdate;
    this.commandService = commandService;
    this.cache = cache;
    this.initialTriggerKind = initialTriggerKind;
    this.languageConfigurationService = languageConfigurationService;
    this.registry = registry;
    this.debounce = debounce;
    this.minReservedLineCount = 0;
    this.updateOperation = this._register(new lifecycle.MutableDisposable());
    this.updateSoon = this._register(new async.RunOnceScheduler(() => {
      let triggerKind = this.initialTriggerKind;
      this.initialTriggerKind = languages.InlineCompletionTriggerKind.Automatic;
      return this.update(triggerKind);
    }, 50));
    this.currentlySelectedCompletionId = void 0;
    let lastCompletionItem = void 0;
    this._register(this.onDidChange(() => {
      const currentCompletion = this.currentCompletion;
      if (currentCompletion && currentCompletion.sourceInlineCompletion !== lastCompletionItem) {
        lastCompletionItem = currentCompletion.sourceInlineCompletion;
        const provider = currentCompletion.sourceProvider;
        if (provider.handleItemDidShow) {
          provider.handleItemDidShow(currentCompletion.sourceInlineCompletions, lastCompletionItem);
        }
      }
    }));
    this._register(lifecycle.toDisposable(() => {
      this.cache.clear();
    }));
    this._register(this.editor.onDidChangeCursorPosition((e) => {
      if (this.cache.value) {
        this.onDidChangeEmitter.fire();
      }
    }));
    this._register(this.editor.onDidChangeModelContent((e) => {
      this.scheduleAutomaticUpdate();
    }));
    this._register(this.registry.onDidChange(() => {
      this.updateSoon.schedule(this.debounce.get(this.editor.getModel()));
    }));
    this.scheduleAutomaticUpdate();
  }
  fixAndGetIndexOfCurrentSelection() {
    if (!this.currentlySelectedCompletionId || !this.cache.value) {
      return 0;
    }
    if (this.cache.value.completions.length === 0) {
      return 0;
    }
    const idx = this.cache.value.completions.findIndex((v) => v.semanticId === this.currentlySelectedCompletionId);
    if (idx === -1) {
      this.currentlySelectedCompletionId = void 0;
      return 0;
    }
    return idx;
  }
  get currentCachedCompletion() {
    if (!this.cache.value) {
      return void 0;
    }
    return this.cache.value.completions[this.fixAndGetIndexOfCurrentSelection()];
  }
  showNextInlineCompletion() {
    var _a;
    return __awaiter(this, void 0, void 0, function* () {
      yield this.ensureUpdateWithExplicitContext();
      const completions = ((_a = this.cache.value) === null || _a === void 0 ? void 0 : _a.completions) || [];
      if (completions.length > 0) {
        const newIdx = (this.fixAndGetIndexOfCurrentSelection() + 1) % completions.length;
        this.currentlySelectedCompletionId = completions[newIdx].semanticId;
      } else {
        this.currentlySelectedCompletionId = void 0;
      }
      this.onDidChangeEmitter.fire();
    });
  }
  showPreviousInlineCompletion() {
    var _a;
    return __awaiter(this, void 0, void 0, function* () {
      yield this.ensureUpdateWithExplicitContext();
      const completions = ((_a = this.cache.value) === null || _a === void 0 ? void 0 : _a.completions) || [];
      if (completions.length > 0) {
        const newIdx = (this.fixAndGetIndexOfCurrentSelection() + completions.length - 1) % completions.length;
        this.currentlySelectedCompletionId = completions[newIdx].semanticId;
      } else {
        this.currentlySelectedCompletionId = void 0;
      }
      this.onDidChangeEmitter.fire();
    });
  }
  ensureUpdateWithExplicitContext() {
    var _a;
    return __awaiter(this, void 0, void 0, function* () {
      if (this.updateOperation.value) {
        if (this.updateOperation.value.triggerKind === languages.InlineCompletionTriggerKind.Explicit) {
          yield this.updateOperation.value.promise;
        } else {
          yield this.update(languages.InlineCompletionTriggerKind.Explicit);
        }
      } else if (((_a = this.cache.value) === null || _a === void 0 ? void 0 : _a.triggerKind) !== languages.InlineCompletionTriggerKind.Explicit) {
        yield this.update(languages.InlineCompletionTriggerKind.Explicit);
      }
    });
  }
  hasMultipleInlineCompletions() {
    var _a;
    return __awaiter(this, void 0, void 0, function* () {
      yield this.ensureUpdateWithExplicitContext();
      return (((_a = this.cache.value) === null || _a === void 0 ? void 0 : _a.completions.length) || 0) > 1;
    });
  }
  //#endregion
  get ghostText() {
    const currentCompletion = this.currentCompletion;
    const mode = this.editor.getOptions().get(
      55
      /* inlineSuggest */
    ).mode;
    return currentCompletion ? inlineCompletionToGhostText.inlineCompletionToGhostText(currentCompletion, this.editor.getModel(), mode, this.editor.getPosition()) : void 0;
  }
  get currentCompletion() {
    const completion = this.currentCachedCompletion;
    if (!completion) {
      return void 0;
    }
    return completion.toLiveInlineCompletion();
  }
  get isValid() {
    return this.editor.getPosition().lineNumber === this.triggerPosition.lineNumber;
  }
  scheduleAutomaticUpdate() {
    this.updateOperation.clear();
    this.updateSoon.schedule(this.debounce.get(this.editor.getModel()));
  }
  update(triggerKind) {
    return __awaiter(this, void 0, void 0, function* () {
      if (!this.shouldUpdate()) {
        return;
      }
      const position = this.editor.getPosition();
      const startTime = /* @__PURE__ */ new Date();
      const promise = async.createCancelablePromise((token) => __awaiter(this, void 0, void 0, function* () {
        let result;
        try {
          result = yield provideInlineCompletions(this.registry, position, this.editor.getModel(), { triggerKind, selectedSuggestionInfo: void 0 }, token, this.languageConfigurationService);
          const endTime = /* @__PURE__ */ new Date();
          this.debounce.update(this.editor.getModel(), endTime.getTime() - startTime.getTime());
        } catch (e) {
          errors.onUnexpectedError(e);
          return;
        }
        if (token.isCancellationRequested) {
          return;
        }
        this.cache.setValue(this.editor, result, triggerKind);
        this.onDidChangeEmitter.fire();
      }));
      const operation = new UpdateOperation(promise, triggerKind);
      this.updateOperation.value = operation;
      yield promise;
      if (this.updateOperation.value === operation) {
        this.updateOperation.clear();
      }
    });
  }
  takeOwnership(disposable) {
    this._register(disposable);
  }
  commitCurrentCompletion() {
    if (!this.ghostText) {
      return;
    }
    const completion = this.currentCompletion;
    if (completion) {
      this.commit(completion);
    }
  }
  commit(completion) {
    const cache = this.cache.clearAndLeak();
    this.editor.executeEdits("inlineSuggestion.accept", [
      editOperation.EditOperation.replaceMove(completion.range, completion.text)
    ]);
    if (completion.command) {
      this.commandService.executeCommand(completion.command.id, ...completion.command.arguments || []).finally(() => {
        cache === null || cache === void 0 ? void 0 : cache.dispose();
      }).then(void 0, errors.onUnexpectedExternalError);
    } else {
      cache === null || cache === void 0 ? void 0 : cache.dispose();
    }
    this.onDidChangeEmitter.fire();
  }
}
class UpdateOperation {
  constructor(promise, triggerKind) {
    this.promise = promise;
    this.triggerKind = triggerKind;
  }
  dispose() {
    this.promise.cancel();
  }
}
class SynchronizedInlineCompletionsCache extends lifecycle.Disposable {
  constructor(editor, completionsSource, onChange, triggerKind) {
    super();
    this.triggerKind = triggerKind;
    const decorationIds = editor.deltaDecorations([], completionsSource.items.map((i) => ({
      range: i.range,
      options: {
        description: "inline-completion-tracking-range"
      }
    })));
    this._register(lifecycle.toDisposable(() => {
      editor.deltaDecorations(decorationIds, []);
    }));
    this.completions = completionsSource.items.map((c, idx) => new CachedInlineCompletion(c, decorationIds[idx]));
    this._register(editor.onDidChangeModelContent(() => {
      let hasChanged = false;
      const model = editor.getModel();
      for (const c of this.completions) {
        const newRange = model.getDecorationRange(c.decorationId);
        if (!newRange) {
          errors.onUnexpectedError(new Error("Decoration has no range"));
          continue;
        }
        if (!c.synchronizedRange.equalsRange(newRange)) {
          hasChanged = true;
          c.synchronizedRange = newRange;
        }
      }
      if (hasChanged) {
        onChange();
      }
    }));
    this._register(completionsSource);
  }
}
class CachedInlineCompletion {
  constructor(inlineCompletion, decorationId) {
    this.inlineCompletion = inlineCompletion;
    this.decorationId = decorationId;
    this.semanticId = JSON.stringify({
      text: this.inlineCompletion.text,
      startLine: this.inlineCompletion.range.startLineNumber,
      startColumn: this.inlineCompletion.range.startColumn,
      command: this.inlineCompletion.command
    });
    this.synchronizedRange = inlineCompletion.range;
  }
  toLiveInlineCompletion() {
    return {
      text: this.inlineCompletion.text,
      range: this.synchronizedRange,
      command: this.inlineCompletion.command,
      sourceProvider: this.inlineCompletion.sourceProvider,
      sourceInlineCompletions: this.inlineCompletion.sourceInlineCompletions,
      sourceInlineCompletion: this.inlineCompletion.sourceInlineCompletion
    };
  }
}
function getDefaultRange(position, model) {
  const word = model.getWordAtPosition(position);
  const maxColumn = model.getLineMaxColumn(position.lineNumber);
  return word ? new range.Range(position.lineNumber, word.startColumn, position.lineNumber, maxColumn) : range.Range.fromPositions(position, position.with(void 0, maxColumn));
}
function provideInlineCompletions(registry, position, model, context, token = cancellation.CancellationToken.None, languageConfigurationService) {
  return __awaiter(this, void 0, void 0, function* () {
    const defaultReplaceRange = getDefaultRange(position, model);
    const providers = registry.all(model);
    const results = yield Promise.all(providers.map((provider) => __awaiter(this, void 0, void 0, function* () {
      const completions = yield Promise.resolve(provider.provideInlineCompletions(model, position, context, token)).catch(errors.onUnexpectedExternalError);
      return {
        completions,
        provider,
        dispose: () => {
          if (completions) {
            provider.freeInlineCompletions(completions);
          }
        }
      };
    })));
    const itemsByHash = /* @__PURE__ */ new Map();
    for (const result of results) {
      const completions = result.completions;
      if (completions) {
        for (const item of completions.items) {
          const range$1 = item.range ? range.Range.lift(item.range) : defaultReplaceRange;
          if (range$1.startLineNumber !== range$1.endLineNumber) {
            continue;
          }
          const text = languageConfigurationService && item.completeBracketPairs ? closeBrackets(item.text, range$1.getStartPosition(), model, languageConfigurationService) : item.text;
          const trackedItem = {
            text,
            range: range$1,
            command: item.command,
            sourceProvider: result.provider,
            sourceInlineCompletions: completions,
            sourceInlineCompletion: item
          };
          itemsByHash.set(JSON.stringify({ text, range: item.range }), trackedItem);
        }
      }
    }
    return {
      items: [...itemsByHash.values()],
      dispose: () => {
        for (const result of results) {
          result.dispose();
        }
      }
    };
  });
}
function closeBrackets(text, position, model, languageConfigurationService) {
  const lineStart = model.getLineContent(position.lineNumber).substring(0, position.column - 1);
  const newLine = lineStart + text;
  const newTokens = model.tokenizeLineWithEdit(position, newLine.length - (position.column - 1), text);
  const slicedTokens = newTokens === null || newTokens === void 0 ? void 0 : newTokens.sliceAndInflate(position.column - 1, newLine.length, 0);
  if (!slicedTokens) {
    return text;
  }
  console.log(slicedTokens);
  const newText = fixBrackets.fixBracketsInLine(slicedTokens, languageConfigurationService);
  return newText;
}
function minimizeInlineCompletion(model, inlineCompletion) {
  if (!inlineCompletion) {
    return inlineCompletion;
  }
  const valueToReplace = model.getValueInRange(inlineCompletion.range);
  const commonPrefixLen = strings.commonPrefixLength(valueToReplace, inlineCompletion.text);
  const startOffset = model.getOffsetAt(inlineCompletion.range.getStartPosition()) + commonPrefixLen;
  const start = model.getPositionAt(startOffset);
  const remainingValueToReplace = valueToReplace.substr(commonPrefixLen);
  const commonSuffixLen = strings.commonSuffixLength(remainingValueToReplace, inlineCompletion.text);
  const end = model.getPositionAt(Math.max(startOffset, model.getOffsetAt(inlineCompletion.range.getEndPosition()) - commonSuffixLen));
  return {
    range: range.Range.fromPositions(start, end),
    text: inlineCompletion.text.substr(commonPrefixLen, inlineCompletion.text.length - commonPrefixLen - commonSuffixLen)
  };
}
exports.InlineCompletionsSession = InlineCompletionsSession;
exports.SynchronizedInlineCompletionsCache = SynchronizedInlineCompletionsCache;
exports.UpdateOperation = UpdateOperation;
exports.minimizeInlineCompletion = minimizeInlineCompletion;
exports.provideInlineCompletions = provideInlineCompletions;
