162 lines
4.1 KiB
TypeScript
162 lines
4.1 KiB
TypeScript
import {
|
|
formattingChar,
|
|
invalidFormatting,
|
|
isSpaceOrNewLine,
|
|
} from "./checksData.ts";
|
|
|
|
export type Character = {
|
|
char: string;
|
|
formatBefore: string;
|
|
formatAfter: string;
|
|
};
|
|
export type Characters = Character[];
|
|
|
|
const emptyChar: Character = { char: "", formatBefore: "r", formatAfter: "r" };
|
|
|
|
export default class ChecksCharProcessor {
|
|
// Filled up initially with all characters from value.
|
|
public source: Characters;
|
|
|
|
// Resultant list
|
|
public result: Characters;
|
|
|
|
private currIndex;
|
|
|
|
constructor(value: string) {
|
|
this.source = [];
|
|
|
|
// Init source with characters
|
|
for (let i = 0; i < value.length; i++) {
|
|
let char = value.charAt(i);
|
|
|
|
if (char !== formattingChar) {
|
|
this.add(char, this.source);
|
|
continue;
|
|
}
|
|
|
|
let next = value.charAt(++i) ?? "";
|
|
|
|
// Don't add if next character is also formatting, just leave it as lone signal and have formatter deal with it
|
|
// Newlines and spaces BQu ignores after a formatting char, so keep them
|
|
if (next === formattingChar) {
|
|
next = "";
|
|
|
|
// Decrement i again, so the next character is added separately
|
|
i--;
|
|
}
|
|
|
|
this.addFormat(next, this.source);
|
|
}
|
|
|
|
this.result = [];
|
|
// Start index at -1, as we always use index to get the 'next' char, not the current char
|
|
this.currIndex = -1;
|
|
}
|
|
|
|
/**
|
|
* Adds a normal character to the specified list. Defaults to result list.
|
|
*/
|
|
public add(char: string, list: Characters = this.result): void {
|
|
if (!char) return;
|
|
|
|
const fmt = this.getCurrentFormat(list);
|
|
list.push({ char, formatBefore: fmt, formatAfter: fmt });
|
|
}
|
|
|
|
/**
|
|
* Adds a formatting signal to the specified list. Defaults to result list.
|
|
*/
|
|
public addFormat(signal: string, list: Characters = this.result): void {
|
|
const result = formattingChar + signal;
|
|
|
|
let formatAfter = signal;
|
|
if (invalidFormatting.test(result)) {
|
|
// Use previous
|
|
formatAfter = this.getCurrentFormat(list);
|
|
}
|
|
|
|
list.push({
|
|
char: result,
|
|
formatBefore: this.getCurrentFormat(list),
|
|
formatAfter,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Gets the current format, in the specified list. Defaults to result list.
|
|
*/
|
|
public getCurrentFormat(list: Characters = this.result): string {
|
|
return this.getLast(list).formatAfter;
|
|
}
|
|
|
|
/**
|
|
* Returns whether there are more characters to process in the source list.
|
|
*/
|
|
public hasNext(): boolean {
|
|
return this.currIndex < this.source.length;
|
|
}
|
|
|
|
/**
|
|
* Gets the next character in the source list, and increments the current index.
|
|
* @return character Character in the source list. Returns empty character if out of bounds.
|
|
*/
|
|
public toNext(): Character {
|
|
return this.at(++this.currIndex, this.source);
|
|
}
|
|
|
|
/**
|
|
* Gets the next character in the source list.
|
|
* @param amount The amount to get after currentIndex. This should never be less than 1. Defaults to 1
|
|
* (the next character to process)
|
|
* @return character Character in the source list. Returns empty character if out of bounds.
|
|
*/
|
|
public getNext(amount: number = 1): Character {
|
|
return this.at(this.currIndex + amount, this.source);
|
|
}
|
|
|
|
/**
|
|
* Gets the character at the index of the specified list safely. Defaults to result list.
|
|
* Returns empty character if out of bounds.
|
|
*/
|
|
public at(index: number, list = this.result): Character {
|
|
return list.at(index) ?? emptyChar;
|
|
}
|
|
|
|
/**
|
|
* Gets the last character in the specified list. Defaults to result list.
|
|
*/
|
|
public getLast(list = this.result) {
|
|
return this.at(-1, list);
|
|
}
|
|
|
|
/**
|
|
* Removes the last amt characters added to the results. Defaults to 1.
|
|
*/
|
|
public removeLast(amt: number = 1) {
|
|
this.result = this.result.slice(0, -amt);
|
|
}
|
|
|
|
/**
|
|
* Finds the last non-space/newline character in the specified list. Defaults to results list.
|
|
* If none found, returns an empty character.
|
|
*/
|
|
public getLastNonSpaceLike(list = this.result): {
|
|
char: Character;
|
|
index: number;
|
|
} {
|
|
const index = list.findLastIndex(
|
|
(char) => !isSpaceOrNewLine.test(char.char),
|
|
);
|
|
return index === -1
|
|
? { char: emptyChar, index }
|
|
: { char: this.at(index, list), index };
|
|
}
|
|
|
|
/**
|
|
* Gets the string this builder condenses into.
|
|
*/
|
|
public getResult(): string {
|
|
return this.result.map((char) => char.char).join("");
|
|
}
|
|
}
|