'use babel';

import SwaggerParser from 'swagger-parser';
import _, { flatten } from 'lodash';

function tokenizedLineForRow(editor, lineNumber) {
  return editor.displayBuffer.tokenizedBuffer.tokenizedLineForRow(lineNumber);
}

function checkTokenScope(scopes) {
  return scopes.includes('entity.name.tag.yaml') ||
    scopes.includes('meta.structure.dictionary.json');
}

function extractRange(givenPath, editor) {
  let lineNumber = 0;
  let pathIndex = 0;
  let foundRange;
  const maxLine = editor.getLineCount();
  // remove numeric indexes
  const path = givenPath.filter(str => isNaN(str));

  const checkLineTokens = (tokens) => {
    let offset = 0;
    tokens.forEach((token) => {
      if (checkTokenScope(token.scopes) &&
          token.value === path[pathIndex]) {
        pathIndex += 1;
        if (pathIndex >= path.length) {
          foundRange = [[lineNumber, offset], [lineNumber, offset + token.bufferDelta]];
          return;
        }
      }
      offset += token.bufferDelta;
    });
  };

  while (lineNumber <= maxLine) {
    const tokenizedLine = tokenizedLineForRow(editor, lineNumber);
    if (typeof tokenizedLine === 'undefined') {
      break;
    }
    checkLineTokens(tokenizedLine.tokens);
    if (foundRange) {
      return foundRange;
    }
    lineNumber += 1;
  }

  // Unable to determine the range for some reason
  return null;
}

function canValidate(path, text) {
  return text.length > 8 &&
    /"?swagger"?\s*:\s*['"]\d+\.\d+['"]/g.test(text);
}

function errorsToLinterMessages(err, path, editor) {
  const errObj = err.toJSON();
  if (!errObj.details) {
    return [{
      type: 'Error',
      text: errObj.message,
      filePath: path,
    }];
  }
  return errObj.details.map((detail) => {
    if (detail.code === 'ONE_OF_MISSING' && detail.inner) {
      const errors = _(detail.inner).map(innerDetail => (
        {
          type: 'Error',
          text: innerDetail.message,
          filePath: path,
          range: extractRange(innerDetail.path, editor),
        }
      )).valueOf();
      return errors;
    }
    return {
      type: 'Error',
      text: detail.message,
      filePath: path,
      range: extractRange(detail.path, editor),
    };
  });
}

export default async function tryValidate(editor) {
  const path = editor.getPath();
  const text = editor.getText();
  if (!canValidate(path, text)) {
    return [];
  }

  const swaggerParserOpts = {
    validate: {
      // Validate against the Swagger 2.0 spec
      // https://github.com/BigstickCarpet/swagger-parser/blob/master/docs/options.md
      spec: true,
    },
  };

  try {
    await SwaggerParser.validate(path, swaggerParserOpts);
    return [];
  } catch (err) {
    const arg = errorsToLinterMessages(err, path, editor);
    const linterMessages = flatten(arg).valueOf();
    return linterMessages;
  }
}

@@ -2694,24 +2694,160 @@
 tch (err) %7B%0A
+    if (editor.getText() !== text) %7B%0A      // Editor contents have changed, tell Linter not to update messages%0A      return null;%0A    %7D%0A
     const ar

Fix a race condition