@diplodoc/transform 4.17.2 → 4.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/css/print.css +12 -0
- package/dist/css/print.css.map +3 -3
- package/dist/css/yfm.css +83 -2
- package/dist/css/yfm.css.map +3 -3
- package/dist/css/yfm.min.css +1 -1
- package/dist/css/yfm.min.css.map +3 -3
- package/dist/js/yfm.js +99 -73
- package/dist/js/yfm.js.map +3 -3
- package/dist/js/yfm.min.js +1 -1
- package/dist/js/yfm.min.js.map +3 -3
- package/lib/liquid/conditions.js +1 -1
- package/lib/liquid/conditions.js.map +1 -1
- package/lib/liquid/index.js +3 -1
- package/lib/liquid/index.js.map +1 -1
- package/lib/liquid/lexical.d.ts +2 -0
- package/lib/liquid/lexical.js +4 -1
- package/lib/liquid/lexical.js.map +1 -1
- package/lib/liquid/substitutions.d.ts +1 -1
- package/lib/liquid/substitutions.js +29 -9
- package/lib/liquid/substitutions.js.map +1 -1
- package/lib/plugins/checkbox/checkbox.d.ts +14 -2
- package/lib/plugins/checkbox/checkbox.js +22 -10
- package/lib/plugins/checkbox/checkbox.js.map +1 -1
- package/lib/plugins/checkbox/index.d.ts +2 -2
- package/lib/plugins/checkbox/index.js.map +1 -1
- package/lib/plugins/table/index.js +160 -4
- package/lib/plugins/table/index.js.map +1 -1
- package/package.json +2 -2
- package/src/scss/_anchor.scss +2 -2
- package/src/scss/_table.scss +32 -0
- package/src/scss/print/cut.scss +1 -0
- package/src/scss/print/tabs.scss +6 -0
- package/src/scss/print/term.scss +10 -0
- package/src/scss/print.scss +2 -1
- package/src/scss/yfm.scss +1 -0
- package/src/transform/liquid/conditions.ts +1 -1
- package/src/transform/liquid/index.ts +4 -1
- package/src/transform/liquid/lexical.ts +2 -0
- package/src/transform/liquid/substitutions.ts +43 -9
- package/src/transform/plugins/checkbox/checkbox.ts +26 -11
- package/src/transform/plugins/checkbox/index.ts +2 -2
- package/src/transform/plugins/table/index.ts +178 -6
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import StateBlock from 'markdown-it/lib/rules_block/state_block';
|
|
2
2
|
import {MarkdownItPluginCb} from '../typings';
|
|
3
|
+
import Token from 'markdown-it/lib/token';
|
|
3
4
|
|
|
4
5
|
const pluginName = 'yfm_table';
|
|
5
6
|
const pipeChar = 0x7c; // |
|
|
@@ -91,12 +92,17 @@ class StateIterator {
|
|
|
91
92
|
}
|
|
92
93
|
}
|
|
93
94
|
|
|
94
|
-
|
|
95
|
+
interface RowPositions {
|
|
96
|
+
rows: [number, number, [Stats, Stats][]][];
|
|
97
|
+
endOfTable: number | null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function getTableRowPositions(
|
|
95
101
|
state: StateBlock,
|
|
96
102
|
startPosition: number,
|
|
97
103
|
endPosition: number,
|
|
98
104
|
startLine: number,
|
|
99
|
-
) {
|
|
105
|
+
): RowPositions {
|
|
100
106
|
let endOfTable = null;
|
|
101
107
|
let tableLevel = 0;
|
|
102
108
|
let currentRow: [Stats, Stats][] = [];
|
|
@@ -210,6 +216,144 @@ function getTableRows(
|
|
|
210
216
|
return {rows, endOfTable};
|
|
211
217
|
}
|
|
212
218
|
|
|
219
|
+
/**
|
|
220
|
+
* Removes the specified attribute from attributes in the content of a token.
|
|
221
|
+
*
|
|
222
|
+
* @param {Token} contentToken - The target token.
|
|
223
|
+
* @param {string} attr - The attribute to be removed from the token content.
|
|
224
|
+
*
|
|
225
|
+
* @return {void}
|
|
226
|
+
*/
|
|
227
|
+
function removeAttrFromTokenContent(contentToken: Token, attr: string): void {
|
|
228
|
+
// Replace the attribute in the token content with an empty string.
|
|
229
|
+
const blockRegex = /\s*\{[^}]*}/;
|
|
230
|
+
const allAttrs = contentToken.content.match(blockRegex);
|
|
231
|
+
if (!allAttrs) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
let replacedContent = allAttrs[0].replace(`.${attr}`, '');
|
|
235
|
+
if (replacedContent.trim() === '{}') {
|
|
236
|
+
replacedContent = '';
|
|
237
|
+
}
|
|
238
|
+
contentToken.content = contentToken.content.replace(allAttrs[0], replacedContent);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Extracts the class attribute from the given content token and applies it to the tdOpenToken.
|
|
243
|
+
* Preserves other attributes.
|
|
244
|
+
*
|
|
245
|
+
* @param {Token} contentToken - Search the content of this token for the class.
|
|
246
|
+
* @param {Token} tdOpenToken - Parent td_open token. Extracted class is applied to this token.
|
|
247
|
+
* @returns {void}
|
|
248
|
+
*/
|
|
249
|
+
function extractAndApplyClassFromToken(contentToken: Token, tdOpenToken: Token): void {
|
|
250
|
+
// Regex to find class attribute in any position within brackets
|
|
251
|
+
const classAttrRegex = /(?<=\{[^}]*)\.([-_a-zA-Z0-9]+)/g;
|
|
252
|
+
const classAttrMatch = classAttrRegex.exec(contentToken.content);
|
|
253
|
+
if (classAttrMatch) {
|
|
254
|
+
const classAttr = classAttrMatch[1];
|
|
255
|
+
tdOpenToken.attrSet('class', classAttr);
|
|
256
|
+
removeAttrFromTokenContent(contentToken, classAttr);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const COLSPAN_SYMBOL = '>';
|
|
261
|
+
const ROWSPAN_SYMBOL = '^';
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Traverses through the content map, applying row/colspan attributes and marking the special cells for deletion.
|
|
265
|
+
* Upon encountering a symbol denoting a row span or a column span, proceed backwards in row or column
|
|
266
|
+
* until text cell is found. Upon finding the text cell, store the colspan or rowspan value.
|
|
267
|
+
* During the backward traversal, if the same symbol is encountered, increment the value of rowspan/colspan.
|
|
268
|
+
* Colspan symbol is ignored for the first column. Rowspan symbol is ignored for the first row
|
|
269
|
+
*
|
|
270
|
+
* @param contentMap string[][]
|
|
271
|
+
* @param tokenMap Token[][]
|
|
272
|
+
* @return {void}
|
|
273
|
+
*/
|
|
274
|
+
const applySpans = (contentMap: string[][], tokenMap: Token[][]): void => {
|
|
275
|
+
for (let i = 0; i < contentMap.length; i++) {
|
|
276
|
+
for (let j = 0; j < contentMap[0].length; j++) {
|
|
277
|
+
if (contentMap[i][j] === COLSPAN_SYMBOL) {
|
|
278
|
+
// skip the first column
|
|
279
|
+
if (j === 0) {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
tokenMap[i][j].meta = {markForDeletion: true};
|
|
283
|
+
let colspanFactor = 2;
|
|
284
|
+
// traverse columns backwards
|
|
285
|
+
for (let col = j - 1; col >= 0; col--) {
|
|
286
|
+
if (contentMap[i][col] === COLSPAN_SYMBOL) {
|
|
287
|
+
colspanFactor++;
|
|
288
|
+
tokenMap[i][col].meta = {markForDeletion: true};
|
|
289
|
+
} else if (contentMap[i][col] === ROWSPAN_SYMBOL) {
|
|
290
|
+
// Do nothing, this should be applied on the row that's being extended
|
|
291
|
+
break;
|
|
292
|
+
} else {
|
|
293
|
+
tokenMap[i][col].attrSet('colspan', colspanFactor.toString());
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (contentMap[i][j] === ROWSPAN_SYMBOL) {
|
|
300
|
+
// skip the first row
|
|
301
|
+
if (i === 0) {
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
tokenMap[i][j].meta = {markForDeletion: true};
|
|
305
|
+
let rowSpanFactor = 2;
|
|
306
|
+
// traverse rows upward
|
|
307
|
+
for (let row = i - 1; row >= 0; row--) {
|
|
308
|
+
if (contentMap[row][j] === ROWSPAN_SYMBOL) {
|
|
309
|
+
rowSpanFactor++;
|
|
310
|
+
tokenMap[row][j].meta = {markForDeletion: true};
|
|
311
|
+
} else if (contentMap[row][j] === COLSPAN_SYMBOL) {
|
|
312
|
+
break;
|
|
313
|
+
} else {
|
|
314
|
+
tokenMap[row][j].attrSet('rowspan', rowSpanFactor.toString());
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Removes td_open and matching td_close tokens and the content within them
|
|
325
|
+
*
|
|
326
|
+
* @param {number} tableStart - The index of the start of the table in the state tokens array.
|
|
327
|
+
* @param {Token[]} tokens - The array of tokens from state.
|
|
328
|
+
* @returns {void}
|
|
329
|
+
*/
|
|
330
|
+
const clearTokens = (tableStart: number, tokens: Token[]): void => {
|
|
331
|
+
// use splices array to avoid modifying the tokens array during iteration
|
|
332
|
+
const splices: number[][] = [];
|
|
333
|
+
for (let i = tableStart; i < tokens.length; i++) {
|
|
334
|
+
if (tokens[i].meta?.markForDeletion) {
|
|
335
|
+
// Use unshift instead of push so that the splices indexes are in reverse order.
|
|
336
|
+
// Reverse order guarantees that we don't mess up the indexes while removing the items.
|
|
337
|
+
splices.unshift([i]);
|
|
338
|
+
const level = tokens[i].level;
|
|
339
|
+
// find matching td_close with the same level
|
|
340
|
+
for (let j = i + 1; j < tokens.length; j++) {
|
|
341
|
+
if (tokens[j].type === 'yfm_td_close' && tokens[j].level === level) {
|
|
342
|
+
splices[0].push(j);
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
splices.forEach(([start, end]) => {
|
|
349
|
+
// check that we have both start and end defined
|
|
350
|
+
// it's possible we didn't find td_close index
|
|
351
|
+
if (start && end) {
|
|
352
|
+
tokens.splice(start, end - start + 1);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
};
|
|
356
|
+
|
|
213
357
|
const yfmTable: MarkdownItPluginCb = (md) => {
|
|
214
358
|
md.block.ruler.before(
|
|
215
359
|
'code',
|
|
@@ -232,7 +376,12 @@ const yfmTable: MarkdownItPluginCb = (md) => {
|
|
|
232
376
|
return true;
|
|
233
377
|
}
|
|
234
378
|
|
|
235
|
-
const {rows, endOfTable} =
|
|
379
|
+
const {rows, endOfTable} = getTableRowPositions(
|
|
380
|
+
state,
|
|
381
|
+
startPosition,
|
|
382
|
+
endPosition,
|
|
383
|
+
startLine,
|
|
384
|
+
);
|
|
236
385
|
|
|
237
386
|
if (!endOfTable) {
|
|
238
387
|
token = state.push('__yfm_lint', '', 0);
|
|
@@ -247,6 +396,7 @@ const yfmTable: MarkdownItPluginCb = (md) => {
|
|
|
247
396
|
state.lineMax = endOfTable;
|
|
248
397
|
state.line = startLine;
|
|
249
398
|
|
|
399
|
+
const tableStart = state.tokens.length;
|
|
250
400
|
token = state.push('yfm_table_open', 'table', 1);
|
|
251
401
|
token.map = [startLine, endOfTable];
|
|
252
402
|
|
|
@@ -255,9 +405,18 @@ const yfmTable: MarkdownItPluginCb = (md) => {
|
|
|
255
405
|
|
|
256
406
|
const maxRowLength = Math.max(...rows.map(([, , cols]) => cols.length));
|
|
257
407
|
|
|
408
|
+
// cellsMaps is a 2-D map of all td_open tokens in the table.
|
|
409
|
+
// cellsMap is used to access the table cells by [row][column] coordinates
|
|
410
|
+
const cellsMap: Token[][] = [];
|
|
411
|
+
|
|
412
|
+
// contentMap is a 2-D map of the text content within cells in the table.
|
|
413
|
+
// To apply spans, traverse the contentMap and modify the cells from cellsMap
|
|
414
|
+
const contentMap: string[][] = [];
|
|
415
|
+
|
|
258
416
|
for (let i = 0; i < rows.length; i++) {
|
|
259
417
|
const [rowLineStarts, rowLineEnds, cols] = rows[i];
|
|
260
|
-
|
|
418
|
+
cellsMap.push([]);
|
|
419
|
+
contentMap.push([]);
|
|
261
420
|
const rowLength = cols.length;
|
|
262
421
|
|
|
263
422
|
token = state.push('yfm_tr_open', 'tr', 1);
|
|
@@ -266,6 +425,7 @@ const yfmTable: MarkdownItPluginCb = (md) => {
|
|
|
266
425
|
for (let j = 0; j < cols.length; j++) {
|
|
267
426
|
const [begin, end] = cols[j];
|
|
268
427
|
token = state.push('yfm_td_open', 'td', 1);
|
|
428
|
+
cellsMap[i].push(token);
|
|
269
429
|
token.map = [begin.line, end.line];
|
|
270
430
|
|
|
271
431
|
const oldTshift = state.tShift[begin.line];
|
|
@@ -279,14 +439,23 @@ const yfmTable: MarkdownItPluginCb = (md) => {
|
|
|
279
439
|
state.lineMax = end.line + 1;
|
|
280
440
|
|
|
281
441
|
state.md.block.tokenize(state, begin.line, end.line + 1);
|
|
442
|
+
const contentToken = state.tokens[state.tokens.length - 2];
|
|
443
|
+
|
|
444
|
+
// In case of ">" within a cell without whitespace it gets consumed as a blockquote.
|
|
445
|
+
// To handle that, check markup as well
|
|
446
|
+
const content = contentToken.content.trim() || contentToken.markup.trim();
|
|
447
|
+
contentMap[i].push(content);
|
|
448
|
+
|
|
449
|
+
token = state.push('yfm_td_close', 'td', -1);
|
|
450
|
+
state.tokens[state.tokens.length - 1].map = [end.line, end.line + 1];
|
|
282
451
|
|
|
283
452
|
state.lineMax = oldLineMax;
|
|
284
453
|
state.tShift[begin.line] = oldTshift;
|
|
285
454
|
state.bMarks[begin.line] = oldBMark;
|
|
286
455
|
state.eMarks[end.line] = oldEMark;
|
|
287
456
|
|
|
288
|
-
|
|
289
|
-
|
|
457
|
+
const rowTokens = cellsMap[cellsMap.length - 1];
|
|
458
|
+
extractAndApplyClassFromToken(contentToken, rowTokens[rowTokens.length - 1]);
|
|
290
459
|
}
|
|
291
460
|
|
|
292
461
|
if (rowLength < maxRowLength) {
|
|
@@ -300,6 +469,9 @@ const yfmTable: MarkdownItPluginCb = (md) => {
|
|
|
300
469
|
token = state.push('yfm_tr_close', 'tr', -1);
|
|
301
470
|
}
|
|
302
471
|
|
|
472
|
+
applySpans(contentMap, cellsMap);
|
|
473
|
+
clearTokens(tableStart, state.tokens);
|
|
474
|
+
|
|
303
475
|
token = state.push('yfm_tbody_close', 'tbody', -1);
|
|
304
476
|
|
|
305
477
|
token = state.push('yfm_table_close', 'table', -1);
|