UNPKG

agda-web-docs-lib

Version:

Library for enhancing Agda-generated HTML documentation

206 lines 9.43 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.AgdaDocsIndexer = void 0; const fs = __importStar(require("fs")); const path = __importStar(require("path")); const jsdom_1 = require("jsdom"); class AgdaDocsIndexer { /** * Gets the global position-to-line mappings for all files * Used for passing mappings to worker threads */ static getGlobalMappings() { return { ...AgdaDocsIndexer.globalPositionMappings }; } /** * Sets the global position-to-line mappings * Used for receiving mappings in worker threads */ static setGlobalMappings(mappings) { AgdaDocsIndexer.globalPositionMappings = { ...mappings }; } /** * Builds position-to-line mappings for the Agda documentation * @param inputDir Directory containing the HTML files * @param progressCallback Optional callback function for progress reporting */ static async buildPositionMappings(inputDir, progressCallback) { AgdaDocsIndexer.globalPositionMappings = {}; console.log('Building indexes for cross-file references...'); try { // First pass: process all files to build position-to-line mappings const files = fs.readdirSync(inputDir).filter((f) => f.endsWith('.html')); console.log(`Scanning ${files.length} files for position mappings...`); // Process files in batches to avoid memory issues const batchSize = 20; // Process 20 files at a time let processedCount = 0; for (let i = 0; i < files.length; i += batchSize) { const batch = files.slice(i, i + batchSize); // Process each file in the batch for (const file of batch) { let tempDom = null; try { const content = fs.readFileSync(path.join(inputDir, file), 'utf-8'); tempDom = new jsdom_1.JSDOM(content); const document = tempDom.window.document; // Build mappings for this file const mappings = {}; // Add line numbers to code blocks first AgdaDocsIndexer.addLineNumbersToElementsInDocument(document); // Look for all elements with an id attribute that is numeric const elementsWithNumericId = document.querySelectorAll('[id]'); elementsWithNumericId.forEach((element) => { const id = element.getAttribute('id'); if (id && /^\d+$/.test(id)) { // Find the line number for this element const lineNumber = AgdaDocsIndexer.findLineNumberForElement(element); if (lineNumber) { mappings[id] = lineNumber; } } }); // Store mappings for this file AgdaDocsIndexer.globalPositionMappings[file] = mappings; // Update progress counter and callback processedCount++; if (progressCallback) { progressCallback(processedCount, files.length); } } catch (error) { console.error(`Error processing file ${file}:`, error); } finally { // Explicitly clean up JSDOM to free memory if (tempDom) { tempDom.window.close(); tempDom = null; } } } // Add a small delay between batches to allow garbage collection if (i + batchSize < files.length) { await new Promise((resolve) => setTimeout(resolve, 50)); } } } catch (error) { console.error('Error building position mappings index:', error); } } /** * Adds line numbers to code blocks in a document * Static version used during pre-processing */ static addLineNumbersToElementsInDocument(document) { // Add line numbers to code blocks const codeBlocks = document.querySelectorAll('pre.Agda'); codeBlocks.forEach((codeBlock, blockIndex) => { // Assign a unique ID to the code block const blockId = `block-${blockIndex + 1}`; codeBlock.id = blockId; // Create a container for the code content const codeContent = document.createElement('div'); codeContent.className = 'code-content'; // Get the original HTML content const originalContent = codeBlock.innerHTML; // Split by lines for processing const lines = originalContent.split('\n'); // Create modified content with line IDs const linesHTML = []; // Process lines const actualLines = lines.filter((line, index) => !(index === lines.length - 1 && line.trim() === '')); actualLines.forEach((line, index) => { const lineNum = index + 1; // Add the line with a block-specific ID that can be linked to const lineContent = line.trim() === '' ? '&nbsp;' : line; linesHTML.push(`<div id="${blockId}-LC${lineNum}" class="code-line">${lineContent}</div>`); }); // Update code content codeContent.innerHTML = linesHTML.join(''); // Replace content with line-numbered version codeBlock.innerHTML = ''; codeBlock.appendChild(codeContent); }); } /** * Static version of findLineNumberForElement used during pre-processing */ static findLineNumberForElement(element) { // Find the nearest code block ancestor const codeBlock = element.closest('pre.Agda'); if (!codeBlock) return null; // Get the block ID const blockId = codeBlock.id || 'block-1'; // Find the code container within the code block const codeContainer = codeBlock.querySelector('.code-content'); if (!codeContainer) return null; // Get all code lines const codeLines = Array.from(codeContainer.querySelectorAll('.code-line')); // Find which line contains this element for (let i = 0; i < codeLines.length; i++) { if (codeLines[i].contains(element) || element === codeLines[i]) { return i + 1; // Line numbers are 1-indexed } } // If not found in code lines, check if the element is the code block itself if (element === codeBlock) { return 1; // Return first line if it's the code block itself } // Try to find by walking up and then down the DOM let parent = element.parentElement; while (parent && parent !== codeBlock) { // Check if any siblings before this parent contain line numbers let sibling = parent.previousElementSibling; while (sibling) { const lineId = sibling.id; if (lineId && lineId.startsWith(`${blockId}-LC`)) { const lineNum = parseInt(lineId.substring(blockId.length + 3)); // +3 for "-LC" return lineNum; } sibling = sibling.previousElementSibling; } parent = parent.parentElement; } // Default to beginning of file if we can't determine the exact line return 1; } } exports.AgdaDocsIndexer = AgdaDocsIndexer; AgdaDocsIndexer.globalPositionMappings = {}; //# sourceMappingURL=indexer.js.map