UNPKG

docutils-ts

Version:

Port of the Python Docutils library to TypeScript

420 lines (408 loc) 14.5 kB
import * as nodes from './nodes.js'; function findCombiningChars(text) { const combiningCharPattern = /\p{Mark}/u; return [...text].reduce((indices, char, index) => { if (combiningCharPattern.test(char)) { indices.push(index); } return indices; }, []); /* ORIGINAL PYTHON CODE: def find_combining_chars(text: str) -> list[int]: """Return indices of all combining chars in Unicode string `text`. >>> from docutils.utils import find_combining_chars >>> find_combining_chars('A t̆ab̆lĕ') [3, 6, 9] """ return [i for i, c in enumerate(text) if unicodedata.combining(c)] */ } /* Return whether or not to trim footnote space. If trim_footnote_reference_space is not None, return it. If trim_footnote_reference_space is None, return False unless the footnote reference style is 'superscript'. */ export function getTrimFootnoteRefSpace(settings) { return false; } /* if settings.trim_footnote_reference_space is None: return hasattr(settings, 'footnote_references') and \ settings.footnote_references == 'superscript' else: return settings.trim_footnote_reference_space */ function columnWidth(text) { return text.length; } /** * @deprecated * @param obj */ export function isIterable(obj) { // checks for null and undefined /* instanbul ignore if */ if (obj === undefined) { return false; } return Symbol.iterator in Object(obj) && typeof obj[Symbol.iterator] === "function"; } export const punctuation_chars = { openers: "\"\\'(<\\\\[{\\u0f3a\\u0f3c\\u169b\\u2045\\u207d\\u208d\\u2329\\u2768" + "\\u276a\\u276c\\u276e\\u2770\\u2772\\u2774\\u27c5\\u27e6\\u27e8\\u27ea" + "\\u27ec\\u27ee\\u2983\\u2985\\u2987\\u2989\\u298b\\u298d\\u298f\\u2991" + "\\u2993\\u2995\\u2997\\u29d8\\u29da\\u29fc\\u2e22\\u2e24\\u2e26\\u2e28" + "\\u3008\\u300a\\u300c\\u300e\\u3010\\u3014\\u3016\\u3018\\u301a\\u301d" + "\\u301d\\ufd3e\\ufe17\\ufe35\\ufe37\\ufe39\\ufe3b\\ufe3d\\ufe3f\\ufe41" + "\\ufe43\\ufe47\\ufe59\\ufe5b\\ufe5d\\uff08\\uff3b\\uff5b\\uff5f\\uff62" + "\\xab\\u2018\\u201c\\u2039\\u2e02\\u2e04\\u2e09\\u2e0c\\u2e1c\\u2e20" + "\\u201a\\u201e\\xbb\\u2019\\u201d\\u203a\\u2e03\\u2e05\\u2e0a\\u2e0d" + "\\u2e1d\\u2e21\\u201b\\u201f", closers: "\"\\')>\\\\]}\\u0f3b\\u0f3d\\u169c\\u2046\\u207e\\u208e\\u232a\\u2769" + "\\u276b\\u276d\\u276f\\u2771\\u2773\\u2775\\u27c6\\u27e7\\u27e9\\u27eb" + "\\u27ed\\u27ef\\u2984\\u2986\\u2988\\u298a\\u298c\\u298e\\u2990\\u2992" + "\\u2994\\u2996\\u2998\\u29d9\\u29db\\u29fd\\u2e23\\u2e25\\u2e27\\u2e29" + "\\u3009\\u300b\\u300d\\u300f\\u3011\\u3015\\u3017\\u3019\\u301b\\u301e" + "\\u301f\\ufd3f\\ufe18\\ufe36\\ufe38\\ufe3a\\ufe3c\\ufe3e\\ufe40\\ufe42" + "\\ufe44\\ufe48\\ufe5a\\ufe5c\\ufe5e\\uff09\\uff3d\\uff5d\\uff60\\uff63" + "\\xbb\\u2019\\u201d\\u203a\\u2e03\\u2e05\\u2e0a\\u2e0d\\u2e1d\\u2e21" + "\\u201b\\u201f\\xab\\u2018\\u201c\\u2039\\u2e02\\u2e04\\u2e09\\u2e0c" + "\\u2e1c\\u2e20\\u201a\\u201e" }; /* Return a string with escape-backslashes converted to nulls. */ function escape2null(text) { const parts = []; let start = 0; while (true) { const found = text.indexOf("\\", start); if (found === -1) { parts.push(text.substring(start)); return parts.join(""); } parts.push(text.substring(start, found)); parts.push(`\x00${text.substring(found + 1, found + 2)}`); start = found + 2; // skip character after escape } } /** * Split `text` on escaped whitespace (null+space or null+newline). * Return a list of strings. */ function splitEscapedWhitespace(text) { const strings = text.split("\x00 "); const s = []; for (const string of strings) { s.push(...string.split("\x00\n")); } return s; } /** * Indices of Unicode string `text` when skipping combining characters. * * >>> from docutils.utils import column_indices * >>> column_indices(u'A t ab le ') * [0, 1, 2, 4, 5, 7, 8] */ function columnIndicies(text) { const stringIndicies = new Array(text.length); for (let i = 0; i < text.length; i += 1) { stringIndicies[i] = i; } findCombiningChars(text).forEach((index) => { stringIndicies[index] = undefined; }); return stringIndicies.filter((i) => typeof i !== "undefined"); } // TODO: account for asian wide chars here instead of using dummy // replacements in the tableparser? /* string_indices = range(len(text)) findCombiningChars(text).forEach((index) => { string_indices[index] = undefined; }); return [i for i in string_indices if i is not None] } */ export function escapeRegExp(strVal) { return strVal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string } // export default { // newDocument, // }; export function stripCombiningChars(text) { return text; // TODO: fixme // return u''.join([c for c in text if not unicodedata.combining(c)]) } export function pySplit(text, max) { return text.trim().split(/s+/, max); } export function checkDocumentArg(document) { if (typeof document === "undefined") { throw new Error("undefined document"); } return true; } export function relativePath(source, target) { /* Build and return a path to `target`, relative to `source` (both files). If there is no common prefix, return the absolute path to `target`. */ return `${source}/${target}`; // fixme broken url /* source_parts = os.path.abspath(source or type(target)('dummy_file') ).split(os.sep) target_parts = os.path.abspath(target).split(os.sep) # Check first 2 parts because '/dir'.split('/') == ['', 'dir']: if source_parts[:2] != target_parts[:2]: # Nothing in common between paths. # Return absolute path, using '/' for URLs: return '/'.join(target_parts) source_parts.reverse() target_parts.reverse() while (source_parts and target_parts and source_parts[-1] == target_parts[-1]): # Remove path components in common: source_parts.pop() target_parts.pop() target_parts.reverse() parts = ['..'] * (len(source_parts) - 1) + target_parts return '/'.join(parts) */ } /** * Return a list of normalized combinations for a `BCP 47` language tag. * * Example: * * >>> from docutils.utils import normalize_language_tag * >>> normalize_language_tag('de_AT-1901') * ['de-at-1901', 'de-at', 'de-1901', 'de'] * >>> normalize_language_tag('de-CH-x_altquot') * ['de-ch-x-altquot', 'de-ch', 'de-x-altquot', 'de'] */ function normalizedLanguageTag(tag) { // normalize: let myTag = tag.toLowerCase().replace(/-/g, "_"); // split (except singletons, which mark the following tag as non-standard): tag = tag.replace(/_([a-zA-Z0-9])_/g, "$1-"); const subtags = tag.split("_"); const baseTag = subtags.pop(); // find all combinations of subtags const taglist = []; /* for(let i = subtags.length; i >= 0; i -= 1) { // for tags in unique_combinations(subtags, n): for tags in itertools.combinations(subtags, n): taglist.append('-'.join(base_tag+tags)) taglist += base_tag return taglist */ return taglist; } function assembleOptionDict(optionList, optionsSpec) { return {}; } class BadOptionError { constructor(message) { this.message = message; this.name = 'BadOptionsError'; } } class BadOptionDataError { constructor(message) { this.name = 'BadOptionDataError'; this.message = message; } } /* """ Return a mapping of option names to values. :Parameters: - `option_list`: A list of (name, value) pairs (the output of `extract_options()`). - `options_spec`: Dictionary mapping known option names to a conversion function such as `int` or `float`. :Exceptions: - `KeyError` for unknown option names. - `DuplicateOptionError` for duplicate options. - `ValueError` for invalid option values (raised by conversion function). - `TypeError` for invalid option value types (raised by conversion function). """ options = {} for name, value in option_list: convertor = options_spec[name] # raises KeyError if unknown if convertor is None: raise KeyError(name) # or if explicitly disabled if name in options: raise DuplicateOptionError('duplicate option "%s"' % name) try: options[name] = convertor(value) except (ValueError, TypeError) as detail: raise detail.__class__('(option: "%s"; value: %r)\n%s' % (name, value, ' '.join(detail.args))) return options */ /** * Return a list of option (name, value) pairs from field names & bodies. * * :Parameter: * `field_list`: A flat field list, where each field name is a single * word and each field body consists of a single paragraph only. * * :Exceptions: * - `BadOptionError` for invalid fields. * - `BadOptionDataError` for invalid option data (missing name, * missing data, bad quotes, etc.). */ function extractOptions(fieldList) { const optionList = []; for (let i = 0; i < fieldList.getNumChildren(); i += 1) { const field = fieldList.getChild(i); if (pySplit(field.getChild(0).astext()).length !== 1) { throw new BadOptionError('extension option field name may not contain multiple words'); } const name = field.getChild(0).astext().toLowerCase(); const body = field.getChild(1); let data; if (!body.hasChildren()) { data = undefined; } else if (body.getNumChildren() > 1 || !(body.getChild(0) instanceof nodes.paragraph) || body.getChild(0).getNumChildren() !== -1 || !(body.getChild(0).getChild(0) instanceof nodes.Text)) { throw new BadOptionDataError(`extension option field body may contain\n` + `a single paragraph only (option "${name}")`); } else { data = body.getChild(0).getChild(0).astext(); } optionList.push([name, data]); } return optionList; } /** * Return a dictionary mapping extension option names to converted values. * * :Parameters: * - `field_list`: A flat field list without field arguments, where each * field body consists of a single paragraph only. * - `options_spec`: Dictionary mapping known option names to a * conversion function such as `int` or `float`. * * :Exceptions: * - `KeyError` for unknown option names. * - `ValueError` for invalid option values (raised by the conversion * function). * - `TypeError` for invalid option value types (raised by conversion * function). * - `DuplicateOptionError` for duplicate options. * - `BadOptionError` for invalid fields. * - `BadOptionDataError` for invalid option data (missing name, * missing data, bad quotes, etc.). */ export function extractExtensionOptions(fieldList, optionsSpec) { const optionList = extractOptions(fieldList); const optionDict = assembleOptionDict(optionList, optionsSpec); return optionDict; } function toRoman(input) { let val; let num = Math.floor(input); let s = ''; let i = 0; let v = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]; let r = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I']; function toBigRoman(n) { let n1; let ret = '', rem = n; while (rem > 1000) { let is = rem; let prefix = '', suffix = '', n = rem, magnitude = 1; while (n > 1000) { n /= 1000; magnitude *= 1000; prefix += '('; suffix += ')'; } n1 = Math.floor(n); rem = is - (n1 * magnitude); ret += prefix + toRoman(n1) + suffix; } return ret + toRoman(rem); } if (input - num || num < 1) num = 0; if (num > 3999) return toBigRoman(num); while (num) { val = v[i]; while (num >= val) { num -= val; s += r[i]; } ++i; } return s; } ; export function fromRoman(roman, accept) { let s = roman.toUpperCase().replace(/ +/g, ''), L = s.length, sum = 0, i = 0, next, val, R = { M: 1000, D: 500, C: 100, L: 50, X: 10, V: 5, I: 1 }; function fromBigRoman(rn) { let n = 0, x, n1, S, rx = /(\(*)([MDCLXVI]+)/g; while ((S = rx.exec(rn)) != null) { x = S[1].length; n1 = fromRoman(S[2], accept); if (isNaN(n1)) return NaN; if (x) n1 *= Math.pow(1000, x); n += n1; } return n; } if (/^[MDCLXVI)(]+$/.test(s)) { if (s.indexOf('(') == 0) return fromBigRoman(s); while (i < L) { val = R[s.charAt(i++)]; next = R[s.charAt(i)] || 0; if (next - val > 0) val *= -1; sum += val; } if (accept || toRoman(sum) === s) return sum; } return NaN; } ; export function _getCallerFileAndLine() { const originalFunc = Error.prepareStackTrace; let callerfile; let callerlineno; try { const err = new Error(); Error.prepareStackTrace = (myErr, stack) => stack; if (!err.stack) { return [undefined, undefined]; } const stack = err.stack; const x = stack.shift(); const currentfile = x.getFileName(); while (stack.length) { const x2 = stack.shift(); callerfile = x2.getFileName(); callerlineno = x2.getLineNumber(); if (currentfile !== callerfile) break; } } catch (e) { console.log(e); } Error.prepareStackTrace = originalFunc; return [callerfile, callerlineno]; } export { findCombiningChars, columnWidth, escape2null, splitEscapedWhitespace, columnIndicies, normalizedLanguageTag };