Skip to content

Commit

Permalink
Fix tab handling
Browse files Browse the repository at this point in the history
This commit mirrors several changes from jgm/commonmark.js:

 - Fix tabs in list indentation (commonmark/commonmark.js#86)
 - Fix handling of partially-consumed tabs
 - Proper tab handling with blockquotes, fenced code, lists
  • Loading branch information
colinodell committed Mar 27, 2016
1 parent b3198bb commit 0c816dd
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 33 deletions.
4 changes: 1 addition & 3 deletions src/Block/Element/BlockQuote.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,7 @@ public function matchesNextLine(Cursor $cursor)
if (!$cursor->isIndented() && $cursor->getFirstNonSpaceCharacter() === '>') {
$cursor->advanceToFirstNonSpace();
$cursor->advance();
if ($cursor->getCharacter() === ' ') {
$cursor->advance();
}
$cursor->advanceBySpaceOrTab();

return true;
}
Expand Down
4 changes: 1 addition & 3 deletions src/Block/Parser/BlockQuoteParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,7 @@ public function parse(ContextInterface $context, Cursor $cursor)

$cursor->advanceToFirstNonSpace();
$cursor->advance();
if ($cursor->getCharacter() === ' ') {
$cursor->advance();
}
$cursor->advanceBySpaceOrTab();

$context->addBlock(new BlockQuote());

Expand Down
13 changes: 6 additions & 7 deletions src/Block/Parser/ListParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,19 +91,18 @@ private function calculateListMarkerPadding(Cursor $cursor, $markerLength)
$start = $cursor->saveState();
$spacesStartCol = $cursor->getColumn();

do {
$cursor->advanceBy(1, true);
$nextChar = $cursor->getCharacter();
} while ($cursor->getColumn() - $spacesStartCol < 5 && ($nextChar === ' ' || $nextChar === "\t"));
while ($cursor->getColumn() - $spacesStartCol < 5) {
if (!$cursor->advanceBySpaceOrTab()) {
break;
}
}

$blankItem = $cursor->peek() === null;
$spacesAfterMarker = $cursor->getColumn() - $spacesStartCol;

if ($spacesAfterMarker >= 5 || $spacesAfterMarker < 1 || $blankItem) {
$cursor->restoreState($start);
if ($cursor->peek() === ' ') {
$cursor->advanceBy(1, true);
}
$cursor->advanceBySpaceOrTab();

return $markerLength + 1;
}
Expand Down
59 changes: 42 additions & 17 deletions src/Cursor.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ class Cursor
*/
private $firstNonSpaceCache;

/**
* @var bool
*/
private $partiallyConsumedTab = false;

/**
* @param string $line
*/
Expand Down Expand Up @@ -186,30 +191,42 @@ public function advanceBy($characters, $advanceByColumns = false)
return;
}

$this->previousPosition = $this->currentPosition;
$this->firstNonSpaceCache = null;

$i = 0;
$cols = 0;
while ($advanceByColumns ? ($cols < $characters) : ($i < $characters)) {
if ($this->peek($i) === "\t") {
$cols += (4 - (($this->column + $cols) % 4));
while ($characters > 0 && ($c = $this->getCharacter()) !== null) {
if ($c === "\t") {
$charsToTab = 4 - ($this->column % 4);
$this->partiallyConsumedTab = $advanceByColumns && $charsToTab > $characters;
$charsToAdvance = $charsToTab > $characters ? $characters : $charsToTab;
$this->column += $charsToAdvance;
$this->currentPosition += $this->partiallyConsumedTab ? 0 : 1;
$characters -= ($advanceByColumns ? $charsToAdvance : 1);
} else {
$cols++;
$this->partiallyConsumedTab = false;
$this->currentPosition++;
$this->column++;
$characters--;
}

$i++;
}
}

$this->previousPosition = $this->currentPosition;
$newPosition = $this->currentPosition + $i;
/**
* Advances the cursor by a single space or tab, if present
*
* @return bool
*/
public function advanceBySpaceOrTab()
{
$character = $this->getCharacter();

$this->column += $cols;
if ($character === ' ' || $character === "\t") {
$this->advanceBy(1, true);

if ($newPosition >= $this->length) {
$this->currentPosition = $this->length;
} else {
$this->currentPosition = $newPosition;
return true;
}

return false;
}

/**
Expand Down Expand Up @@ -274,9 +291,17 @@ public function getRemainder()
{
if ($this->isAtEnd()) {
return '';
} else {
return mb_substr($this->line, $this->currentPosition, $this->length, 'utf-8');
}

$prefix = '';
$position = $this->currentPosition;
if ($this->partiallyConsumedTab) {
$position++;
$charsToTab = 4 - ($this->column % 4);
$prefix = str_repeat(' ', $charsToTab);
}

return $prefix . mb_substr($this->line, $position, $this->length, 'utf-8');
}

/**
Expand Down
11 changes: 8 additions & 3 deletions tests/unit/CursorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,16 @@ public function testAdvanceByColumnOffset()
{
$cursor = new Cursor("1. \t\tthere");
$cursor->advanceBy(3);

$this->assertEquals(5, $cursor->getIndent());
$this->assertEquals(3, $cursor->getPosition());
$this->assertEquals(3, $cursor->getColumn());

$cursor->advanceBy(4, true);

$this->assertEquals(0, $cursor->getIndent());
$this->assertEquals(5, $cursor->getPosition());
$this->assertEquals(8, $cursor->getColumn());
$this->assertEquals(1, $cursor->getIndent());
$this->assertEquals(4, $cursor->getPosition());
$this->assertEquals(7, $cursor->getColumn());
}

/**
Expand Down

0 comments on commit 0c816dd

Please sign in to comment.