var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// src/index.ts
var src_exports = {};
__export(src_exports, {
  activate: () => activate
});
module.exports = __toCommonJS(src_exports);
var import_coc2 = require("coc.nvim");

// src/bufferItem.ts
var import_coc = require("coc.nvim");
var import_util = require("util");
var ColorBuffer = class {
  constructor(doc, config, output, manager) {
    this.doc = doc;
    this.config = config;
    this.output = output;
    this.manager = manager;
    this.tokenSource = new import_coc.CancellationTokenSource();
    this.mutex = new import_coc.Mutex();
    if (!this.config.colorsEnable) {
      this.warn(`Color highlight disabled by "highlight.colors.enable": false`);
    } else if (this.isDisabled) {
      this.warn(`Color highlight of ${doc.uri} ignored by "highlight.disableLanguages"`);
    }
  }
  async getColorInformation(token) {
    let colors;
    let timer;
    let disposable;
    if (!this.colors)
      void this.getColors();
    await new Promise((resolve) => {
      disposable = token.onCancellationRequested(() => {
        resolve();
      });
      timer = setTimeout(() => {
        resolve();
      }, 1e3);
      let clean = () => {
        clearTimeout(timer);
        if (disposable)
          disposable.dispose();
      };
      this.mutex.acquire().then((release) => {
        clean();
        release();
        if (this.colors) {
          colors = [];
          for (let i = 0; i < this.colors.length; i++) {
            let arr = this.colors[i];
            for (let item of arr) {
              colors.push({
                color: item.color,
                range: import_coc.Range.create(i, item.span[0], i, item.span[1])
              });
            }
          }
        }
        resolve();
      }, () => {
        clean();
        resolve();
      });
    });
    return colors;
  }
  cancel() {
    if (this.tokenSource) {
      this.tokenSource.cancel();
    }
    this.manager.cancel(this.doc.bufnr);
  }
  get isDisabled() {
    if (!this.config.colorsEnable)
      return true;
    if (this.config.disableLanguages.includes(this.doc.filetype))
      return true;
    return false;
  }
  onChange(e) {
    if (e.contentChanges.length == 0 || !this.colors)
      return;
    void this.getColors(e);
  }
  checkColorsList(list, start) {
    let n = 0;
    for (let arr of list) {
      n = n + arr.length;
    }
    this.debug(`Parsed ${n} colors from buffer ${this.doc.bufnr} cost ${Date.now() - start}ms`);
  }
  async getColors(e) {
    let { doc } = this;
    if (!doc.attached || this.isDisabled)
      return;
    let { bufnr } = doc;
    let textDocument = doc.textDocument;
    await this.mutex.use(async () => {
      if (!doc.attached)
        return;
      try {
        let st = Date.now();
        if (!this.colors) {
          let len = textDocument.lines.length;
          let colorsList = await this.manager.parseLines(bufnr, textDocument.lines, { colorNamesEnable: this.config.colorNamesEnable });
          if (colorsList && colorsList.length == len) {
            this.colors = colorsList;
            this.checkColorsList(colorsList, st);
          }
        } else if (e) {
          let { range, text } = e.contentChanges[0];
          let { start, end } = range;
          let sl = start.line;
          let el = end.line;
          let del = el - sl;
          let newLines = textDocument.lines.slice(sl, sl + text.split(/\n/).length);
          let colorsList = await this.manager.parseLines(bufnr, newLines, { colorNamesEnable: this.config.colorNamesEnable });
          if (colorsList && colorsList.length === newLines.length) {
            this.colors.splice(sl, del + 1, ...colorsList);
            this.checkColorsList(colorsList, st);
          }
        }
      } catch (err) {
        if (err instanceof Error && err.message.includes("Cancelled"))
          return;
        this.error(`Error on getColors`, err);
      }
    });
  }
  info(message, data) {
    this.logLevel("Info", message, data);
  }
  debug(message, data) {
    if (this.config.trace !== "verbose")
      return;
    this.logLevel("Debug", message, data);
  }
  warn(message, data) {
    this.logLevel("Warn", message, data);
  }
  error(message, data) {
    this.logLevel("Error", message, data);
  }
  logLevel(level, message, data) {
    if (this.config.trace === "off" && level !== "Error")
      return;
    this.output.appendLine(
      `[${level} - ${now()}] ${message}`
    );
    if (data)
      this.output.appendLine(this.data2String(data));
  }
  data2String(data) {
    if (data instanceof Error) {
      if (typeof data.stack === "string") {
        return data.stack;
      }
      return data.message;
    }
    if (typeof data === "string" || typeof data === "boolean") {
      return data.toString();
    }
    return (0, import_util.inspect)(data, { maxArrayLength: 5 });
  }
  dispose() {
    this.cancel();
  }
};
function now() {
  const now2 = new Date();
  return padLeft(now2.getUTCHours() + "", 2, "0") + ":" + padLeft(now2.getMinutes() + "", 2, "0") + ":" + padLeft(now2.getUTCSeconds() + "", 2, "0") + "." + now2.getMilliseconds();
}
function padLeft(s, n, pad = " ") {
  return pad.repeat(Math.max(0, n - s.length)) + s;
}

