"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
var _react = _interopRequireDefault(require("react"));
var _monacoEditor = require("monaco-editor");
var _core = require("@kui-shell/core");
var _fonts = _interopRequireDefault(require("./lib/fonts"));
var _defaults = _interopRequireDefault(require("./lib/defaults"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
 * Copyright 2020 The Kubernetes Authors
 *
 * 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.
 */

class SimpleEditor extends _react.default.Component {
  constructor(props) {
    super(props);
    // created below in render() via ref={...} -> initMonaco()
    this.state = {
      cleaners: [],
      wrapper: _react.default.createRef(),
      editor: undefined,
      catastrophicError: undefined
    };
  }
  static getDerivedStateFromError(error) {
    return {
      catastrophicError: error
    };
  }
  componentDidCatch(error, errorInfo) {
    console.error('catastrophic error in Editor', error, errorInfo);
  }
  shouldComponentUpdate(nextProps, nextState) {
    return !nextState.editor || !!nextState.catastrophicError;
  }
  /** Called whenever we have proposed (props,state); we derive a new State */
  static getDerivedStateFromProps(props, state) {
    if (state.editor) {
      const model = state.editor.getModel();
      const currentLanguage = model.getLanguageId();
      const newLanguage = SimpleEditor.language(props.contentType);
      if (model.getValue() !== props.content || currentLanguage !== newLanguage) {
        state.editor.setValue(props.content);
        _monacoEditor.editor.setModelLanguage(model, newLanguage);
      }
    }
    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() {
    this.state.cleaners.forEach(cleaner => cleaner());
  }
  /** When content of the editor changes, call `onContentChange`  */
  static onChange(props, editor) {
    return () => {
      props.onContentChange(editor.getValue());
    };
  }
  /** Register onSave and onCancel keyboard shortcuts */
  registerKeyboardShortcuts(editor) {
    const {
      onSave,
      onCancel
    } = this.props;
    // if the enclosing view has passed in an onSave controller, we
    // will register CtrlCmd+S and Shift+Enter as keyboard shortcuts
    // for this controller
    if (onSave) {
      editor.addCommand(_monacoEditor.KeyMod.CtrlCmd | _monacoEditor.KeyCode.KeyS, () => {
        onSave(editor.getValue());
      });
      editor.addCommand(_monacoEditor.KeyMod.Shift | _monacoEditor.KeyCode.Enter, () => {
        onSave(editor.getValue());
      });
    }
    // ibid, but for Escape -> onCancel
    if (onCancel) {
      editor.onKeyUp(evt => {
        if (evt.browserEvent.key === 'Escape') {
          // swallow the event, so other kui components don't also respond
          evt.browserEvent.stopPropagation();
          // notify the enclosing component that we have cancelled editing
          onCancel(editor.getValue());
        }
      });
    }
  }
  static language(contentType) {
    return /^(ba)?sh$/.test(contentType) ? 'shell' : contentType;
  }
  /** Called when we have a ready wrapper (monaco's init requires an wrapper */
  initMonaco(props, state) {
    const cleaners = [];
    const nLines = props.content.split(/\n/).length;
    try {
      // here we instantiate an editor widget
      const providedOptions = {
        value: props.content,
        readOnly: props.readonly !== undefined ? props.readonly : true,
        fontSize: props.fontSize || (0, _fonts.default)() * (props.fontSizeAdjust || 1),
        language: SimpleEditor.language(props.contentType),
        wordWrap: props.wordWrap,
        simple: props.simple
      };
      const overrides = {
        theme: props.light ? 'vs' : 'vs-dark'
      };
      const options = Object.assign((0, _defaults.default)(Object.assign({
        showLineNumbers: nLines > 1,
        lineNumbersMinChars: nLines.toString().length + 1
      }, providedOptions)), providedOptions, overrides);
      const editor = _monacoEditor.editor.create(state.wrapper.current, options);
      this.registerKeyboardShortcuts(editor);
      const adjustHeight = !props.simple ? undefined : () => {
        // if we know 1) the height of the content won't change, and
        // 2) we are running in "simple" mode (this is mostly the case
        // for inline editor components, as opposed to editor
        // components that are intended to fill the full view), then:
        // size the height to fit the content
        const minHeight = this.props.minHeight !== undefined ? this.props.minHeight : 250;
        const contentHeight = Math.max(minHeight, editor.getContentHeight());
        state.wrapper.current.style.height = (minHeight === 0 ? contentHeight : Math.min(0.3 * window.innerHeight, contentHeight)) + 'px';
      };
      adjustHeight && adjustHeight();
      state.wrapper.current['getValueForTests'] = () => {
        return editor.getValue();
      };
      editor.onDidContentSizeChange(() => {
        adjustHeight && adjustHeight();
        editor.layout();
      });
      if (props.onContentChange) {
        // Notes: using onDidChangeModelContent does not let us
        // distinguish between the initial model change and changes
        // resulting from user input. Maybe we could look at the
        // `versionId` property of the model content change event
        // (does 1 mean the first version? it seems to be 2, dunno
        // why)... but i'd rather not get into that kind of low-level
        // dependence on the internal workings of monaco-editor
        // editor.onDidChangeModelContent(SimpleEditor.onChange(props, editor))
        editor.onKeyUp(SimpleEditor.onChange(props, editor));
      }
      if (!options.readOnly && this.props.focus !== false) {
        setTimeout(() => editor.focus());
      }
      const onZoom = () => {
        editor.updateOptions({
          fontSize: (0, _fonts.default)()
        });
      };
      _core.Events.eventChannelUnsafe.on('/zoom', onZoom);
      cleaners.push(() => _core.Events.eventChannelUnsafe.off('/zoom', onZoom));
      if (props.tabUUID) {
        const onTabLayoutChange = () => {
          editor.layout();
        };
        _core.Events.eventBus.onTabLayoutChange(props.tabUUID, onTabLayoutChange);
        cleaners.push(() => _core.Events.eventBus.offTabLayoutChange(props.tabUUID, onTabLayoutChange));
      }
      setTimeout(() => editor.layout(), 50);
      cleaners.push(() => {
        editor.dispose();
        const model = editor.getModel();
        if (model) {
          model.dispose();
        }
      });
      this.setState({
        editor,
        cleaners
      });
    } catch (err) {
      console.error('Error initing Monaco: ', err);
      this.setState({
        catastrophicError: err
      });
    }
  }
  componentDidMount() {
    if (!this.state.editor && this.state.wrapper.current) {
      // then we are ready to render monaco into the wrapper
      this.initMonaco(this.props, this.state);
    } else if (this.props.scrollIntoView && this.state.wrapper.current) {
      this.state.wrapper.current.scrollIntoView();
    }
  }
  render() {
    if (this.state.catastrophicError) {
      return _react.default.createElement("div", {
        className: "oops"
      }, " ", this.state.catastrophicError.toString());
    } else {
      const className = 'monaco-editor-wrapper' + (this.props.className ? ' ' + this.props.className : '');
      return _react.default.createElement("div", {
        className: className,
        ref: this.state.wrapper
      });
    }
  }
}
exports.default = SimpleEditor;