@@ -29,7 +29,7 @@ public class PipeTableParser : InlineParser, IPostInlineProcessor
2929 public PipeTableParser ( LineBreakInlineParser lineBreakParser , PipeTableOptions ? options = null )
3030 {
3131 _lineBreakParser = lineBreakParser ?? throw new ArgumentNullException ( nameof ( lineBreakParser ) ) ;
32- OpeningCharacters = [ '|' , '\n ' , '\r ' ] ;
32+ OpeningCharacters = [ '|' , '\n ' , '\r ' , ':' ] ;
3333 Options = options ?? new PipeTableOptions ( ) ;
3434 }
3535
@@ -51,6 +51,18 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice)
5151
5252 var c = slice . CurrentChar ;
5353
54+ if ( c == ':' )
55+ {
56+ var tableStateForColon = processor . ParserStates [ Index ] as TableState ;
57+ if ( tableStateForColon is null || ! IsHeaderSeparatorColonBeforePipe ( slice ) )
58+ {
59+ return false ;
60+ }
61+
62+ MatchLiteralColon ( processor , ref slice ) ;
63+ return true ;
64+ }
65+
5466 // If we have not a delimiter on the first line of a paragraph, don't bother to continue
5567 // tracking other delimiters on following lines
5668 var tableState = processor . ParserStates [ Index ] as TableState ;
@@ -116,6 +128,142 @@ public override bool Match(InlineProcessor processor, ref StringSlice slice)
116128 return true ;
117129 }
118130
131+ private static bool IsHeaderSeparatorColonBeforePipe ( StringSlice slice )
132+ {
133+ if ( slice . PeekChar ( ) != '|' )
134+ {
135+ return false ;
136+ }
137+
138+ var text = slice . Text ;
139+ int currentLineStart = FindLineStart ( text , slice . Start ) ;
140+ int currentLineEnd = FindLineEnd ( text , slice . Start , slice . End ) ;
141+
142+ if ( ! IsHeaderSeparatorLine ( text , currentLineStart , currentLineEnd ) )
143+ {
144+ return false ;
145+ }
146+
147+ int previousLineStart = FindPreviousLineStart ( text , currentLineStart ) ;
148+ return previousLineStart >= 0 && LineHasPipe ( text , previousLineStart , currentLineStart - 1 ) ;
149+ }
150+
151+ private static void MatchLiteralColon ( InlineProcessor processor , ref StringSlice slice )
152+ {
153+ int position = processor . GetSourcePosition ( slice . Start , out int line , out int column ) ;
154+ if ( processor . Inline is LiteralInline previousLiteral
155+ && ReferenceEquals ( previousLiteral . Content . Text , slice . Text )
156+ && previousLiteral . Content . End + 1 == slice . Start )
157+ {
158+ previousLiteral . Content . End = slice . Start ;
159+ previousLiteral . Span . End = position ;
160+ processor . Inline = previousLiteral ;
161+ }
162+ else
163+ {
164+ processor . Inline = new LiteralInline
165+ {
166+ Content = new StringSlice ( slice . Text , slice . Start , slice . Start ) ,
167+ Span = new SourceSpan ( position , position ) ,
168+ Line = line ,
169+ Column = column ,
170+ } ;
171+ }
172+
173+ slice . SkipChar ( ) ;
174+ }
175+
176+ private static int FindLineStart ( string text , int position )
177+ {
178+ for ( int i = position - 1 ; i >= 0 ; i -- )
179+ {
180+ if ( text [ i ] == '\r ' || text [ i ] == '\n ' )
181+ {
182+ return i + 1 ;
183+ }
184+ }
185+
186+ return 0 ;
187+ }
188+
189+ private static int FindLineEnd ( string text , int position , int sliceEnd )
190+ {
191+ int end = Math . Min ( sliceEnd , text . Length - 1 ) ;
192+ for ( int i = position ; i <= end ; i ++ )
193+ {
194+ if ( text [ i ] == '\r ' || text [ i ] == '\n ' )
195+ {
196+ return i - 1 ;
197+ }
198+ }
199+
200+ return end ;
201+ }
202+
203+ private static int FindPreviousLineStart ( string text , int currentLineStart )
204+ {
205+ int previousLineEnd = currentLineStart - 2 ;
206+ if ( previousLineEnd >= 0 && text [ previousLineEnd ] == '\r ' )
207+ {
208+ previousLineEnd -- ;
209+ }
210+
211+ if ( previousLineEnd < 0 )
212+ {
213+ return - 1 ;
214+ }
215+
216+ for ( int i = previousLineEnd ; i >= 0 ; i -- )
217+ {
218+ if ( text [ i ] == '\r ' || text [ i ] == '\n ' )
219+ {
220+ return i + 1 ;
221+ }
222+ }
223+
224+ return 0 ;
225+ }
226+
227+ private static bool IsHeaderSeparatorLine ( string text , int start , int end )
228+ {
229+ bool hasPipe = false ;
230+ bool hasDash = false ;
231+
232+ for ( int i = start ; i <= end ; i ++ )
233+ {
234+ switch ( text [ i ] )
235+ {
236+ case '|' :
237+ hasPipe = true ;
238+ break ;
239+ case '-' :
240+ hasDash = true ;
241+ break ;
242+ case ':' :
243+ case ' ' :
244+ case '\t ' :
245+ break ;
246+ default :
247+ return false ;
248+ }
249+ }
250+
251+ return hasPipe && hasDash ;
252+ }
253+
254+ private static bool LineHasPipe ( string text , int start , int end )
255+ {
256+ for ( int i = start ; i <= end ; i ++ )
257+ {
258+ if ( text [ i ] == '|' )
259+ {
260+ return true ;
261+ }
262+ }
263+
264+ return false ;
265+ }
266+
119267 /// <summary>
120268 /// Performs the post process operation.
121269 /// </summary>
0 commit comments