diff --git a/src/Markdig.Tests/TestSourcePosition.cs b/src/Markdig.Tests/TestSourcePosition.cs index 27212653..eed7139a 100644 --- a/src/Markdig.Tests/TestSourcePosition.cs +++ b/src/Markdig.Tests/TestSourcePosition.cs @@ -834,6 +834,29 @@ literal ( 2, 2) 11-11 ", "pipetables"); } + [Test] + public void TestGridTable() + { + Check("0\n\n+-+-+\n|A|B|\n+=+=+\n|C|D|\n+-+-+", @" +paragraph ( 0, 0) 0-0 +literal ( 0, 0) 0-0 +table ( 2, 0) 3-31 +tablerow ( 3, 0) 9-13 +tablecell ( 3, 0) 9-11 +paragraph ( 3, 1) 10-10 +literal ( 3, 1) 10-10 +tablecell ( 3, 2) 11-13 +paragraph ( 3, 3) 12-12 +literal ( 3, 3) 12-12 +tablerow ( 5, 0) 21-25 +tablecell ( 5, 0) 21-23 +paragraph ( 5, 1) 22-22 +literal ( 5, 1) 22-22 +tablecell ( 5, 2) 23-25 +paragraph ( 5, 3) 24-24 +literal ( 5, 3) 24-24", "gridtables"); + } + [Test] public void TestIndentedCode() { diff --git a/src/Markdig/Extensions/Tables/GridTableParser.cs b/src/Markdig/Extensions/Tables/GridTableParser.cs index 847bd94c..30ce2c3d 100644 --- a/src/Markdig/Extensions/Tables/GridTableParser.cs +++ b/src/Markdig/Extensions/Tables/GridTableParser.cs @@ -1,10 +1,11 @@ // Copyright (c) Alexandre Mutel. All rights reserved. -// This file is licensed under the BSD-Clause 2 license. +// This file is licensed under the BSD-Clause 2 license. // See the license.txt file in the project root for more information. using Markdig.Helpers; using Markdig.Parsers; using Markdig.Syntax; +using System.Linq; namespace Markdig.Extensions.Tables; @@ -60,7 +61,12 @@ public class GridTableParser : BlockParser } // Store the line (if we need later to build a ParagraphBlock because the GridTable was in fact invalid) tableState.AddLine(ref processor.Line); - var table = new Table(this); + var table = new Table(this) + { + Line = processor.LineIndex, + Column = processor.Column, + Span = { Start = lineStart } + }; table.SetData(typeof(GridTableState), tableState); // Calculate the total width of all columns @@ -94,10 +100,12 @@ public class GridTableParser : BlockParser tableState.AddLine(ref processor.Line); if (processor.CurrentChar == '+') { + gridTable.UpdateSpanEnd(processor.Line.End); return HandleNewRow(processor, tableState, gridTable); } if (processor.CurrentChar == '|') { + gridTable.UpdateSpanEnd(processor.Line.End); return HandleContents(processor, tableState, gridTable); } TerminateCurrentRow(processor, tableState, gridTable, true); @@ -182,8 +190,18 @@ public class GridTableParser : BlockParser var columnSlice = columns[i]; if (columnSlice.CurrentCell != null) { - currentRow ??= new TableRow(); - + if (currentRow == null) + { + TableCell firstCell = columns.First(c => c.CurrentCell != null).CurrentCell!; + TableCell lastCell = columns.Last(c => c.CurrentCell != null).CurrentCell!; + + currentRow ??= new TableRow() + { + Span = new SourceSpan(firstCell.Span.Start, lastCell.Span.End), + Line = firstCell.Line + }; + } + // If this cell does not already belong to a row if (columnSlice.CurrentCell.Parent is null) { @@ -271,7 +289,10 @@ public class GridTableParser : BlockParser columnSlice.CurrentCell = new TableCell(this) { ColumnSpan = columnSlice.CurrentColumnSpan, - ColumnIndex = i + ColumnIndex = i, + Column = columnSlice.Start, + Line = processor.LineIndex, + Span = new SourceSpan(line.Start + columnSlice.Start, line.Start + columnSlice.End) }; columnSlice.BlockProcessor ??= processor.CreateChild(); @@ -281,7 +302,8 @@ public class GridTableParser : BlockParser } // Process the content of the cell columnSlice.BlockProcessor!.LineIndex = processor.LineIndex; - columnSlice.BlockProcessor.ProcessLine(sliceForCell); + + columnSlice.BlockProcessor.ProcessLinePart(sliceForCell, sliceForCell.Start - line.Start); } // Go to next column diff --git a/src/Markdig/Parsers/BlockProcessor.cs b/src/Markdig/Parsers/BlockProcessor.cs index 225ed971..39f03d3f 100644 --- a/src/Markdig/Parsers/BlockProcessor.cs +++ b/src/Markdig/Parsers/BlockProcessor.cs @@ -493,8 +493,34 @@ public class BlockProcessor ContinueProcessingLine = true; - ResetLine(newLine); + ResetLine(newLine, 0); + Process(); + + LineIndex++; + } + + /// + /// Processes part of a line. + /// + /// The line. + /// The column. + public void ProcessLinePart(StringSlice line, int column) + { + CurrentLineStartPosition = line.Start - column; + + ContinueProcessingLine = true; + + ResetLine(line, column); + + Process(); + } + + /// + /// Process current string slice. + /// + private void Process() + { TryContinueBlocks(); // If the line was not entirely processed by pending blocks, try to process it with any new block @@ -502,8 +528,6 @@ public class BlockProcessor // Close blocks that are no longer opened CloseAll(false); - - LineIndex++; } internal bool IsOpen(Block block) @@ -956,18 +980,17 @@ public class BlockProcessor ContinueProcessingLine = !result.IsDiscard(); } - private void ResetLine(StringSlice newLine) + private void ResetLine(StringSlice newLine, int column) { Line = newLine; - Column = 0; + Column = column; ColumnBeforeIndent = 0; StartBeforeIndent = Start; - originalLineStart = newLine.Start; + originalLineStart = newLine.Start - column; TriviaStart = newLine.Start; } - [MemberNotNull(nameof(Document), nameof(Parsers))] internal void Setup(MarkdownDocument document, BlockParserList parsers, MarkdownParserContext? context, bool trackTrivia) {