<commit_before>'use strict';

var _ = require('lodash'),
    Entry = require('./entry.js'),
    getEntries = Entry.getEntries,
    util = require('./util.js');

var push = Array.prototype.push,
    specialCategories = ['Methods', 'Properties'],
    token = '@@token@@';

var reCode = /`.*?`/g,
    reToken = /@@token@@/g,
    reSpecialCategory = RegExp('^(?:' + specialCategories.join('|') + ')$');

var htmlEscapes = {
  '*': '&#42;',
  '[': '&#91;',
  ']': '&#93;'
};

/*----------------------------------------------------------------------------*/

/**
 * Escape special Markdown characters in a string.
 *
 * @private
 * @param {string} string The string to escape.
 * @returns {string} Returns the escaped string.
 */
function escape(string) {
  var snippets = [];

  // Replace all code snippets with a token.
  string = string.replace(reCode, function(match) {
    snippets.push(match);
    return token;
  });

  _.forOwn(htmlEscapes, function(replacement, chr) {
    string = string.replace(RegExp('(\\\\?)\\' + chr, 'g'), function(match, backslash) {
      return backslash ? match : replacement;
    });
  });

  // Replace all tokens with code snippets.
  return string.replace(reToken, function(match) {
    return snippets.shift();
  });
}

/**
 * Get the seperator (`.` or `.prototype.`)
 *
 * @private
 * @param {Entry} Entry object to get selector for.
 * @returns {string} Returns the member seperator.
 */
function getSeparator(entry) {
  return entry.isPlugin() ? '.prototype.' : '.';
}

/**
 * Modify a string by replacing named tokens with matching associated object values.
 *
 * @private
 * @param {string} string The string to modify.
 * @param {Object} data The template data object.
 * @returns {string} Returns the modified string.
 */
function interpolate(string, data) {
  return util.format(_.template(string)(data));
}

/**
 * Make an anchor link.
 *
 * @private
 * @param {string} href The anchor href.
 * @param {string} text The anchor text.
 * @returns {string} Returns the anchor HTML.
 */
function makeAnchor(href, text) {
  return '<a href="' + href + '">' + _.toString(text) + '</a>';
}

/*----------------------------------------------------------------------------*/

/**
 * Generates the documentation from JS source.
 *
 * @param {string} The source code to generate the documentation for.
 * @param {object} The options object.
 * @returns {string} Returns the documentation markdown.
 */
