antsibull-docs
Version:
TypeScript library for processing Ansible documentation markup
214 lines • 9.2 kB
JavaScript
;
/*
Simplified BSD License (see LICENSES/BSD-2-Clause.txt or https://opensource.org/licenses/BSD-2-Clause)
SPDX-FileCopyrightText: Ansible Project
SPDX-License-Identifier: BSD-2-Clause
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.quoteRST = quoteRST;
exports.postprocessRSTParagraph = postprocessRSTParagraph;
exports.toRST = toRST;
const opts_1 = require("./opts");
const format_1 = require("./format");
const util_1 = require("./util");
function quoteRST(text, escape_starting_whitespace = false, escape_ending_whitespace = false, must_not_be_empty = false) {
text = text.replace(/([\\<>_*`|])/g, '\\$1');
if (escape_ending_whitespace && /\s$/.test(text.substring(text.length - 1))) {
text = text + '\\ ';
}
if (escape_starting_whitespace && /^\s/.test(text)) {
text = '\\ ' + text;
}
if (must_not_be_empty && text === '') {
text = '\\ ';
}
return text;
}
function removeBackslashSpace(line) {
let start = 0;
let end = line.length;
while (true) {
// Remove starting '\ '. These have no effect.
while ((0, util_1.startsWith)(line, '\\ ', start, end)) {
start += 2;
}
// If the line now starts with regular whitespace, trim it.
if ((0, util_1.startsWith)(line, ' ', start, end)) {
start += 1;
}
else {
// If there is none, we're done.
break;
}
// Remove more starting whitespace, and then check again for leading '\ ' etc.
while ((0, util_1.startsWith)(line, ' ', start, end)) {
start += 1;
}
}
while (true) {
/*
Remove trailing '\ ' resp. '\' (after line.trim()). These actually have an effect,
since they remove the linebreak. *But* if our markup generator emits '\ ' followed
by a line break, we still want the line break to count, so this is actually fixing
a bug.
*/
if ((0, util_1.endsWith)(line, '\\', start, end)) {
end -= 1;
}
while ((0, util_1.endsWith)(line, '\\ ', start, end)) {
end -= 2;
}
// If the line now ends with regular whitespace, trim it.
if ((0, util_1.endsWith)(line, ' ', start, end)) {
end -= 1;
}
else {
// If there is none, we're done.
break;
}
// Remove more ending whitespace, and then check again for trailing '\' etc.
while ((0, util_1.endsWith)(line, ' ', start, end)) {
end -= 1;
}
}
// Return subset of the line
line = line.substring(start, end);
line = line.replace(/\\ (?:\\ )+/g, '\\ ');
line = line.replace(/(?<![\\])([ ])\\ (?![`])/g, '$1');
line = line.replace(/(?<!:`)\\ ([ .])/g, '$1');
return line;
}
function checkLine(index, lines, line) {
if (index < 0 || index >= lines.length) {
return false;
}
return lines[index] === line;
}
function modifyLine(index, line, lines) {
const raw_html = '.. raw:: html';
const dashes = '------------';
const hr = ' <hr>';
if (line !== '' && line !== raw_html && line !== dashes && line !== hr) {
return true;
}
if (line === raw_html || line === dashes) {
return false;
}
if (line === hr && checkLine(index - 2, lines, raw_html)) {
return false;
}
if (line === '' &&
(checkLine(index + 1, lines, raw_html) ||
checkLine(index - 1, lines, raw_html) ||
checkLine(index - 3, lines, raw_html))) {
return false;
}
if (line === '' && (checkLine(index + 1, lines, dashes) || checkLine(index - 1, lines, dashes))) {
return false;
}
return true;
}
function postprocessRSTParagraph(par) {
let lines = (0, util_1.splitLines)(par.trim());
lines = lines.map((line, index) => modifyLine(index, line, lines) ? removeBackslashSpace(line.trim().replace('\t', ' ')) : line);
return lines.filter((line, index) => (modifyLine(index, line, lines) ? !!line : true)).join('\n');
}
function formatAntsibullOptionLike(part, role) {
const result = [];
if (part.plugin) {
result.push(part.plugin.fqcn);
result.push('#');
result.push(part.plugin.type);
result.push(':');
}
if (part.entrypoint !== undefined) {
result.push(part.entrypoint);
result.push(':');
}
result.push(part.name);
if (part.value !== undefined) {
result.push('=');
result.push(part.value);
}
return `\\ :${role}:\`${quoteRST(result.join(''), true, true, true)}\`\\ `;
}
function formatPlainOptionLike(part) {
const plugin = [];
if (part.plugin) {
plugin.push(part.plugin.type);
if (!['module', 'role', 'playbook'].includes(part.plugin.type)) {
plugin.push(' plugin');
}
plugin.push(` :ref:\`${quoteRST(part.plugin.fqcn)} <ansible_collections.${part.plugin.fqcn}_${part.plugin.type}>\``);
}
if (part.entrypoint !== undefined) {
if (plugin.length) {
plugin.push(', ');
}
plugin.push('entrypoint ');
plugin.push(quoteRST(part.entrypoint, true));
}
let value = part.name;
if (part.value !== undefined) {
value = `${value}=${part.value}`;
}
const pluginText = plugin.length ? ` (of ${plugin.join('')})` : '';
const mainText = `:literal:\`${quoteRST(value, true, true, true)}\``;
return `\\ ${mainText}${pluginText}\\ `;
}
const DEFAULT_FORMATTER = {
formatError: (part) => `\\ :strong:\`ERROR while parsing\`\\ : ${quoteRST(part.message, true, true, true)}\\ `,
formatBold: (part) => `\\ :strong:\`${quoteRST(part.text, true, true, true)}\`\\ `,
formatCode: (part) => `\\ :literal:\`${quoteRST(part.text, true, true, true)}\`\\ `,
formatHorizontalLine: () => '\n\n.. raw:: html\n\n <hr>\n\n',
formatItalic: (part) => `\\ :emphasis:\`${quoteRST(part.text, true, true, true)}\`\\ `,
formatLink: (part) => part.text === ''
? ''
: part.url === ''
? quoteRST(part.text)
: `\\ \`${quoteRST(part.text)} <${encodeURI(part.url)}>\`__\\ `,
formatModule: (part) => `\\ :ref:\`${quoteRST(part.fqcn, true, true, true)} <ansible_collections.${part.fqcn}_module>\`\\ `,
formatRSTRef: (part) => `\\ :ref:\`${quoteRST(part.text, true, true, true)} <${part.ref}>\`\\ `,
formatURL: (part) => (part.url === '' ? '' : `\\ \`${quoteRST(part.url)} <${encodeURI(part.url)}>\`__\\ `),
formatText: (part) => quoteRST(part.text),
formatEnvVariable: (part) => `\\ :envvar:\`${quoteRST(part.name, true, true, true)}\`\\ `,
formatOptionName: (part) => formatAntsibullOptionLike(part, 'ansopt'),
formatOptionValue: (part) => `\\ :ansval:\`${quoteRST(part.value, true, true, true)}\`\\ `,
formatPlugin: (part) => `\\ :ref:\`${quoteRST(part.plugin.fqcn)} <ansible_collections.${part.plugin.fqcn}_${part.plugin.type}>\`\\ `,
formatReturnValue: (part) => formatAntsibullOptionLike(part, 'ansretval'),
};
const PLAIN_FORMATTER = {
formatError: (part) => `\\ :strong:\`ERROR while parsing\`\\ : ${quoteRST(part.message, true, true, true)}\\ `,
formatBold: (part) => `\\ :strong:\`${quoteRST(part.text, true, true, true)}\`\\ `,
formatCode: (part) => `\\ :literal:\`${quoteRST(part.text, true, true, true)}\`\\ `,
formatHorizontalLine: () => '\n\n------------\n\n',
formatItalic: (part) => `\\ :emphasis:\`${quoteRST(part.text, true, true, true)}\`\\ `,
formatLink: (part) => part.text === ''
? ''
: part.url === ''
? quoteRST(part.text)
: `\\ \`${quoteRST(part.text)} <${encodeURI(part.url)}>\`__\\ `,
formatModule: (part) => `\\ :ref:\`${quoteRST(part.fqcn, true, true, true)} <ansible_collections.${part.fqcn}_module>\`\\ `,
formatRSTRef: (part) => `\\ :ref:\`${quoteRST(part.text, true, true, true)} <${part.ref}>\`\\ `,
formatURL: (part) => (part.url === '' ? '' : `\\ \`${quoteRST(part.url)} <${encodeURI(part.url)}>\`__\\ `),
formatText: (part) => quoteRST(part.text),
formatEnvVariable: (part) => `\\ :envvar:\`${quoteRST(part.name, true, true, true)}\`\\ `,
formatOptionName: (part) => formatPlainOptionLike(part),
formatOptionValue: (part) => `\\ :literal:\`${quoteRST(part.value, true, true, true)}\`\\ `,
formatPlugin: (part) => `\\ :ref:\`${quoteRST(part.plugin.fqcn)} <ansible_collections.${part.plugin.fqcn}_${part.plugin.type}>\`\\ `,
formatReturnValue: (part) => formatPlainOptionLike(part),
};
function toRST(paragraphs, opts) {
opts = opts !== null && opts !== void 0 ? opts : {};
const style = opts.style || 'antsibull-docs';
const mergedOpts = (0, opts_1.mergeOpts)(opts, style === 'antsibull-docs' ? DEFAULT_FORMATTER : PLAIN_FORMATTER);
const result = [];
for (const paragraph of paragraphs) {
const line = [];
(0, format_1.addToDestination)(line, paragraph, mergedOpts);
const lineStr = postprocessRSTParagraph(line.join(''));
result.push(lineStr || '\\');
}
return result.join('\n\n');
}
//# sourceMappingURL=rst.js.map