Skip to content

Commit

Permalink
fix: handling of multiple block anchors in transform
Browse files Browse the repository at this point in the history
This commit fixes handling of multiple block anchors in block anchor plugin and adds a new test case for handling multiple anchor tags in the input. Additionally, the block-anchor plugin code has been refactored for better readability and maintainability.
  • Loading branch information
main-kun committed May 3, 2024
1 parent 0679e67 commit ff507ba
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 8 deletions.
27 changes: 19 additions & 8 deletions src/transform/plugins/block-anchor/block-anchor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,22 @@ import Token from 'markdown-it/lib/token';
const pattern = /^{%[^\S\r\n]*anchor[^\S\r\n]+([\w-]+)[^\S\r\n]*%}/;
export const TOKEN_NAME = 'anchor';

function matchOpenToken(tokens: Token[], i: number) {
function isParagraph(tokens: Token[], i: number) {
return (
tokens[i].type === 'paragraph_open' &&
tokens[i + 1].type === 'inline' &&
tokens[i + 2].type === 'paragraph_close' &&
tokens[i + 1].children?.length === 1 &&
tokens[i + 1].children?.[0].type === 'text' &&
tokens[i + 2].type === 'paragraph_close'
);
}

function hasSingleChildWithText(tokens: Token[], i: number) {
return tokens[i + 1].children?.length === 1 && tokens[i + 1].children?.[0].type === 'text';
}

function matchOpenToken(tokens: Token[], i: number) {
return (
isParagraph(tokens, i) &&
hasSingleChildWithText(tokens, i) &&
pattern.exec(tokens[i + 1].children?.[0].content as string)
);
}
Expand All @@ -25,7 +34,7 @@ function createAnchorToken(state: StateCore, anchorId: string, position: number)

export function replaceTokens(state: StateCore) {
const blockTokens = state.tokens;
// i hate the idea of splicing the array while we're iterating over it
// I hate the idea of splicing the array while we're iterating over it
// so first lets find all the places where we will need to splice it and then actually do the splicing
const splicePointsMap: Map<number, string> = new Map();
for (let i = 0; i < blockTokens.length; i++) {
Expand All @@ -37,9 +46,11 @@ export function replaceTokens(state: StateCore) {

splicePointsMap.set(i, match[1]);
}
splicePointsMap.forEach((anchorId, position) => {
blockTokens.splice(position, 3, createAnchorToken(state, anchorId, position));
});
Array.from(splicePointsMap)
.sort(([keyA], [keyB]) => keyB - keyA)
.forEach(([position, anchorId]) => {
blockTokens.splice(position, 3, createAnchorToken(state, anchorId, position));
});
}

export function renderTokens(tokens: Token[], idx: number) {
Expand Down
6 changes: 6 additions & 0 deletions test/__snapshots__/block-anchor.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ exports[`block-anchor does not parse produce an anchor if there is content befor
"
`;

exports[`block-anchor handles multiple anchors in the input 1`] = `
"<hr id=\\"first-anchor\\" class=\\"visually-hidden\\" /><p>Some content</p>
<hr id=\\"second-anchor\\" class=\\"visually-hidden\\" /><p>Some more content</p>
<hr id=\\"third-anchor\\" class=\\"visually-hidden\\" />"
`;

exports[`block-anchor parses anchors surrounded by other blocks 1`] = `
"<h1 id=\\"heading\\"><a href=\\"#heading\\" class=\\"yfm-anchor\\" aria-hidden=\\"true\\"><span class=\\"visually-hidden\\">Heading</span></a>Heading</h1>
<hr id=\\"my-anchor\\" class=\\"visually-hidden\\" /><p>paragraph with content</p>
Expand Down
8 changes: 8 additions & 0 deletions test/block-anchor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,12 @@ describe('block-anchor', function () {

expect(actual).toMatchSnapshot();
});

it('handles multiple anchors in the input', () => {
const input =
'{%anchor first-anchor%}\n\nSome content\n\n{%anchor second-anchor%}\n\nSome more content\n\n{%anchor third-anchor%}';
const actual = compile(parse(input));

expect(actual).toMatchSnapshot();
});
});

0 comments on commit ff507ba

Please sign in to comment.