@peaceroad/markdown-it-table-ex 0.3.0 → 0.3.2

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.
Files changed (2) hide show
  1. package/index.js +316 -182
  2. package/package.json +3 -3
package/index.js CHANGED
@@ -34,15 +34,17 @@ const isStrongWrappedInline = (inline, allowFallback) => {
34
34
  if (!inline || typeof inline.content !== 'string') return false;
35
35
  const content = inline.content;
36
36
  if (!content.startsWith('**') || !content.endsWith('**')) return false;
37
- if (!Array.isArray(inline.children)) return !!allowFallback;
38
- if (getLeadingStrongCloseIndex(inline) !== -1) return true;
39
- return !!allowFallback;
37
+ if (allowFallback) return true;
38
+ if (!Array.isArray(inline.children)) return false;
39
+ return getLeadingStrongCloseIndex(inline) !== -1;
40
40
  };
41
41
 
42
42
  const hasLeadingStrongMarker = (inline, allowFallback) => {
43
- if (getLeadingStrongCloseIndex(inline) !== -1) return true;
44
- if (!allowFallback || !inline || typeof inline.content !== 'string') return false;
45
- return inline.content.startsWith('**');
43
+ if (!inline || typeof inline.content !== 'string') return false;
44
+ if (!inline.content.startsWith('**')) return false;
45
+ if (allowFallback) return true;
46
+ if (!Array.isArray(inline.children)) return false;
47
+ return getLeadingStrongCloseIndex(inline) !== -1;
46
48
  };
47
49
 
