"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;
exports.onKeyUp = onKeyUp;
var _debug = _interopRequireDefault(require("debug"));
var _react = _interopRequireDefault(require("react"));
var _core = require("@kui-shell/core");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
 * Copyright 2018 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.
 */
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 debug = (0, _debug.default)('core-support/history/reverse-i-search');
/** state of the reverse-i-search */
class ActiveISearch {
  // eslint-disable-next-line no-useless-constructor
  constructor(input, history, currentSearchIdx = -1, reachedTheEnd = false) {
    this.input = input;
    this.history = history;
    this.currentSearchIdx = currentSearchIdx;
    this.reachedTheEnd = reachedTheEnd;
  }
  fixedPart() {
    const className = 'small-right-pad' + (this.reachedTheEnd ? ' alert-pulse' : '');
    if (this.reachedTheEnd) {
      // clear alert-pulse after a second
      setTimeout(() => this.input.setState({
        isearch: new ActiveISearch(this.input, this.history, this.currentSearchIdx, false)
      }), 1000);
    }
    return _react.default.createElement("span", {
      className: className
    }, "(reverse-i-search", _react.default.createElement("span", {
      className: "sub-text semi-transparent"
    }, this.currentSearchIdx === -1 ? '' : ' ' + this.currentSearchIdx.toString()), ")", !this.input.state.prompt.value ? '' : '`' + this.input.state.prompt.value + '`:');
  }
  matchedPrefixPart(newValue, caretPosition) {
    const matchedPrefix = newValue.substring(0, caretPosition - 1);
    return _react.default.createElement("span", {
      className: "slightly-deemphasize"
    }, matchedPrefix);
  }
  matchedSuffixPart(newValue, caretPosition) {
    const matchedSuffix = newValue.substring(caretPosition + this.input.state.prompt.value.length - 1);
    return _react.default.createElement("span", {
      className: "slightly-deemphasize"
    }, matchedSuffix);
  }
  typedPart() {
    // show matched whitespaces with an underscore
    return _react.default.createElement("strong", {
      className: "red-text kui--prompt-like"
    }, this.input.state.prompt.value.replace(/ /g, '_'));
  }
  matchAt(idx = this.currentSearchIdx) {
    return this.history.line(idx).raw;
  }
  currentMatch() {
    if (this.currentSearchIdx === -1) {
      return '';
    } else {
      return this.matchAt();
    }
  }
  render() {
    if (this.currentSearchIdx < 0) {
      return this.fixedPart();
    } else {
      const newValue = this.currentMatch();
      const caretPosition = newValue.indexOf(this.input.state.prompt.value) + 1;
      return _react.default.createElement(_react.default.Fragment, null, this.fixedPart(), this.matchedPrefixPart(newValue, caretPosition), this.typedPart(), this.matchedSuffixPart(newValue, caretPosition));
    }
  }
  /**
   * For various reasons, user has cancelled a reverse-i-search.
   *
   */
  cancelISearch() {
    this.input.setState({
      isearch: undefined
    });
  }
  /**
   * Search command history for a match, skipping over identicals.
   *
   * @param startIdx start searching backwards from here
   * @param userHitCtrlR is this a request to find another match further in the past?
   *
   * @return a command history index, or -1 if not match is found
   *
   */
  findPrevious(startIdx, userHitCtrlR) {
    if (startIdx < 0) {
      return -1;
    }
    const {
      prompt
    } = this.input.state;
    const newSearchIdx = prompt.value ? this.history.findIndex(prompt.value, startIdx) : -1;
    if (!userHitCtrlR || newSearchIdx < 0 || this.currentSearchIdx < 0 || this.matchAt(newSearchIdx) !== this.matchAt()) {
      return newSearchIdx;
    } else {
      // skip this match because it is the same as the current match
      return this.findPrevious(newSearchIdx - 1, userHitCtrlR);
    }
  }
  /**
   * Attempt to initiate or extend a search
   *
   */
  doSearch(evt) {
    // where do we want to start the search? if the user is just
    // typing, then start from the end of history; if the user hit
    // ctrl+r, then they want to search for the next match
    const userHitCtrlR = evt.ctrlKey && evt.key === 'r';
    const startIdx = userHitCtrlR ? this.currentSearchIdx - 1 : undefined;
    debug('doSearch', userHitCtrlR, startIdx);
    const {
      prompt
    } = this.input.state;
    const newSearchIdx = this.findPrevious(startIdx, userHitCtrlR);
    debug('search index', prompt.value, newSearchIdx);
    if (newSearchIdx > 0) {
      this.input.setState({
        isearch: new ActiveISearch(this.input, this.history, newSearchIdx)
      });
    } else if (userHitCtrlR) {
      // if we found no match, reset the match text, unless the user
      // is using repeated ctrl+R to search backwards; in this case,
      // let's continue to display the previous match if no new match
      // is found
    } else {
      this.input.setState({
        isearch: new ActiveISearch(this.input, this.history, newSearchIdx, true)
      });
    }
  }
  /** fill in the result of a search */
  completeSearch() {
    debug('completing search');
    this.input.state.prompt.value = this.currentMatch();
    this.cancelISearch();
  }
}
/**
 * Listen for ctrl+R
 *
 */
exports.default = ActiveISearch;
function onKeyUp(evt) {
  return __awaiter(this, void 0, void 0, function* () {
    const activeSearch = this.state.isearch;
    //
    // we want ctrl+R; but if we're in a browser and on linux or
    // windows, then ctrl+R will result in a browser reload :(
    //
    // Note: even if not in a browser (i.e. running in electron mode),
    // on linux and windows we have to be careful not to use the
    // default reload keyboard shortcut; see app/src/main/menu.js
    //
    // re: RUNNING_SHELL_TEST; there seems to be some weird bug here; on linux, the ctrlKey modifier becomes sticky
    if (evt.ctrlKey && (process.platform === 'darwin' || /Macintosh/.test(navigator.userAgent) || !_core.Capabilities.inBrowser() && !process.env.RUNNING_SHELL_TEST || evt.metaKey)) {
      if (evt.key === 'r') {
        debug('got ctrl+r');
        if (activeSearch) {
          debug('continuation of existing reverse-i-search');
          activeSearch.doSearch(evt);
        } else {
          debug('new reverse-i-search');
          this.setState({
            isearch: new ActiveISearch(this, yield (0, _core.History)(this.props.tab))
          });
        }
      } else if (activeSearch && (0, _core.isCursorMovement)(evt.nativeEvent)) {
        activeSearch.completeSearch();
      } else if (activeSearch) {
        // with ctrl key down, let any other keycode result in cancelling the outstanding i-search
        debug('cancel', evt.keyCode);
        activeSearch.cancelISearch();
      }
    } else if (activeSearch && (0, _core.isCursorMovement)(evt.nativeEvent)) {
      activeSearch.completeSearch();
    } else if (evt.key === 'Enter' && this.state.isearch) {
      this.state.isearch.completeSearch();
      this.props.tab.REPL.pexec(this.state.isearch.currentMatch() || this.state.prompt.value);
    } else if (this.state.isearch && evt.key !== 'Control') {
      evt.preventDefault();
      this.state.isearch.doSearch(evt);
    }
  });
}