@mieweb/wikigdrive
Version:
Google Drive to MarkDown synchronization
192 lines (154 loc) • 5.72 kB
text/typescript
import {MarkdownNodes, TagPayload} from '../MarkdownNodes.ts';
import {walkRecursiveSync} from '../markdownNodesUtils.ts';
export function processListsAndNumbering(markdownChunks: MarkdownNodes) {
const body = markdownChunks.body;
const counters: { [id: string]: number } = {};
let preserveMinLevel = 999;
function fetchListNo(styleName: string) {
if (counters[styleName]) {
return counters[styleName];
}
return 0;
}
function storeListNo(styleName: string, val: number) {
if (!styleName) {
return;
}
counters[styleName] = val;
}
function clearListsNo(styleNamePrefix: string, minLevel: number) {
for (const k in counters) {
if (!k.startsWith(styleNamePrefix)) {
continue;
}
const level = parseInt(k.substring(styleNamePrefix.length));
if (level < minLevel) {
continue;
}
if (level > minLevel + 1) {
// continue;
}
counters[k] = 0;
}
}
function getTopListStyleName(): string {
for (let i = stack.length - 1; i >=0; i--) {
const leaf = stack[i];
if (leaf?.payload?.listStyle?.name) {
return leaf.payload.listStyle.name;
}
}
return null;
}
function topElement<A>(stack: A[]): A {
if (stack.length === 0) {
return null;
}
return stack[stack.length - 1];
}
const stack: Array<{ tag: string, payload: TagPayload }> = [];
stack.push({
tag: 'BODY',
payload: {}
});
const margins = {};
// let lastItem = null;
walkRecursiveSync(body, (chunk, ctx: { level: number }) => {
if (!chunk.isTag) {
return;
}
const tag = chunk.tag;
const parentLevel = topElement(stack);
if (['TOC', 'UL', 'LI', 'P'].includes(tag)) {
stack.push({
tag,
payload: chunk.payload
});
} else {
return ;
}
const listLevel = stack.filter(item => item.tag === 'UL').length;
const currentElement = topElement(stack);
if ('UL' === currentElement.tag) {
currentElement.payload.listLevel = listLevel;
if (currentElement.payload.continueNumbering || currentElement.payload.continueList) {
preserveMinLevel = listLevel;
}
const listStyleName = (currentElement.payload.listStyle?.name || getTopListStyleName()) + '_' + listLevel;
if (!(preserveMinLevel <= listLevel)) {
clearListsNo((currentElement.payload.listStyle?.name || getTopListStyleName()) + '_', listLevel);
} else {
currentElement.payload.number = fetchListNo(listStyleName) + 1;
currentElement.payload.continueNumbering = true;
}
const firstChild = chunk.children[0];
if (firstChild && firstChild.isTag && firstChild.tag === 'LI') {
const firstGrandChild = firstChild.children[0];
if (firstGrandChild && firstGrandChild.isTag && firstGrandChild.tag !== 'P') {
currentElement.payload.continueNumbering = true;
}
}
}
if ('LI' === currentElement.tag) {
if (parentLevel?.tag === 'UL') {
currentElement.payload.listLevel = parentLevel.payload.listLevel;
const listStyleName = (currentElement.payload.listStyle?.name || getTopListStyleName()) + '_' + currentElement.payload.listLevel;
if (!(chunk.children.length > 0 && chunk.children[0].isTag && chunk.children[0].tag === 'UL')) { // Has para, increase number
currentElement.payload.number = currentElement.payload.number || fetchListNo(listStyleName);
currentElement.payload.number++;
storeListNo(listStyleName, currentElement.payload.number);
}
}
}
if ('P' === currentElement.tag) {
if (parentLevel?.tag === 'LI') {
if (!margins[currentElement.payload.marginLeft]) {
margins[currentElement.payload.marginLeft] = listLevel;
}
let level = parentLevel.payload.listLevel;
if (margins[currentElement.payload.marginLeft]) {
level = margins[currentElement.payload.marginLeft];
}
const listStyle = parentLevel.payload.listStyle || currentElement.payload.listStyle;
const listLevelStyleNumber = listStyle?.listLevelStyleNumber.find(i => i.level == listLevel);
currentElement.payload.listLevel = level;
parentLevel.payload.listLevel = level;
const firstLevelStyleNumber = listStyle?.listLevelStyleNumber ? listStyle?.listLevelStyleNumber[0] : undefined;
currentElement.payload.numFormat = listLevelStyleNumber?.numFormat;
parentLevel.payload.numFormat = listLevelStyleNumber?.numFormat;
if (listLevelStyleNumber?.numFormat) {
currentElement.payload.number = parentLevel.payload.number;
if (firstLevelStyleNumber?.startValue) {
currentElement.payload.number = +firstLevelStyleNumber.startValue;
parentLevel.payload.number = +firstLevelStyleNumber.startValue;
}
} else {
currentElement.payload.bullet = true;
parentLevel.payload.bullet = true;
}
}
}
return { ...ctx, level: ctx.level + 1 };
}, { level: 0 }, (chunk) => {
if (!chunk.isTag) {
return;
}
const tag = chunk.tag;
if ('LI' === tag) {
const parentElement = chunk.parent;
if (parentElement?.tag === 'UL') {
// parentElement.payload.listLevel = chunk.payload.listLevel;
}
}
if (['TOC', 'UL', 'LI', 'P'].includes(tag)) {
const currentLevel = topElement(stack);
chunk.payload.listLevel = currentLevel.payload.listLevel;
stack.pop();
if (tag === 'UL') {
// this.listLevel--;
preserveMinLevel = 999;
}
return ;
}
});
}