Browse Source

Write a custom Asciidoctor tree processor

master
buttercat1791 2 years ago
parent
commit
f4be33f607
  1. 114
      src/lib/parser.ts

114
src/lib/parser.ts

@ -1,19 +1,34 @@ @@ -1,19 +1,34 @@
import { Asciidoctor, Document, Extensions, type ProcessorOptions } from 'asciidoctor';
import { AbstractNode, Asciidoctor, Block, Document, Extensions, Section, type ProcessorOptions } from 'asciidoctor';
class Pharos extends Asciidoctor {
export default class Pharos extends Asciidoctor {
private html?: string | Document;
/**
* A map of node IDs to the nodes themselves.
*/
private nodes: Map<string, AbstractNode> = new Map<string, AbstractNode>();
/**
* A map of node IDs to the integer event kind that will be used to represent the node.
*/
private kinds: Map<string, number> = new Map<string, number>();
/**
* A map of index IDs to the IDs of the nodes they reference.
*/
private indices: Map<string, Set<string>> = new Map<string, Set<string>>();
constructor() {
super();
const pharos = this;
this.Extensions.register(function () {
pharos.Extensions.register(function () {
const registry = this;
registry.treeProcessor(function () {
const dsl = this;
dsl.process(function (document) {
const treeProcessor = this;
pharos.pharosTreeProcessor(treeProcessor, document);
pharos.treeProcessor(treeProcessor, document);
});
})
});
@ -24,8 +39,93 @@ class Pharos extends Asciidoctor { @@ -24,8 +39,93 @@ class Pharos extends Asciidoctor {
this.Block
}
private pharosTreeProcessor(treeProcessor: Extensions.TreeProcessor, document: Document) {
// Recursively map sections to their note kinds down to five levels of depth.
document.getSections();
/**
* Walks the Asciidoctor Abstract Syntax Tree (AST) and performs the following mappings:
* - Each node ID is mapped to the node itself.
* - Each node ID is mapped to an integer event kind that will be used to represent the node.
* - Each ID of a node containing children is mapped to the set of IDs of its children.
*/
private treeProcessor(treeProcessor: Extensions.TreeProcessor, document: Document) {
const id = document.getId();
this.nodes.set(id, document);
this.kinds.set(id, 30040);
this.indices.set(id, new Set<string>());
/** FIFO queue (uses `Array.push()` and `Array.shift()`). */
const queue: AbstractNode[] = document.getBlocks();
while (queue.length > 0) {
const block = queue.shift();
if (!block) {
continue;
}
if (block instanceof Section) {
const children = this.processSection(block);
queue.push(...children);
} else {
this.processBlock(block as Block);
}
}
}
/**
* Processes a section of the Asciidoctor AST.
* @param section The section to process.
* @returns An array of the section's child nodes. If there are no child nodes, returns an empty
* array.
* @remarks Sections are mapped as kind 30040 indices by default.
*/
private processSection(section: Section): AbstractNode[] {
const id = section.getId();
// Prevent duplicates.
if (this.nodes.has(id)) {
return [];
}
this.nodes.set(id, section);
this.kinds.set(id, 30040); // Sections are indices by default.
this.indices.set(id, new Set<string>());
const parentId = section.getParent()?.getId();
if (!parentId) {
return [];
}
// Add the section to its parent index.
this.indices.get(parentId)?.add(id);
// Limit to 5 levels of section depth.
if (section.getLevel() >= 5) {
return [];
}
return section.getBlocks();
}
/**
* Processes a block of the Asciidoctor AST.
* @param block The block to process.
* @remarks Blocks are mapped as kind 30041 zettels by default.
*/
private processBlock(block: Block): void {
const id = block.getId();
// Prevent duplicates.
if (this.nodes.has(id)) {
return;
}
this.nodes.set(id, block);
this.kinds.set(id, 30041); // Blocks are zettels by default.
const parentId = block.getParent()?.getId();
if (!parentId) {
return;
}
// Add the block to its parent index.
this.indices.get(parentId)?.add(id);
}
}
Loading…
Cancel
Save