2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2021-08-31 14:46:46 +02:00
|
|
|
export default function(markdownIt: any) {
|
2024-04-05 13:16:49 +02:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
2021-08-31 14:46:46 +02:00
|
|
|
markdownIt.core.ruler.push('anchorHeader', (state: any): boolean => {
|
|
|
|
const tokens = state.tokens;
|
|
|
|
const Token = state.Token;
|
|
|
|
const doneNames = [];
|
|
|
|
|
|
|
|
const headingTextToAnchorName = (text: string, doneNames: string[]) => {
|
|
|
|
const allowed = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
|
|
let lastWasDash = true;
|
|
|
|
let output = '';
|
|
|
|
for (let i = 0; i < text.length; i++) {
|
|
|
|
const c = text[i];
|
|
|
|
if (allowed.indexOf(c) < 0) {
|
|
|
|
if (lastWasDash) continue;
|
|
|
|
lastWasDash = true;
|
|
|
|
output += '-';
|
|
|
|
} else {
|
|
|
|
lastWasDash = false;
|
|
|
|
output += c;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
output = output.toLowerCase();
|
|
|
|
|
|
|
|
while (output.length && output[output.length - 1] === '-') {
|
|
|
|
output = output.substr(0, output.length - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
let temp = output;
|
|
|
|
let index = 1;
|
|
|
|
while (doneNames.indexOf(temp) >= 0) {
|
|
|
|
temp = `${output}-${index}`;
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
output = temp;
|
|
|
|
|
|
|
|
return output;
|
|
|
|
};
|
|
|
|
|
|
|
|
const createAnchorTokens = (anchorName: string) => {
|
|
|
|
const output = [];
|
|
|
|
|
|
|
|
{
|
|
|
|
const token = new Token('heading_anchor_open', 'a', 1);
|
|
|
|
token.attrs = [
|
|
|
|
['name', anchorName],
|
|
|
|
['href', `#${anchorName}`],
|
|
|
|
['class', 'heading-anchor'],
|
|
|
|
];
|
|
|
|
output.push(token);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const token = new Token('text', '', 0);
|
|
|
|
token.content = '🔗';
|
|
|
|
output.push(token);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
const token = new Token('heading_anchor_close', 'a', -1);
|
|
|
|
output.push(token);
|
|
|
|
}
|
|
|
|
|
|
|
|
return output;
|
|
|
|
};
|
|
|
|
|
|
|
|
let insideHeading = false;
|
|
|
|
for (let i = 0; i < tokens.length; i++) {
|
|
|
|
const token = tokens[i];
|
|
|
|
|
|
|
|
if (token.type === 'heading_open') {
|
|
|
|
insideHeading = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (token.type === 'heading_close') {
|
|
|
|
insideHeading = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (insideHeading && token.type === 'inline') {
|
|
|
|
const anchorName = headingTextToAnchorName(token.content, doneNames);
|
|
|
|
doneNames.push(anchorName);
|
|
|
|
const anchorTokens = createAnchorTokens(anchorName);
|
|
|
|
// token.children = anchorTokens.concat(token.children);
|
|
|
|
token.children = token.children.concat(anchorTokens);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|