<commit_before>'use babel';

// eslint-disable-next-line import/no-extraneous-dependencies, import/extensions
import { CompositeDisposable, Range } from 'atom';
import { findAsync, rangeFromLineNumber, exec } from 'atom-linter';
import { dirname, join, relative, sep } from 'path';
import { existsSync, readFileSync, readdirSync } from 'fs';

const tmp = require('tmp');

// Internal values
const elixirProjectPathCache = new Map();
let elixircPath;
let mixPath;
let forceElixirc;

function regexp(string, flags) {
  return new RegExp(
    string
      .replace(/\\ /gm, 'randomstring123')
      .replace(/\s/gm, '')
      .replace(/randomstring123/gm, '\\ '),
    flags,
  );
}

// Find elixir project in the file path by locating mix.exs, otherwise
//  fallback to project path or file path
const findElixirProjectPath = async (editorPath) => {
  const editorDir = dirname(editorPath);
  const mixexsPath = await findAsync(editorDir, 'mix.exs');
  if (mixexsPath !== null) {
    return mixexsPath;
  }
  const projPath = atom.project.relativizePath(editorPath)[0];
  if (projPath !== null) {
    return projPath;
  }
  return editorDir;
};

// Memoize the project path per file (traversing is quite expensive)
const elixirProjectPath = async (textEditor) => {
  const editorPath = textEditor.getPath();
  if (elixirProjectPathCache.has(editorPath)) {
    return elixirProjectPathCache.get(editorPath);
  }
  const projectPath = await findElixirProjectPath(editorPath);
  elixirProjectPathCache.set(editorPath, projectPath);
  return projectPath;
};

const isMixProject = async (textEditor) => {
  const project = await elixirProjectPath(textEditor);
  return existsSync(join(project, 'mix.exs'));
};

const isTestFile = async (textEditor) => {
  const project = await elixirProjectPath(textEditor);
  const relativePath = relative(project, textEditor.getPath());
  // Is the first directory of the relative path "test"?
  return relativePath.split(sep)[0] === 'test';
};

const isForcedElixirc = () => forceElixirc;

const isExsFile = textEditor => textEditor.getPath().endsWith('.exs');

const isPhoenixProject = async (textEditor) => {
  const project = await elixirProjectPath(textEditor);
  const mixLockPath = join(project, 'mix.lock');
  try {
    const mixLockContent = readFileSync(mixLockPath, 'utf-8');
    return mixLockContent.indexOf('"phoenix"') > 0;
  } catch (error) {
    return false;
  }
};

const findTextEditor = (filePath) => {
  const allEditors = atom.workspace.getTextEditors();
  const matchingEditor = allEditors.find(
    textEditor => textEditor.getPath() === filePath);
  if (matchingEditor !== undefined) {
    return matchingEditor;
  }
  return false;
};

const parseError = async (toParse, textEditor) => {
  const messages = [];
  const re = regexp(
    `
    \\*\\*[\\ ]+
    \\((\\w+)\\)                   ${''/* 1 - (TypeOfError)*/}
    [\\ ](?:                       ${''/* Two message formats.... mode one*/}
      ([\\w\\ ]+)                  ${''/* 2 - Message*/}
      [\\r\\n]{1,2}.+[\\r\\n]{1,2} ${''/* Internal elixir code*/}
      [\\ ]+(.+)                   ${''/* 3 - File*/}
      :(\\d+):                     ${''/* 4 - Line*/}
    |                              ${''/* Or... mode two*/}
      (.+)                         ${''/* 5 - File*/}
      :(\\d+):                     ${''/* 6 - Line*/}
      [\\ ](.+)                    ${''/* 7 - Message*/}
    )
  `,
    'gm',
  );
  const projectPath = await elixirProjectPath(textEditor);
  let reResult = re.exec(toParse);
  while (reResult !== null) {
    let text;
    let filePath;
    let range;
    if (reResult[2] !== undefined) {
      text = `(${reResult[1]}) ${reResult[2]}`;
      filePath = join(projectPath, reResult[3]);
      const fileEditor = findTextEditor(filePath);
      if (fileEditor) {
        // If there is an open TextEditor instance for the file from the Error,
        // we can get a better range using rangeFromLineNumber, otherwise
        // generate a 1 character range that can be updated to a proper range
        // if/when the file is opened.
        range = rangeFromLineNumber(fileEditor, reResult[4] - 1);
      } else {
        range = new Range([reResult[4] - 1, 0], [reResult[4] - 1, 1]);
      }
    } else {
      text = `(${reResult[1]}) ${reResult[7]}`;
      filePath = join(projectPath, reResult[5]);
      if (filePath === textEditor.getPath()) {
        range = rangeFromLineNumber(textEditor, reResult[6] - 1);
      } else {
        range = new Range([reResult[6] - 1, 0], [reResult[6] - 1, 1]);
      }
    }
    messages.push({
      type: 'Error',
      text,
      filePath,
      range,
    });
    reResult = re.exec(toParse);
  }
  return messages;
};

