diff --git a/lib/cartridge/cartridge.test.ts b/lib/cartridge/cartridge.test.ts index 84e116f..f168e5e 100644 --- a/lib/cartridge/cartridge.test.ts +++ b/lib/cartridge/cartridge.test.ts @@ -7,6 +7,9 @@ import { CartridgeEvent } from "./cartridge_event.ts"; */ Deno.test("hello world", () => { const cartridge = new Cartridge(); + cartridge.on(CartridgeEvent.Load, (event) => { + event.data; + }); cartridge.on(CartridgeEvent.FileStart, console.log); assertEquals(1, 1); }); diff --git a/lib/cartridge/cartridge.ts b/lib/cartridge/cartridge.ts index c6c5f92..63af5dc 100644 --- a/lib/cartridge/cartridge.ts +++ b/lib/cartridge/cartridge.ts @@ -1,4 +1,4 @@ -import { CartridgeEvent } from "./cartridge_event.ts"; +import { CartridgeEvent, CartridgeEventReturnType } from "./cartridge_event.ts"; import type { CartridgeEventContext, CartridgeHandler, @@ -14,35 +14,35 @@ export class Cartridge { private handlers: CartridgeHandlerMap = {}, ) {} - addEventListener( + public addEventListener( name: CartridgeEvent.FileStart, handler: CartridgeHandler, ): void; - addEventListener( + public addEventListener( name: CartridgeEvent.InlineComment, handler: CartridgeHandler, ): void; - addEventListener( + public addEventListener( name: CartridgeEvent.MultilineComment, handler: CartridgeHandler, ): void; - addEventListener( + public addEventListener( name: CartridgeEvent.Load, handler: CartridgeHandler, ): void; - addEventListener( + public addEventListener( name: CartridgeEvent.StructOpen, handler: CartridgeHandler, ): void; - addEventListener( + public addEventListener( name: CartridgeEvent.SetProperty, handler: CartridgeHandler, ): void; - addEventListener( + public addEventListener( name: CartridgeEvent.FileEnd, handler: CartridgeHandler, ): void; - addEventListener( + public addEventListener( name: CartridgeEvent, // deno-lint-ignore no-explicit-any handler: any, @@ -53,12 +53,24 @@ export class Cartridge { /** * `on` is an alias for `addEventListener` */ - on = this.addEventListener.bind(this); + public on = this.addEventListener.bind(this); - removeEventListener(name: CartridgeEvent) { + public removeEventListener(name: CartridgeEvent) { delete this.handlers[name]; } + public async dispatch( + name: CartridgeEvent, + ctx: CartridgeEventContext, + ): Promise { + const handleEvent = this.handlers[name] as CartridgeHandler; + if (handleEvent === undefined) return null; + const executionResult = handleEvent(ctx); + return executionResult instanceof Promise + ? await executionResult + : executionResult; + } + // TODO(@ethanthatonekid): add a `dispatch` method // @see } diff --git a/lib/cartridge/cartridge_event.ts b/lib/cartridge/cartridge_event.ts index 2422a3b..1021458 100644 --- a/lib/cartridge/cartridge_event.ts +++ b/lib/cartridge/cartridge_event.ts @@ -11,9 +11,19 @@ export enum CartridgeEvent { FileEnd = "file_end", } +export type CartridgeEventReturnType = ( + | void + | Promise + | string + | Promise + | null +); + export interface CartridgeEventContext { type: T; - code: { append: (code: string) => void }; + code: { + append: (code: string) => CartridgeEventReturnType; + }; tokens: Token[]; data: T extends CartridgeEvent.InlineComment ? { comments: string[] } : T extends CartridgeEvent.MultilineComment ? { comments: string[] } @@ -32,7 +42,7 @@ export interface CartridgeEventContext { */ export type CartridgeHandler = ( event: CartridgeEventContext, -) => void | Promise; +) => CartridgeEventReturnType; export interface CartridgeHandlerMap { [CartridgeEvent.FileStart]?: CartridgeHandler; diff --git a/lib/text_builder/code_block/code_block.test.ts b/lib/code_block/code_block.test.ts similarity index 91% rename from lib/text_builder/code_block/code_block.test.ts rename to lib/code_block/code_block.test.ts index 5246362..f97724f 100644 --- a/lib/text_builder/code_block/code_block.test.ts +++ b/lib/code_block/code_block.test.ts @@ -1,54 +1,54 @@ -import { assertEquals } from "../../../deps/std/testing.ts"; -import { CodeBlock } from "./code_block.ts"; - -Deno.test("new code block is empty", () => { - assertEquals(new CodeBlock().export(), ""); -}); - -Deno.test("add 3 lines of code to the block", () => { - const block = new CodeBlock(); - block.append("a"); - block.append("b"); - block.append("c"); - const expectation = "a\nb\nc"; - const reality = block.export(); - assertEquals(expectation, reality); -}); - -Deno.test("add 3 lines of code to the block (indented)", () => { - const block = new CodeBlock(); - block.append("a", 0); - block.append("b", 1); - block.append("c", 2); - const expectation = "a\n b\n c"; - const reality = block.export(); - assertEquals(expectation, reality); -}); - -Deno.test("join 3 code blocks", () => { - const block1 = new CodeBlock(); - block1.append("a", 0); - block1.append("b", 1); - block1.append("c", 2); - const block2 = new CodeBlock(); - block2.append("d", 1); - block2.append("e", 0); - block2.append("f", 1); - const block3 = new CodeBlock(); - block3.append("g", 2); - block3.append("h", 1); - block3.append("i", 0); - const expectation = `a - b - c - - d -e - f - - g - h -i`; - const reality = CodeBlock.join(block1, block2, block3); - assertEquals(expectation, reality); -}); +import { assertEquals } from "../../deps/std/testing.ts"; +import { CodeBlock } from "./code_block.ts"; + +Deno.test("new code block is empty", () => { + assertEquals(new CodeBlock().export(), ""); +}); + +Deno.test("add 3 lines of code to the block", () => { + const block = new CodeBlock(); + block.append("a"); + block.append("b"); + block.append("c"); + const expectation = "a\nb\nc"; + const reality = block.export(); + assertEquals(expectation, reality); +}); + +Deno.test("add 3 lines of code to the block (indented)", () => { + const block = new CodeBlock(); + block.append("a", 0); + block.append("b", 1); + block.append("c", 2); + const expectation = "a\n b\n c"; + const reality = block.export(); + assertEquals(expectation, reality); +}); + +Deno.test("join 3 code blocks", () => { + const block1 = new CodeBlock(); + block1.append("a", 0); + block1.append("b", 1); + block1.append("c", 2); + const block2 = new CodeBlock(); + block2.append("d", 1); + block2.append("e", 0); + block2.append("f", 1); + const block3 = new CodeBlock(); + block3.append("g", 2); + block3.append("h", 1); + block3.append("i", 0); + const expectation = `a + b + c + + d +e + f + + g + h +i`; + const reality = CodeBlock.join(block1, block2, block3); + assertEquals(expectation, reality); +}); diff --git a/lib/text_builder/code_block/code_block.ts b/lib/code_block/code_block.ts similarity index 80% rename from lib/text_builder/code_block/code_block.ts rename to lib/code_block/code_block.ts index 5e0d669..8aa68c3 100644 --- a/lib/text_builder/code_block/code_block.ts +++ b/lib/code_block/code_block.ts @@ -1,54 +1,58 @@ -import { getIndent, Indent, IndentOption } from "../../indent/mod.ts"; - -export interface LineOfCode { - content: string; - indentLevel: number; -} - -/** - * Represents a block of code. - */ -export class CodeBlock { - public code: LineOfCode[] = []; - - append(content: string, indentLevel = 0): void { - this.code.push( - ...content.split("\n").map((line) => ({ content: line, indentLevel })), - ); - } - - export(indent: IndentOption = Indent.Space2): string { - return this.code - .map(({ content, indentLevel }) => - getIndent(indent, indentLevel) + content - ) - .join("\n"); - } - - /** - * `toString` is an alias for `CodeBlock.export`. - */ - toString = this.export.bind(this); - - static join( - indentOrFirstBlock: IndentOption | CodeBlock, - ...blocks: CodeBlock[] - ): string { - const blockPadding = 2; // lines between each code block - const blockSeparator = "\n".repeat(blockPadding); - const indentSpecified = !(indentOrFirstBlock instanceof CodeBlock); - if (!indentSpecified) blocks = [indentOrFirstBlock, ...blocks]; - return blocks - .filter((block) => block !== null) - .reduce( - (file, block, i) => { - const exportedCode = indentSpecified - ? block.export(indentOrFirstBlock) - : block.export(); - return file + exportedCode + - (blocks.length - 1 > i ? blockSeparator : ""); - }, - "", - ); - } -} +import { getIndent, Indent, IndentOption } from "../indent/mod.ts"; + +export interface LineOfCode { + content: string; + indentLevel: number; +} + +/** + * Represents a block of code. + */ +export class CodeBlock { + public code: LineOfCode[] = []; + + /** + * @param content string that is split up by line break + * @param indentLevel depth of nesting; defaults to 0 + */ + append(content: string, indentLevel = 0): void { + this.code.push( + ...content.split("\n").map((line) => ({ content: line, indentLevel })), + ); + } + + export(indent: IndentOption = Indent.Space2): string { + return this.code + .map(({ content, indentLevel }) => + getIndent(indent, indentLevel) + content + ) + .join("\n"); + } + + /** + * `toString` is an alias for `CodeBlock.export`. + */ + toString = this.export.bind(this); + + static join( + indentOrFirstBlock: IndentOption | CodeBlock, + ...blocks: CodeBlock[] + ): string { + const blockPadding = 2; // lines between each code block + const blockSeparator = "\n".repeat(blockPadding); + const indentSpecified = !(indentOrFirstBlock instanceof CodeBlock); + if (!indentSpecified) blocks = [indentOrFirstBlock, ...blocks]; + return blocks + .filter((block) => block !== null) + .reduce( + (file, block, i) => { + const exportedCode = indentSpecified + ? block.export(indentOrFirstBlock) + : block.export(); + const isLast = blocks.length - 1 <= i; + return file + exportedCode + (isLast ? "" : blockSeparator); + }, + "", + ); + } +} diff --git a/lib/text_builder/code_block/mod.ts b/lib/code_block/mod.ts similarity index 100% rename from lib/text_builder/code_block/mod.ts rename to lib/code_block/mod.ts diff --git a/lib/text_builder/mod.ts b/lib/text_builder/mod.ts index 36b25b4..e91e626 100644 --- a/lib/text_builder/mod.ts +++ b/lib/text_builder/mod.ts @@ -1,2 +1 @@ export { TextBuilder } from "./text_builder.ts"; -export { CodeBlock } from "./code_block/mod.ts"; diff --git a/lib/text_builder/text_builder.ts b/lib/text_builder/text_builder.ts index 32d8867..3bfc180 100644 --- a/lib/text_builder/text_builder.ts +++ b/lib/text_builder/text_builder.ts @@ -1,7 +1,8 @@ -import { CodeBlock } from "./code_block/mod.ts"; +import { CodeBlock } from "../code_block/mod.ts"; import { Indent, IndentOption } from "../indent/mod.ts"; import { Cartridge, CartridgeEvent } from "../cartridge/mod.ts"; import { Token } from "../tokenize/mod.ts"; +import { makeFileStartEventContext } from "./utils.ts"; export class TextBuilder { private blocks: CodeBlock[]; @@ -18,10 +19,14 @@ export class TextBuilder { * @todo @ethanthatonekid complete this method * @see https://github.com/EthanThatOneKid/fart/blob/c43f233345/lib/gen/builder.ts#L20 */ - // deno-lint-ignore no-unused-vars - append(event: CartridgeEvent, tokens: Token[]): void { + async append(event: CartridgeEvent, tokens: Token[]): Promise { switch (event) { case CartridgeEvent.FileStart: { + const code = await this.cartridge.dispatch( + CartridgeEvent.FileStart, + makeFileStartEventContext(this.currentBlock, tokens), + ); + if (typeof code === "string") this.currentBlock.append(code); break; } case CartridgeEvent.InlineComment: { diff --git a/lib/text_builder/utils.ts b/lib/text_builder/utils.ts new file mode 100644 index 0000000..c27f672 --- /dev/null +++ b/lib/text_builder/utils.ts @@ -0,0 +1,8 @@ +import type { CodeBlock } from "../code_block/mod.ts"; +import type { Token } from "../tokenize/mod.ts"; +import { CartridgeEvent } from "../cartridge/mod.ts"; + +export const makeFileStartEventContext = ( + code: CodeBlock, + tokens: Token[], +) => ({ type: CartridgeEvent.FileStart, code, tokens, data: null });