From 95fe7c774589a50b62624db6743498717fdeee35 Mon Sep 17 00:00:00 2001 From: Preston McGee Date: Sat, 29 Aug 2026 21:46:37 -0500 Subject: [PATCH] getting there. structures are weird rn --- client/src/app/markleft/markleft.scss | 3 +- client/src/app/markleft/markleft.ts | 197 +++++++++++++++++--------- 2 files changed, 130 insertions(+), 70 deletions(-) diff --git a/client/src/app/markleft/markleft.scss b/client/src/app/markleft/markleft.scss index 752f19c..e575a9c 100644 --- a/client/src/app/markleft/markleft.scss +++ b/client/src/app/markleft/markleft.scss @@ -1,6 +1,7 @@ .format-plain { - font-weight: normal; + // empty on purpose + color: #000000; } .format-bold { diff --git a/client/src/app/markleft/markleft.ts b/client/src/app/markleft/markleft.ts index 500c130..d0f6c0c 100644 --- a/client/src/app/markleft/markleft.ts +++ b/client/src/app/markleft/markleft.ts @@ -50,29 +50,29 @@ export class Markleft { ngOnInit() { // DOM manipulation can't occur in constructor - const nodes: textNode[] = this.parsePlaintext("%P{this is a %B{test.} This should be unbolded. But %I{this} should be italicized.}"); - console.log(nodes); + const nodes: (textNode | blockNode)[] = this.parsePlaintext(this.plaintext); + console.log(nodes) nodes.forEach((node) => { this.renderNode(node); }); } // full demo right here - plaintext: String = ` - #H1{Hello World!} + plaintext: string = ` + #H{Hello World!} #P{ This is some markleft text. This is %I{italic} and this is %B{bold}. Some might %I{even be %B{both}}. } This should be plain and unstylized. `; // TODO: live editor - parsePlaintext(input: string): textNode[] { + parsePlaintext(input: string): (textNode | blockNode)[] { let index: number = 0; - let nodes: textNode[] = []; + let nodes: (textNode | blockNode)[] = []; + + function parseStyle(parentNode: blockNode | null = null, parentStyles: string[] | null = null) { - // starting with ONLY text styles, no structure blocks yet - function parseStyle(parentStyles: string | null = null) { index++; // step onto specifier code const code: string = input[index]; // TODO: validate if this code is a usable code index++; @@ -92,7 +92,6 @@ export class Markleft { let text: string = ""; while(true) { // assemble text index++; - console.log(input[index]) if(input[index] === "%") { // encountered another style specifier // commit the current node @@ -101,16 +100,93 @@ export class Markleft { text: text, styles: styles }; - nodes.push(node); + if(parentNode) { + parentNode.children.push(node); + } else { + nodes.push(node); + } text = ""; // clear the already commited text // and then move to the next - parseStyle(); // recursion + parseStyle(parentNode ? parentNode : null, styles); // recursion } else if(input[index] == "}") { // this node has closed + if(text.length === 0) break; // an empty text block is to be ignored const node: textNode = { type: "text", text: text, styles: styles }; + if(parentNode) { + parentNode.children.push(node); + } else { + nodes.push(node); + } + break; + } else { // regular text + text += input[index]; + } + } + } + + function parseStructure(parentNode: blockNode | null = null) { + index++; // step onto specifier code + const code: string = input[index]; // TODO: validate if this code is a usable code + index++; + if(input[index] !== "{") { // TODO: implement some expect("{") function to better validate + console.log("{ not found following format specifier"); + // return null node + } + console.log("Parsing structure for format specifier", code); + + let text: string = ""; + while(true) { // assemble text + index++; + if(input[index] === "%" || input[index] === "#") { // encountered another format specifier + + // create the node + const node: blockNode = { + type: "block", + kind: code, + children: [] // this blockNode will get passed to children so they can be added to the parent + }; + + // commit text to the block + if(text.length !== 0) { + const textNode: textNode = { + type: "text", + text: text, + styles: [] // none + }; + node.children.push(textNode); + } + + if(parentNode) { + parentNode.children.push(node); + } else { + nodes.push(node); + } + + text = ""; // clear the already commited text + // and then move to the next + if(input[index] === "%") { + parseStyle(node, null); // pass in the current node as the future node's parent + } else if(input[index] === "#") { + parseStructure(node); + } + } else if(input[index] == "}") { // this node has closed + + // create the node + const node: blockNode = { + type: "block", + kind: code, + children: [] // this blockNode will get passed to children so they can be added to the parent + }; + // add its text + const textNode: textNode = { + type: "text", + text: text, + styles: [] // none in this case + }; + node.children.push(textNode); nodes.push(node); break; } else { // regular text @@ -129,7 +205,7 @@ export class Markleft { if(input[index] === "%") { // encountered a style specifier parseStyle(); } else if(input[index] === "#") { // encountered a structure specifier - // nodes.push(parseStructure()); + parseStructure(); } else if(input[index] === "@") { // encountered an attribute specifier // nodes.push(parseAttribute()); } else { @@ -139,80 +215,63 @@ export class Markleft { } } - /* pseudocode (c lol) - - int index = 0; - - void parse(char endChar = null, formatDescriptor[]) { - const nodes = []; // start with an empty list of nodes - - while(index < input.length) { // iterate over the input string - // break if } encountered, step forward (this format block has exited) - if(endChar && (input[pos] == endChar)) { - index++; - break; - } - - if(input[index] == "%") { - nodes.push(parseStyle(...)); - } else if(input[index] == "#") { - nodes.push(parseBlock(...)); - } else { - nodes.push(parseText(...)); - } - - return mergeAdjacentText(nodes); - } - - } - - void parseStyle(parentStyle) { - index++; // step onto specifier - char code = readIdentifier(input[index]) - index++; - expect("{"); // parsing error if not - const styles = [...parentStyles, code] // append style code to styles list of this node - const children = parseUntil("}", styles); // recursion probably - - return children - } - ... and so on for parseBlock and parseText - - */ - parse(); return nodes; } - renderNode(node: textNode): void { + renderNode(node: textNode | blockNode, parentContainer: any = null): void { - console.log("rendering..."); - - if(node.type == "text") { // text nodes only need span elements + if("styles" in node) { // this node is a textNode const span = this.renderer.createElement("span"); // create container - node.styles.forEach((style) => { + node.styles.forEach((style: string) => { this.renderer.addClass(span, styles[style].className); }); const text = this.renderer.createText(node.text); // create text this.renderer.appendChild(span, text); // add text to container - this.renderer.appendChild(this.container.nativeElement, span); // add container to DOM - - } /* else if(node.type == "block") { - - element block = html.createElement(node.kind); // or whatever it is - - // recursion - foreach(nodeType child in node.children) { - block.children.add(renderNode(child)); + if(parentContainer) { // this container is a child + this.renderer.appendChild(parentContainer, span); + } else { // add to the top-level DOM + this.renderer.appendChild(this.container.nativeElement, span); } - return block; - } */ + } else if("kind" in node) { // this is a blockNode + + let container: any; + if(node.kind === "H") { // TODO: registry for block types + container = this.renderer.createElement("h1"); + } else if(node.kind === "P") { + container = this.renderer.createElement("p"); + } else { + console.log("container type not recognized"); + } + + node.children.forEach((child: textNode | blockNode) => { + if("styles" in child) { // this is a textNode + if(child.styles.length === 0) { + // just add text + const text = this.renderer.createText(child.text); // create text + this.renderer.appendChild(container, text); // add text to container + } else { + // render styles + this.renderNode(child, container); // recursion + } + } else if("kind" in child) { // this is a block node + this.renderNode(child, container); // recursion + } + }); + + // add container to DOM + if(parentContainer) { // this container is a child + this.renderer.appendChild(parentContainer, container); + } else { // add to the top-level DOM + this.renderer.appendChild(this.container.nativeElement, container); + } + } return; }