function generateDoc(source, options) {
  var api = [],
      byCategories = options.toc == 'categories',
      entries = getEntries(source),
      hashStyle = options.hash,
      organized = {},
      sortEntries = options.sort,
      url = options.url;

  // Add entries and aliases to the API list.
  _.each(entries, function(entry) {
    entry = new Entry(entry, source);
    api.push(entry);

    var aliases = entry.getAliases();
    if (!_.isEmpty(aliases)) {
      push.apply(api, aliases);
    }
  });

  // Build the list of categories for the TOC and generate content for each entry.
  _.each(api, function(entry) {
    // Exit early if the entry is private or has no name.
    var name = entry.getName();
    if (!name || entry.isPrivate()) {
      return;
    }
    var tocGroup,
        member = entry.getMembers(0) || '',
        separator = member ? getSeparator(entry) : '';

    // Add the entry to the TOC.
    if (byCategories) {
      var category = entry.getCategory();
      tocGroup = organized[category] || (organized[category] = []);
    }
    else {
      var memberGroup;
      if (!member ||
          entry.isCtor() ||
          (entry.getType() == 'Object' &&
            !/[=:]\s*(?:null|undefined)\s*[,;]?$/gi.test(entry.entry))
      ) {
        memberGroup = (member ? member + getSeparator(entry) : '') + name;
      } else if (entry.isStatic()) {
        memberGroup = member;
      } else if (!entry.isCtor()) {
        memberGroup = member + getSeparator(entry).slice(0, -1);
      }
      tocGroup = organized[memberGroup] || (organized[memberGroup] = []);
    }
    tocGroup.push(entry);

    // Skip aliases.
    if (entry.isAlias()) {
      return;
    }
    // Start markdown for the entry.
    var entryMarkdown = ['\n<!-- div -->\n'];

    var entryData = {
      'call': entry.getCall(),
      'category': entry.getCategory(),
      'entryHref': '#${hash}',
      'entryLink': _.get(options, 'entryLink', '[#](${entryHref})'),
      'hash': entry.getHash(hashStyle),
      'member': member,
      'name': name,
      'separator': separator,
      'sourceHref': url + '#L' + entry.getLineNumber(),
      'sourceLink': _.get(options, 'sourceLink', '[&#x24C8;](${sourceHref} "View in source")'),
      'tocHref': '1',
      'tocLink': _.get(options, 'tocLink', '[&#x24C9;][${tocHref}]')
    };

    _.each([
      'entryHref', 'sourceHref', 'tocHref',
      'entryLink', 'sourceLink', 'tocLink'
    ], function(option) {
      entryData[option] = interpolate(entryData[option], entryData);
    });

    // Add the heading.
    entryMarkdown.push(interpolate(
      '### <a id="${hash}"></a>`${member}${separator}${call}`\n' +
      interpolate(
        _([
          '${entryLink} ${sourceLink}',
          _.get(options, 'entryLinks', []),
          '${tocLink}'
        ])
        .flatten()
        .compact()
        .join(' '),
        entryData
      )
      .replace(/ {2,}/g, ' '),
      entryData
    ));

    // Add the description.
    entryMarkdown.push('\n' + entry.getDesc() + '\n');

    // Add optional since version.
    var since = entry.getSince();
    if (!_.isEmpty(since)) {
      entryMarkdown.push(<commit_msg>Modify heading and entryLink styles.<commit_after>'use strict';

var _ = require('lodash'),
    Entry = require('./entry.js'),
    getEntries = Entry.getEntries,
    util = require('./util.js');

var push = Array.prototype.push,
    specialCategories = ['Methods', 'Properties'],
    token = '@@token@@';

var reCode = /`.*?`/g,
    reToken = /@@token@@/g,
    reSpecialCategory = RegExp('^(?:' + specialCategories.join('|') + ')$');

var htmlEscapes = {
  '*': '&#42;',
  '[': '&#91;',
  ']': '&#93;'
};

/*----------------------------------------------------------------------------*/

/**
 * Escape special Markdown characters in a string.
 *
 * @private
 * @param {string} string The string to escape.
 * @returns {string} Returns the escaped string.
 */
function escape(string) {
  var snippets = [];

  // Replace all code snippets with a token.
  string = string.replace(reCode, function(match) {
    snippets.push(match);
    return token;
  });

  _.forOwn(htmlEscapes, function(replacement, chr) {
    string = string.replace(RegExp('(\\\\?)\\' + chr, 'g'), function(match, backslash) {
      return backslash ? match : replacement;
    });
  });

  // Replace all tokens with code snippets.
  return string.replace(reToken, function(match) {
    return snippets.shift();
  });
}

/**
 * Get the seperator (`.` or `.prototype.`)
 *
 * @private
 * @param {Entry} Entry object to get selector for.
 * @returns {string} Returns the member seperator.
 */
function getSeparator(entry) {
  return entry.isPlugin() ? '.prototype.' : '.';
}

/**
 * Modify a string by replacing named tokens with matching associated object values.
 *
 * @private
 * @param {string} string The string to modify.
 * @param {Object} data The template data object.
 * @returns {string} Returns the modified string.
 */
function interpolate(string, data) {
  return util.format(_.template(string)(data));
}

/**
 * Make an anchor link.
 *
 * @private
 * @param {string} href The anchor href.
 * @param {string} text The anchor text.
 * @returns {string} Returns the anchor HTML.
 */
function makeAnchor(href, text) {
  return '<a href="' + href + '">' + _.toString(text) + '</a>';
}

/*----------------------------------------------------------------------------*/

/**
 * Generates the documentation from JS source.
 *
 * @param {string} The source code to generate the documentation for.
 * @param {object} The options object.
 * @returns {string} Returns the documentation markdown.
 */
function generateDoc(source, options) {
  var api = [],
      byCategories = options.toc == 'categories',
      entries = getEntries(source),
      hashStyle = options.hash,
      organized = {},
      sortEntries = options.sort,
      url = options.url;

  // Add entries and aliases to the API list.
  _.each(entries, function(entry) {
    entry = new Entry(entry, source);
    api.push(entry);

    var aliases = entry.getAliases();
    if (!_.isEmpty(aliases)) {
      push.apply(api, aliases);
    }
  });

  // Build the list of categories for the TOC and generate content for each entry.
  _.each(api, function(entry) {
    // Exit early if the entry is private or has no name.
    var name = entry.getName();
    if (!name || entry.isPrivate()) {
      return;
    }
    var tocGroup,
        member = entry.getMembers(0) || '',
        separator = member ? getSeparator(entry) : '';

    // Add the entry to the TOC.
    if (byCategories) {
      var category = entry.getCategory();
      tocGroup = organized[category] || (organized[category] = []);
    }
    else {
      var memberGroup;
      if (!member ||
          entry.isCtor() ||
          (entry.getType() == 'Object' &&
            !/[=:]\s*(?:null|undefined)\s*[,;]?$/gi.test(entry.entry))
      ) {
        memberGroup = (member ? member + getSeparator(entry) : '') + name;
      } else if (entry.isStatic()) {
        memberGroup = member;
      } else if (!entry.isCtor()) {
        memberGroup = member + getSeparator(entry).slice(0, -1);
      }
      tocGroup = organized[memberGroup] || (organized[memberGroup] = []);
    }
    tocGroup.push(entry);

    // Skip aliases.
    if (entry.isAlias()) {
      return;
    }
    // Start markdown for the entry.
    var entryMarkdown = ['\n<!-- div -->\n'];

    var entryData = {
      'call': entry.getCall(),
      'category': entry.getCategory(),
      'entryHref': '#${hash}',
      'entryLink': _.get(options, 'entryLink', '<a href="${entryHref}">#</a> '),
      'hash': entry.getHash(hashStyle),
      'member': member,
      'name': name,
      'separator': separator,
      'sourceHref': url + '#L' + entry.getLineNumber(),
      'sourceLink': _.get(options, 'sourceLink', '[&#x24C8;](${sourceHref} "View in source")'),
      'tocHref': '1',
      'tocLink': _.get(options, 'tocLink', '[&#x24C9;][${tocHref}]')
    };

    _.each([
      'entryHref', 'sourceHref', 'tocHref',
      'entryLink', 'sourceLink', 'tocLink'
    ], function(option) {
      entryData[option] = interpolate(entryData[option], entryData);
    });

    // Add the heading.
    entryMarkdown.push(interpolate(
      '<h3 id="${hash}">${entryLink}<code>${member}${separator}${call}</code></h3>\n' +
      interpolate(
        _([
          '${sourceLink}',
          _.get(options, 'entryLinks', []),
          '${tocLink}'
        ])
        .flatten()
        .compact()
        .join(' '),
        entryData
      )
      .replace(/ {2,}/g, ' '),
      entryData
    ));

    // Add the description.
    entryMarkdown.push('\n' + entry.getDesc() + '\n');

    // Add optional since version.
    var since = entry.getSince();
    if (!_.isEmpty(since)) {
      entryMarkdown.push(
{'fraction_non_alphanumeric': 0.19992900878516284, 'fraction_numerical': 0.0031946046676723755, 'mean_word_length': 2.4891640866873064, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6463361', 'n_tokens_mistral': 3535, 'n_tokens_neox': 3434, 'n_words': 877}