text: "use strict";
  Object.defineProperty(exports, "__esModule", { value: true });
  var tslib_1 = require("tslib");
  var utils_1 = require("../utils");
  /**
   * Holds information about whether source files or directories are in the project.
   *
   * todo: Move this to a different folder.
   */
  var InProjectCoordinator = /** @class */ (function () {
      function InProjectCoordinator(compilerFactory) {
          var _this = this;
          this.compilerFactory = compilerFactory;
          this.notInProjectFiles = utils_1.createHashSet();
          compilerFactory.onSourceFileRemoved(function (sourceFile) {
              _this.notInProjectFiles.delete(sourceFile);
          });
      }
      /** Sets the source file as not being in the project. */
      InProjectCoordinator.prototype.setSourceFileNotInProject = function (sourceFile) {
          this.notInProjectFiles.add(sourceFile);
          sourceFile._inProject = false;
      };
      /** Marks the specified source file being included in the project. */
      InProjectCoordinator.prototype.markSourceFileAsInProject = function (sourceFile) {
          if (this.isSourceFileInProject(sourceFile))
              return;
          this._internalMarkSourceFileAsInProject(sourceFile);
          this.notInProjectFiles.delete(sourceFile);
      };
      /**
       * Marks source files not in a not in project node_modules folder as being in the project.
       * @returns Source files that were marked as being in the project.
       */
      InProjectCoordinator.prototype.markSourceFilesAsInProjectForResolution = function () {
          var e_1, _a;
          var nodeModulesSearchName = "/node_modules/";
          var compilerFactory = this.compilerFactory;
          var changedSourceFiles = [];
          var unchangedSourceFiles = [];
          try {
              for (var _b = tslib_1.__values(tslib_1.__spread(this.notInProjectFiles.values())), _c = _b.next(); !_c.done; _c = _b.next()) {
                  var sourceFile = _c.value;
                  if (shouldMarkInProject(sourceFile)) {
                      this._internalMarkSourceFileAsInProject(sourceFile);
                      this.notInProjectFiles.delete(sourceFile);
                      changedSourceFiles.push(sourceFile);
                  }
                  else {
                      unchangedSourceFiles.push(sourceFile);
                  }
              }
          }
          catch (e_1_1) { e_1 = { error: e_1_1 }; }
          finally {
              try {
                  if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
              }
              finally { if (e_1) throw e_1.error; }
          }
          return { changedSourceFiles: changedSourceFiles, unchangedSourceFiles: unchangedSourceFiles };
          function shouldMarkInProject(sourceFile) {
              // check if there's a node_modules directory (use the closest one)
              var filePath = sourceFile.getFilePath();
              var index = filePath.toLowerCase().lastIndexOf(nodeModulesSearchName);
              if (index === -1)
                  return true;
              // see if the node_modules directory is in the project
              var nodeModulesPath = filePath.substring(0, index + nodeModulesSearchName.length - 1); // remove last slash
              var nodeModulesDir = compilerFactory.getDirectoryFromCacheOnlyIfInCache(nodeModulesPath);
              if (nodeModulesDir != null && nodeModulesDir._isInProject())
                  return true;
              // see if there's a directory between this and the node_modules directory that's in the project
              var directory = sourceFile.getDirectory();
              while (directory != null && directory.getPath() !== nodeModulesPath) {
                  if (directory._isInProject())
                      return true;
                  directory = compilerFactory.getDirectoryFromCacheOnlyIfInCache(utils_1.FileUtils.getDirPath(directory.getPath()));
              }
              // otherwise, don't mark it as in the project
              return false;
          }
      };
      InProjectCoordinator.prototype._internalMarkSourceFileAsInProject = function (sourceFile) {
          sourceFile._inProject = true;
          this.markDirectoryAsInProject(sourceFile.getDirectory());
      };
      /** Checks if the specified source file is in the project. */
      InProjectCoordinator.prototype.isSourceFileInProject = function (sourceFile) {
          return sourceFile._inProject === true;
      };
      /** Sets the directory and files as not being in the project for testing. */
      InProjectCoordinator.prototype.setDirectoryAndFilesAsNotInProjectForTesting = function (directory) {
          var e_2, _a, e_3, _b;
          try {
              for (var _c = tslib_1.__values(directory.getDirectories()), _d = _c.next(); !_d.done; _d = _c.next()) {
                  var subDir = _d.value;
                  this.setDirectoryAndFilesAsNotInProjectForTesting(subDir);
              }
          }
          catch (e_2_1) { e_2 = { error: e_2_1 }; }
          finally {
              try {
                  if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
              }
              finally { if (e_2) throw e_2.error; }
          }
          try {
              for (var _e = tslib_1.__values(directory.getSourceFiles()), _f = _e.next(); !_f.done; _f = _e.next()) {
                  var file = _f.value;
                  delete file._inProject;
                  this.notInProjectFiles.add(file);
              }
          }
          catch (e_3_1) { e_3 = { error: e_3_1 }; }
          finally {
              try {
                  if (_f && !_f.done && (_b = _e.return)) _b.call(_e);
              }
              finally { if (e_3) throw e_3.error; }
          }
          delete directory._inProject;
      };
      /** Marks the specified directory as being in the project. */
      InProjectCoordinator.prototype.markDirectoryAsInProject = function (directory) {
          if (this.isDirectoryInProject(directory))
              return;
          var inProjectCoordinator = this;
          var compilerFactory = this.compilerFactory;
          directory._inProject = true;
          markAncestorDirs(directory);
          function markAncestorDirs(dir) {
              var e_4, _a;
              var ancestorDirs = utils_1.ArrayUtils.from(getAncestorsUpToOneInProject(dir));
              // only mark the ancestor directories as being in the project if the top one is in the project
              var topAncestor = ancestorDirs[ancestorDirs.length - 1];
              if (topAncestor == null || !inProjectCoordinator.isDirectoryInProject(topAncestor))
                  return;
              try {
                  for (var ancestorDirs_1 = tslib_1.__values(ancestorDirs), ancestorDirs_1_1 = ancestorDirs_1.next(); !ancestorDirs_1_1.done; ancestorDirs_1_1 = ancestorDirs_1.next()) {
                      var ancestorDir = ancestorDirs_1_1.value;
                      ancestorDir._inProject = true;
                  }
              }
              catch (e_4_1) { e_4 = { error: e_4_1 }; }
              finally {
                  try {
                      if (ancestorDirs_1_1 && !ancestorDirs_1_1.done && (_a = ancestorDirs_1.return)) _a.call(ancestorDirs_1);
                  }
                  finally { if (e_4) throw e_4.error; }
              }
          }
          function getAncestorsUpToOneInProject(dir) {
              var parentDirPath, parentDir;
              return tslib_1.__generator(this, function (_a) {
                  switch (_a.label) {
                      case 0:
                          if (utils_1.FileUtils.isRootDirPath(dir.getPath()))
                              return [2 /*return*/];
                          parentDirPath = utils_1.FileUtils.getDirPath(dir.getPath());
                          parentDir = compilerFactory.getDirectoryFromCacheOnlyIfInCache(parentDirPath);
                          if (parentDir == null)
                              return [2 /*return*/];
                          return [4 /*yield*/, parentDir];
                      case 1:
                          _a.sent();
                          if (!!inProjectCoordinator.isDirectoryInProject(parentDir)) return [3 /*break*/, 3];
                          return [5 /*yield**/, tslib_1.__values(getAncestorsUpToOneInProject(parentDir))];
                      case 2:
                          _a.sent();
                          _a.label = 3;
                      case 3: return [2 /*return*/];
                  }
              });
          }
      };
      /** Checks if the specified directory is in the project. */
      InProjectCoordinator.prototype.isDirectoryInProject = function (directory) {
          return directory._inProject === true;
      };
      return InProjectCoordinator;
  }());
  exports.InProjectCoordinator = InProjectCoordinator;
  