"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var React = _interopRequireWildcard(require("react"));

var _path = require("path");

var _monacoEditor = require("monaco-editor");

var _fs = require("@kui-shell/plugin-bash-like/fs");

var _core = require("@kui-shell/core");

var _ClearButton = _interopRequireDefault(require("./ClearButton"));

var _SaveFileButton = _interopRequireDefault(require("./SaveFileButton"));

var _RevertFileButton = _interopRequireDefault(require("./RevertFileButton"));

var _fileTypes = require("./lib/file-types");

var _defaults = _interopRequireDefault(require("./lib/defaults"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }

function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

/*
 * Copyright 2020 IBM Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
var __awaiter = void 0 && (void 0).__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());
  });
};

const strings = (0, _core.i18n)('plugin-client-common', 'editor');

class Editor extends React.PureComponent {
  constructor(props) {
    super(props); // created below in render() via ref={...} -> initMonaco()

    this.state = {
      editor: undefined,
      wrapper: undefined,
      catastrophicError: undefined
    };
  }

  static getDerivedStateFromError(error) {
    return {
      catastrophicError: error
    };
  }

  componentDidCatch(error, errorInfo) {
    console.error('catastrophic error in Editor', error, errorInfo);
  }
  /**
   * ToolbarText for a clean editor, i.e. no changes have been made
   * since last save.
   *
   */


  static allClean(props) {
    return {
      type: 'info',
      text: strings(!props.readOnly ? 'isUpToDate' : 'isUpToDateReadonly')
    };
  }
  /**
   * ToolbarText to indicate error saving.
   *
   */


  static error(props, msg, ...args) {
    return {
      type: 'error',
      text: strings(msg, ...args)
    };
  }
  /** Called whenever we have proposed (props,state); we derive a new State */


  static getDerivedStateFromProps(props, state) {
    if (!state.editor && state.wrapper) {
      // then we are ready to render monaco into the wrapper
      return Editor.initMonaco(props, state);
    } else {
      return state;
    }
  }
  /** Called when this component is no longer attached to the document */


  componentWillUnmount() {
    this.destroyMonaco();
  }
  /** Called when we no longer need the monaco-editor instance */


  destroyMonaco() {
    if (this.state.editor) {
      this.state.editor.dispose();
      const model = this.state.editor.getModel();

      if (model) {
        model.dispose();
      }

      if (this.state.subscription) {
        this.state.subscription.dispose();
      }
    }
  }

  static isClearable(props) {
    return (0, _fs.isFile)(props.response) && !props.readOnly || !(0, _fs.isFile)(props.response) && props.content.spec && props.content.spec.clearable !== false;
  }

  static onChange(props, editor) {
    let currentDecorations;
    return () => {
      if (currentDecorations) {
        editor.deltaDecorations(currentDecorations, []);
        currentDecorations = undefined;
      }

      const clearable = Editor.isClearable(props);
      const buttons = []; // save

      if ((0, _fs.isFile)(props.response)) {
        const save = (0, _SaveFileButton.default)(editor, props.repl, props.response, success => {
          if (success) {
            props.willUpdateToolbar(this.allClean(props), !clearable ? undefined : [(0, _ClearButton.default)(editor)]);
          } else {
            props.willUpdateToolbar(this.error(props, 'errorSaving'));
          }
        });
        buttons.push(save);
      } else if (props.content.spec && props.content.spec.save) {
        const {
          onSave
        } = props.content.spec.save;
        buttons.push({
          mode: 'Save',
          label: props.content.spec.save.label || strings('saveLocalFile'),
          kind: 'view',
          command: () => __awaiter(this, void 0, void 0, function* () {
            try {
              const save = yield onSave(editor.getValue());

              if (!(save && save.noToolbarUpdate)) {
                props.willUpdateToolbar(save && save.toolbarText || this.allClean(props), !clearable ? undefined : [(0, _ClearButton.default)(editor)]);
              }
            } catch (error) {
              const err = error;

              if (err.revealLine) {
                editor.revealLineInCenter(err.revealLine);
                currentDecorations = editor.deltaDecorations(currentDecorations || [], [{
                  range: new _monacoEditor.Range(err.revealLine, 1, err.revealLine, 1),
                  options: {
                    isWholeLine: true,
                    className: 'kui--editor-line-highlight',
                    glyphMarginClassName: 'kui--editor-line-glyph'
                  }
                }]);
              }

              const alert = {
                type: 'warning',
                text: strings('isModified'),
                alerts: [{
                  type: 'error',
                  title: strings('errorApplying'),
                  body: err.message
                }]
              };
              props.willUpdateToolbar(alert, buttons, undefined);
            }
          })
        });
      } // revert


      if ((0, _fs.isFile)(props.response)) {
        const revert = (0, _RevertFileButton.default)(editor, props.repl, props.response, (success, data) => {
          if (success) {
            editor.setValue(data);
            props.willUpdateToolbar(this.allClean(props), !clearable ? undefined : [(0, _ClearButton.default)(editor)]);
          } else {
            props.willUpdateToolbar(this.error(props, 'errorReverting'));
          }
        });
        buttons.push(revert);
      } else if (props.content.spec && props.content.spec.revert) {
        const {
          onRevert
        } = props.content.spec.revert;
        buttons.push({
          mode: 'Revert',
          label: props.content.spec.revert.label || strings('revert'),
          kind: 'view',
          command: () => __awaiter(this, void 0, void 0, function* () {
            try {
              const data = yield onRevert();
              editor.setValue(data);
              props.willUpdateToolbar(this.allClean(props), !clearable ? undefined : [(0, _ClearButton.default)(editor)]);
            } catch (err) {
              console.error(err);
              props.willUpdateToolbar(this.error(props, 'errorReverting'));
            }
          })
        });
      } // clear


      if (clearable) {
        buttons.push((0, _ClearButton.default)(editor));
      }

      props.willUpdateToolbar({
        type: 'warning',
        text: strings('isModified')
      }, buttons);
    };
  }
  /** Handle Toolbar registrations */


  static subscribeToChanges(props, editor) {
    if (props.willUpdateToolbar) {
      // send an initial update; note how the initial toolbarText may
      // be governed by the response
      const msg = props.response.toolbarText || this.allClean(props);
      const buttons = props.response.toolbarText ? [] : !Editor.isClearable(props) ? undefined : [(0, _ClearButton.default)(editor)];
      props.willUpdateToolbar(msg, buttons); // then subscribe to future model change events

      return editor.onDidChangeModelContent(Editor.onChange(props, editor));
    }
  }
  /** Called when we have a ready wrapper (monaco's init requires an wrapper */


  static initMonaco(props, state) {
    try {
      // here we instantiate an editor widget
      const providedOptions = {
        value: props.content.content,
        readOnly: !(0, _fs.isFile)(props.response) && (!props.content.spec || props.content.spec.readOnly !== false) && (props.readOnly || !(0, _fs.isFile)(props.response) || false),
        language: props.content.contentType === 'text/plain' ? (0, _fileTypes.language)(props.content.contentType, (0, _fs.isFile)(props.response) ? (0, _path.extname)(props.response.spec.filepath).slice(1) : undefined) : props.content.contentType || undefined
      };
      const options = Object.assign((0, _defaults.default)(providedOptions), providedOptions);

      const editor = _monacoEditor.editor.create(state.wrapper, options);

      editor['clearDecorations'] = () => {
        // debug('clearing decorations', editor['__cloudshell_decorations'])
        const none = [{
          range: new _monacoEditor.Range(1, 1, 1, 1),
          options: {}
        }];
        editor['__cloudshell_decorations'] = editor.deltaDecorations(editor['__cloudshell_decorations'] || [], none);
      };

      state.wrapper['getValueForTests'] = () => {
        return editor.getValue();
      };

      if (!props.readOnly) {
        state.wrapper.focus();
      }

      return {
        editor,
        subscription: Editor.subscribeToChanges(props, editor)
      };
    } catch (err) {
      console.error('Error initing Monaco: ', err);
      state.catastrophicError = err;
      return state;
    }
  }

  render() {
    if (this.state.catastrophicError) {
      return React.createElement("div", {
        className: "oops"
      }, " ", this.state.catastrophicError.toString());
    } else {
      return React.createElement("div", {
        className: "code-highlighting"
      }, React.createElement("div", {
        className: "monaco-editor-wrapper",
        ref: wrapper => this.setState({
          wrapper
        })
      }));
    }
  }

}

exports.default = Editor;