Skip to content

Commit cdafc3d

Browse files
committed
refactor(#141): simplifications & improve readability
Heavy refactor of the core md-it parsing done while trying to understand how it works.
1 parent 441e507 commit cdafc3d

File tree

1 file changed

+44
-27
lines changed

1 file changed

+44
-27
lines changed

src/parser/alerts.ts

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -81,40 +81,57 @@ function capitalize(str: string): string {
8181
}
8282

8383
const MarkdownItAlerts = (md: MarkdownIt) => {
84+
// Alert title line example:
85+
// > [!marker] Optional title
86+
8487
// Allow multi word alphanumeric markers (includes underscore)
8588
// Additionally allow dashes in marker
86-
const markerRE = '[\\w- ]+';
87-
88-
// Match marker case insensitively
89-
// Note: config icons and titles keys must be fully lowercase
90-
const RE = new RegExp(`^\\\\?\\[\\!(${markerRE})\\]([^\\n\\r]*)`, 'i');
89+
// Match marker case-insensitively
90+
const titlePattern = /^\[!([\w\- ]+)\]([^\n\r]*)/i;
9191

9292
md.core.ruler.after('block', 'alerts', (state) => {
9393
const tokens = state.tokens;
94+
9495
for (let i = 0; i < tokens.length; i++) {
95-
if (tokens[i].type === 'blockquote_open') {
96-
const open = tokens[i];
97-
const startIndex = i;
98-
while (tokens[i]?.type !== 'blockquote_close' && i <= tokens.length) i += 1;
99-
const close = tokens[i];
100-
const endIndex = i;
101-
const firstContent = tokens
102-
.slice(startIndex, endIndex + 1)
103-
.find((token) => token.type === 'inline');
104-
if (!firstContent) continue;
105-
const match = firstContent.content.match(RE);
106-
if (!match) continue;
107-
const marker = match[1].toLowerCase();
108-
const title = match[2].trim() || (titles[marker] ?? capitalize(marker));
109-
const isFallback = !(marker in resolvedIcons); // For styling unconfigured markers
110-
const icon = isFallback ? fallbackIcon : resolvedIcons[marker];
111-
firstContent.content = firstContent.content.slice(match[0].length).trimStart();
112-
open.type = 'alert_open';
113-
open.tag = 'div';
114-
open.meta = { marker, title, icon, isFallback };
115-
close.type = 'alert_close';
116-
close.tag = 'div';
96+
if (tokens[i].type !== 'blockquote_open') continue;
97+
98+
const open = tokens[i];
99+
const start = i;
100+
101+
while (i < tokens.length && tokens[i].type !== 'blockquote_close') i++;
102+
103+
const close = tokens[i];
104+
const end = i;
105+
106+
// Get the first inline token, consists of:
107+
// 1. title line e.g. [!marker] Optional title
108+
// 2. the first paragraph in the alert body
109+
let firstBlock;
110+
for (let j = start; j <= end; j++) {
111+
if (tokens[j].type === 'inline') {
112+
firstBlock = tokens[j];
113+
break;
114+
}
117115
}
116+
if (!firstBlock) continue;
117+
118+
// Is this blockquote an alert?
119+
const match = firstBlock.content.match(titlePattern);
120+
if (!match) continue;
121+
122+
const marker = match[1].toLowerCase();
123+
const title = match[2].trim() || (titles[marker] ?? capitalize(marker));
124+
const isFallback = !(marker in resolvedIcons); // For styling unconfigured markers
125+
const icon = isFallback ? fallbackIcon : resolvedIcons[marker];
126+
127+
// Remove the title line, to be replaced by the final alert title
128+
firstBlock.content = firstBlock.content.slice(match[0].length).trimStart();
129+
130+
open.type = 'alert_open';
131+
open.tag = 'div';
132+
open.meta = { marker, title, icon, isFallback };
133+
close.type = 'alert_close';
134+
close.tag = 'div';
118135
}
119136
});
120137
md.renderer.rules.alert_open = function (tokens, idx) {

0 commit comments

Comments
 (0)