|
7 | 7 | } from './helpers.ts';
|
8 | 8 | import type { Rules } from './rules.ts';
|
9 | 9 | import type { _Lexer } from './Lexer.ts';
|
10 |
| -import type { Links, Tokens } from './Tokens.ts'; |
| 10 | +import type { Links, Tokens, Token } from './Tokens.ts'; |
11 | 11 | import type { MarkedOptions } from './MarkedOptions.ts';
|
12 | 12 |
|
13 | 13 | function outputLink(cap: string[], link: Pick<Tokens.Link, 'href' | 'title'>, raw: string, lexer: _Lexer): Tokens.Link | Tokens.Image {
|
@@ -148,24 +148,89 @@ export class _Tokenizer {
|
148 | 148 | if (cap) {
|
149 | 149 | return {
|
150 | 150 | type: 'hr',
|
151 |
| - raw: cap[0] |
| 151 | + raw: rtrim(cap[0], '\n') |
152 | 152 | };
|
153 | 153 | }
|
154 | 154 | }
|
155 | 155 |
|
156 | 156 | blockquote(src: string): Tokens.Blockquote | undefined {
|
157 | 157 | const cap = this.rules.block.blockquote.exec(src);
|
158 | 158 | if (cap) {
|
159 |
| - // precede setext continuation with 4 spaces so it isn't a setext |
160 |
| - let text = cap[0].replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g, '\n $1'); |
161 |
| - text = rtrim(text.replace(/^ *>[ \t]?/gm, ''), '\n'); |
162 |
| - const top = this.lexer.state.top; |
163 |
| - this.lexer.state.top = true; |
164 |
| - const tokens = this.lexer.blockTokens(text); |
165 |
| - this.lexer.state.top = top; |
| 159 | + let lines = rtrim(cap[0], '\n').split('\n'); |
| 160 | + let raw = ''; |
| 161 | + let text = ''; |
| 162 | + const tokens: Token[] = []; |
| 163 | + |
| 164 | + while (lines.length > 0) { |
| 165 | + let inBlockquote = false; |
| 166 | + const currentLines = []; |
| 167 | + |
| 168 | + let i; |
| 169 | + for (i = 0; i < lines.length; i++) { |
| 170 | + // get lines up to a continuation |
| 171 | + if (/^ {0,3}>/.test(lines[i])) { |
| 172 | + currentLines.push(lines[i]); |
| 173 | + inBlockquote = true; |
| 174 | + } else if (!inBlockquote) { |
| 175 | + currentLines.push(lines[i]); |
| 176 | + } else { |
| 177 | + break; |
| 178 | + } |
| 179 | + } |
| 180 | + lines = lines.slice(i); |
| 181 | + |
| 182 | + const currentRaw = currentLines.join('\n'); |
| 183 | + const currentText = currentRaw |
| 184 | + // precede setext continuation with 4 spaces so it isn't a setext |
| 185 | + .replace(/\n {0,3}((?:=+|-+) *)(?=\n|$)/g, '\n $1') |
| 186 | + .replace(/^ {0,3}>[ \t]?/gm, ''); |
| 187 | + raw = raw ? `${raw}\n${currentRaw}` : currentRaw; |
| 188 | + text = text ? `${text}\n${currentText}` : currentText; |
| 189 | + |
| 190 | + // parse blockquote lines as top level tokens |
| 191 | + // merge paragraphs if this is a continuation |
| 192 | + const top = this.lexer.state.top; |
| 193 | + this.lexer.state.top = true; |
| 194 | + this.lexer.blockTokens(currentText, tokens, true); |
| 195 | + this.lexer.state.top = top; |
| 196 | + |
| 197 | + // if there is no continuation then we are done |
| 198 | + if (lines.length === 0) { |
| 199 | + break; |
| 200 | + } |
| 201 | + |
| 202 | + const lastToken = tokens[tokens.length - 1]; |
| 203 | + |
| 204 | + if (lastToken?.type === 'code') { |
| 205 | + // blockquote continuation cannot be preceded by a code block |
| 206 | + break; |
| 207 | + } else if (lastToken?.type === 'blockquote') { |
| 208 | + // include continuation in nested blockquote |
| 209 | + const oldToken = lastToken as Tokens.Blockquote; |
| 210 | + const newText = oldToken.raw + '\n' + lines.join('\n'); |
| 211 | + const newToken = this.blockquote(newText)!; |
| 212 | + tokens[tokens.length - 1] = newToken; |
| 213 | + |
| 214 | + raw = raw.substring(0, raw.length - oldToken.raw.length) + newToken.raw; |
| 215 | + text = text.substring(0, text.length - oldToken.text.length) + newToken.text; |
| 216 | + break; |
| 217 | + } else if (lastToken?.type === 'list') { |
| 218 | + // include continuation in nested list |
| 219 | + const oldToken = lastToken as Tokens.List; |
| 220 | + const newText = oldToken.raw + '\n' + lines.join('\n'); |
| 221 | + const newToken = this.list(newText)!; |
| 222 | + tokens[tokens.length - 1] = newToken; |
| 223 | + |
| 224 | + raw = raw.substring(0, raw.length - lastToken.raw.length) + newToken.raw; |
| 225 | + text = text.substring(0, text.length - oldToken.raw.length) + newToken.raw; |
| 226 | + lines = newText.substring(tokens[tokens.length - 1].raw.length).split('\n'); |
| 227 | + continue; |
| 228 | + } |
| 229 | + } |
| 230 | + |
166 | 231 | return {
|
167 | 232 | type: 'blockquote',
|
168 |
| - raw: cap[0], |
| 233 | + raw, |
169 | 234 | tokens,
|
170 | 235 | text
|
171 | 236 | };
|
|
0 commit comments