const fs = require('fs'); const path = require('path'); const util = require('util'); class DocsWriter { /** * Utility class used to write the HTML files used on the documentation. * * Initializes the writer to the specified output file, * creating the parent directories when used if required. */ constructor(filename, typeToPath) { this.filename = filename; this._parent = path.join(this.filename, '..'); this.handle = null; this.title = ''; // Should be set before calling adding items to the menu this.menuSeparatorTag = null; // Utility functions this.typeToPath = t => this._rel(typeToPath(t)); // Control signals this.menuBegan = false; this.tableColumns = 0; this.tableColumnsLeft = null; this.writeCopyScript = false; this._script = ''; } /** * Get the relative path for the given path from the current * file by working around https://bugs.python.org/issue20012. */ _rel(path_) { return path .relative(this._parent, path_) .replace(new RegExp(`\\${path.sep}`, 'g'), '/'); } /** * Writes the head part for the generated document, * with the given title and CSS */ // High level writing writeHead(title, cssPath, defaultCss) { this.title = title; this.write( ` ${title}
` ); } /** * Sets the menu separator. * Must be called before adding entries to the menu */ setMenuSeparator(img) { if (img) { this.menuSeparatorTag = `/`; } else { this.menuSeparatorTag = null; } } /** * Adds a menu entry, will create it if it doesn't exist yet */ addMenu(name, link) { if (this.menuBegan) { if (this.menuSeparatorTag) { this.write(this.menuSeparatorTag); } } else { // First time, create the menu tag this.write(''); } /** * Writes a title header in the document body, * with an optional depth level */ writeTitle(title, level, id) { level = level || 1; if (id) { this.write(`${title}`); } else { this.write(`${title}`); } } /** * Writes the code for the given 'tlobject' properly * formatted with hyperlinks */ writeCode(tlobject) { this.write( `
---${tlobject.isFunction ? 'functions' : 'types'}---\n`
        );

        // Write the function or type and its ID
        if (tlobject.namespace) {
            this.write(tlobject.namespace);
            this.write('.');
        }

        this.write(
            `${tlobject.name}#${tlobject.id.toString(16).padStart(8, '0')}`
        );

        // Write all the arguments (or do nothing if there's none)
        for (const arg of tlobject.args) {
            this.write(' ');
            const addLink = !arg.genericDefinition && !arg.isGeneric;

            // "Opening" modifiers
            if (arg.genericDefinition) {
                this.write('{');
            }

            // Argument name
            this.write(arg.name);
            this.write(':');

            // "Opening" modifiers
            if (arg.isFlag) {
                this.write(`flags.${arg.flagIndex}?`);
            }

            if (arg.isGeneric) {
                this.write('!');
            }

            if (arg.isVector) {
                this.write(
                    `Vector<`
                );
            }

            // Argument type
            if (arg.type) {
                if (addLink) {
                    this.write(``);
                }

                this.write(arg.type);

                if (addLink) {
                    this.write('');
                }
            } else {
                this.write('#');
            }

            // "Closing" modifiers
            if (arg.isVector) {
                this.write('>');
            }

            if (arg.genericDefinition) {
                this.write('}');
            }
        }

        // Now write the resulting type (result from a function/type)
        this.write(' = ');
        const [genericName] = tlobject.args
            .filter(arg => arg.genericDefinition)
            .map(arg => arg.name);

        if (tlobject.result === genericName) {
            // Generic results cannot have any link
            this.write(tlobject.result);
        } else {
            if (/^vector+$/, '');

                this.write(
                    `${vector}<`
                );
                this.write(
                    `${inner}>`
                );
            } else {
                this.write(
                    `${
                        tlobject.result
                    }`
                );
            }
        }

        this.write('
'); } /** * Begins a table with the given 'column_count', required to automatically * create the right amount of columns when adding items to the rows */ beginTable(columnCount) { this.tableColumns = columnCount; this.tableColumnsLeft = 0; this.write(''); } /** * This will create a new row, or add text to the next column * of the previously created, incomplete row, closing it if complete */ addRow(text, link, bold, align) { if (!this.tableColumnsLeft) { // Starting a new row this.write(''); this.tableColumnsLeft = this.tableColumns; } this.write(''); if (bold) { this.write(''); } if (link) { this.write(``); } // Finally write the real table data, the given text this.write(text); if (link) { this.write(''); } if (bold) { this.write(''); } this.write(''); this.tableColumnsLeft -= 1; if (!this.tableColumnsLeft) { this.write(''); } } endTable() { if (this.tableColumnsLeft) { this.write(''); } this.write('
'); } /** * Writes a paragraph of text */ writeText(text) { this.write(`

${text}

`); } /** * Writes a button with 'text' which can be used * to copy 'textToCopy' to clipboard when it's clicked. */ writeCopyButton(text, textToCopy) { this.writeCopyScript = true; this.write( `` ); } addScript(src, path) { if (path) { this._script += ``; } else if (src) { this._script += ``; } } /*** * Ends the whole document. This should be called the last */ endBody() { if (this.writeCopyScript) { this.write( '' + '' ); } this.write(`
${this._script}`); } /** * Wrapper around handle.write */ // "Low" level writing write(s, ...args) { if (args.length) { fs.appendFileSync(this.handle, util.format(s, ...args)); } else { fs.appendFileSync(this.handle, s); } } open() { // Sanity check const parent = path.join(this.filename, '..'); fs.mkdirSync(parent, { recursive: true }); this.handle = fs.openSync(this.filename, 'w'); return this; } close() { fs.closeSync(this.handle); } } module.exports = { DocsWriter, };