prettier-plugin-multiline-arrays
Version:
Prettier plugin to force all arrays to be multiline.
169 lines (168 loc) • 6.63 kB
JavaScript
import { getObjectTypedKeys } from '@augment-vir/common';
import { nextLinePatternComment, nextWrapThresholdComment, resetComment, setLinePatternComment, setWrapThresholdComment, untilNextLinePatternCommentRegExp, untilNextWrapThresholdCommentRegExp, untilSetLinePatternCommentRegExp, untilSetWrapThresholdCommentRegExp, } from '../options.js';
import { extractComments } from './comments.js';
const mappedCommentTriggers = new WeakMap();
export function getCommentTriggers(key, debug) {
const alreadyExisting = mappedCommentTriggers.get(key);
if (!alreadyExisting) {
return setCommentTriggers(key, debug);
}
return alreadyExisting;
}
function setCommentTriggers(rootNode, debug) {
// parse comments only on the root node so it only happens once
const comments = extractComments(rootNode);
if (debug) {
console.info({ comments });
}
const starterTriggers = {
nextLineCounts: {},
setLineCounts: {},
nextWrapThresholds: {},
setWrapThresholds: {},
resets: [],
};
const internalCommentTriggers = comments.reduce((accum, currentComment) => {
const commentText = currentComment.value?.replace(/\n/g, ' ');
if (!currentComment.loc) {
throw new Error(`Cannot read line location for comment ${currentComment.value}`);
}
const nextLineCounts = getLineCounts(commentText, true, debug);
if (nextLineCounts.length) {
accum.nextLineCounts[currentComment.loc.end.line] = nextLineCounts;
}
const nextWrapThreshold = getWrapThreshold(commentText, true);
if (nextWrapThreshold != undefined) {
accum.nextWrapThresholds[currentComment.loc.end.line] = nextWrapThreshold;
}
const setLineCounts = getLineCounts(commentText, false, debug);
if (setLineCounts.length) {
accum.setLineCounts[currentComment.loc.end.line] = {
data: setLineCounts,
lineEnd: Infinity,
};
}
const setWrapThreshold = getWrapThreshold(commentText, false);
if (setWrapThreshold != undefined) {
accum.setWrapThresholds[currentComment.loc.end.line] = {
data: setWrapThreshold,
lineEnd: Infinity,
};
}
const resetComment = isResetComment(commentText);
if (resetComment) {
accum.resets.push(currentComment.loc.end.line);
}
return accum;
}, starterTriggers);
internalCommentTriggers.resets.sort();
setResets(internalCommentTriggers);
const commentTriggers = { ...internalCommentTriggers };
delete commentTriggers.resets;
// save to a map so we don't have to recalculate these every time
mappedCommentTriggers.set(rootNode, commentTriggers);
return commentTriggers;
}
function setResets(internalCommentTriggers) {
if (!internalCommentTriggers.resets.length) {
return;
}
const setLineCountLineNumbers = getObjectTypedKeys(internalCommentTriggers.setLineCounts);
if (setLineCountLineNumbers.length) {
setLineCountLineNumbers.forEach((lineNumber) => {
const currentLineNumberStats = internalCommentTriggers.setLineCounts[lineNumber];
if (!currentLineNumberStats) {
throw new Error(`Line number stats were undefined for "${lineNumber}" in "${JSON.stringify(internalCommentTriggers.setLineCounts)}"`);
}
const endLineNumber = internalCommentTriggers.resets.find((resetLineNumber) => {
return lineNumber < resetLineNumber;
}) ?? currentLineNumberStats.lineEnd;
currentLineNumberStats.lineEnd = endLineNumber;
});
}
}
function getWrapThreshold(commentText, nextOnly) {
const searchText = nextOnly ? nextWrapThresholdComment : setWrapThresholdComment;
const searchRegExp = nextOnly
? untilNextWrapThresholdCommentRegExp
: untilSetWrapThresholdCommentRegExp;
if (commentText?.toLowerCase().includes(searchText)) {
const thresholdValue = Number(commentText.toLowerCase().replace(searchRegExp, '').trim());
if (isNaN(thresholdValue)) {
return undefined;
}
else {
return thresholdValue;
}
}
else {
return undefined;
}
}
export function parseNextLineCounts(input, nextOnly, debug) {
if (!input) {
return [];
}
const searchRegExp = nextOnly
? untilNextLinePatternCommentRegExp
: untilSetLinePatternCommentRegExp;
const split = input
.toLowerCase()
.replace(searchRegExp, '')
.replace(/,/g, '')
.split(' ')
.filter((entry) => !!entry);
const firstSplit = split[0];
if (firstSplit === '[') {
split.splice(0, 1);
}
else if (firstSplit?.startsWith('[')) {
split[0] = firstSplit.replace(/^\[/, '');
}
const lastSplitIndex = split.length - 1;
const lastSplit = split[lastSplitIndex];
if (lastSplit === ']') {
split.splice(split.length - 1, 1);
}
else if (lastSplit?.endsWith(']')) {
split[lastSplitIndex] = lastSplit.replace(/\]$/, '');
}
const numbers = split.map((entry) => entry && !!entry.trim().match(/^\d+$/) ? Number(entry.trim()) : NaN);
const invalidNumbers = numbers
.map((entry, index) => ({ index, entry, original: split[index] }))
.filter((entry) => {
return isNaN(entry.entry);
});
if (invalidNumbers.length) {
if (debug) {
console.error(invalidNumbers.map((entry) => ({
index: entry.index,
original: entry.original,
parsed: entry,
split,
input,
numbers,
trim: entry.original?.trim(),
match: entry.original?.trim().match(/^\d+$/),
matched: !!entry.original?.trim().match(/^\d+$/),
})));
}
console.error(`Invalid number(s) for elements per line option/comment: ${invalidNumbers
.map((entry) => entry.original)
.join()}`);
return [];
}
return numbers;
}
function isResetComment(commentText) {
return !!commentText?.toLowerCase().includes(resetComment);
}
function getLineCounts(commentText, nextOnly, debug) {
const searchText = nextOnly ? nextLinePatternComment : setLinePatternComment;
if (commentText?.toLowerCase().includes(searchText)) {
return parseNextLineCounts(commentText, nextOnly, debug);
}
else {
return [];
}
}