@@ -96,8 +96,8 @@ public class CommentsPreparator extends ASTVisitor {
9696 private final static Pattern MARKDOWN_HEADINGS_PATTERN_1 = Pattern .compile ("(?:(?<=^)|(?<=///[ \\ t]*))(#{1,6})([ \\ t]+)([^\\ r\\ n]*)" ); //$NON-NLS-1$
9797 private final static Pattern MARKDOWN_HEADINGS_PATTERN_2 = Pattern .compile ("(?:^|(?<=///[ \\ t]+))[ \\ t]*([=-])\\ 1*[ \\ t]*(?=\\ r?\\ n|$)" ); //$NON-NLS-1$
9898 private final static Pattern MARKDOWN_FENCE_PATTERN = Pattern .compile ("(`{3,}|~{3,})(.*)" ); //$NON-NLS-1$
99- private final static Pattern MARKDOWN_TABLE_START = Pattern .compile ("(?m)(?<=^[ \\ t]* )\\ | " ); //$NON-NLS-1$
100- private final static Pattern MARKDOWN_TABLE_END = Pattern .compile ("(?m) \\ |(?!.* \\ |) " ); //$NON-NLS-1$
99+ private final static Pattern MARKDOWN_TABLE_COLUMN_SEP = Pattern .compile ("\\ ||(?<= \\ | )\\ s*-+ \\ s*(?= \\ |) " ); //$NON-NLS-1$
100+ private final static Pattern MARKDOWN_TABLE_COLUMN_VALIDATOR = Pattern .compile ("^[ : \\ -|]+$ " ); //$NON-NLS-1$
101101
102102 // Param tags list copied from IJavaDocTagConstants in legacy formatter for compatibility.
103103 // There were the following comments:
@@ -134,6 +134,7 @@ public class CommentsPreparator extends ASTVisitor {
134134 private final ArrayList <Integer > commonAttributeAnnotations = new ArrayList <>();
135135 private DefaultCodeFormatter preTagCodeFormatter ;
136136 private DefaultCodeFormatter snippetCodeFormatter ;
137+ private String markdownTablePipe = "|" ; //$NON-NLS-1$
137138
138139 public CommentsPreparator (TokenManager tm , DefaultCodeFormatterOptions options , String sourceLevel ) {
139140 this .tm = tm ;
@@ -998,39 +999,162 @@ && formatCode(codeBlockStartIndex, codeBlockEndIndex + 1, false, true)) {
998999 }
9991000
10001001 private void handleMarkdownTable (List <ASTNode > fragments ) {
1001- int tableStartIndex = -1 ;
1002- int tableLastIndex = -1 ;
1003- boolean columnUnderlineFound = false ;
10041002 Matcher matcher ;
1005- for (Object fragment : fragments ) {
1006- if (fragment instanceof TextElement textElement ) {
1003+ ASTNode columnHeader = null ;
1004+ boolean hasFormattedColumnHeader = false ;
1005+ int maxRowDataLen = 0 ;
1006+ List <Token > columnHeaderTokens = new ArrayList <>();
1007+ List <Token > columnSeperatorTokens = new ArrayList <>();
1008+ for (int i = 0 ; i < fragments .size (); i ++) {
1009+ if (fragments .get (i ) instanceof TextElement textElement ) {
10071010 String textContent = textElement .getText ();
1008- matcher = MARKDOWN_TABLE_START .matcher (textContent );
1009- if (matcher .find ()) {
1010- if (tableStartIndex == -1 ) {
1011- int startPos = matcher .start () + textElement .getStartPosition ();
1012- tableStartIndex = tokenStartingAt (startPos );
1013- } else if (tableStartIndex != -1 && !columnUnderlineFound ) {
1014- boolean foundStart = textContent .contains ("|-" ); //$NON-NLS-1$
1015- boolean foundEnd = textContent .contains ("-|" ); //$NON-NLS-1$
1016- if (foundStart && foundEnd ) {
1017- columnUnderlineFound = true ;
1011+ if (columnSeperatorTokens .isEmpty ()) {
1012+ matcher = MARKDOWN_TABLE_COLUMN_VALIDATOR .matcher (textContent );
1013+ if (matcher .find ()) {
1014+ columnHeader = fragments .get (i - 1 );
1015+ // find the most lengthy cell data
1016+ for (int rowIndex = i ; rowIndex < fragments .size (); rowIndex ++) {
1017+ TextElement element = (TextElement ) fragments .get (rowIndex );
1018+ String rowData = element .getText ();
1019+ int maxRow = Arrays .stream (rowData .split ("\\ |" )).map (e -> e .trim ()) //$NON-NLS-1$
1020+ .filter (s -> !s .isEmpty ()).mapToInt (String ::length ).max ().orElse (0 );
1021+ maxRowDataLen = Math .max (maxRow , maxRowDataLen );
1022+ }
1023+ String headData = ((TextElement ) columnHeader ).getText ();
1024+ int maxHead = Arrays .stream (headData .split ("\\ |" )).map (e -> e .trim ()) //$NON-NLS-1$
1025+ .filter (s -> !s .isEmpty ()).mapToInt (String ::length ).max ().orElse (0 );
1026+ maxRowDataLen = maxHead == maxRowDataLen ? maxRowDataLen + 2
1027+ : Math .max (maxHead , maxRowDataLen + 2 );
1028+ matcher = MARKDOWN_TABLE_COLUMN_SEP .matcher (textContent );
1029+ while (matcher .find ()) {
1030+ int startPos = matcher .start () + fragments .get (i ).getStartPosition ();
1031+ int tokenIndex = this .ctm .findIndex (startPos , ANY , true );
1032+ Token columnSeperatorToken = this .ctm .get (tokenIndex );
1033+ columnSeperatorToken .setWrapPolicy (WrapPolicy .DISABLE_WRAP );
1034+ columnSeperatorToken .setColumnSeparator (true );
1035+ columnSeperatorTokens .add (columnSeperatorToken );
1036+ }
1037+ if (!columnSeperatorTokens .isEmpty ()) {
1038+ columnSeperatorTokens .get (columnSeperatorTokens .size () - 1 ).breakAfter ();
10181039 }
1019- } else if (columnUnderlineFound ) {
1020- matcher = MARKDOWN_TABLE_END .matcher (textContent );
1021- matcher .find (); // find the last one
1040+ }
1041+ } else if (!columnSeperatorTokens .isEmpty () && !hasFormattedColumnHeader ) {
1042+ String columnString = ((TextElement ) columnHeader ).getText ();
1043+ Pattern p = Pattern .compile ("\\ ||(?<=\\ ||\\ s)[^|\\ s]+(?=\\ s|\\ ||$)" ); //$NON-NLS-1$
1044+ matcher = p .matcher (columnString );
1045+ while (matcher .find ()) {
1046+ int startPos = matcher .start () + columnHeader .getStartPosition ();
1047+ int tokenIndex = this .ctm .findIndex (startPos , ANY , true );
1048+ Token columnToken = this .ctm .get (tokenIndex );
1049+ columnToken .setWrapPolicy (WrapPolicy .DISABLE_WRAP );
1050+ String content = this .ctm .toString (columnToken );
1051+ if (!content .equals (this .markdownTablePipe )) {
1052+ columnToken .setMarkdownColumnHeader (true );
1053+ }
1054+ columnHeaderTokens .add (columnToken );
1055+ }
1056+ columnHeaderTokens .get (columnSeperatorTokens .size () - 1 ).breakAfter ();
1057+ formatMarkdownTableHeader (columnHeaderTokens , columnSeperatorTokens , maxRowDataLen );
1058+ hasFormattedColumnHeader = true ;
1059+ }
1060+ if (hasFormattedColumnHeader ) {
1061+ List <Token > rowTokens = new ArrayList <>();
1062+ String rowSet = textContent ;
1063+ Pattern p = Pattern .compile ("\\ ||(?<=\\ ||^)[^|]+(?=\\ ||$)" ); //$NON-NLS-1$
1064+ matcher = p .matcher (rowSet );
1065+ while (matcher .find ()) {
10221066 int startPos = matcher .start () + textElement .getStartPosition ();
1023- tableLastIndex = tokenStartingAt (startPos );
1067+ int tokenIndex = this .ctm .findIndex (startPos , ANY , true );
1068+ if (tokenIndex >= this .ctm .size ()) {
1069+ continue ;
1070+ }
1071+ Token rowToken = this .ctm .get (tokenIndex );
1072+ rowToken .setWrapPolicy (WrapPolicy .DISABLE_WRAP );
1073+ rowTokens .add (rowToken );
10241074 }
1075+ rowTokens .get (rowTokens .size () - 1 ).breakAfter ();
1076+ formatMarkdownTableRow (rowTokens , maxRowDataLen );
1077+ }
1078+ }
1079+ }
1080+ }
1081+
1082+ private void formatMarkdownTableHeader (List <Token > columnHeaderTokens , List <Token > columnSeperatorTokens ,
1083+ int maxRowDataLen ) {
1084+ Token cellStart = null ;
1085+ Token cellEnd = null ;
1086+ List <Token > cellContent = new ArrayList <>();
1087+ int cel = 0 ;
1088+ for (int j = 0 ; j < columnHeaderTokens .size (); j ++) {
1089+ Token columnToken = columnHeaderTokens .get (j );
1090+ ;
1091+ if (!columnToken .isMarkdownColumnHeader () && cellStart == null ) {
1092+ cellStart = columnToken ;
1093+ } else if (!columnToken .isMarkdownColumnHeader () && cellEnd == null ) {
1094+ cellEnd = columnToken ;
1095+ } else {
1096+ cellContent .add (columnToken );
1097+ }
1098+ if (cellStart != null && cellEnd != null ) {
1099+ cellStart .spaceAfter ();
1100+ cellContent .get (cellContent .size () - 1 ).clearSpaceAfter ();
1101+ cellContent .get (0 ).clearSpaceBefore ();
1102+ int contentLength = cellContent .stream ().mapToInt (t -> this .tm .toString (t ).length ()).sum ();
1103+ contentLength = cellContent .size () > 1 ? contentLength + cellContent .size () - 1 : contentLength ;
1104+ int newLen = (maxRowDataLen - contentLength );
1105+ int prev = cellStart .getAlign () == 0 ? 4 : cellStart .getAlign ();
1106+ Token previous = cellContent .get (0 );
1107+ if (cellContent .size () == 1 ) {
1108+ cellContent .get (0 ).setAlign (prev + (newLen / 2 ) + 1 );
10251109 } else {
1026- tableStartIndex = -1 ;
1027- tableLastIndex = -1 ;
1028- columnUnderlineFound = false ;
1110+ cellContent .get (0 ).setAlign (prev + (newLen / 2 ) + 1 );
1111+ if (cellContent .size () > 1 ) {
1112+ for (int i = 1 ; i < cellContent .size (); i ++) {
1113+ Token tmp = cellContent .get (i );
1114+ tmp .setAlign (previous .getAlign ());
1115+ previous = tmp ;
1116+ cel ++;
1117+ contentLength = this .tm .toString (previous ).length ();
1118+ }
1119+ }
10291120 }
1121+ cellEnd .setAlign (previous .getAlign () + contentLength + ((newLen / 2 )));
1122+ cellStart = cellEnd ;
1123+ cellEnd = null ;
1124+ int toBeAdded = this .tm .toString (columnSeperatorTokens .get (j - 1 - cel )).length ();
1125+ int padding = contentLength % 2 == 0 ? 0 : -1 ;
1126+ columnSeperatorTokens .get (j - 1 - cel ).setMarkdownColumnLength (maxRowDataLen - toBeAdded + padding );
1127+ cellContent .clear ();
1128+ }
1129+ }
1130+ }
1131+
1132+ private void formatMarkdownTableRow (List <Token > rowTokens , int maxRowDataLen ) {
1133+ Token cellStart = null ;
1134+ Token cellEnd = null ;
1135+ Token cellContent = null ;
1136+ for (int j = 0 ; j < rowTokens .size (); j ++) {
1137+ Token columnToken = rowTokens .get (j );
1138+ String colVal = this .tm .toString (columnToken );
1139+ if (colVal .equals (this .markdownTablePipe ) && cellStart == null ) {
1140+ cellStart = columnToken ;
1141+ } else if (colVal .equals (this .markdownTablePipe ) && cellEnd == null ) {
1142+ cellEnd = columnToken ;
1143+ } else {
1144+ cellContent = columnToken ;
10301145 }
1031- if (tableStartIndex != -1 && tableLastIndex != -1 ) {
1032- // TODO fix column alignment and format cells
1033- disableFormattingExclusively (tableStartIndex , tableLastIndex );
1146+ if (cellStart != null && cellEnd != null ) {
1147+ cellContent .clearSpaceAfter ();
1148+ cellContent .clearSpaceBefore ();
1149+ int contentLength = this .tm .toString (cellContent ).length ();
1150+ int newLen = (maxRowDataLen - contentLength );
1151+ cellStart .spaceAfter ();
1152+ int prev = cellStart .getAlign () == 0 ? 0 : cellStart .getAlign ();
1153+ int padding = contentLength == maxRowDataLen ? 0 : 1 ;
1154+ cellContent .setAlign (prev + (newLen / 2 ) + padding );
1155+ cellEnd .setAlign (cellContent .getAlign () + contentLength + ((newLen / 2 )));
1156+ cellStart = cellEnd ;
1157+ cellEnd = null ;
10341158 }
10351159 }
10361160 }
0 commit comments