modded7/tools/tasks/helpers/questChecks/checksFormatting.ts
2024-12-08 00:51:05 +11:00

218 lines
5.9 KiB
TypeScript

/* Storage for Per Char Checks */
// Builder for per character
import ChecksData, {
doubleSpace,
formattingChar,
invalidFormatting,
isSpaceOrNewLine,
isFormattingSignal,
resettingSignal,
} from "./checksData.ts";
import {
isResettingSignal,
logOrThrowProblem,
setupUtil,
} from "./checksUtil.ts";
/**
* Makes various space and newline related checks, and checks formatting.
*/
export default function checkSpacesAndFormatting(
shouldCheck: boolean,
value: string,
id: number,
name: string,
key: string,
): string {
const data = new ChecksData(shouldCheck, value, id, name, key);
setupUtil(data);
checkFormatting(data);
if (doubleSpace.test(data.value)) {
logOrThrowProblem("Double Space(s)");
data.value = data.value.replace(doubleSpace, " ");
}
const trimmedResult = data.value.trim();
if (trimmedResult !== data.value) {
logOrThrowProblem("Extra Spaces or New Lines at Beginning or End");
data.value = trimmedResult;
}
if (!data.value.includes("\n")) return data.value;
const lineBuilder: string[] = [];
for (const bit of data.value.split("\n")) {
const trimmedBit = bit.trim();
if (trimmedBit !== bit)
logOrThrowProblem("Extra Spaces at Beginning or End of a Line");
lineBuilder.push(trimmedBit);
}
return lineBuilder.join("\n");
}
/**
* Whole string formatting checks.
*/
function checkFormatting(data: ChecksData) {
if (!data.value.includes(formattingChar)) return;
checkFormattingChar(data);
/* End of String Checks */
let prevChar = data.processor.getLast();
// Check for redundant formatting at end
if (isFormattingSignal.test(prevChar.char)) {
// If not resetting change, or we have no need to reset
// Note the second case should be caught by redundant formatting check, so no need to check
if (!isResettingSignal(prevChar.char)) {
logOrThrowProblem("Redundant Formatting At End");
data.processor.removeLast();
}
// For continued processing, refresh prevChar
prevChar = data.processor.getLast();
}
data.saveValue();
// Check for missing resetting signal at end
if (
!isResettingSignal(prevChar.char) &&
prevChar.formatBefore !== resettingSignal
) {
logOrThrowProblem("Resetting Formatting At End", "Missing", "Adding");
// Trim value, then add §r to it
data.value = data.value.trim() + formattingChar + resettingSignal;
}
}
/**
* Per character formatting checks.
*/
function checkFormattingChar(data: ChecksData) {
while (data.processor.hasNext()) {
const char = data.processor.toNext();
// If not formatting signal, ignore
if (!char.char.includes(formattingChar)) {
data.processor.add(char.char);
continue;
}
let signal = "";
// Check for invalid formatting, including legacy 'f' signal
if (invalidFormatting.test(char.char)) {
logOrThrowProblem(
`Invalid Formatting Signal '${char.char}'`,
"",
"Replacing",
"",
"with '§r'",
);
// BQu treats signals like §z as a resetting signal, so make them reset
// Yes, even ones followed after by `'`, a space, or a newline
signal = "r";
} else signal = char.char.replace(isFormattingSignal, "$1");
// Note: Do not use char.char past this point, use signal instead!
// Check if formatting is right after another (ignoring spaces, newlines, etc.)
let prevIgnoreSpaces = data.processor.getLastNonSpaceLike();
if (isFormattingSignal.test(prevIgnoreSpaces.char.char)) {
logOrThrowProblem("Redundant Formatting");
// Remove previous formatting
// Note: this won't be called if not found, as empty string is not a formatting signal
// Instead of splicing, remove all, then add back, so we ensure correct formatting of spaces
const empties = data.processor.result.slice(prevIgnoreSpaces.index + 1);
data.processor.removeLast(
data.processor.result.length - prevIgnoreSpaces.index,
);
empties.forEach((char) => data.processor.add(char.char));
}
/* Redundant Formatting Checks */
// Check if formatting does nothing
if (data.processor.getCurrentFormat() === signal) {
logOrThrowProblem("Redundant Formatting");
// No need to do anything, just continue
continue;
}
/* End Redundant Formatting Checks */
/* Formatting Order Checks */
// Make sure resetting formatting is BEFORE spaces
if (signal === "r") {
if (isSpaceOrNewLine.test(data.processor.getLast().char)) {
logOrThrowProblem(
"Resetting Formatting",
"",
"Moving",
"After Space(s)",
"Before Space(s)",
);
// Refetch ignore spaces, it may have changed
prevIgnoreSpaces = data.processor.getLastNonSpaceLike();
if (prevIgnoreSpaces.index === -1) {
// We don't need to add resetting formatting at start of processor
// Also, this condition should have already been caught through redundant checks
// Ignore
continue;
}
// Move in before spaces (Add after existing character, so use index + 1)
// Instead of splicing, remove all, then add back, so we ensure correct formatting of spaces
const empties = data.processor.result.slice(prevIgnoreSpaces.index + 1);
data.processor.removeLast(
data.processor.result.length - prevIgnoreSpaces.index - 1, // -1, we don't want to remove last-non-space char itself
);
data.processor.addFormat(signal);
empties.forEach((char) => data.processor.add(char.char));
continue;
}
data.processor.addFormat(signal);
continue;
}
// Make sure non-resetting formatting is AFTER spaces
// Note: we don't have to check if have next, as empty string is not space or newline
if (!isSpaceOrNewLine.test(data.processor.getNext().char)) {
data.processor.addFormat(signal);
continue;
}
logOrThrowProblem(
"Non-Resetting Formatting",
"",
"Moving",
"Before Space(s)",
"After Space(s)",
);
// Add all upcoming space/newline characters
while (
data.processor.hasNext() &&
isSpaceOrNewLine.test(data.processor.getNext().char)
) {
data.processor.add(data.processor.toNext().char);
}
// Add formatting
data.processor.addFormat(signal);
}
}