// src/workers.ts
var import_worker_threads = require("worker_threads");
var import_os = __toESM(require("os"));
var WORKER_COUNT = Math.min(4, import_os.default.cpus().length);
var createUID = (() => {
  let id = 0;
  return () => {
    return id++;
  };
})();
var WorkersManager = class {
  constructor(file, logger) {
    this.file = file;
    this.logger = logger;
    this.workers = [];
    this._disposed = false;
    this.callbacks = /* @__PURE__ */ new Map();
    for (let i = 0; i < WORKER_COUNT; i++) {
      this.createWorker();
    }
  }
  createWorker() {
    let worker = new import_worker_threads.Worker(this.file, {
      stdout: true,
      stderr: true
    });
    let item = { busy: false, worker };
    this.workers.push(item);
    worker.on("error", (err) => {
      this.logger.error(`Worker error ${err.message}`, err.stack);
    });
    worker.on("exit", () => {
      if (this._disposed)
        return;
      let idx = this.workers.findIndex((o) => o === item);
      if (idx !== -1)
        this.workers.splice(idx, 1);
    });
    worker.on("message", (value) => {
      let cb;
      if (typeof value.id === "number") {
        cb = this.callbacks.get(value.id);
        if (cb)
          this.callbacks.delete(value.id);
      }
      if (value.kind === "error") {
        this.logger.error(`Worker error ${value.error}`, value.stack);
        if (cb) {
          let err = new Error(value.error);
          if (value.stack)
            err.stack = value.stack;
          cb(void 0, err);
        }
      } else if (value.kind === "result") {
        if (cb) {
          if (value.error) {
            let err = new Error(value.error);
            if (value.stack)
              err.stack = value.stack;
            cb(void 0, err);
          } else {
            cb(value.result);
          }
        }
      } else {
        this.logger.error(`Unknown message from worker`, value);
      }
    });
    return item;
  }
  getWorker(bufnr) {
    let item = this.workers.find((o) => o.bufnr === bufnr);
    if (item)
      return item;
    item = this.workers.find((o) => !o.busy);
    if (item)
      return item;
    item = this.createWorker();
    return item;
  }
  async parseLines(bufnr, lines, opts) {
    let item = this.getWorker(bufnr);
    item.bufnr = bufnr;
    item.busy = true;
    let { worker } = item;
    let exited = false;
    let res = await new Promise((resolve, reject) => {
      let id = createUID();
      let fn = () => {
        this.callbacks.delete(id);
        exited = true;
        resolve(void 0);
      };
      worker.once("exit", fn);
      this.callbacks.set(id, (result, error) => {
        worker.removeListener("exit", fn);
        if (error)
          return reject(error);
        resolve(result);
      });
      worker.postMessage({ id, kind: "parse", colorNamesEnable: opts.colorNamesEnable, lines });
    });
    if (this.workers.length > WORKER_COUNT && !exited) {
      item.worker.terminate();
    } else {
      item.busy = false;
    }
    return res;
  }
  cancel(bufnr) {
    let item = this.workers.find((o) => o.bufnr === bufnr);
    if (item) {
      item.worker.postMessage({ kind: "cancel" });
      item.busy = false;
    }
  }
  dispose() {
    this._disposed = true;
    this.workers.forEach((item) => {
      item.worker.terminate();
    });
    this.workers = [];
  }
};

// src/index.ts
async function activate(context) {
  let { subscriptions, logger } = context;
  const workerFile = context.asAbsolutePath("./lib/worker.js");
  const workers = new WorkersManager(workerFile, logger);
  subscriptions.push(workers);
  let settings;
  function getSettings() {
    const config = import_coc2.workspace.getConfiguration("highlight");
    let conf = {
      trace: config.get("trace", "messages"),
      highlightEnable: config.get("document.enable", true),
      colorsEnable: config.get("colors.enable", true),
      colorNamesEnable: config.get("colorNames.enable", true),
      disableLanguages: config.get("disableLanguages", [])
    };
    if (settings) {
      if (settings.colorsEnable != conf.colorsEnable) {
        void import_coc2.window.showWarningMessage(`Reload window by :CocRestart required for change of highlight.colors.enable`);
      }
      Object.assign(settings, conf);
    } else {
      settings = conf;
    }
  }
  getSettings();
  subscriptions.push(import_coc2.workspace.onDidChangeConfiguration((e) => {
    if (e.affectsConfiguration("highlight")) {
      getSettings();
    }
  }));
  const output = import_coc2.window.createOutputChannel("highlight");
  subscriptions.push(output);
  let bufferItems = import_coc2.workspace.registerBufferSync((doc) => {
    return new ColorBuffer(doc, settings, output, workers);
  });
  subscriptions.push(bufferItems);
  function enableColors() {
    subscriptions.push(import_coc2.languages.registerDocumentColorProvider(["*"], {
      provideDocumentColors(document, token) {
        let item = bufferItems.getItem(document.uri);
        return item.getColorInformation(token);
      },
      provideColorPresentations: () => {
        return null;
      }
    }));
  }
  function enableDocumentHighlight() {
    if (typeof import_coc2.workspace.computeWordRanges !== "function") {
      output.appendLine(`workspace.computeWordRanges is not a function, please update coc.nvim.`);
      return;
    }
    subscriptions.push(import_coc2.languages.registerDocumentHighlightProvider(["*"], {
      provideDocumentHighlights: async (textDocument, pos, token) => {
        if (settings.disableLanguages.includes(textDocument.languageId))
          return;
        let doc = import_coc2.workspace.getDocument(textDocument.uri);
        if (!doc)
          return;
        let range = doc.getWordRangeAtPosition(pos);
        if (!range)
          return;
        let line = doc.getline(pos.line);
        let word = line.slice(range.start.character, range.end.character);
        let sl = Math.max(0, pos.line - 300);
        let el = Math.min(pos.line + 300, doc.lineCount);
        let res = await import_coc2.workspace.computeWordRanges(doc.uri, import_coc2.Range.create(sl, 0, el, 0), token);
        let ranges = res[word];
        if (Array.isArray(ranges))
          return ranges.map((r) => {
            return { range: r };
          });
      }
    }));
  }
  if (settings.colorsEnable) {
    enableColors();
  }
  if (settings.highlightEnable) {
    enableDocumentHighlight();
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  activate
});
