<commit_before>'use strict';

const childProcess = require('child_process');

const StringScanner = require('StringScanner');
const escapeRegExp = require('lodash.escaperegexp');
const eslint = require('eslint');
const lodashRange = require('lodash.range');
const minimatch = require('minimatch');
const sortBy = require('lodash.sortby');
const uniqBy = require('lodash.uniqby');

const CommandLineEditor = require('./CommandLineEditor');
const Configuration = require('./Configuration');
const ImportStatement = require('./ImportStatement');
const ImportStatements = require('./ImportStatements');
const JsModule = require('./JsModule');
const xRegExp = require('./xregexp');

const REGEX_SKIP_SECTION = xRegExp(`
  ^
  \\s*                     # preceding whitespace
  (?:
    (?<quote>['"])use\\sstrict\\k<quote>;? # 'use strict';
    |
    //[^\n]*               # single-line comment
    |
    /\\*                   # open multi-line comment
    (?:\n|.)*?             # inside of multi-line comment
    \\*/                   # close multi-line comment
  )?                       # ? b/c we want to match on only whitespace
  \n                       # end of line
  `, 'xs' // free-spacing, dot-match-all
);

class Importer {
  constructor(lines, pathToCurrentFile) {
    this.editor = new CommandLineEditor(lines);
    this.config = new Configuration(pathToCurrentFile);
    this.pathToCurrentFile = pathToCurrentFile;
    this.messages = [];
    this.unresolvedImports = {};
  }

  /**
   * Imports one variable
   *
   * @return {Object}
   */
  results() {
    return {
      messages: this.messages, // array
      fileContent: this.editor.currentFileContent(), // string
      unresolvedImports: this.unresolvedImports, // object
    };
  }

  /**
   * Imports one variable
   *
   * @return {Object}
   */
  import(variableName) {
    if (!variableName) {
      this.message('No variable to import. Place your cursor on a variable, ' +
                   'then try again.');
      return this.results();
    }

    const jsModule = this.findOneJsModule(variableName);
    if (!jsModule) {
      return this.results();
    }

    const oldImports = this.findCurrentImports();
    const importStatement = jsModule.toImportStatement(variableName, this.config);
    oldImports.imports.push(importStatement);
    this.replaceImports(oldImports.range, oldImports.imports);

    return this.results();
  }

  goto(variableName) {
    const jsModules = this.findJsModulesFor(variableName);

    const jsModule = this.resolveModuleUsingCurrentImports(
      jsModules, variableName);

    if (!jsModule) {
      // The current word is not mappable to one of the JS modules that we
      // found. This can happen if the user does not select one from the list.
      // We have nothing to go to, so we return early.
      this.message(`Could not resolve a module for \`${variableName}\``);
      return this.results();
    }

    const filePath = jsModule.openFilePath(this.pathToCurrentFile);
    const results = this.results();
    results.goto = filePath;
    return results;
  }

  // Removes unused imports and adds imports for undefined variables
  fixImports() {
    const cli = new eslint.CLIEngine();
    const config = cli.getConfigForFile(this.pathToCurrentFile);

    // Reset rules to only the ones we care about.
    config.rules = {
      'no-undef': 2,
      'no-unused-vars': 2,
      'react/jsx-no-undef': 2,
      'react/react-in-jsx-scope': 2,
      'react/jsx-uses-vars': 2,
      'react/jsx-uses-react': 2,
    };
    const eslintResult = eslint.linter.verify(
      this.editor.currentFileContent(), config);

    const unusedVariables = {};
    const undefinedVariables = new Set();

    eslintResult.forEach((message) => {
      if (/^Definition for rule '.*?' was not found$/.test(message.message)) {
        return;
      }

      switch (message.ruleId) {
        case 'no-unused-vars': {
          const match = message.message.match(
            /'(.*)' is defined but never used/);
          const variableName = match[1];
          unusedVariables[variableName] = unusedVariables[variableName] || [];
          unusedVariables[variableName].push(message.line);
          break;
        }
        case 'no-undef':
        case 'react/jsx-no-undef': {
          const match = message.message.match(/'(.*)' is not defined/);
          undefinedVariables.add(match[1]);
          break;
        }
        case 'react/react-in-jsx-scope':
          undefinedVariables.add('React');
          break;
        default:
          break;
      }
    });

    if (!Object.keys(unusedVariables).length && !undefinedVariables.size) {
      // return early to avoid rewriting imports
      return this.results();
    }

    const oldImports = this.findCurrentImports();

    const unusedImportVariables = new Set();
    Object.keys(unusedVariables).forEach((variable) => {
      const lineNumbers = unusedVariables[variable];
      if (this.anyNumbersWithinRange(lineNumbers, oldImports.range)) {
        unusedImportVariables.add(variable);
      }
    });

    const newImports = oldImports.imports.clone();
    newImports.deleteVariables(unusedImportVariables);

    undefinedVariables.forEach((variable) => {
      const jsModule = this.findOneJsModule(variable);
      if (!jsModule) {
        return;
      }
      newImports.push(jsModule.toImportStatement(variable, this.config));
    });

    this.replaceImports(oldImports.range, newImports);

    return this.results();
  }

