Repository: thephpleague/commonmark (php)

Issue:
lineStart in Table Extension does not yield the correct line
### Version(s) affected

"2.4.2"

### Description

Using the Table extension, the Table node does not report the correct startLine.

### How to reproduce

```

## Tabelle

| Datum | Programm | Ort |
| --- | --- | --- |
| 22. Mai | Anreise | Eichberg |
| 23. Mai | Programm | Eichberg |
| 24. Mai | Programm | Eichberg |
| 25. Mai | Programm | Eichberg |
```

startLine should be 4 but is 5
endLine should be 9 it is 9

Node after parsing is:

![2024-07-23 at 14 59 20_CleanShot_IntelliJ IDEA@2x](https://github.com/user-attachments/assets/96da3415-f281-4b1b-a7c0-43df7dd488d8)



### Possible solution

As of now I work around it in my renderer by subtracting 1 from the start line

### Additional context

_No response_

### Did this project help you today? Did it make you happy in any way?

oh yes ! it is a great project which helps to get full control over the Markdown processing

Pull request description:
Fix table start line numbers
Fixes #1037 where Tables (and other blocks that replace partially-parsed paragraphs) have incorrect start line numbers.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3c4787d7..30778b69 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ Updates should follow the [Keep a CHANGELOG](https://keepachangelog.com/) princi
 ### Fixed
 
 - Fixed attribute parsing incorrectly parsing mustache-like syntax (#1035)
+- Fixed incorrect `Table` start line numbers (#1037)
 
 ## [2.5.0] - 2024-07-22
 
diff --git a/src/Parser/MarkdownParser.php b/src/Parser/MarkdownParser.php
index 9d684dd2..2fecb9ba 100644
--- a/src/Parser/MarkdownParser.php
+++ b/src/Parser/MarkdownParser.php
@@ -158,12 +158,13 @@ final class MarkdownParser implements MarkdownParserInterface
                 $unmatchedBlocks = 0;
             }
 
+            $oldBlockLineStart = null;
             if ($blockStart->isReplaceActiveBlockParser()) {
-                $this->prepareActiveBlockParserForReplacement();
+                $oldBlockLineStart = $this->prepareActiveBlockParserForReplacement();
             }
 
             foreach ($blockStart->getBlockParsers() as $newBlockParser) {
-                $blockParser    = $this->addChild($newBlockParser);
+                $blockParser    = $this->addChild($newBlockParser, $oldBlockLineStart);
                 $tryBlockStarts = $newBlockParser->isContainer();
             }
         }
@@ -275,12 +276,12 @@ final class MarkdownParser implements MarkdownParserInterface
      * Add block of type tag as a child of the tip. If the tip can't accept children, close and finalize it and try
      * its parent, and so on til we find a block that can accept children.
      */
-    private function addChild(BlockContinueParserInterface $blockParser): BlockContinueParserInterface
+    private function addChild(BlockContinueParserInterface $blockParser, ?int $startLineNumber = null): BlockContinueParserInterface
     {
-        $blockParser->getBlock()->setStartLine($this->lineNumber);
+        $blockParser->getBlock()->setStartLine($startLineNumber ?? $this->lineNumber);
 
         while (! $this->getActiveBlockParser()->canContain($blockParser->getBlock())) {
-            $this->closeBlockParsers(1, $this->lineNumber - 1);
+            $this->closeBlockParsers(1, ($startLineNumber ?? $this->lineNumber) - 1);
         }
 
         $this->getActiveBlockParser()->getBlock()->appendChild($blockParser->getBlock());
@@ -307,7 +308,10 @@ final class MarkdownParser implements MarkdownParserInterface
         return $popped;
     }
 
-    private function prepareActiveBlockParserForReplacement(): void
+    /**
+     * @return int|null The line number where the old block started
+     */
+    private function prepareActiveBlockParserForReplacement(): ?int
     {
         // Note that we don't want to parse inlines or finalize this block, as it's getting replaced.
         $old = $this->deactivateBlockParser();
@@ -317,6 +321,8 @@ final class MarkdownParser implements MarkdownParserInterface
         }
 
         $old->getBlock()->detach();
+
+        return $old->getBlock()->getStartLine();
     }
 
     /**


Test patch:
diff --git a/tests/functional/Extension/Table/TableMarkdownTest.php b/tests/functional/Extension/Table/TableMarkdownTest.php
index c38ef0ce..8ddedc85 100644
--- a/tests/functional/Extension/Table/TableMarkdownTest.php
+++ b/tests/functional/Extension/Table/TableMarkdownTest.php
@@ -18,8 +18,10 @@ namespace League\CommonMark\Tests\Functional\Extension\Table;
 use League\CommonMark\ConverterInterface;
 use League\CommonMark\Environment\Environment;
 use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
+use League\CommonMark\Extension\Table\Table;
 use League\CommonMark\Extension\Table\TableExtension;
 use League\CommonMark\MarkdownConverter;
+use League\CommonMark\Parser\MarkdownParser;
 use League\CommonMark\Tests\Functional\AbstractLocalDataTestCase;
 
 /**
@@ -46,4 +48,29 @@ final class TableMarkdownTest extends AbstractLocalDataTestCase
     {
         yield from self::loadTests(__DIR__ . '/md');
     }
+
+    public function testStartEndLinesProperlySet(): void
+    {
+        $markdown = <<<MD
+
+## Tabelle
+
+| Datum | Programm | Ort |
+| --- | --- | --- |
+| 22. Mai | Anreise | Eichberg |
+| 23. Mai | Programm | Eichberg |
+MD;
+
+        $environment = new Environment([]);
+        $environment->addExtension(new CommonMarkCoreExtension());
+        $environment->addExtension(new TableExtension());
+
+        $parser = new MarkdownParser($environment);
+        $doc    = $parser->parse($markdown);
+
+        $table = $doc->lastChild();
+        $this->assertInstanceOf(Table::class, $table);
+        $this->assertSame(4, $table->getStartLine());
+        $this->assertSame(7, $table->getEndLine());
+    }
 }