export default function(markdownIt: any) { 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; }); }