Compare commits

..

2 Commits

Author SHA1 Message Date
039dc4de0c i think this is a good checkpoint
All checks were successful
Build and Deploy Client / build-and-deploy (push) Successful in 10s
2026-08-29 23:47:59 -05:00
95fe7c7745 getting there. structures are weird rn 2026-08-29 21:46:37 -05:00
2 changed files with 138 additions and 77 deletions

View File

@@ -1,6 +1,6 @@
.format-plain { p {
font-weight: normal; font-size: larger;
} }
.format-bold { .format-bold {

View File

@@ -50,29 +50,29 @@ export class Markleft {
ngOnInit() { // DOM manipulation can't occur in constructor 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.}"); const nodes: (textNode | blockNode)[] = this.parsePlaintext(this.plaintext);
console.log(nodes); console.log(nodes)
nodes.forEach((node) => { nodes.forEach((node) => {
this.renderNode(node); this.renderNode(node);
}); });
} }
// full demo right here // full demo right here
plaintext: String = ` plaintext: string = `
#H1{Hello World!} #H{Hello World!}
#P{ #P{
This is some markleft text. This is %I{italic} and this is %B{bold}. Some might %I{even be %B{both}}. 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. This should be plain and unstylized.
`; // TODO: live editor `; // TODO: live editor
parsePlaintext(input: string): textNode[] { parsePlaintext(input: string): (textNode | blockNode)[] {
let index: number = 0; 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 index++; // step onto specifier code
const code: string = input[index]; // TODO: validate if this code is a usable code const code: string = input[index]; // TODO: validate if this code is a usable code
index++; index++;
@@ -92,7 +92,6 @@ export class Markleft {
let text: string = ""; let text: string = "";
while(true) { // assemble text while(true) { // assemble text
index++; index++;
console.log(input[index])
if(input[index] === "%") { // encountered another style specifier if(input[index] === "%") { // encountered another style specifier
// commit the current node // commit the current node
@@ -101,17 +100,17 @@ export class Markleft {
text: text, text: text,
styles: styles styles: styles
}; };
nodes.push(node); if(parentNode) {
parentNode.children.push(node);
} else {
nodes.push(node);
}
text = ""; // clear the already commited text text = ""; // clear the already commited text
// and then move to the next // and then move to the next
parseStyle(); // recursion parseStyle(parentNode ? parentNode : null, styles); // recursion
} else if(input[index] == "}") { // this node has closed } else if(input[index] == "}") { // this node has closed
const node: textNode = { addTextNode(text, styles, parentNode);
type: "text", //index++; // step to next
text: text,
styles: styles
};
nodes.push(node);
break; break;
} else { // regular text } else { // regular text
text += input[index]; text += input[index];
@@ -119,100 +118,162 @@ export class Markleft {
} }
} }
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);
// 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
};
let text: string = "";
while(true) { // assemble text
index++;
if(input[index] === "%" || input[index] === "#") { // encountered another format specifier
// commit current plain text to the block
if(text.length !== 0) {
const textNode: textNode = {
type: "text",
text: text,
styles: [] // none
};
node.children.push(textNode);
}
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
index++;
// add its text
addTextNode(text, null, node);
// push node to children list
if(parentNode) {
parentNode.children.push(node);
} else {
nodes.push(node);
}
break;
} else { // regular text
text += input[index];
}
}
}
function addTextNode(text: string, styles: string[] | null = null, parentNode: blockNode | null = null) {
if(text.length === 0) return;
const textNode: textNode = {
type: "text",
text: text,
styles: styles ? styles : []
}
if(parentNode) {
parentNode.children.push(textNode);
} else {
nodes.push(textNode);
}
}
function parse(endChar: string | null = null) { function parse(endChar: string | null = null) {
console.log("parsing..."); console.log("parsing...");
let currentText: string = "";
while(index < input.length) { while(index < input.length) {
if(endChar && (input[index] === endChar)) { if(endChar && (input[index] === endChar)) {
index++; index++;
break; break;
} }
if(input[index] === "%") { // encountered a style specifier if(input[index] === "%") { // encountered a style specifier
addTextNode(currentText);
parseStyle(); parseStyle();
currentText = "";
} else if(input[index] === "#") { // encountered a structure specifier } else if(input[index] === "#") { // encountered a structure specifier
// nodes.push(parseStructure()); addTextNode(currentText);
parseStructure();
currentText = "";
} else if(input[index] === "@") { // encountered an attribute specifier } else if(input[index] === "@") { // encountered an attribute specifier
// nodes.push(parseAttribute()); // nodes.push(parseAttribute());
} else { } else {
// regular character // regular character
currentText += input[index];
index++; index++;
} }
} }
addTextNode(currentText);
} }
/* 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(); parse();
return nodes; return nodes;
} }
renderNode(node: textNode): void { renderNode(node: textNode | blockNode, parentContainer: any = null): void {
console.log("rendering..."); if("styles" in node) { // this node is a textNode
if(node.type == "text") { // text nodes only need span elements
const span = this.renderer.createElement("span"); // create container const span = this.renderer.createElement("span"); // create container
node.styles.forEach((style) => { node.styles.forEach((style: string) => {
this.renderer.addClass(span, styles[style].className); this.renderer.addClass(span, styles[style].className);
}); });
const text = this.renderer.createText(node.text); // create text const text = this.renderer.createText(node.text); // create text
this.renderer.appendChild(span, text); // add text to container this.renderer.appendChild(span, text); // add text to container
this.renderer.appendChild(this.container.nativeElement, span); // add container to DOM if(parentContainer) { // this container is a child
this.renderer.appendChild(parentContainer, span);
} /* else if(node.type == "block") { } else { // add to the top-level DOM
this.renderer.appendChild(this.container.nativeElement, span);
element block = html.createElement(node.kind); // or whatever it is
// recursion
foreach(nodeType child in node.children) {
block.children.add(renderNode(child));
} }
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; return;
} }