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, }