"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const errors = require("../../../../base/common/errors.js");
const lifecycle = require("../../../../base/common/lifecycle.js");
const position = require("../../../common/core/position.js");
const range = require("../../../common/core/range.js");
const network = require("../../../../base/common/network.js");
const uri = require("../../../../base/common/uri.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());
  });
};
class InlayHintAnchor {
  constructor(range2, direction) {
    this.range = range2;
    this.direction = direction;
  }
}
class InlayHintItem {
  constructor(hint, anchor, provider) {
    this.hint = hint;
    this.anchor = anchor;
    this.provider = provider;
    this._isResolved = false;
  }
  with(delta) {
    const result = new InlayHintItem(this.hint, delta.anchor, this.provider);
    result._isResolved = this._isResolved;
    result._currentResolve = this._currentResolve;
    return result;
  }
  resolve(token) {
    return __awaiter(this, void 0, void 0, function* () {
      if (typeof this.provider.resolveInlayHint !== "function") {
        return;
      }
      if (this._currentResolve) {
        yield this._currentResolve;
        if (token.isCancellationRequested) {
          return;
        }
        return this.resolve(token);
      }
      if (!this._isResolved) {
        this._currentResolve = this._doResolve(token).finally(() => this._currentResolve = void 0);
      }
      yield this._currentResolve;
    });
  }
  _doResolve(token) {
    var _a, _b;
    return __awaiter(this, void 0, void 0, function* () {
      try {
        const newHint = yield Promise.resolve(this.provider.resolveInlayHint(this.hint, token));
        this.hint.tooltip = (_a = newHint === null || newHint === void 0 ? void 0 : newHint.tooltip) !== null && _a !== void 0 ? _a : this.hint.tooltip;
        this.hint.label = (_b = newHint === null || newHint === void 0 ? void 0 : newHint.label) !== null && _b !== void 0 ? _b : this.hint.label;
        this._isResolved = true;
      } catch (err) {
        errors.onUnexpectedExternalError(err);
        this._isResolved = false;
      }
    });
  }
}
class InlayHintsFragments {
  constructor(ranges, data, model) {
    this._disposables = new lifecycle.DisposableStore();
    this.ranges = ranges;
    this.provider = /* @__PURE__ */ new Set();
    const items = [];
    for (const [list, provider] of data) {
      this._disposables.add(list);
      this.provider.add(provider);
      for (const hint of list.hints) {
        let position2 = model.validatePosition(hint.position);
        let direction = "before";
        const wordRange = InlayHintsFragments._getRangeAtPosition(model, position2);
        let range$1;
        if (wordRange.getStartPosition().isBefore(position2)) {
          range$1 = range.Range.fromPositions(wordRange.getStartPosition(), position2);
          direction = "after";
        } else {
          range$1 = range.Range.fromPositions(position2, wordRange.getEndPosition());
          direction = "before";
        }
        items.push(new InlayHintItem(hint, new InlayHintAnchor(range$1, direction), provider));
      }
    }
    this.items = items.sort((a, b) => position.Position.compare(a.hint.position, b.hint.position));
  }
  static create(registry, model, ranges, token) {
    return __awaiter(this, void 0, void 0, function* () {
      const data = [];
      const promises = registry.ordered(model).reverse().map((provider) => ranges.map((range2) => __awaiter(this, void 0, void 0, function* () {
        try {
          const result = yield provider.provideInlayHints(model, range2, token);
          if (result === null || result === void 0 ? void 0 : result.hints.length) {
            data.push([result, provider]);
          }
        } catch (err) {
          errors.onUnexpectedExternalError(err);
        }
      })));
      yield Promise.all(promises.flat());
      if (token.isCancellationRequested || model.isDisposed()) {
        throw new errors.CancellationError();
      }
      return new InlayHintsFragments(ranges, data, model);
    });
  }
  dispose() {
    this._disposables.dispose();
  }
  static _getRangeAtPosition(model, position2) {
    const line = position2.lineNumber;
    const word = model.getWordAtPosition(position2);
    if (word) {
      return new range.Range(line, word.startColumn, line, word.endColumn);
    }
    model.tokenizeIfCheap(line);
    const tokens = model.getLineTokens(line);
    const offset = position2.column - 1;
    const idx = tokens.findTokenIndexAtOffset(offset);
    let start = tokens.getStartOffset(idx);
    let end = tokens.getEndOffset(idx);
    if (end - start === 1) {
      if (start === offset && idx > 1) {
        start = tokens.getStartOffset(idx - 1);
        end = tokens.getEndOffset(idx - 1);
      } else if (end === offset && idx < tokens.getCount() - 1) {
        start = tokens.getStartOffset(idx + 1);
        end = tokens.getEndOffset(idx + 1);
      }
    }
    return new range.Range(line, start + 1, line, end + 1);
  }
}
function asCommandLink(command) {
  return uri.URI.from({
    scheme: network.Schemas.command,
    path: command.id,
    query: command.arguments && encodeURIComponent(JSON.stringify(command.arguments))
  }).toString();
}
exports.InlayHintAnchor = InlayHintAnchor;
exports.InlayHintItem = InlayHintItem;
exports.InlayHintsFragments = InlayHintsFragments;
exports.asCommandLink = asCommandLink;