  /**
   * @param {Array<Object>} jsonImports
   */
  addImports(imports) {
    const oldImports = this.findCurrentImports();
    const newImports = oldImports.imports.clone();

    Object.keys(imports).forEach((variableName) => {
      const importPath = imports[variableName];
      newImports.push(new JsModule({ importPath }).toImportStatement(
        variableName, this.config));
    });

    this.replaceImports(oldImports.range, newImports);

    return this.results();
  }

  rewriteImports() {
    const oldImports = this.findCurrentImports();
    const newImports = new ImportStatements(this.config);

    oldImports.imports.forEach((imp) => {
      imp.variables().forEach((variable) => {
        const jsModule = this.resolveModuleUsingCurrentImports(
          this.findJsModulesFor(variable),
          variable
        );

        if (!jsModule) {
          return;<commit_msg>Change jsonImports to imports in JSDoc<commit_after>'use strict';

const childProcess = require('child_process');

const StringScanner = require('StringScanner');
const escapeRegExp = require('lodash.escaperegexp');
const eslint = require('eslint');
const lodashRange = require('lodash.range');
const minimatch = require('minimatch');
const sortBy = require('lodash.sortby');
const uniqBy = require('lodash.uniqby');

const CommandLineEditor = require('./CommandLineEditor');
const Configuration = require('./Configuration');
const ImportStatement = require('./ImportStatement');
const ImportStatements = require('./ImportStatements');
const JsModule = require('./JsModule');
const xRegExp = require('./xregexp');

const REGEX_SKIP_SECTION = xRegExp(`
  ^
  \\s*                     # preceding whitespace
  (?:
    (?<quote>['"])use\\sstrict\\k<quote>;? # 'use strict';
    |
    //[^\n]*               # single-line comment
    |
    /\\*                   # open multi-line comment
    (?:\n|.)*?             # inside of multi-line comment
    \\*/                   # close multi-line comment
  )?                       # ? b/c we want to match on only whitespace
  \n                       # end of line
  `, 'xs' // free-spacing, dot-match-all
);

class Importer {
  constructor(lines, pathToCurrentFile) {
    this.editor = new CommandLineEditor(lines);
    this.config = new Configuration(pathToCurrentFile);
    this.pathToCurrentFile = pathToCurrentFile;
    this.messages = [];
    this.unresolvedImports = {};
  }

  /**
   * Imports one variable
   *
   * @return {Object}
   */
  results() {
    return {
      messages: this.messages, // array
      fileContent: this.editor.currentFileContent(), // string
      unresolvedImports: this.unresolvedImports, // object
    };
  }

  /**
   * Imports one variable
   *
   * @return {Object}
   */
  import(variableName) {
    if (!variableName) {
      this.message('No variable to import. Place your cursor on a variable, ' +
                   'then try again.');
      return this.results();
    }

    const jsModule = this.findOneJsModule(variableName);
    if (!jsModule) {
      return this.results();
    }

    const oldImports = this.findCurrentImports();
    const importStatement = jsModule.toImportStatement(variableName, this.config);
    oldImports.imports.push(importStatement);
    this.replaceImports(oldImports.range, oldImports.imports);

    return this.results();
  }

  goto(variableName) {
    const jsModules = this.findJsModulesFor(variableName);

    const jsModule = this.resolveModuleUsingCurrentImports(
      jsModules, variableName);

    if (!jsModule) {
      // The current word is not mappable to one of the JS modules that we
      // found. This can happen if the user does not select one from the list.
      // We have nothing to go to, so we return early.
      this.message(`Could not resolve a module for \`${variableName}\``);
      return this.results();
    }

    const filePath = jsModule.openFilePath(this.pathToCurrentFile);
    const results = this.results();
    results.goto = filePath;
    return results;
  }

  // Removes unused imports and adds imports for undefined variables
  fixImports() {
    const cli = new eslint.CLIEngine();
    const config = cli.getConfigForFile(this.pathToCurrentFile);

    // Reset rules to only the ones we care about.
    config.rules = {
      'no-undef': 2,
      'no-unused-vars': 2,
      'react/jsx-no-undef': 2,
      'react/react-in-jsx-scope': 2,
      'react/jsx-uses-vars': 2,
      'react/jsx-uses-react': 2,
    };
    const eslintResult = eslint.linter.verify(
      this.editor.currentFileContent(), config);

    const unusedVariables = {};
    const undefinedVariables = new Set();

    eslintResult.forEach((message) => {
      if (/^Definition for rule '.*?' was not found$/.test(message.message)) {
        return;
      }

      switch (message.ruleId) {
        case 'no-unused-vars': {
          const match = message.message.match(
            /'(.*)' is defined but never used/);
          const variableName = match[1];
          unusedVariables[variableName] = unusedVariables[variableName] || [];
          unusedVariables[variableName].push(message.line);
          break;
        }
        case 'no-undef':
        case 'react/jsx-no-undef': {
          const match = message.message.match(/'(.*)' is not defined/);
          undefinedVariables.add(match[1]);
          break;
        }
        case 'react/react-in-jsx-scope':
          undefinedVariables.add('React');
          break;
        default:
          break;
      }
    });

    if (!Object.keys(unusedVariables).length && !undefinedVariables.size) {
      // return early to avoid rewriting imports
      return this.results();
    }

    const oldImports = this.findCurrentImports();

    const unusedImportVariables = new Set();
    Object.keys(unusedVariables).forEach((variable) => {
      const lineNumbers = unusedVariables[variable];
      if (this.anyNumbersWithinRange(lineNumbers, oldImports.range)) {
        unusedImportVariables.add(variable);
      }
    });

    const newImports = oldImports.imports.clone();
    newImports.deleteVariables(unusedImportVariables);

    undefinedVariables.forEach((variable) => {
      const jsModule = this.findOneJsModule(variable);
      if (!jsModule) {
        return;
      }
      newImports.push(jsModule.toImportStatement(variable, this.config));
    });

    this.replaceImports(oldImports.range, newImports);

    return this.results();
  }

  /**
   * @param {Array<Object>} imports
   */
  addImports(imports) {
    const oldImports = this.findCurrentImports();
    const newImports = oldImports.imports.clone();

    Object.keys(imports).forEach((variableName) => {
      const importPath = imports[variableName];
      newImports.push(new JsModule({ importPath }).toImportStatement(
        variableName, this.config));
    });

    this.replaceImports(oldImports.range, newImports);

    return this.results();
  }

  rewriteImports() {
    const oldImports = this.findCurrentImports();
    const newImports = new ImportStatements(this.config);

    oldImports.imports.forEach((imp) => {
      imp.variables().forEach((variable) => {
        const jsModule = this.resolveModuleUsingCurrentImports(
          this.findJsModulesFor(variable),
          variable
        );

        if (!jsModule) {
          return;
{'fraction_non_alphanumeric': 0.1284990253411306, 'fraction_numerical': 0.0012475633528265108, 'mean_word_length': 2.6814006888633752, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5747548', 'n_tokens_mistral': 3744, 'n_tokens_neox': 3654, 'n_words': 810}