"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default;
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _objectSpread2 = _interopRequireDefault(require("@babel/runtime/helpers/esm/objectSpread2"));
var _isHotkey = _interopRequireDefault(require("is-hotkey"));
var _slate = require("slate");
var _core = require("../../core");
var _helpers = require("./helpers");
var _elementTypes = require("../../constants/element-types");
const withCodeBlock = editor => {
  const {
    normalizeNode,
    insertFragment,
    insertText,
    insertBreak,
    insertData,
    insertNode,
    onHotKeyDown
  } = editor;
  const newEditor = editor;
  newEditor.insertData = data => {
    if (data.types.includes('text/code-block') && !(0, _core.getSelectedNodeByType)(editor, _elementTypes.CODE_BLOCK)) {
      const codeBlockNode = JSON.parse(data.getData('text/code-block'));
      return insertNode(codeBlockNode);
    }
    insertData(data);
  };
  newEditor.insertFragment = data => {
    // only selected code block content
    if (data.length === 1 && data[0].type === _elementTypes.CODE_BLOCK && !(0, _core.getSelectedNodeByType)(editor, _elementTypes.CODE_BLOCK)) {
      data.forEach((node, index) => {
        if (node.type === _elementTypes.CODE_BLOCK) {
          const newBlock = node.children.map(line => {
            const text = _slate.Node.string(line);
            const children = (0, _core.generateElement)(_elementTypes.PARAGRAPH, {
              childrenOrText: text
            });
            return children;
          });
          data.splice(index, 1, ...newBlock);
        }
      });
      return insertFragment(data);
    } else {
      if ((0, _core.getSelectedNodeByType)(editor, _elementTypes.CODE_BLOCK)) {
        // Paste into code block

        // Pasted data is code block split with code-line
        data.forEach((node, index) => {
          if (node.type === _elementTypes.CODE_BLOCK) {
            const codeLineArr = node.children.map(line => line);
            data.splice(index, 1, ...codeLineArr);
            // Paste the copied content in place in code_block
          } else if (node.type === _elementTypes.CODE_LINE) {
            data.splice(index, 1, node);
          }
        });
        const insertCodeLines = data.map(node => {
          const text = _slate.Node.string(node);
          const codeLine = (0, _core.generateElement)(_elementTypes.CODE_LINE, {
            childrenOrText: text
          });
          return codeLine;
        });

        // Current focus code-line string not empty
        const string = _slate.Editor.string(newEditor, newEditor.selection.focus.path);
        // Paste the copied content in place in code_block
        if (insertCodeLines.length === 1 && _slate.Range.isExpanded(newEditor.selection)) {
          const text = _slate.Node.string(insertCodeLines[0]);
          insertText(text);
          return;
        }
        if (string.length !== 0 && _slate.Range.isCollapsed(newEditor.selection)) {
          const [node, ...restNode] = insertCodeLines;
          const text = _slate.Node.string(node);
          insertText(text);
          if (restNode.length !== 0) {
            insertBreak();
            insertFragment(restNode);
          }
          return;
        }
        return insertFragment(insertCodeLines);
      } else {
        // Paste into not a code block
        return insertFragment(data);
      }
    }
  };

  // Rewrite normalizeNode
  newEditor.normalizeNode = _ref => {
    let [node, path] = _ref;
    const type = (0, _core.getNodeType)(node);
    if (type === _elementTypes.CODE_LINE && path.length <= 1) {
      _slate.Transforms.setNodes(newEditor, {
        type: _elementTypes.PARAGRAPH
      }, {
        at: path
      });
      return;
    } else if (type === _elementTypes.CODE_LINE && node.children.length > 1) {
      // If one code_line has more than one text nodes, merge all text nodes into a single text node
      const mergedText = node.children.map(textNode => textNode.text).join('');
      _slate.Transforms.removeNodes(editor, {
        at: [...path]
      });
      _slate.Transforms.insertNodes(editor, {
        type: _elementTypes.CODE_LINE,
        children: [{
          text: mergedText
        }]
      }, {
        at: [...path]
      });
      return;
    }
    if (type === _elementTypes.CODE_BLOCK) {
      if (node.children.length === 0) {
        _slate.Transforms.delete(newEditor, {
          at: path
        });
        return;
      }

      // code-block is the last node in the editor and needs to be followed by a p node
      const isLast = (0, _core.isLastNode)(newEditor, node);
      if (isLast) {
        const paragraph = (0, _core.generateEmptyElement)(_elementTypes.PARAGRAPH);
        _slate.Transforms.insertNodes(newEditor, paragraph, {
          at: [path[0] + 1]
        });
      }
      const isFirst = (0, _core.isFirstNode)(newEditor, node);
      if (isFirst) {
        const paragraph = (0, _core.generateEmptyElement)(_elementTypes.PARAGRAPH);
        _slate.Transforms.insertNodes(newEditor, paragraph, {
          at: [path[0]]
        });
      }

      // Here must be a code node below code-block
      if ((0, _core.getNodeType)(node.children[0]) !== _elementTypes.CODE_LINE) {
        _slate.Transforms.unwrapNodes(newEditor);
        _slate.Transforms.setNodes(newEditor, {
          type: _elementTypes.PARAGRAPH
        }, {
          mode: 'highest'
        });
      }
      if (node.children.length > 1) {
        node.children.forEach((child, index) => {
          if (child.type !== _elementTypes.CODE_LINE) {
            _slate.Transforms.setNodes(newEditor, {
              type: _elementTypes.CODE_LINE
            }, {
              at: [...path, index]
            });
          }
        });
      }
    }
    // Perform default behavior
    return normalizeNode([node, path]);
  };
  newEditor.onHotKeyDown = event => {
    const wrapperCodeBlock = (0, _helpers.getCodeBlockNodeEntry)(newEditor);
    if (!wrapperCodeBlock) return onHotKeyDown && onHotKeyDown(event);
    if ((0, _isHotkey.default)('mod+enter', event)) {
      event.preventDefault();
      if (newEditor.selection && !_slate.Range.isExpanded(newEditor.selection)) {
        const path = _slate.Editor.path(newEditor, newEditor.selection);
        const newParagraphPath = [path[0] + 1];
        const newParagraph = (0, _core.generateEmptyElement)(_elementTypes.PARAGRAPH);
        _slate.Transforms.insertNodes(newEditor, newParagraph, {
          at: newParagraphPath
        });
        _slate.Transforms.select(newEditor, newParagraphPath);
      }
      return true;
    }
    if ((0, _isHotkey.default)('tab', event)) {
      if (newEditor.hasMovedSelection) event.stopPropagation();
      event.preventDefault();
      const nodeEntries = _slate.Editor.nodes(newEditor, {
        mode: 'lowest',
        match: node => node.type === _elementTypes.CODE_LINE
      });
      const nodeEntryList = Array.from(nodeEntries);
      for (const nodeEntry of nodeEntryList) {
        const [, path] = nodeEntry;
        // Insert 4 spaces for easier remove space
        _slate.Transforms.insertText(newEditor, ' '.repeat(4), {
          at: {
            path: [...path, 0],
            offset: 0
          }
        });
      }
      const firstCodeLinePath = nodeEntryList.at(0)[1].concat(0);
      const lastCodeLinePath = nodeEntryList.at(-1)[1].concat(0);
      const newRange = _slate.Editor.range(newEditor, firstCodeLinePath, lastCodeLinePath);
      nodeEntryList.length > 1 ? _slate.Transforms.select(newEditor, newRange) : _slate.Transforms.select(newEditor);
      return true;
    }
    if ((0, _isHotkey.default)('shift+tab', event)) {
      if (newEditor.hasMovedSelection) event.stopPropagation();
      event.preventDefault();
      // Match the beginning of the line space, delete up to 4 spaces at a time
      const originSelection = newEditor.selection;
      const matchBeginSpace = /^\s*/;
      const nodeEntries = _slate.Editor.nodes(newEditor, {
        mode: 'lowest',
        match: node => node.type === _elementTypes.CODE_LINE
      });
      const nodeEntryList = Array.from(nodeEntries);
      let removedSpaceCount = 0;
      for (const nodeEntry of nodeEntryList) {
        const [node, path] = nodeEntry;
        const spaceNum = _slate.Node.string(node).match(matchBeginSpace);
        // skip empty line and no space begining line
        if (!spaceNum || !spaceNum[0].length) continue;
        const deleteNum = Math.min(spaceNum[0].length, 4);
        removedSpaceCount += deleteNum;
        for (let i = 0; i < deleteNum; i++) {
          _slate.Transforms.select(newEditor, {
            path: [...path, 0],
            offset: 0
          });
          _slate.Editor.deleteForward(newEditor, {
            unit: 'character'
          });
        }
      }
      // Select multiple rows when operating more then one line
      // Keep cursor location when operating one line
      if (nodeEntryList.length > 1) {
        const firstCodeLinePath = nodeEntryList.at(0)[1].concat(0);
        const lastCodeLinePath = nodeEntryList.at(-1)[1].concat(0);
        const selectLocation = _slate.Editor.range(newEditor, firstCodeLinePath, lastCodeLinePath);
        _slate.Transforms.select(newEditor, selectLocation);
      } else {
        const {
          anchor,
          focus
        } = originSelection;
        const isCollapsed = _slate.Range.isCollapsed(originSelection);
        if (isCollapsed) {
          const selectLocation = (0, _objectSpread2.default)((0, _objectSpread2.default)({}, originSelection.focus), {}, {
            offset: originSelection.focus.offset - removedSpaceCount
          });
          _slate.Transforms.select(newEditor, selectLocation);
        } else {
          const selectLocation = {
            anchor: (0, _objectSpread2.default)((0, _objectSpread2.default)({}, anchor), {}, {
              offset: anchor.offset - removedSpaceCount
            }),
            focus: (0, _objectSpread2.default)((0, _objectSpread2.default)({}, focus), {}, {
              offset: focus.offset - removedSpaceCount
            })
          };
          _slate.Transforms.select(newEditor, selectLocation);
        }
      }
      return true;
    }
    if ((0, _isHotkey.default)('mod+a', event)) {
      event.preventDefault();
      const codeBlockEntry = _slate.Editor.nodes(newEditor, {
        mode: 'highest',
        match: node => node.type === _elementTypes.CODE_BLOCK
      });
      if (!codeBlockEntry) return;
      const codeBlockEntryList = Array.from(...codeBlockEntry);
      _slate.Transforms.select(newEditor, codeBlockEntryList[1]);
      return true;
    }
  };
  return newEditor;
};
var _default = exports.default = withCodeBlock;