// Only Elixir 1.3+
const parseWarning = async (toParse, textEditor) => {
  const messages = [];
  const re = regexp(
    `
    warning:\\ (.*)\\n  ${''/* warning */}
    \\ \\ (.*):([0-9]+) ${''/* file and file number */}
    `,
    'g',
  );
  const projectPath = await elixirProjectPath(textEditor);
  let reResult = re.exec(toParse);
<commit_msg>Search for a TextEditor in Error-mode2 messages<commit_after>'use babel';

// eslint-disable-next-line import/no-extraneous-dependencies, import/extensions
import { CompositeDisposable, Range } from 'atom';
import { findAsync, rangeFromLineNumber, exec } from 'atom-linter';
import { dirname, join, relative, sep } from 'path';
import { existsSync, readFileSync, readdirSync } from 'fs';

const tmp = require('tmp');

// Internal values
const elixirProjectPathCache = new Map();
let elixircPath;
let mixPath;
let forceElixirc;

function regexp(string, flags) {
  return new RegExp(
    string
      .replace(/\\ /gm, 'randomstring123')
      .replace(/\s/gm, '')
      .replace(/randomstring123/gm, '\\ '),
    flags,
  );
}

// Find elixir project in the file path by locating mix.exs, otherwise
//  fallback to project path or file path
const findElixirProjectPath = async (editorPath) => {
  const editorDir = dirname(editorPath);
  const mixexsPath = await findAsync(editorDir, 'mix.exs');
  if (mixexsPath !== null) {
    return mixexsPath;
  }
  const projPath = atom.project.relativizePath(editorPath)[0];
  if (projPath !== null) {
    return projPath;
  }
  return editorDir;
};

// Memoize the project path per file (traversing is quite expensive)
const elixirProjectPath = async (textEditor) => {
  const editorPath = textEditor.getPath();
  if (elixirProjectPathCache.has(editorPath)) {
    return elixirProjectPathCache.get(editorPath);
  }
  const projectPath = await findElixirProjectPath(editorPath);
  elixirProjectPathCache.set(editorPath, projectPath);
  return projectPath;
};

const isMixProject = async (textEditor) => {
  const project = await elixirProjectPath(textEditor);
  return existsSync(join(project, 'mix.exs'));
};

const isTestFile = async (textEditor) => {
  const project = await elixirProjectPath(textEditor);
  const relativePath = relative(project, textEditor.getPath());
  // Is the first directory of the relative path "test"?
  return relativePath.split(sep)[0] === 'test';
};

const isForcedElixirc = () => forceElixirc;

const isExsFile = textEditor => textEditor.getPath().endsWith('.exs');

const isPhoenixProject = async (textEditor) => {
  const project = await elixirProjectPath(textEditor);
  const mixLockPath = join(project, 'mix.lock');
  try {
    const mixLockContent = readFileSync(mixLockPath, 'utf-8');
    return mixLockContent.indexOf('"phoenix"') > 0;
  } catch (error) {
    return false;
  }
};

const findTextEditor = (filePath) => {
  const allEditors = atom.workspace.getTextEditors();
  const matchingEditor = allEditors.find(
    textEditor => textEditor.getPath() === filePath);
  if (matchingEditor !== undefined) {
    return matchingEditor;
  }
  return false;
};

const parseError = async (toParse, textEditor) => {
  const messages = [];
  const re = regexp(
    `
    \\*\\*[\\ ]+
    \\((\\w+)\\)                   ${''/* 1 - (TypeOfError)*/}
    [\\ ](?:                       ${''/* Two message formats.... mode one*/}
      ([\\w\\ ]+)                  ${''/* 2 - Message*/}
      [\\r\\n]{1,2}.+[\\r\\n]{1,2} ${''/* Internal elixir code*/}
      [\\ ]+(.+)                   ${''/* 3 - File*/}
      :(\\d+):                     ${''/* 4 - Line*/}
    |                              ${''/* Or... mode two*/}
      (.+)                         ${''/* 5 - File*/}
      :(\\d+):                     ${''/* 6 - Line*/}
      [\\ ](.+)                    ${''/* 7 - Message*/}
    )
  `,
    'gm',
  );
  const projectPath = await elixirProjectPath(textEditor);
  let reResult = re.exec(toParse);
  while (reResult !== null) {
    let text;
    let filePath;
    let range;
    if (reResult[2] !== undefined) {
      text = `(${reResult[1]}) ${reResult[2]}`;
      filePath = join(projectPath, reResult[3]);
      const fileEditor = findTextEditor(filePath);
      if (fileEditor) {
        // If there is an open TextEditor instance for the file from the Error,
        // we can get a better range using rangeFromLineNumber, otherwise
        // generate a 1 character range that can be updated to a proper range
        // if/when the file is opened.
        range = rangeFromLineNumber(fileEditor, reResult[4] - 1);
      } else {
        range = new Range([reResult[4] - 1, 0], [reResult[4] - 1, 1]);
      }
    } else {
      text = `(${reResult[1]}) ${reResult[7]}`;
      filePath = join(projectPath, reResult[5]);
      const fileEditor = findTextEditor(filePath);
      if (fileEditor) {
        range = rangeFromLineNumber(fileEditor, reResult[6] - 1);
      } else {
        range = new Range([reResult[6] - 1, 0], [reResult[6] - 1, 1]);
      }
    }
    messages.push({
      type: 'Error',
      text,
      filePath,
      range,
    });
    reResult = re.exec(toParse);
  }
  return messages;
};

// Only Elixir 1.3+
const parseWarning = async (toParse, textEditor) => {
  const messages = [];
  const re = regexp(
    `
    warning:\\ (.*)\\n  ${''/* warning */}
    \\ \\ (.*):([0-9]+) ${''/* file and file number */}
    `,
    'g',
  );
  const projectPath = await elixirProjectPath(textEditor);
  let reResult = re.exec(toParse);

{'fraction_non_alphanumeric': 0.16133777952138093, 'fraction_numerical': 0.009709690074539035, 'mean_word_length': 2.6719481454807346, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 27, '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': '1814339', 'n_tokens_mistral': 3364, 'n_tokens_neox': 3253, 'n_words': 812}