text: 'use babel';
  
  // eslint-disable-next-line import/extensions, import/no-extraneous-dependencies
  import { CompositeDisposable } from 'atom';
  
  let fs;
  let path;
  let helpers;
  let semver;
  
  function loadDeps() {
    if (!semver) {
      semver = require('semver');
    }
    if (!fs) {
      fs = require('fs-plus');
    }
    if (!helpers) {
      helpers = require('atom-linter');
    }
    if (!path) {
      path = require('path');
    }
  }
  
  // Local variables
  const parseRegex = /(\d+):(\d+):\s(([A-Z])\d{2,3})\s+(.*)/g;
  const execPathVersions = new Map();
  
  const applySubstitutions = (givenExecPath, projDir) => {
    let execPath = givenExecPath;
    const projectName = path.basename(projDir);
    execPath = execPath.replace(/\$PROJECT_NAME/ig, projectName);
    execPath = execPath.replace(/\$PROJECT/ig, projDir);
    const paths = execPath.split(';');
    for (let i = 0; i < paths.length; i += 1) {
      if (fs.existsSync(paths[i])) {
        return paths[i];
      }
    }
    return execPath;
  };
  
  const getVersionString = async (versionPath) => {
    if (!Object.hasOwnProperty.call(getVersionString, 'cache')) {
      getVersionString.cache = new Map();
    }
    if (!getVersionString.cache.has(versionPath)) {
      getVersionString.cache.set(
        versionPath,
        await helpers.exec(versionPath, ['--version']),
      );
    }
    return getVersionString.cache.get(versionPath);
  };
  
  const generateInvalidPointTrace = async (execPath, match, filePath, textEditor, point) => {
    const flake8Version = await getVersionString(execPath);
    const issueURL = 'https://github.com/AtomLinter/linter-flake8/issues/new';
    const title = encodeURIComponent(`Flake8 rule '${match[3]}' reported an invalid point`);
    const body = encodeURIComponent([
      `Flake8 reported an invalid point for the rule \`${match[3]}\`, ` +
      `with the messge \`${match[5]}\`.`,
      '', '',
      '<!-- If at all possible, please include code that shows this issue! -->',
      '', '',
      'Debug information:',
      `Atom version: ${atom.getVersion()}`,
      `Flake8 version: \`${flake8Version}\``,
    ].join('\n'));
    const newIssueURL = `${issueURL}?title=${title}&body=${body}`;
    return {
      severity: 'error',
      description: [
        'ERROR: Flake8 provided an invalid point! See below the details. ',
        `<a href="${newIssueURL}">Report this</a>`, '', '',
        `Original message: ${match[3]} — ${match[5]}`, '', '',
        `Requested point: ${point[0]}:${point[1]}`,
      ].join('\n'),
      location: {
        file: filePath,
        position: helpers.generateRange(textEditor, 0),
      },
      excerpt: 'Flake8 error: invalid point',
    };
  };
  
  const determineExecVersion = async (execPath) => {
    const versionString = await helpers.exec(execPath, ['--version'], { ignoreExitCode: true });
    const versionPattern = /^[^\s]+/g;
    const match = versionString.match(versionPattern);
    if (match !== null) {
      execPathVersions.set(execPath, match[0]);
    }
  };
  
  const getFlake8Version = async (execPath) => {
    if (!execPathVersions.has(execPath)) {
      await determineExecVersion(execPath);
    }
    return execPathVersions.get(execPath);
  };
  
  export default {
    activate() {
      this.idleCallbacks = new Set();
  
      let packageDepsID;
      const linterFlake8Deps = () => {
        this.idleCallbacks.delete(packageDepsID);
  
        // Request checking / installation of package dependencies
        if (!atom.inSpecMode()) {
          require('atom-package-deps').install('linter-flake8');
        }
  
        // FIXME: Remove after a few versions
        if (typeof atom.config.get('linter-flake8.disableTimeout') !== 'undefined') {
          atom.config.unset('linter-flake8.disableTimeout');
        }
        loadDeps();
      };
      packageDepsID = window.requestIdleCallback(linterFlake8Deps);
      this.idleCallbacks.add(packageDepsID);
  
      this.subscriptions = new CompositeDisposable();
      this.subscriptions.add(
        atom.config.observe('linter-flake8.projectConfigFile', (value) => {
          this.projectConfigFile = value;
        }),
        atom.config.observe('linter-flake8.maxLineLength', (value) => {
          this.maxLineLength = value;
        }),
        atom.config.observe('linter-flake8.ignoreErrorCodes', (value) => {
          this.ignoreErrorCodes = value;
        }),
        atom.config.observe('linter-flake8.maxComplexity', (value) => {
          this.maxComplexity = value;
        }),
        atom.config.observe('linter-flake8.selectErrors', (value) => {
          this.selectErrors = value;
        }),
        atom.config.observe('linter-flake8.hangClosing', (value) => {
          this.hangClosing = value;
        }),
        atom.config.observe('linter-flake8.executablePath', (value) => {
          this.executablePath = value;
        }),
        atom.config.observe('linter-flake8.pycodestyleErrorsToWarnings', (value) => {
          this.pycodestyleErrorsToWarnings = value;
        }),
        atom.config.observe('linter-flake8.flakeErrors', (value) => {
          this.flakeErrors = value;
        }),
        atom.config.observe('linter-flake8.builtins', (value) => {
          this.builtins = value;
        }),
      );
    },
  
    deactivate() {
      this.idleCallbacks.forEach(callbackID => window.cancelIdleCallback(callbackID));
      this.idleCallbacks.clear();
      this.subscriptions.dispose();
    },
  
    provideLinter() {
      return {
        name: 'Flake8',
        grammarScopes: ['source.python', 'source.python.django'],
        scope: 'file',
        lintsOnChange: true,
        lint: async (textEditor) => {
          if (!atom.workspace.isTextEditor(textEditor)) {
            // Invalid TextEditor
            return null;
          }
  
          const filePath = textEditor.getPath();
          if (!filePath) {
            // Invalid path
            return null;
          }
          const fileText = textEditor.getText();
  
          // Load dependencies if they aren't already
          loadDeps();
  
          const parameters = ['--format=default'];
  
          const projectPath = atom.project.relativizePath(filePath)[0];
          const baseDir = projectPath !== null ? projectPath : path.dirname(filePath);
          const configFilePath = await helpers.findCachedAsync(baseDir, this.projectConfigFile);
          const execPath = fs.normalize(applySubstitutions(this.executablePath, baseDir));
  
          // get the version of Flake8
          const version = await getFlake8Version(execPath);
  
          // stdin-display-name available since 3.0.0
          if (semver.valid(version) && semver.gte(version, '3.0.0')) {
            parameters.push('--stdin-display-name', filePath);
          }
  
          if (this.projectConfigFile && baseDir !== null && configFilePath !== null) {
            parameters.push('--config', configFilePath);
          } else {
            if (this.maxLineLength) {
              parameters.push('--max-line-length', this.maxLineLength);
            }
            if (this.ignoreErrorCodes.length) {
              parameters.push('--ignore', this.ignoreErrorCodes.join(','));
            }
            if (this.maxComplexity !== 79) {
              parameters.push('--max-complexity', this.maxComplexity);
            }
            if (this.hangClosing) {
              parameters.push('--hang-closing');
            }
            if (this.selectErrors.length) {
              parameters.push('--select', this.selectErrors.join(','));
            }
            if (this.builtins.length) {
              parameters.push('--builtins', this.builtins.join(','));
            }
          }
  
          parameters.push('-');
  
          const forceTimeout = 1000 * 60 * 5; // (ms * s * m) = Five minutes
          const options = {
            stdin: fileText,
            cwd: baseDir,
            ignoreExitCode: true,
            timeout: forceTimeout,
            uniqueKey: `linter-flake8:${filePath}`,
          };
  
          let result;
          try {
            result = await helpers.exec(execPath, parameters, options);
          } catch (e) {
            const pyTrace = e.message.split('\n');
            const pyMostRecent = pyTrace[pyTrace.length - 1];
            atom.notifications.addError('Flake8 crashed!', {
              detail: 'linter-flake8:: Flake8 threw an error related to:\n' +
                `${pyMostRecent}\n` +
                "Please check Atom's Console for more details",
            });
            // eslint-disable-next-line no-console
            console.error('linter-flake8:: Flake8 returned an error', e.message);
            // Tell Linter to not update any current messages it may have
            return null;
          }
  
          if (result === null) {
            // Process was killed by a future invocation
            return null;
          }
  
          if (textEditor.getText() !== fileText) {
            // Editor contents have changed, tell Linter not to update
            return null;
          }
  
          const messages = [];
  
          let match = parseRegex.exec(result);
          while (match !== null) {
            // Note that these positions are being converted to 0-indexed
            const line = Number.parseInt(match[1], 10) - 1 || 0;
            const col = Number.parseInt(match[2], 10) - 1 || undefined;
  
            var severity = 'error'
            if (match[4] === 'E')
            {
                severity = 'info'
            }
            else if (match[4] === 'F')
            {
              if(this.flakeErrors)
              {
                severity = 'error'
              }
              else
              {
                severity = 'warning' 
              }
            }
            else if (match[4] === 'D')
            {
              severity = 'info'
            }
  
  
            try {
              messages.push({
                severity: severity,
                excerpt: `${match[3]} — ${match[5]}`,
                location: {
                  file: filePath,
                  position: helpers.generateRange(textEditor, line, col),
                },
              });
            } catch (error) {
              // generateRange encountered an invalid point
              const point = [line + 1, col + 1];
              const m = generateInvalidPointTrace(execPath, match, filePath, textEditor, point);
              messages.push(m);
            }
  
            match = parseRegex.exec(result);
          }
          // Ensure that any invalid point messages have finished resolving
          return Promise.all(messages);
        },
      };
    },
  };
  