/** * @param InlineParserContext $inlineContext * * @return bool */ public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); $ch = $cursor->getCharacter(); // Ellipses if ($ch === '.' && ($matched = $cursor->match('/^\\.( ?\\.)\\1/'))) { $inlineContext->getContainer()->appendChild(new Text('…')); return true; } elseif ($ch === '-' && ($matched = $cursor->match('/^(?<!-)(-{2,})/'))) { $count = strlen($matched); $en_dash = '–'; $en_count = 0; $em_dash = '—'; $em_count = 0; if ($count % 3 === 0) { // If divisible by 3, use all em dashes $em_count = $count / 3; } elseif ($count % 2 === 0) { // If divisible by 2, use all en dashes $en_count = $count / 2; } elseif ($count % 3 === 2) { // If 2 extra dashes, use en dash for last 2; em dashes for rest $em_count = ($count - 2) / 3; $en_count = 1; } else { // Use en dashes for last 4 hyphens; em dashes for rest $em_count = ($count - 4) / 3; $en_count = 2; } $inlineContext->getContainer()->appendChild(new Text(str_repeat($em_dash, $em_count) . str_repeat($en_dash, $en_count))); return true; } return false; }
/** * @param InlineParserContext $inlineContext * * @return bool */ public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); if ($m = $cursor->match(self::EMAIL_REGEX)) { $email = substr($m, 1, -1); $inlineContext->getContainer()->appendChild(new Link('mailto:' . UrlEncoder::unescapeAndEncode($email), $email)); return true; } elseif ($m = $cursor->match(self::OTHER_LINK_REGEX)) { $dest = substr($m, 1, -1); $inlineContext->getContainer()->appendChild(new Link(UrlEncoder::unescapeAndEncode($dest), $dest)); return true; } return false; }
public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); // The 'g/' prefix must not have any other characters immediately prior $previousChar = $cursor->peek(-1); $nextChar = $cursor->peek(1); if ($previousChar !== null && $previousChar !== ' ') { // peek() doesn't modify the cursor, so no need to restore state first return false; } if ($nextChar !== '/') { return false; } // Save the cursor state in case we need to rewind and bail $previousState = $cursor->saveState(); // Advance past the 'g/' prefix to keep parsing simpler $cursor->advanceBy(2); // Parse the handle $handle = $cursor->match('/^[A-Za-z0-9_]{1,32}(?!\\w)/'); if (empty($handle)) { // Regex failed to match; this isn't a valid username $cursor->restoreState($previousState); return false; } $group = Group::name($handle)->first(); if (!$group) { $cursor->restoreState($previousState); return false; } $groupUrl = route('group_contents', $group, false); $inlineContext->getContainer()->appendChild(new Link($groupUrl, 'g/' . $handle)); return true; }
/** * @param InlineParserContext $inlineContext * * @return bool */ public function parse(InlineParserContext $inlineContext) { $character = $inlineContext->getCursor()->getCharacter(); if (!in_array($character, $this->getCharacters())) { return false; } $numDelims = 0; $cursor = $inlineContext->getCursor(); $charBefore = $cursor->peek(-1); if ($charBefore === null) { $charBefore = "\n"; } while ($cursor->peek($numDelims) === $character) { ++$numDelims; } if ($numDelims === 0) { return false; } // Skip single delims if emphasis is disabled if ($numDelims === 1 && !$this->config->getConfig('enable_em')) { return false; } $cursor->advanceBy($numDelims); $charAfter = $cursor->getCharacter(); if ($charAfter === null) { $charAfter = "\n"; } list($canOpen, $canClose) = $this->determineCanOpenOrClose($charBefore, $charAfter, $character); $node = new Text($cursor->getPreviousText(), ['delim' => true, 'emphasis_config' => $this->config]); $inlineContext->getContainer()->appendChild($node); // Add entry to stack to this opener $delimiter = new Delimiter($character, $numDelims, $node, $canOpen, $canClose); $inlineContext->getDelimiterStack()->push($delimiter); return true; }
/** * Parse a line and determine if it contains an emoji. * * If it does, then we do the necessary. * * @param \League\CommonMark\InlineParserContext $inlineContext * * @return bool */ public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); $previous = $cursor->peek(-1); if ($previous !== null && $previous !== ' ') { return false; } $saved = $cursor->saveState(); $cursor->advance(); $handle = $cursor->match('/^[a-z0-9\\+\\-_]+:/'); if (!$handle) { $cursor->restoreState($saved); return false; } $next = $cursor->peek(0); if ($next !== null && $next !== ' ') { $cursor->restoreState($saved); return false; } $key = substr($handle, 0, -1); if (!array_key_exists($key, $this->map)) { $cursor->restoreState($saved); return false; } $inline = new Image($this->map[$key], $key); $inline->data['attributes'] = ['class' => 'emoji', 'data-emoji' => $key]; $inlineContext->getContainer()->appendChild($inline); return true; }
/** * @param InlineParserContext $inlineContext * * @return bool */ public function parse(InlineParserContext $inlineContext) { $character = $inlineContext->getCursor()->getCharacter(); if (in_array($character, $this->double)) { $character = '“'; } elseif (in_array($character, $this->single)) { $character = '’'; } else { return false; } $cursor = $inlineContext->getCursor(); $charBefore = $cursor->peek(-1); if ($charBefore === null) { $charBefore = "\n"; } $cursor->advance(); $charAfter = $cursor->getCharacter(); if ($charAfter === null) { $charAfter = "\n"; } $afterIsWhitespace = preg_match('/\\pZ|\\s/u', $charAfter); $afterIsPunctuation = preg_match(RegexHelper::REGEX_PUNCTUATION, $charAfter); $beforeIsWhitespace = preg_match('/\\pZ|\\s/u', $charBefore); $beforeIsPunctuation = preg_match(RegexHelper::REGEX_PUNCTUATION, $charBefore); $leftFlanking = !$afterIsWhitespace && !($afterIsPunctuation && !$beforeIsWhitespace && !$beforeIsPunctuation); $rightFlanking = !$beforeIsWhitespace && !($beforeIsPunctuation && !$afterIsWhitespace && !$afterIsPunctuation); $canOpen = $leftFlanking && !$rightFlanking; $canClose = $rightFlanking; $node = new Text($character, ['delim' => true]); $inlineContext->getContainer()->appendChild($node); // Add entry to stack to this opener $inlineContext->getDelimiterStack()->push(new Delimiter($character, 1, $node, $canOpen, $canClose)); return true; }
/** * @param InlineParserContext $inlineContext * * @return bool */ public function parse(InlineParserContext $inlineContext) { if ($m = $inlineContext->getCursor()->match('/^' . RegexHelper::REGEX_ENTITY . '/i')) { $inlineContext->getContainer()->appendChild(new Text(Html5Entities::decodeEntity($m))); return true; } return false; }
/** * @param InlineParserContext $inlineContext * * @return bool */ public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); if ($m = $cursor->match(RegexHelper::getInstance()->getHtmlTagRegex())) { $inlineContext->getContainer()->appendChild(new HtmlInline($m)); return true; } return false; }
/** * @param InlineParserContext $inlineContext * * @return bool */ public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); if ($cursor->getCharacter() !== '\\') { return false; } $nextChar = $cursor->peek(); if ($nextChar === "\n") { $cursor->advanceBy(2); $inlineContext->getContainer()->appendChild(new Newline(Newline::HARDBREAK)); return true; } elseif ($nextChar !== null && preg_match('/' . RegexHelper::REGEX_ESCAPABLE . '/', $nextChar)) { $cursor->advanceBy(2); $inlineContext->getContainer()->appendChild(new Text($nextChar)); return true; } $cursor->advance(); $inlineContext->getContainer()->appendChild(new Text('\\')); return true; }
/** * @param InlineParserContext $inlineContext * * @return bool */ public function parse(InlineParserContext $inlineContext) { $inlineContext->getCursor()->advance(); // Check previous inline for trailing spaces $spaces = 0; $lastInline = $inlineContext->getContainer()->lastChild(); if ($lastInline && $lastInline instanceof Text) { $trimmed = rtrim($lastInline->getContent(), ' '); $spaces = strlen($lastInline->getContent()) - strlen($trimmed); if ($spaces) { $lastInline->setContent($trimmed); } } if ($spaces >= 2) { $inlineContext->getContainer()->appendChild(new Newline(Newline::HARDBREAK)); } else { $inlineContext->getContainer()->appendChild(new Newline(Newline::SOFTBREAK)); } return true; }
/** * @param InlineParserContext $inlineContext * * @return bool */ public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); $ticks = $cursor->match('/^`+/'); if ($ticks === '') { return false; } $previousState = $cursor->saveState(); while ($matchingTicks = $cursor->match('/`+/m')) { if ($matchingTicks === $ticks) { $code = mb_substr($cursor->getLine(), $previousState->getCurrentPosition(), $cursor->getPosition() - $previousState->getCurrentPosition() - strlen($ticks), 'utf-8'); $c = preg_replace(RegexHelper::REGEX_WHITESPACE, ' ', $code); $inlineContext->getContainer()->appendChild(new Code(trim($c))); return true; } } // If we got here, we didn't match a closing backtick sequence $cursor->restoreState($previousState); $inlineContext->getContainer()->appendChild(new Text($ticks)); return true; }
/** * @param InlineParserContext $inlineContext * * @return bool */ public function parse(InlineParserContext $inlineContext) { if ($inlineContext->getCursor()->getCharacter() !== '[') { return false; } $inlineContext->getCursor()->advance(); $node = new Text('[', ['delim' => true]); $inlineContext->getContainer()->appendChild($node); // Add entry to stack for this opener $delimiter = new Delimiter('[', 1, $node, true, false, $inlineContext->getCursor()->getPosition()); $inlineContext->getDelimiterStack()->push($delimiter); return true; }
/** * @param InlineParserContext $inlineContext * * @return bool */ public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); $delimiterStack = $inlineContext->getDelimiterStack(); if ($cursor->peek(-1) !== '[') { return false; } if ($cursor->peek(-2) == '!' || $cursor->peek(-1) !== '[' || $cursor->peek(1) !== ']' || $cursor->peek(2) !== ' ') { return false; } $status = $cursor->peek(0) == 'x'; $cursor->advanceBy(2); // Add entry to stack for this opener $delimiter = new Delimiter('[', 1, $inlineContext->getContainer()->firstChild(), true, false, 0); $delimiterStack->push($delimiter); $opener = $delimiterStack->searchByCharacter(['[']); $opener->getInlineNode()->replaceWith(new TaskListsCheckbox($status)); $delimiterStack->removeDelimiter($opener); return true; }
/** * @param InlineParserContext $inlineContext * * @return bool */ public function parse(InlineParserContext $inlineContext) { $character = $inlineContext->getCursor()->getCharacter(); if (!in_array($character, $this->getCharacters())) { return false; } $numDelims = 0; $cursor = $inlineContext->getCursor(); $charBefore = $cursor->peek(-1); if ($charBefore === null) { $charBefore = "\n"; } while ($cursor->peek($numDelims) === $character) { ++$numDelims; } $cursor->advanceBy($numDelims); $charAfter = $cursor->getCharacter(); if ($charAfter === null) { $charAfter = "\n"; } $afterIsWhitespace = preg_match('/\\pZ|\\s/u', $charAfter); $afterIsPunctuation = preg_match(RegexHelper::REGEX_PUNCTUATION, $charAfter); $beforeIsWhitespace = preg_match('/\\pZ|\\s/u', $charBefore); $beforeIsPunctuation = preg_match(RegexHelper::REGEX_PUNCTUATION, $charBefore); $leftFlanking = $numDelims > 0 && !$afterIsWhitespace && !($afterIsPunctuation && !$beforeIsWhitespace && !$beforeIsPunctuation); $rightFlanking = $numDelims > 0 && !$beforeIsWhitespace && !($beforeIsPunctuation && !$afterIsWhitespace && !$afterIsPunctuation); if ($character === '_') { $canOpen = $leftFlanking && (!$rightFlanking || $beforeIsPunctuation); $canClose = $rightFlanking && (!$leftFlanking || $afterIsPunctuation); } else { $canOpen = $leftFlanking; $canClose = $rightFlanking; } $node = new Text($cursor->getPreviousText(), ['delim' => true]); $inlineContext->getContainer()->appendChild($node); // Add entry to stack to this opener $delimiter = new Delimiter($character, $numDelims, $node, $canOpen, $canClose); $inlineContext->getDelimiterStack()->push($delimiter); return true; }
/** * @param InlineParserContext $inlineContext * * @return bool */ public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); $character = $this->getCharacterType($cursor->getCharacter()); $charBefore = $cursor->peek(-1); if ($charBefore === null) { $charBefore = "\n"; } $cursor->advance(); $charAfter = $cursor->getCharacter(); if ($charAfter === null) { $charAfter = "\n"; } list($leftFlanking, $rightFlanking) = $this->determineFlanking($charBefore, $charAfter); $canOpen = $leftFlanking && !$rightFlanking; $canClose = $rightFlanking; $node = new Text($character, ['delim' => true]); $inlineContext->getContainer()->appendChild($node); // Add entry to stack to this opener $inlineContext->getDelimiterStack()->push(new Delimiter($character, 1, $node, $canOpen, $canClose)); return true; }
public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); if ($cursor->getFirstNonSpaceCharacter() !== '{') { return false; } $char = $cursor->getCharacter(); if ('{' === $char) { $char = (string) $cursor->getCharacter($cursor->getPosition() - 1); } $attributes = AttributesUtils::parse($cursor); if (empty($attributes)) { return false; } if ('' === $char) { $cursor->advanceToFirstNonSpace(); } $node = new InlineAttributes($attributes, ' ' === $char || '' === $char); $inlineContext->getContainer()->appendChild($node); $inlineContext->getDelimiterStack()->push(new Delimiter('attributes', 1, $node, false, false)); return true; }
public function parse(InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); // The @ symbol must not have any other characters immediately prior $previousChar = $cursor->peek(-1); if ($previousChar !== null && $previousChar !== ' ') { // peek() doesn't modify the cursor, so no need to restore state first return false; } // Save the cursor state in case we need to rewind and bail $previousState = $cursor->saveState(); // Advance past the @ symbol to keep parsing simpler $cursor->advance(); // Parse the handle $handle = $cursor->match('/^[A-Za-z0-9_]{1,15}(?!\\w)/'); if (empty($handle)) { // Regex failed to match; this isn't a valid Twitter handle $cursor->restoreState($previousState); return false; } $profileUrl = 'https://twitter.com/' . $handle; $inlineContext->getContainer()->appendChild(new Link($profileUrl, '@' . $handle)); return true; }