"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _lodash = _interopRequireDefault(require("lodash"));

var _mainUmd = require("regextras/dist/main-umd");

var _iterateJsdoc = _interopRequireDefault(require("../iterateJsdoc"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const otherDescriptiveTags = [// 'copyright' and 'see' might be good addition, but as the former may be
//   sensitive text, and the latter may have just a link, they are not
//   included by default
'summary', 'file', 'fileoverview', 'overview', 'classdesc', 'todo', 'deprecated', 'throws', 'exception', 'yields', 'yield'];

const extractParagraphs = text => {
  // Todo [engine:node@>8.11.0]: Uncomment following line with neg. lookbehind instead
  // return text.split(/(?<![;:])\n\n/u);
  return [...text].reverse().join('').split(/\n\n(?![;:])/u).map(par => {
    return [...par].reverse().join('');
  }).reverse();
};

const extractSentences = (text, abbreviationsRegex) => {
  const txt = text // Remove all {} tags.
  .replace(/\{[\s\S]*?\}\s*/gu, '') // Remove custom abbreviations
  .replace(abbreviationsRegex, '');
  const sentenceEndGrouping = /([.?!])(?:\s+|$)/u;
  const puncts = (0, _mainUmd.RegExtras)(sentenceEndGrouping).map(txt, punct => {
    return punct;
  });
  return txt.split(/[.?!](?:\s+|$)/u) // Re-add the dot.
  .map((sentence, idx) => {
    return /^\s*$/u.test(sentence) ? sentence : `${sentence}${puncts[idx] || ''}`;
  });
};

const isNewLinePrecededByAPeriod = text => {
  let lastLineEndsSentence;
  const lines = text.split('\n');
  return !lines.some(line => {
    if (lastLineEndsSentence === false && /^[A-Z][a-z]/u.test(line)) {
      return true;
    }

    lastLineEndsSentence = /[.:?!|]$/u.test(line);
    return false;
  });
};

const isCapitalized = str => {
  return str[0] === str[0].toUpperCase();
};

const isTable = str => {
  return str.charAt() === '|';
};

const capitalize = str => {
  return str.charAt(0).toUpperCase() + str.slice(1);
};

const validateDescription = (description, reportOrig, jsdocNode, abbreviationsRegex, sourceCode, tag) => {
  if (!description) {
    return false;
  }

  const paragraphs = extractParagraphs(description);
  return paragraphs.some((paragraph, parIdx) => {
    const sentences = extractSentences(paragraph, abbreviationsRegex);

    const fix = fixer => {
      let text = sourceCode.getText(jsdocNode);

      if (!/[.:?!]$/u.test(paragraph)) {
        const line = _lodash.default.last(paragraph.split('\n'));

        text = text.replace(new RegExp(`${_lodash.default.escapeRegExp(line)}$`, 'mu'), `${line}.`);
      }

      var _iteratorNormalCompletion = true;
      var _didIteratorError = false;
      var _iteratorError = undefined;

      try {
        for (var _iterator = sentences.filter(sentence_ => {
          return !/^\s*$/u.test(sentence_) && !isCapitalized(sentence_) && !isTable(sentence_);
        })[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
          const sentence = _step.value;
          const beginning = sentence.split('\n')[0];

          if (tag.tag) {
            const reg = new RegExp(`(@${_lodash.default.escapeRegExp(tag.tag)}.*)${_lodash.default.escapeRegExp(beginning)}`, 'u');
            text = text.replace(reg, ($0, $1) => {
              return $1 + capitalize(beginning);
            });
          } else {
            text = text.replace(beginning, capitalize(beginning));
          }
        }
      } catch (err) {
        _didIteratorError = true;
        _iteratorError = err;
      } finally {
        try {
          if (!_iteratorNormalCompletion && _iterator.return != null) {
            _iterator.return();
          }
        } finally {
          if (_didIteratorError) {
            throw _iteratorError;
          }
        }
      }

      return fixer.replaceText(jsdocNode, text);
    };

    const report = (msg, fixer, tagObj) => {
      tagObj.line += parIdx * 2; // Avoid errors if old column doesn't exist here

      tagObj.column = 0;
      reportOrig(msg, fixer, tagObj);
    };

    if (sentences.some(sentence => {
      return !/^\s*$/u.test(sentence) && !isCapitalized(sentence) && !isTable(sentence);
    })) {
      report('Sentence should start with an uppercase character.', fix, tag);
    }

    const paragraphNoAbbreviations = paragraph.replace(abbreviationsRegex, '');

    if (!/[.!?|]$/u.test(paragraphNoAbbreviations)) {
      report('Sentence must end with a period.', fix, tag);
      return true;
    }

    if (!isNewLinePrecededByAPeriod(paragraphNoAbbreviations)) {
      report('A line of text is started with an uppercase character, but preceding line does not end the sentence.', null, tag);
      return true;
    }

    return false;
  });
};

var _default = (0, _iterateJsdoc.default)(({
  sourceCode,
  context,
  jsdoc,
  report,
  jsdocNode,
  utils
}) => {
  const options = context.options[0] || {};
  const _options$abbreviation = options.abbreviations,
        abbreviations = _options$abbreviation === void 0 ? [] : _options$abbreviation;
  const abbreviationsRegex = abbreviations.length ? new RegExp('\\b' + abbreviations.map(abbreviation => {
    return _lodash.default.escapeRegExp(abbreviation.replace(/\.$/g, '') + '.');
  }).join('|') + '(?:$|\\s)', 'gu') : '';

  if (!jsdoc.tags || validateDescription(jsdoc.description, report, jsdocNode, abbreviationsRegex, sourceCode, {
    line: jsdoc.line + 1
  })) {
    return;
  }

  utils.forEachPreferredTag('description', matchingJsdocTag => {
    const description = `${matchingJsdocTag.name} ${matchingJsdocTag.description}`.trim();
    validateDescription(description, report, jsdocNode, abbreviationsRegex, sourceCode, matchingJsdocTag);
  }, true);

  const _utils$getTagsByType = utils.getTagsByType(jsdoc.tags),
        tagsWithNames = _utils$getTagsByType.tagsWithNames;

  const tagsWithoutNames = utils.filterTags(({
    tag: tagName
  }) => {
    return otherDescriptiveTags.includes(tagName) || utils.hasOptionTag(tagName) && !tagsWithNames.some(({
      tag
    }) => {
      // If user accidentally adds tags with names (or like `returns`
      //  get parsed as having names), do not add to this list
      return tag === tagName;
    });
  });
  tagsWithNames.some(tag => {
    const description = _lodash.default.trimStart(tag.description, '- ');

    return validateDescription(description, report, jsdocNode, abbreviationsRegex, sourceCode, tag);
  });
  tagsWithoutNames.some(tag => {
    const description = `${tag.name} ${tag.description}`.trim();
    return validateDescription(description, report, jsdocNode, abbreviationsRegex, sourceCode, tag);
  });
}, {
  iterateAllJsdocs: true,
  meta: {
    fixable: 'code',
    schema: [{
      additionalProperties: false,
      properties: {
        abbreviations: {
          items: {
            type: 'string'
          },
          type: 'array'
        },
        tags: {
          items: {
            type: 'string'
          },
          type: 'array'
        }
      },
      type: 'object'
    }],
    type: 'suggestion'
  }
});

exports.default = _default;
module.exports = exports.default;
//# sourceMappingURL=requireDescriptionCompleteSentence.js.map