text: "use strict";
  /********************************************************************************
   * Copyright (C) 2017-2018 Ericsson and others.
   *
   * This program and the accompanying materials are made available under the
   * terms of the Eclipse Public License v. 2.0 which is available at
   * http://www.eclipse.org/legal/epl-2.0.
   *
   * This Source Code may also be made available under the following Secondary
   * Licenses when the conditions for such availability set forth in the Eclipse
   * Public License v. 2.0 are satisfied: GNU General Public License, version 2
   * with the GNU Classpath Exception which is available at
   * https://www.gnu.org/software/classpath/license.html.
   *
   * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
   ********************************************************************************/
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
      var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
      if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
      else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
      return c > 3 && r && Object.defineProperty(target, key, r), r;
  };
  var __metadata = (this && this.__metadata) || function (k, v) {
      if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
  };
  var __param = (this && this.__param) || function (paramIndex, decorator) {
      return function (target, key) { decorator(target, key, paramIndex); }
  };
  var __values = (this && this.__values) || function (o) {
      var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
      if (m) return m.call(o);
      return {
          next: function () {
              if (o && i >= o.length) o = void 0;
              return { value: o && o[i++], done: !o };
          }
      };
  };
  var __read = (this && this.__read) || function (o, n) {
      var m = typeof Symbol === "function" && o[Symbol.iterator];
      if (!m) return o;
      var i = m.call(o), r, ar = [], e;
      try {
          while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
      }
      catch (error) { e = { error: error }; }
      finally {
          try {
              if (r && !r.done && (m = i["return"])) m.call(i);
          }
          finally { if (e) throw e.error; }
      }
      return ar;
  };
  var __spread = (this && this.__spread) || function () {
      for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
      return ar;
  };
  Object.defineProperty(exports, "__esModule", { value: true });
  var core_1 = require("@theia/core");
  var inversify_1 = require("inversify");
  var node_1 = require("@theia/process/lib/node");
  var vscode_ripgrep_1 = require("vscode-ripgrep");
  var file_uri_1 = require("@theia/core/lib/node/file-uri");
  var RipgrepSearchInWorkspaceServer = /** @class */ (function () {
      function RipgrepSearchInWorkspaceServer(logger, rawProcessFactory) {
          this.logger = logger;
          this.rawProcessFactory = rawProcessFactory;
          // List of ongoing searches, maps search id to a the started rg process.
          this.ongoingSearches = new Map();
          // Each incoming search is given a unique id, returned to the client.  This is the next id we will assigned.
          this.nextSearchId = 0;
          // Highlighted red
          this.FILENAME_START = '^\x1b\\[0?m\x1b\\[31m';
          this.FILENAME_END = '\x1b\\[0?m:';
          // Highlighted green
          this.LINE_START = '^\x1b\\[0?m\x1b\\[32m';
          this.LINE_END = '\x1b\\[0?m:';
          // Highlighted yellow
          this.CHARACTER_START = '^\x1b\\[0?m\x1b\\[33m';
          this.CHARACTER_END = '\x1b\\[0?m:';
          // Highlighted blue
          this.MATCH_START = '\x1b\\[0?m\x1b\\[34m';
          this.MATCH_END = '\x1b\\[0?m';
      }
      RipgrepSearchInWorkspaceServer.prototype.setClient = function (client) {
          this.client = client;
      };
      RipgrepSearchInWorkspaceServer.prototype.getArgs = function (options) {
          var args = ['--vimgrep', '--color=always',
              '--colors=path:none',
              '--colors=line:none',
              '--colors=column:none',
              '--colors=match:none',
              '--colors=path:fg:red',
              '--colors=line:fg:green',
              '--colors=column:fg:yellow',
              '--colors=match:fg:blue',
              '--sort-files',
              '--max-count=100',
              '--max-columns=250'];
          args.push(options && options.matchCase ? '--case-sensitive' : '--ignore-case');
          if (options && options.matchWholeWord) {
              args.push('--word-regexp');
          }
          if (options && options.includeIgnored) {
              args.push('-uu');
          }
          args.push(options && options.useRegExp ? '--regexp' : '--fixed-strings');
          return args;
      };
      // Search for the string WHAT in directory ROOT.  Return the assigned search id.
      RipgrepSearchInWorkspaceServer.prototype.search = function (what, rootUri, opts) {
          var _this = this;
          var e_1, _a, e_2, _b;
          // Start the rg process.  Use --vimgrep to get one result per
          // line, --color=always to get color control characters that
          // we'll use to parse the lines.
          var searchId = this.nextSearchId++;
          var args = this.getArgs(opts);
          var globs = [];
          if (opts && opts.include) {
              try {
                  for (var _c = __values(opts.include), _d = _c.next(); !_d.done; _d = _c.next()) {
                      var include = _d.value;
                      if (include !== '') {
                          globs.push('--glob=**/' + include);
                      }
                  }
              }
              catch (e_1_1) { e_1 = { error: e_1_1 }; }
              finally {
                  try {
                      if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
                  }
                  finally { if (e_1) throw e_1.error; }
              }
          }
          if (opts && opts.exclude) {
              try {
                  for (var _e = __values(opts.exclude), _f = _e.next(); !_f.done; _f = _e.next()) {
                      var exclude = _f.value;
                      if (exclude !== '') {
                          globs.push('--glob=!**/' + exclude);
                      }
                  }
              }
              catch (e_2_1) { e_2 = { error: e_2_1 }; }
              finally {
                  try {
                      if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
                  }
                  finally { if (e_2) throw e_2.error; }
              }
          }
          var processOptions = {
              command: vscode_ripgrep_1.rgPath,
              args: __spread(args, [what], globs, [file_uri_1.FileUri.fsPath(rootUri)])
          };
          var process = this.rawProcessFactory(processOptions);
          this.ongoingSearches.set(searchId, process);
          process.onError(function (error) {
              // tslint:disable-next-line:no-any
              var errorCode = error.code;
              // Try to provide somewhat clearer error messages, if possible.
              if (errorCode === 'ENOENT') {
                  errorCode = 'could not find the ripgrep (rg) binary';
              }
              else if (errorCode === 'EACCES') {
                  errorCode = 'could not execute the ripgrep (rg) binary';
              }
              var errorStr = "An error happened while searching (" + errorCode + ").";
              _this.wrapUpSearch(searchId, errorStr);
          });
          // Running counter of results.
          var numResults = 0;
          // Buffer to accumulate incoming output.
          var databuf = '';
          var lastMatch = {
              file: '',
              line: 0,
              index: 0,
          };
          process.output.on('data', function (chunk) {
              // We might have already reached the max number of
              // results, sent a TERM signal to rg, but we still get
              // the data that was already output in the mean time.
              // It's not necessary to return early here (the check
              // for maxResults below would avoid sending extra
              // results), but it avoids doing unnecessary work.
              if (opts && opts.maxResults && numResults >= opts.maxResults) {
                  return;
              }
              databuf += chunk;
              while (1) {
                  // Check if we have a complete line.
                  var eolIdx = databuf.indexOf('\n');
                  if (eolIdx < 0) {
                      break;
                  }
                  // Get and remove the line from the data buffer.
                  var lineBuf = databuf.slice(0, eolIdx);
                  databuf = databuf.slice(eolIdx + 1);
                  // Extract the various fields using the ANSI
                  // control characters for colors as guides.
                  // Extract filename (magenta).
                  var filenameRE = new RegExp(_this.FILENAME_START + "(.+?)" + _this.FILENAME_END);
                  var match = filenameRE.exec(lineBuf);
                  if (!match) {
                      continue;
                  }
                  var filename = match[1];
                  lineBuf = lineBuf.slice(match[0].length);
                  // Extract line number (green).
                  var lineRE = new RegExp(_this.LINE_START + "(\\d+)" + _this.LINE_END);
                  match = lineRE.exec(lineBuf);
                  if (!match) {
                      continue;
                  }
                  var line = +match[1];
                  lineBuf = lineBuf.slice(match[0].length);
                  // Extract character number (column), but don't
                  // do anything with it.  ripgrep reports the
                  // offset in bytes, which is not good when
                  // dealing with multi-byte UTF-8 characters.
                  var characterNumRE = new RegExp(_this.CHARACTER_START + "(\\d+)" + _this.CHARACTER_END);
                  match = characterNumRE.exec(lineBuf);
                  if (!match) {
                      continue;
                  }
                  lineBuf = lineBuf.slice(match[0].length);
                  // If there are two matches in a line,
                  // --vimgrep will make rg output two lines, but
                  // both matches will be highlighted in both
                  // lines.  If we have consecutive matches at
                  // the same file / line, make sure to pick the
                  // right highlighted match.
                  if (lastMatch.file === filename && lastMatch.line === line) {
                      lastMatch.index++;
                  }
                  else {
                      lastMatch.file = filename;
                      lastMatch.line = line;
                      lastMatch.index = 0;
                  }
                  // Extract the match text (red).
                  var matchRE = new RegExp(_this.MATCH_START + "(.*?)" + _this.MATCH_END);
                  var characterNum = 0;
                  var matchWeAreLookingFor = undefined;
                  for (var i = 0;; i++) {
                      var nextMatch = lineBuf.match(matchRE);
                      if (!nextMatch) {
                          break;
                      }
                      // Just to make typescript happy.
                      if (nextMatch.index === undefined) {
                          break;
                      }
                      if (i === lastMatch.index) {
                          matchWeAreLookingFor = nextMatch;
                          characterNum = nextMatch.index + 1;
                      }
                      // Remove the control characters around the match.  This allows to:
                      //   - prepare the line text so it can be returned to the client without control characters
                      //   - get the character index of subsequent matches right
                      lineBuf =
                          lineBuf.slice(0, nextMatch.index)
                              + nextMatch[1]
                              + lineBuf.slice(nextMatch.index + nextMatch[0].length);
                  }
                  if (!matchWeAreLookingFor || characterNum === 0) {
                      continue;
                  }
                  if (matchWeAreLookingFor[1].length === 0) {
                      continue;
                  }
                  var result = {
                      file: filename,
                      line: line,
                      character: characterNum,
                      length: matchWeAreLookingFor[1].length,
                      lineText: lineBuf,
                  };
                  numResults++;
                  if (_this.client) {
                      _this.client.onResult(searchId, result);
                  }
                  // Did we reach the maximum number of results?
                  if (opts && opts.maxResults && numResults >= opts.maxResults) {
                      process.kill();
                      _this.wrapUpSearch(searchId);
                      break;
                  }
              }
          });
          process.output.on('end', function () {
              // If we reached maxResults, we should have already
              // wrapped up the search.  Returning early avoids
              // logging a warning message in wrapUpSearch.
              if (opts && opts.maxResults && numResults >= opts.maxResults) {
                  return;
              }
              _this.wrapUpSearch(searchId);
          });
          return Promise.resolve(searchId);
      };
      // Cancel an ongoing search.  Trying to cancel a search that doesn't exist isn't an
      // error, otherwise we'd have to deal with race conditions, where a client cancels a
      // search that finishes normally at the same time.
      RipgrepSearchInWorkspaceServer.prototype.cancel = function (searchId) {
          var process = this.ongoingSearches.get(searchId);
          if (process) {
              process.kill();
              this.wrapUpSearch(searchId);
          }
          return Promise.resolve();
      };
      // Send onDone to the client and clean up what we know about search searchId.
      RipgrepSearchInWorkspaceServer.prototype.wrapUpSearch = function (searchId, error) {
          if (this.ongoingSearches.delete(searchId)) {
              if (this.client) {
                  this.logger.debug('Sending onDone for ' + searchId, error);
                  this.client.onDone(searchId, error);
              }
              else {
                  this.logger.debug('Wrapping up search ' + searchId + ' but no client');
              }
          }
          else {
              this.logger.debug("Trying to wrap up a search we don't know about " + searchId);
          }
      };
      RipgrepSearchInWorkspaceServer.prototype.dispose = function () {
      };
      RipgrepSearchInWorkspaceServer = __decorate([
          inversify_1.injectable(),
          __param(0, inversify_1.inject(core_1.ILogger)),
          __param(1, inversify_1.inject(node_1.RawProcessFactory)),
          __metadata("design:paramtypes", [Object, Function])
      ], RipgrepSearchInWorkspaceServer);
      return RipgrepSearchInWorkspaceServer;
  }());
  exports.RipgrepSearchInWorkspaceServer = RipgrepSearchInWorkspaceServer;
  //# sourceMappingURL=ripgrep-search-in-workspace-server.js.map