Skip formatting inside incomplete code blocks
Browse filesAdds logic to prevent incomplete bold, italic, and bold-italic markdown formatting from being processed if the marker is inside an incomplete code block. This avoids incorrectly modifying text that is meant to be inside code blocks, improving the robustness of markdown parsing.
src/lib/utils/parseIncompleteMarkdown.ts
CHANGED
|
@@ -18,6 +18,29 @@ const hasCompleteCodeBlock = (text: string): boolean => {
|
|
| 18 |
return tripleBackticks > 0 && tripleBackticks % 2 === 0 && text.includes("\n");
|
| 19 |
};
|
| 20 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
// Handles incomplete links and images by preserving them with a special marker
|
| 22 |
const handleIncompleteLinksAndImages = (text: string): string => {
|
| 23 |
// First check for incomplete URLs: [text](partial-url or 
|
|
@@ -84,6 +107,11 @@ const handleIncompleteBold = (text: string): string => {
|
|
| 84 |
// Check if the bold marker is in a list item context
|
| 85 |
// Find the position of the matched bold marker
|
| 86 |
const markerIndex = text.lastIndexOf(boldMatch[1]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
const beforeMarker = text.substring(0, markerIndex);
|
| 88 |
const lastNewlineBeforeMarker = beforeMarker.lastIndexOf("\n");
|
| 89 |
const lineStart = lastNewlineBeforeMarker === -1 ? 0 : lastNewlineBeforeMarker + 1;
|
|
@@ -111,6 +139,11 @@ const handleIncompleteBold = (text: string): string => {
|
|
| 111 |
|
| 112 |
// Completes incomplete italic formatting with double underscores (__)
|
| 113 |
const handleIncompleteDoubleUnderscoreItalic = (text: string): string => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
const italicMatch = text.match(italicPattern);
|
| 115 |
|
| 116 |
if (italicMatch) {
|
|
@@ -125,6 +158,11 @@ const handleIncompleteDoubleUnderscoreItalic = (text: string): string => {
|
|
| 125 |
// Check if the underscore marker is in a list item context
|
| 126 |
// Find the position of the matched underscore marker
|
| 127 |
const markerIndex = text.lastIndexOf(italicMatch[1]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
const beforeMarker = text.substring(0, markerIndex);
|
| 129 |
const lastNewlineBeforeMarker = beforeMarker.lastIndexOf("\n");
|
| 130 |
const lineStart = lastNewlineBeforeMarker === -1 ? 0 : lastNewlineBeforeMarker + 1;
|
|
@@ -210,6 +248,11 @@ const handleIncompleteSingleAsteriskItalic = (text: string): string => {
|
|
| 210 |
return text;
|
| 211 |
}
|
| 212 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
// Get content after the first single asterisk
|
| 214 |
const contentAfterFirstAsterisk = text.substring(firstSingleAsteriskIndex + 1);
|
| 215 |
|
|
@@ -329,6 +372,11 @@ const handleIncompleteSingleUnderscoreItalic = (text: string): string => {
|
|
| 329 |
return text;
|
| 330 |
}
|
| 331 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 332 |
// Get content after the first single underscore
|
| 333 |
const contentAfterFirstUnderscore = text.substring(firstSingleUnderscoreIndex + 1);
|
| 334 |
|
|
@@ -535,6 +583,14 @@ const handleIncompleteBoldItalic = (text: string): string => {
|
|
| 535 |
return text;
|
| 536 |
}
|
| 537 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 538 |
const tripleAsteriskCount = countTripleAsterisks(text);
|
| 539 |
if (tripleAsteriskCount % 2 === 1) {
|
| 540 |
return `${text}***`;
|
|
|
|
| 18 |
return tripleBackticks > 0 && tripleBackticks % 2 === 0 && text.includes("\n");
|
| 19 |
};
|
| 20 |
|
| 21 |
+
// Helper function to check if a position is inside an incomplete code block
|
| 22 |
+
const isInsideIncompleteCodeBlock = (text: string, markerIndex: number): boolean => {
|
| 23 |
+
const tripleBackticks = (text.match(/```/g) || []).length;
|
| 24 |
+
// If even number or no backticks, no incomplete code block
|
| 25 |
+
if (tripleBackticks === 0 || tripleBackticks % 2 === 0) {
|
| 26 |
+
return false;
|
| 27 |
+
}
|
| 28 |
+
// Find the position of the last (opening) ```
|
| 29 |
+
let lastBacktickIndex = -1;
|
| 30 |
+
let count = 0;
|
| 31 |
+
for (let i = 0; i <= text.length - 3; i++) {
|
| 32 |
+
if (text.substring(i, i + 3) === "```") {
|
| 33 |
+
count++;
|
| 34 |
+
if (count === tripleBackticks) {
|
| 35 |
+
lastBacktickIndex = i;
|
| 36 |
+
break;
|
| 37 |
+
}
|
| 38 |
+
}
|
| 39 |
+
}
|
| 40 |
+
// If the marker is after the last ```, it's inside the incomplete code block
|
| 41 |
+
return markerIndex > lastBacktickIndex;
|
| 42 |
+
};
|
| 43 |
+
|
| 44 |
// Handles incomplete links and images by preserving them with a special marker
|
| 45 |
const handleIncompleteLinksAndImages = (text: string): string => {
|
| 46 |
// First check for incomplete URLs: [text](partial-url or 
|
|
|
|
| 107 |
// Check if the bold marker is in a list item context
|
| 108 |
// Find the position of the matched bold marker
|
| 109 |
const markerIndex = text.lastIndexOf(boldMatch[1]);
|
| 110 |
+
|
| 111 |
+
// Don't process if the marker is inside an incomplete code block
|
| 112 |
+
if (isInsideIncompleteCodeBlock(text, markerIndex)) {
|
| 113 |
+
return text;
|
| 114 |
+
}
|
| 115 |
const beforeMarker = text.substring(0, markerIndex);
|
| 116 |
const lastNewlineBeforeMarker = beforeMarker.lastIndexOf("\n");
|
| 117 |
const lineStart = lastNewlineBeforeMarker === -1 ? 0 : lastNewlineBeforeMarker + 1;
|
|
|
|
| 139 |
|
| 140 |
// Completes incomplete italic formatting with double underscores (__)
|
| 141 |
const handleIncompleteDoubleUnderscoreItalic = (text: string): string => {
|
| 142 |
+
// Don't process if inside a complete code block
|
| 143 |
+
if (hasCompleteCodeBlock(text)) {
|
| 144 |
+
return text;
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
const italicMatch = text.match(italicPattern);
|
| 148 |
|
| 149 |
if (italicMatch) {
|
|
|
|
| 158 |
// Check if the underscore marker is in a list item context
|
| 159 |
// Find the position of the matched underscore marker
|
| 160 |
const markerIndex = text.lastIndexOf(italicMatch[1]);
|
| 161 |
+
|
| 162 |
+
// Don't process if the marker is inside an incomplete code block
|
| 163 |
+
if (isInsideIncompleteCodeBlock(text, markerIndex)) {
|
| 164 |
+
return text;
|
| 165 |
+
}
|
| 166 |
const beforeMarker = text.substring(0, markerIndex);
|
| 167 |
const lastNewlineBeforeMarker = beforeMarker.lastIndexOf("\n");
|
| 168 |
const lineStart = lastNewlineBeforeMarker === -1 ? 0 : lastNewlineBeforeMarker + 1;
|
|
|
|
| 248 |
return text;
|
| 249 |
}
|
| 250 |
|
| 251 |
+
// Don't process if the marker is inside an incomplete code block
|
| 252 |
+
if (isInsideIncompleteCodeBlock(text, firstSingleAsteriskIndex)) {
|
| 253 |
+
return text;
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
// Get content after the first single asterisk
|
| 257 |
const contentAfterFirstAsterisk = text.substring(firstSingleAsteriskIndex + 1);
|
| 258 |
|
|
|
|
| 372 |
return text;
|
| 373 |
}
|
| 374 |
|
| 375 |
+
// Don't process if the marker is inside an incomplete code block
|
| 376 |
+
if (isInsideIncompleteCodeBlock(text, firstSingleUnderscoreIndex)) {
|
| 377 |
+
return text;
|
| 378 |
+
}
|
| 379 |
+
|
| 380 |
// Get content after the first single underscore
|
| 381 |
const contentAfterFirstUnderscore = text.substring(firstSingleUnderscoreIndex + 1);
|
| 382 |
|
|
|
|
| 583 |
return text;
|
| 584 |
}
|
| 585 |
|
| 586 |
+
// Find the position of the matched bold-italic marker
|
| 587 |
+
const markerIndex = text.lastIndexOf(boldItalicMatch[1]);
|
| 588 |
+
|
| 589 |
+
// Don't process if the marker is inside an incomplete code block
|
| 590 |
+
if (isInsideIncompleteCodeBlock(text, markerIndex)) {
|
| 591 |
+
return text;
|
| 592 |
+
}
|
| 593 |
+
|
| 594 |
const tripleAsteriskCount = countTripleAsterisks(text);
|
| 595 |
if (tripleAsteriskCount % 2 === 1) {
|
| 596 |
return `${text}***`;
|