48
50
  const hasInlineRule = (md, name) => {
@@ -54,6 +56,84 @@ const hasInlineRule = (md, name) => {
54
56
  return false;
55
57
  };
56
58
 
59
+ const createNewlineToken = (Token) => {
60
+ const token = new Token('text', '', 0);
61
+ token.content = '\n';
62
+ return token;
63
+ };
64
+
65
+ const setInlineText = (inline, Token, text) => {
66
+ inline.content = text;
67
+ const textToken = new Token('text', '', 0);
68
+ textToken.content = text;
69
+ textToken.level = 0;
70
+ inline.children = [textToken];
71
+ };
72
+
73
+ const removeHeaderCellAt = (tokens, openIdx) => {
74
+ if (!tokens[openIdx] || tokens[openIdx].type !== 'th_open') return;
75
+ let deleteCount = 3;
76
+ const next = tokens[openIdx + 3];
77
+ if (next && next.type === 'text' && next.content === '\n') {
78
+ deleteCount += 1;
79
+ }
80
+ tokens.splice(openIdx, deleteCount);
81
+ };
82
+
83
+ const findFirstHeaderThPos = (tokens, tableOpenIdx, allowFallback) => {
84
+ let inThead = false;
85
+ for (let i = tableOpenIdx + 1; i < tokens.length; i++) {
86
+ const type = tokens[i].type;
87
+ if (type === 'table_close') break;
88
+ if (type === 'thead_open') {
89
+ inThead = true;
90
+ continue;
91
+ }
92
+ if (!inThead) continue;
93
+ if (type === 'thead_close') break;
94
+ if (type !== 'th_open') continue;
95
+ const inline = tokens[i + 1];
96
+ if (inline && (inline.content === '' || isStrongWrappedInline(inline, allowFallback))) {
97
+ return i;
98
+ }
99
+ return -1;
100
+ }
101
+ return -1;
102
+ };
103
+
104
+ const findFirstHeaderCellPos = (tokens, tableOpenIdx) => {
105
+ let inThead = false;
106
+ for (let i = tableOpenIdx + 1; i < tokens.length; i++) {
107
+ const type = tokens[i].type;
108
+ if (type === 'table_close') break;
109
+ if (type === 'thead_open') {
110
+ inThead = true;
111
+ continue;
112
+ }
113
+ if (!inThead) continue;
114
+ if (type === 'thead_close') break;
115
+ if (type === 'th_open') return i;
116
+ }
117
+ return -1;
118
+ };
119
+
120
+ const createColgroupRegexes = (colgroupWithNoAsterisk) => {
121
+ if (colgroupWithNoAsterisk) {
122
+ return {
123
+ singleHeaderGroup: /^([^::]+)(?::\s*|: +)/,
124
+ singleHeaderStrip: /^[^::]+(?::\s*|: +)(.*)$/,
125
+ multiHeaderGroup: /^([^::]+)(?::\s*|: +)/,
126
+ multiHeaderStrip: /^[^::]+(?::\s*|: +)(.*)$/
127
+ };
128
+ }
129
+ return {
130
+ singleHeaderGroup: /^\*\*([^*::]+)[::]\*\*\s*/,
131
+ singleHeaderStrip: /^\*\*[^*::]+[::]\*\*\s*(.*)$/,
132
+ multiHeaderGroup: /^\*\*([^*::]+)[::]\*\*/,
133
+ multiHeaderStrip: /^\*\*[^*::]+[::]\*\*\s*(.*)$/
134
+ };
135
+ };
136
+
57
137
  const createTokenTemplate = (tokenType, tag, nesting, level) => ({
58
138
  type: tokenType,
59
139
  tag: tag,
@@ -211,24 +291,28 @@ const removeStrongWrappers = (inline, allowFallback) => {
211
291
 
212
292
  const addTheadThScope = (state, theadVar, allowFallback) => {
213
293
  const tokens = state.tokens;
214
- let firstThPos = theadVar.pos;
215
- let j = theadVar.i + 2;
216
- //if (state.tokens[theadVar.i + 1].type !== 'tr_open') return threadVar
294
+ let firstThPos = theadVar.firstThPos;
295
+ let j = theadVar.i + 1;
296
+ let isFirstRow = true;
297
+ let firstRowFirstThSeen = false;
217
298
  while (j < tokens.length) {
218
- if (tokens[j].type === 'th_open') {
219
- tokens[j].attrPush(['scope', 'col']);
220
- if (j === theadVar.i + 2) {
299
+ const tokenType = tokens[j].type;
300
+ if (tokenType === 'th_open') {
301
+ tokens[j].attrSet('scope', 'col');
302
+ if (isFirstRow && !firstRowFirstThSeen) {
303
+ firstRowFirstThSeen = true;
221
304
  const inline = tokens[j + 1];
222
- const content = inline.content;
305
+ const content = inline && typeof inline.content === 'string' ? inline.content : '';
223
306
  if (content === '' || isStrongWrappedInline(inline, allowFallback)) {
224
- firstThPos = j
225
- // Only remove strong tags from the first th content for matrix processing
226
- // The actual removal will be done in changeTdToTh if matrix conditions are met
307
+ firstThPos = j;
227
308
  }
228
309
  }
310
+ } else if (tokenType === 'tr_close' && isFirstRow) {
311
+ isFirstRow = false;
312
+ } else if (tokenType === 'thead_close') {
313
+ break;
229
314
  }
230
- if (tokens[j].type === 'tr_close') break
231
- j++
315
+ j++;
232
316
  }
233
317
  return {i: j, firstThPos: firstThPos}
234
318
  }
@@ -276,7 +360,7 @@ const checkTbody = (state, tbodyVar, allowFallback) => {
276
360
  return { i: j, isAllFirstTh: isAllFirstTh, tbodyFirstThPoses: tbodyFirstThPoses}
277
361
  }
278
362
 
279
- const setColgroup = (state, tableOpenIdx, opt, allowFallback) => {
363
+ const setColgroup = (state, tableOpenIdx, opt, allowFallback, regexes) => {
280
364
  const tokens = state.tokens;
281
365
  const Token = state.Token;
282
366
 
@@ -308,22 +392,24 @@ const setColgroup = (state, tableOpenIdx, opt, allowFallback) => {
308
392
  headerCount: 0
309
393
  };
310
394
 
311
- const colgroupMatchReg = opt.colgroupWithNoAsterisk
312
- ? /^([^::]+)(?::|:)\s*/
313
- : /^\*\*([^*::]+)[::]\*\*\s*/;
314
- const origColgroupMatchReg = opt.colgroupWithNoAsterisk
315
- ? /^[^::]+(?::|:)\s*(.*)$/
316
- : /^\*\*[^*::]+[::]\*\*\s*(.*)$/;
395
+ const colgroupMatchReg = regexes.singleHeaderGroup;
396
+ const origColgroupMatchReg = regexes.singleHeaderStrip;
317
397
 
318
398
  let thIdx = tr1 + 1;
319
- const origThs = [];
399
+ const origThInfos = [];
320
400
  let trCloseIdx = -1;
321
401
 
322
402
  while (thIdx < tokens.length) {
323
403
  const tokenType = tokens[thIdx].type;
324
404
  if (tokenType === 'th_open') {
325
405
  const inline = tokens[thIdx + 1];
326
- origThs.push(inline);
406
+ let map = null;
407
+ if (Array.isArray(tokens[thIdx].map)) {
408
+ map = tokens[thIdx].map.slice();
409
+ } else if (inline && Array.isArray(inline.map)) {
410
+ map = inline.map.slice();
411
+ }
412
+ origThInfos.push({ inline, map });
327
413
  const content = inline.content;
328
414
  const hasLeadingStrong = hasLeadingStrongMarker(inline, allowFallback);
329
415
  let match = null;
@@ -367,7 +453,7 @@ const setColgroup = (state, tableOpenIdx, opt, allowFallback) => {
367
453
  if (hasSpan) {
368
454
  const insertTokens = [
369
455
  new Token('colgroup_open', 'colgroup', 1),
370
- Object.assign(new Token('text', '', 0), { content: '\n' })
456
+ createNewlineToken(Token)
371
457
  ];
372
458
 
373
459
  for (let i = 0; i < groupData.spans.length; i++) {
@@ -377,13 +463,13 @@ const setColgroup = (state, tableOpenIdx, opt, allowFallback) => {
377
463
  }
378
464
  insertTokens.push(
379
465
  colOpen,
380
- Object.assign(new Token('text', '', 0), { content: '\n' })
466
+ createNewlineToken(Token)
381
467
  );
382
468
  }
383
469
 
384
470
  insertTokens.push(
385
471
  new Token('colgroup_close', 'colgroup', -1),
386
- Object.assign(new Token('text', '', 0), { content: '\n' })
472
+ createNewlineToken(Token)
387
473
  );
388
474
 
389
475
  tokens.splice(tableOpenIdx + 1, 0, ...insertTokens);
@@ -391,7 +477,6 @@ const setColgroup = (state, tableOpenIdx, opt, allowFallback) => {
391
477
  // Update indices
392
478
  const offset = insertTokens.length;
393
479
  tr1 += offset;
394
- theadClose += offset;
395
480
  trCloseIdx += offset;
396
481
  }
397
482
 
@@ -399,41 +484,44 @@ const setColgroup = (state, tableOpenIdx, opt, allowFallback) => {
399
484
 
400
485
  // Create new rows more efficiently
401
486
  const newTr1 = [
402
- Object.assign(new Token('text', '', 0), { content: '\n' }),
487
+ createNewlineToken(Token),
403
488
  new Token('tr_open', 'tr', 1),
404
- Object.assign(new Token('text', '', 0), { content: '\n' })
489
+ createNewlineToken(Token)
405
490
  ];
406
491
 
407
492
  const newTr2 = [
408
493
  new Token('tr_open', 'tr', 1),
409
- Object.assign(new Token('text', '', 0), { content: '\n' })
494
+ createNewlineToken(Token)
410
495
  ];
411
496
 
412
497
  let thPtr = 0;
413
498
  for (let i = 0; i < groupData.spans.length; i++) {
499
+ const origInfo = origThInfos[thPtr];
500
+ const cellMap = origInfo && origInfo.map ? origInfo.map : null;
414
501
  if (groupNames[i] === null) {
415
502
  // Leftmost cell: rowspan=2
416
503
  const thOpen = new Token('th_open', 'th', 1);
417
504
  thOpen.attrSet('rowspan', '2');
418
505
  thOpen.attrSet('scope', 'col');
506
+ if (cellMap) thOpen.map = cellMap;
419
507
 
420
508
  const thInline = new Token('inline', '', 0);
421
- const origInline = origThs[thPtr];
509
+ const origInline = origInfo ? origInfo.inline : null;
422
510
 
423
511
  if (origInline) {
424
512
  removeStrongWrappers(origInline, allowFallback);
425
513
  thInline.content = origInline.content;
426
514
  thInline.children = Array.isArray(origInline.children) ? origInline.children : [];
515
+ if (cellMap) thInline.map = cellMap;
427
516
  } else {
428
- thInline.content = '';
429
- thInline.children = [];
517
+ setInlineText(thInline, Token, '');
430
518
  }
431
519
 
432
520
  newTr1.push(
433
521
  thOpen,
434
522
  thInline,
435
523
  new Token('th_close', 'th', -1),
436
- Object.assign(new Token('text', '', 0), { content: '\n' })
524
+ createNewlineToken(Token)
437
525
  );
438
526
  thPtr++;
439
527
  } else {
@@ -443,25 +531,29 @@ const setColgroup = (state, tableOpenIdx, opt, allowFallback) => {
443
531
  thOpen.attrSet('colspan', groupData.spans[i].toString());
444
532
  }
445
533
  thOpen.attrSet('scope', 'col');
534
+ if (cellMap) thOpen.map = cellMap;
446
535
 
447
536
  const thInline = new Token('inline', '', 0);
448
- thInline.content = groupNames[i];
449
- thInline.children = [{ type: 'text', content: groupNames[i], level: 0 }];
537
+ setInlineText(thInline, Token, groupNames[i]);
538
+ if (cellMap) thInline.map = cellMap;
450
539
 
451
540
  newTr1.push(
452
541
  thOpen,
453
542
  thInline,
454
543
  new Token('th_close', 'th', -1),
455
- Object.assign(new Token('text', '', 0), { content: '\n' })
544
+ createNewlineToken(Token)
456
545
  );
457
546
 
458
547
  // Second row: each item in the group
459
548
  for (let j = 0; j < groupData.spans[i]; j++) {
549
+ const subInfo = origThInfos[thPtr];
550
+ const subMap = subInfo && subInfo.map ? subInfo.map : null;
460
551
  const th2Open = new Token('th_open', 'th', 1);
461
552
  th2Open.attrSet('scope', 'col');
553
+ if (subMap) th2Open.map = subMap;
462
554
 
463
555
  const th2Inline = new Token('inline', '', 0);
464
- const origInline = origThs[thPtr];
556
+ const origInline = subInfo ? subInfo.inline : null;
465
557
  const orig = origInline?.content || '';
466
558
  let match = null;
467
559
  if (opt.colgroupWithNoAsterisk) {
@@ -471,14 +563,14 @@ const setColgroup = (state, tableOpenIdx, opt, allowFallback) => {
471
563
  } else if (hasLeadingStrongMarker(origInline, allowFallback)) {
472
564
  match = orig.match(origColgroupMatchReg);
473
565
  }
474
- th2Inline.content = match ? match[1] : orig;
475
- th2Inline.children = [{ type: 'text', content: th2Inline.content, level: 0 }];
566
+ setInlineText(th2Inline, Token, match ? match[1] : orig);
567
+ if (subMap) th2Inline.map = subMap;
476
568
 
477
569
  newTr2.push(
478
570
  th2Open,
479
571
  th2Inline,
480
572
  new Token('th_close', 'th', -1),
481
- Object.assign(new Token('text', '', 0), { content: '\n' })
573
+ createNewlineToken(Token)
482
574
  );
483
575
  thPtr++;
484
576
  }
@@ -487,12 +579,12 @@ const setColgroup = (state, tableOpenIdx, opt, allowFallback) => {
487
579
 
488
580
  newTr1.push(
489
581
  new Token('tr_close', 'tr', -1),
490
- Object.assign(new Token('text', '', 0), { content: '\n' })
582
+ createNewlineToken(Token)
491
583
  );
492
584
 
493
585
  newTr2.push(
494
586
  new Token('tr_close', 'tr', -1),
495
- Object.assign(new Token('text', '', 0), { content: '\n' })
587
+ createNewlineToken(Token)
496
588
  );
497
589
 
498
590
  // Clean up newlines before insertion
@@ -507,40 +599,6 @@ const setColgroup = (state, tableOpenIdx, opt, allowFallback) => {
507
599
 
508
600
  tokens.splice(tr1, trCloseIdx - tr1 + 1, ...newTr1, ...newTr2);
509
601
 
510
- // Handle tbody th conversion
511
- let tbodyOpen = -1, tbodyClose = -1;
512
- for (let i = theadClose + 1; i < tokens.length; i++) {
513
- if (tokens[i].type === 'tbody_open') {
514
- tbodyOpen = i;
515
- } else if (tbodyOpen >= 0 && tokens[i].type === 'tbody_close') {
516
- tbodyClose = i;
517
- break;
518
- }
519
- }
520
-
521
- if (tbodyOpen >= 0 && tbodyClose > tbodyOpen) {
522
- let j = tbodyOpen + 1;
523
- while (j < tbodyClose) {
524
- if (tokens[j].type === 'tr_open') {
525
- const tdIdx = j + 1;
526
- if (tokens[tdIdx].type === 'td_open') {
527
- const inline = tokens[tdIdx + 1];
528
- if (inline && typeof inline.content === 'string' &&
529
- isStrongWrappedInline(inline, allowFallback)) {
530
- tokens[tdIdx].type = 'th_open';
531
- tokens[tdIdx].tag = 'th';
532
- tokens[tdIdx].attrSet('scope', 'row');
533
- removeStrongWrappers(inline, allowFallback);
534
- if (tokens[tdIdx + 2].type === 'td_close') {
535
- tokens[tdIdx + 2].type = 'th_close';
536
- tokens[tdIdx + 2].tag = 'th';
537
- }
538
- }
539
- }
540
- }
541
- j++;
542
- }
543
- }
544
602
  return;
545
603
  }
546
604
 
@@ -549,116 +607,169 @@ const setColgroup = (state, tableOpenIdx, opt, allowFallback) => {
549
607
  // Calculate group names and colspan for multi-row case
550
608
  const groupData = {
551
609
  spans: [],
552
- rawNames: []
610
+ rawNames: [],
611
+ headerCount: 0
553
612
  };
554
- const groupMatchReg = /^\*\*([^*:]+):\*\*/;
613
+ const groupMatchReg = regexes.multiHeaderGroup;
614
+ const groupStripReg = regexes.multiHeaderStrip;
555
615
 
616
+ const firstRowCells = [];
617
+ let firstRowClose = -1;
556
618
  let thIdx = tr1 + 1;
557
- while (thIdx < tokens.length && tokens[thIdx].type !== 'tr_close') {
619
+ while (thIdx < tokens.length) {
558
620
  if (tokens[thIdx].type === 'th_open') {
559
- const inline = tokens[thIdx + 1];
560
- const content = inline.content;
561
- const hasLeadingStrong = hasLeadingStrongMarker(inline, allowFallback);
562
- let match = null;
563
- if (hasLeadingStrong) {
621
+ firstRowCells.push(thIdx);
622
+ } else if (tokens[thIdx].type === 'tr_close') {
623
+ firstRowClose = thIdx;
624
+ break;
625
+ }
626
+ thIdx++;
627
+ }
628
+ if (firstRowClose < 0 || firstRowCells.length === 0) return;
629
+
630
+ const secondRowCells = [];
631
+ let secondRowClose = -1;
632
+ thIdx = tr2 + 1;
633
+ while (thIdx < tokens.length) {
634
+ if (tokens[thIdx].type === 'th_open') {
635
+ secondRowCells.push(thIdx);
636
+ } else if (tokens[thIdx].type === 'tr_close') {
637
+ secondRowClose = thIdx;
638
+ break;
639
+ }
640
+ thIdx++;
641
+ }
642
+ if (secondRowClose < 0 || secondRowCells.length === 0) return;
643
+
644
+ for (let i = 0; i < firstRowCells.length; i++) {
645
+ const inline = tokens[firstRowCells[i] + 1];
646
+ const content = inline && typeof inline.content === 'string' ? inline.content : '';
647
+ let match = null;
648
+ if (opt.colgroupWithNoAsterisk) {
649
+ if (content.indexOf(':') !== -1 || content.indexOf(':') !== -1) {
564
650
  match = content.match(groupMatchReg);
565
651
  }
566
-
567
- if (match) {
568
- const group = match[1].trim();
569
- const lastGroup = groupData.rawNames[groupData.rawNames.length - 1];
570
-
571
- if (groupData.rawNames.length > 0 && lastGroup === group) {
572
- groupData.spans[groupData.spans.length - 1]++;
573
- } else {
574
- groupData.rawNames.push(group);
575
- groupData.spans.push(1);
576
- }
652
+ } else if (hasLeadingStrongMarker(inline, allowFallback)) {
653
+ match = content.match(groupMatchReg);
654
+ }
655
+
656
+ if (match) {
657
+ const group = match[1].trim();
658
+ const lastGroup = groupData.rawNames[groupData.rawNames.length - 1];
659
+ if (groupData.rawNames.length > 0 && lastGroup === group) {
660
+ groupData.spans[groupData.spans.length - 1]++;
577
661
  } else {
578
- groupData.rawNames.push(null);
662
+ groupData.rawNames.push(group);
579
663
  groupData.spans.push(1);
580
664
  }
665
+ groupData.headerCount++;
666
+ } else {
667
+ groupData.rawNames.push(null);
668
+ groupData.spans.push(1);
581
669
  }
582
- thIdx++;
583
670
  }
584
671
 
585
- const groupNames = groupData.rawNames;
586
- const groupStripReg = /^\*\*[^*:]+:\*\*\s*(.*)$/;
672
+ if (groupData.headerCount < 2) return;
587
673
 
588
- // Generate colgroup if needed
589
674
  const hasSpan = groupData.spans.some(span => span > 1);
590
- if (hasSpan) {
591
- const insertTokens = [new Token('colgroup_open', 'colgroup', 1)];
675
+
676
+ // Add colspan/rowspan to thead and strip group prefixes in the second row.
677
+ const firstRowRemove = [];
678
+ const secondRowRemove = [];
679
+ let firstPtr = 0;
680
+ let secondPtr = 0;
681
+ for (let groupIdx = 0; groupIdx < groupData.spans.length; groupIdx++) {
682
+ const span = groupData.spans[groupIdx];
683
+ const groupName = groupData.rawNames[groupIdx];
684
+ const firstOpen = firstRowCells[firstPtr];
685
+ if (typeof firstOpen !== 'number' || !tokens[firstOpen] || tokens[firstOpen].type !== 'th_open') {
686
+ break;
687
+ }
688
+ const firstInline = tokens[firstOpen + 1];
689
+
690
+ if (groupName === null) {
691
+ tokens[firstOpen].attrSet('rowspan', '2');
692
+ tokens[firstOpen].attrSet('scope', 'col');
693
+ removeStrongWrappers(firstInline, allowFallback);
694
+ const secondOpen = secondRowCells[secondPtr];
695
+ if (typeof secondOpen === 'number') {
696
+ secondRowRemove.push(secondOpen);
697
+ secondPtr += 1;
698
+ }
699
+ firstPtr += 1;
700
+ continue;
701
+ }
592
702
 
593
- for (let i = 0; i < groupData.spans.length; i++) {
594
- const colOpen = new Token('col_open', 'col', 1);
595
- if (groupData.spans[i] > 1) {
596
- colOpen.attrPush(['span', groupData.spans[i]]);
703
+ if (span > 1) {
704
+ tokens[firstOpen].attrSet('colspan', span.toString());
705
+ }
706
+ tokens[firstOpen].attrSet('scope', 'col');
707
+ if (firstInline) {
708
+ setInlineText(firstInline, Token, groupName);
709
+ }
710
+
711
+ for (let i = 1; i < span; i++) {
712
+ const extraFirst = firstRowCells[firstPtr + i];
713
+ if (typeof extraFirst === 'number') {
714
+ firstRowRemove.push(extraFirst);
597
715
  }
598
- insertTokens.push(colOpen, new Token('col_close', 'col', -1));
599
716
  }
600
717
 
601
- insertTokens.push(new Token('colgroup_close', 'colgroup', -1));
602
- tokens.splice(tableOpenIdx + 1, 0, ...insertTokens);
718
+ for (let i = 0; i < span; i++) {
719
+ const secondOpen = secondRowCells[secondPtr + i];
720
+ if (typeof secondOpen !== 'number' || !tokens[secondOpen] || tokens[secondOpen].type !== 'th_open') {
721
+ continue;
722
+ }
723
+ tokens[secondOpen].attrSet('scope', 'col');
724
+ const secondInline = tokens[secondOpen + 1];
725
+ if (!secondInline || typeof secondInline.content !== 'string') continue;
726
+ const secondContent = secondInline.content;
727
+ let secondMatch = null;
728
+ if (opt.colgroupWithNoAsterisk) {
729
+ if (secondContent.indexOf(':') !== -1 || secondContent.indexOf(':') !== -1) {
730
+ secondMatch = secondContent.match(groupStripReg);
731
+ }
732
+ } else if (hasLeadingStrongMarker(secondInline, allowFallback)) {
733
+ secondMatch = secondContent.match(groupStripReg);
734
+ }
735
+ if (secondMatch) {
736
+ setInlineText(secondInline, Token, secondMatch[1]);
737
+ }
738
+ }
739
+
740
+ secondPtr += span;
741
+ firstPtr += span;
603
742
  }
604
743
 
605
- // Add colspan/rowspan to thead
606
- let th1 = tr1 + 1, th2 = tr2 + 1, groupIdx = 0;
607
- while (th1 < tokens.length && tokens[th1].type !== 'tr_close') {
608
- if (tokens[th1].type === 'th_open') {
609
- if (groupNames[groupIdx] === null) {
610
- // Leftmost cell: rowspan=2
611
- tokens[th1].attrSet('rowspan', '2');
612
- tokens[th1].attrSet('scope', 'col');
613
- removeStrongWrappers(tokens[th1 + 1], allowFallback);
614
-
615
- // Find corresponding th in second row
616
- let t2 = th2;
617
- while (t2 < tokens.length && tokens[t2].type !== 'tr_close') {
618
- if (tokens[t2].type === 'th_open') {
619
- tokens[t2].attrSet('scope', 'col');
620
- th2 = t2 + 1;
621
- break;
622
- }
623
- t2++;
624
- }
625
- groupIdx++;
626
- } else {
627
- // Group cell
628
- if (groupData.spans[groupIdx] > 1) {
629
- tokens[th1].attrSet('colspan', groupData.spans[groupIdx].toString());
630
- }
631
- tokens[th1].attrSet('scope', 'col');
632
- tokens[th1 + 1].content = groupNames[groupIdx];
633
-
634
- // Process corresponding ths in second row
635
- let count = 0, t2 = th2;
636
- while (t2 < tokens.length && tokens[t2].type !== 'tr_close' && count < groupData.spans[groupIdx]) {
637
- if (tokens[t2].type === 'th_open') {
638
- tokens[t2].attrSet('scope', 'col');
639
- // Remove group name part from content
640
- const t2Inline = tokens[t2 + 1];
641
- const t2content = t2Inline.content;
642
- let t2match = null;
643
- if (hasLeadingStrongMarker(t2Inline, allowFallback)) {
644
- t2match = t2content.match(groupStripReg);
645
- }
646
- if (t2match) {
647
- tokens[t2 + 1].content = t2match[1];
648
- }
649
- count++;
650
- }
651
- t2++;
652
- }
653
- th2 = t2;
654
- groupIdx++;
744
+ if (firstRowRemove.length || secondRowRemove.length) {
745
+ const removeTargets = firstRowRemove.concat(secondRowRemove).sort((a, b) => b - a);
746
+ for (let i = 0; i < removeTargets.length; i++) {
747
+ removeHeaderCellAt(tokens, removeTargets[i]);
748
+ }
749
+ }
750
+
751
+ // Insert colgroup after header edits so index calculations above remain stable.
752
+ if (hasSpan) {
753
+ const insertTokens = [
754
+ new Token('colgroup_open', 'colgroup', 1),
755
+ createNewlineToken(Token)
756
+ ];
757
+ for (let i = 0; i < groupData.spans.length; i++) {
758
+ const colOpen = new Token('col_open', 'col', 1);
759
+ if (groupData.spans[i] > 1) {
760
+ colOpen.attrPush(['span', groupData.spans[i]]);
655
761
  }
762
+ insertTokens.push(colOpen, createNewlineToken(Token));
656
763
  }
657
- th1++;
764
+ insertTokens.push(
765
+ new Token('colgroup_close', 'colgroup', -1),
766
+ createNewlineToken(Token)
767
+ );
768
+ tokens.splice(tableOpenIdx + 1, 0, ...insertTokens);
658
769
  }
659
770
  };
660
771
 
661
- const tableEx = (state, opt) => {
772
+ const tableEx = (state, opt, regexes) => {
662
773
  const tokens = state.tokens;
663
774
  let tokenLength = tokens.length;
664
775
  const allowStrongFallback = !hasInlineRule(state.md, 'strong_ja');
@@ -670,16 +781,18 @@ const tableEx = (state, opt) => {
670
781
  continue;
671
782
  }
672
783
 
673
- const tableOpenIdx = idx;
784
+ let tableOpenIdx = idx;
674
785
 
675
786
  if (opt.wrapper) {
676
787
  const wrapperStartToken = new state.Token('div_open', 'div', 1);
677
788
  wrapperStartToken.attrPush(['class', 'table-wrapper']);
678
- const linebreakToken = new state.Token('text', '', 0);
679
- linebreakToken.content = '\n';
680
- tokens.splice(idx, 0, wrapperStartToken, linebreakToken);
789
+ if (Array.isArray(tokens[idx].map)) {
790
+ wrapperStartToken.map = tokens[idx].map.slice();
791
+ }
792
+ tokens.splice(idx, 0, wrapperStartToken, createNewlineToken(state.Token));
681
793
  tokenLength += 2;
682
794
  idx += 2;
795
+ tableOpenIdx = idx;
683
796
  }
684
797
 
685
798
  let theadVar = {
@@ -691,27 +804,50 @@ const tableEx = (state, opt) => {
691
804
  if (hasThead) {
692
805
  theadVar = addTheadThScope(state, theadVar, allowStrongFallback);
693
806
  idx = theadVar.i + 1;
807
+ const hadMatrixAnchor = Number.isInteger(theadVar.firstThPos) && theadVar.firstThPos >= 0;
694
808
  if (opt.colgroup) {
695
809
  const beforeLength = tokens.length;
696
- setColgroup(state, tableOpenIdx, opt, allowStrongFallback);
810
+ setColgroup(state, tableOpenIdx, opt, allowStrongFallback, regexes);
697
811
  tokenLength += tokens.length - beforeLength;
698
812
  }
813
+ if (opt.matrix) {
814
+ let firstThPos = findFirstHeaderThPos(tokens, tableOpenIdx, allowStrongFallback);
815
+ if (firstThPos < 0 && hadMatrixAnchor) {
816
+ firstThPos = findFirstHeaderCellPos(tokens, tableOpenIdx);
817
+ }
818
+ theadVar.firstThPos = firstThPos;
819
+ }
699
820
  }
700
821
 
701
822
  if (opt.matrix) {
823
+ let tbodyOpenPos = -1;
824
+ for (let i = tableOpenIdx + 1; i < tokenLength; i++) {
825
+ const type = tokens[i].type;
826
+ if (type === 'tbody_open') {
827
+ tbodyOpenPos = i;
828
+ break;
829
+ }
830
+ if (type === 'table_close') {
831
+ break;
832
+ }
833
+ }
834
+
702
835
  let tbodyVar = {
703
- i: idx + 1,
836
+ i: tbodyOpenPos,
704
837
  isAllFirstTh: false,
705
838
  tbodyFirstThPoses: [],
706
839
  };
707
840
 
708
- const hasTbody = tokens[tbodyVar.i] && tokens[tbodyVar.i].type === 'tbody_open';
841
+ const hasTbody = tbodyVar.i >= 0;
709
842
  if (hasTbody) {
710
843
  tbodyVar = checkTbody(state, tbodyVar, allowStrongFallback);
711
844
  idx = tbodyVar.i + 1;
712
845
  }
713
846
 
714
- if (theadVar.firstThPos && tbodyVar.isAllFirstTh) {
847
+ const hasMatrixAnchor = hasThead
848
+ ? Number.isInteger(theadVar.firstThPos) && theadVar.firstThPos >= 0
849
+ : true;
850
+ if (hasMatrixAnchor && tbodyVar.isAllFirstTh) {
715
851
  const firstTdPoses = [...tbodyVar.tbodyFirstThPoses];
716
852
  if (hasThead) {
717
853
  firstTdPoses.unshift(theadVar.firstThPos);
@@ -725,9 +861,7 @@ const tableEx = (state, opt) => {
725
861
  if (tokens[idx].type === 'table_close') {
726
862
  if (opt.wrapper) {
727
863
  const wrapperEndToken = new state.Token('div_close', 'div', -1);
728
- const linebreakToken = new state.Token('text', '', 0);
729
- linebreakToken.content = '\n';
730
- tokens.splice(idx + 1, 0, wrapperEndToken, linebreakToken);
864
+ tokens.splice(idx + 1, 0, wrapperEndToken, createNewlineToken(state.Token));
731
865
  tokenLength += 2;
732
866
  idx += 2;
733
867
  }
@@ -740,17 +874,17 @@ const tableEx = (state, opt) => {
740
874
  };
741
875
 
742
876
  const mditTableEx = (md, option) => {
743
- let opt = {
877
+ const baseOpt = {
744
878
  matrix: true,
745
879
  wrapper: false,
746
880
  colgroup: false,
747
881
  colgroupWithNoAsterisk: false
748
882
  };
749
- for (let key in option) {
750
- opt[key] = option[key]
751
- }
883
+ const userOpt = (option && typeof option === 'object') ? option : {};
884
+ const opt = Object.assign({}, baseOpt, userOpt);
885
+ const regexes = createColgroupRegexes(opt.colgroupWithNoAsterisk);
752
886
  md.core.ruler.after('replacements', 'table-ex', (state) => {
753
- tableEx(state, opt);
887
+ tableEx(state, opt, regexes);
754
888
  });
755
889
  }
756
890
  export default mditTableEx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peaceroad/markdown-it-table-ex",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "A markdown-it plugin. Add matrix and wrapper notation for table processing.",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -16,8 +16,8 @@
16
16
  "author": "peaceroad <peaceroad@gmail.com>",
17
17
  "license": "MIT",
18
18
  "devDependencies": {
19
- "@peaceroad/markdown-it-figure-with-p-caption": "^0.14.2",
20
- "@peaceroad/markdown-it-strong-ja": "^0.5.5",
19
+ "@peaceroad/markdown-it-figure-with-p-caption": "^0.16.0",
20
+ "@peaceroad/markdown-it-strong-ja": "^0.7.2",
21
21
  "markdown-it": "^14.1.0",
22
22
  "markdown-it-multimd-table": "^4.2.3"
23
23
  }