"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.getDocument = getDocument;
exports.hasDocument = hasDocument;
exports.setDocument = setDocument;
exports.removeDocument = removeDocument;
exports.clearDocuments = clearDocuments;
exports.clearDocumentsForSources = clearDocumentsForSources;
exports.resetLineNumberFormat = resetLineNumberFormat;
exports.getMode = getMode;
exports.showSourceText = showSourceText;
loader.lazyRequireGetter(this, "_wasm", "devtools/client/debugger/src/utils/wasm");
loader.lazyRequireGetter(this, "_isMinified", "devtools/client/debugger/src/utils/isMinified");
loader.lazyRequireGetter(this, "_ui", "devtools/client/debugger/src/utils/ui");
loader.lazyRequireGetter(this, "_source", "devtools/client/debugger/src/utils/source");

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
const sourceDocs = new Map();

function getDocument(key) {
  return sourceDocs.get(key);
}

function hasDocument(key) {
  return sourceDocs.has(key);
}

function setDocument(key, doc) {
  sourceDocs.set(key, doc);
}

function removeDocument(key) {
  sourceDocs.delete(key);
}

function clearDocuments() {
  sourceDocs.clear();
}

function clearDocumentsForSources(sources) {
  for (const source of sources) {
    sourceDocs.delete(source.id);
  }
}

function resetLineNumberFormat(editor) {
  const cm = editor.codeMirror;
  cm.setOption("lineNumberFormatter", number => number);
  (0, _ui.resizeBreakpointGutter)(cm);
  (0, _ui.resizeToggleButton)((0, _ui.getLineNumberWidth)(cm));
}

function updateLineNumberFormat(editor, sourceId) {
  if (!(0, _wasm.isWasm)(sourceId)) {
    resetLineNumberFormat(editor);
    return;
  }

  const cm = editor.codeMirror;
  const lineNumberFormatter = (0, _wasm.getWasmLineNumberFormatter)(sourceId);
  cm.setOption("lineNumberFormatter", lineNumberFormatter);
  (0, _ui.resizeBreakpointGutter)(cm);
  (0, _ui.resizeToggleButton)((0, _ui.getLineNumberWidth)(cm));
}

const contentTypeModeMap = new Map([["text/javascript", {
  name: "javascript"
}], ["text/typescript", {
  name: "javascript",
  typescript: true
}], ["text/coffeescript", {
  name: "coffeescript"
}], ["text/typescript-jsx", {
  name: "jsx",
  base: {
    name: "javascript",
    typescript: true
  }
}], ["text/jsx", {
  name: "jsx"
}], ["text/x-elm", {
  name: "elm"
}], ["text/x-clojure", {
  name: "clojure"
}], ["text/x-clojurescript", {
  name: "clojure"
}], ["text/wasm", {
  name: "text"
}], ["text/html", {
  name: "htmlmixed"
}], ["text/plain", {
  name: "text"
}]]);
const nonJSLanguageExtensionMap = new Map([["c", {
  name: "text/x-csrc"
}], ["kt", {
  name: "text/x-kotlin"
}], ["cpp", {
  name: "text/x-c++src"
}], ["m", {
  name: "text/x-objectivec"
}], ["rs", {
  name: "text/x-rustsrc"
}], ["hx", {
  name: "text/x-haxe"
}]]);
/**
 * Returns Code Mirror mode for source content type
 */
// eslint-disable-next-line complexity

function getMode(source, sourceTextContent) {
  const content = sourceTextContent.value; // Disable modes for minified files with 1+ million characters (See Bug 1569829).

  if (content.type === "text" && (0, _isMinified.isMinified)(source, sourceTextContent) && content.value.length > 1000000) {
    return contentTypeModeMap.get("text/plain");
  }

  if (content.type !== "text") {
    return contentTypeModeMap.get("text/plain");
  }

  const extension = source.displayURL.fileExtension;

  if (extension === "jsx") {
    return contentTypeModeMap.get("text/jsx");
  }

  if (extension === "tsx") {
    return contentTypeModeMap.get("text/typescript-jsx");
  }

  if (extension === "ts") {
    return contentTypeModeMap.get("text/typescript");
  } // check for C and other non JS languages


  if (nonJSLanguageExtensionMap.has(extension)) {
    return nonJSLanguageExtensionMap.get(extension);
  } // if the url ends with a known Javascript-like URL, provide JavaScript mode.


  if (_source.javascriptLikeExtensions.has(extension)) {
    return contentTypeModeMap.get("text/javascript");
  }

  const {
    contentType,
    value: text
  } = content; // Use HTML mode for files in which the first non whitespace
  // character is `<` regardless of extension.

  const isHTMLLike = () => text.match(/^\s*</);

  if (!contentType) {
    if (isHTMLLike()) {
      return contentTypeModeMap.get("text/html");
    }

    return contentTypeModeMap.get("text/plain");
  } // // @flow or /* @flow */


  if (text.match(/^\s*(\/\/ @flow|\/\* @flow \*\/)/)) {
    return contentTypeModeMap.get("text/typescript");
  }

  if (contentTypeModeMap.has(contentType)) {
    return contentTypeModeMap.get(contentType);
  }

  if (isHTMLLike()) {
    return contentTypeModeMap.get("text/html");
  }

  return contentTypeModeMap.get("text/plain");
}

function setMode(editor, source, sourceTextContent) {
  const mode = getMode(source, sourceTextContent);
  const currentMode = editor.codeMirror.getOption("mode");

  if (!currentMode || currentMode.name != mode.name) {
    editor.setMode(mode);
  }
}
/**
 * Handle getting the source document or creating a new
 * document with the correct mode and text.
 */


function showSourceText(editor, source, sourceTextContent) {
  if (hasDocument(source.id)) {
    const doc = getDocument(source.id);

    if (editor.codeMirror.doc === doc) {
      setMode(editor, source, sourceTextContent);
      return;
    }

    editor.replaceDocument(doc);
    updateLineNumberFormat(editor, source.id);
    setMode(editor, source, sourceTextContent);
    return;
  }

  const content = sourceTextContent.value;
  const doc = editor.createDocument( // We can set wasm text content directly from the constructor, so we pass an empty string
  // here, and set the text after replacing the document.
  content.type !== "wasm" ? content.value : "", getMode(source, sourceTextContent));
  setDocument(source.id, doc);
  editor.replaceDocument(doc);

  if (content.type === "wasm") {
    const wasmLines = (0, _wasm.renderWasmText)(source.id, content); // cm will try to split into lines anyway, saving memory

    editor.setText({
      split: () => wasmLines,
      match: () => false
    });
  }

  updateLineNumberFormat(editor, source.id);
}