/** * @param ContextInterface $context * @param InlineParserContext $inline_context * * @return bool */ public function parse(ContextInterface $context, InlineParserContext $inline_context) { $cursor = $inline_context->getCursor(); $character = $cursor->getCharacter(); if ($cursor->peek(1) !== $character) { return false; } $tildes = $cursor->match('/^~~+/'); if ($tildes === '') { return false; } $previous_state = $cursor->saveState(); while ($matching_tildes = $cursor->match('/~~+/m')) { if ($matching_tildes === $tildes) { $text = mb_substr($cursor->getLine(), $previous_state->getCurrentPosition(), $cursor->getPosition() - $previous_state->getCurrentPosition() - strlen($tildes), 'utf-8'); $text = preg_replace('/[ \\n]+/', ' ', $text); $inline_context->getInlines()->add(new Strikethrough(trim($text))); return true; } } // If we got here, we didn't match a closing tilde pair sequence $cursor->restoreState($previous_state); $inline_context->getInlines()->add(new Text($tildes)); return true; }
/** * @param ContextInterface $context * @param InlineParserContext $inlineContext * * @return bool */ public function parse(ContextInterface $context, InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); $ch = $cursor->getCharacter(); // Ellipses if ($ch === '.' && ($matched = $cursor->match('/^\\.( ?\\.)\\1/'))) { $inlineContext->getInlines()->add(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->getInlines()->add(new Text(str_repeat($em_dash, $em_count) . str_repeat($en_dash, $en_count))); return true; } return false; }
/** * @param ContextInterface $context * @param InlineParserContext $inlineContext * * @return bool */ public function parse(ContextInterface $context, 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"; } $leftFlanking = $numDelims > 0 && !preg_match('/\\pZ|\\s/u', $charAfter) && !(preg_match(RegexHelper::REGEX_PUNCTUATION, $charAfter) && !preg_match('/\\pZ|\\s/u', $charBefore) && !preg_match(RegexHelper::REGEX_PUNCTUATION, $charBefore)); $rightFlanking = $numDelims > 0 && !preg_match('/\\pZ|\\s/u', $charBefore) && !(preg_match(RegexHelper::REGEX_PUNCTUATION, $charBefore) && !preg_match('/\\pZ|\\s/u', $charAfter) && !preg_match(RegexHelper::REGEX_PUNCTUATION, $charAfter)); if ($character === '_') { $canOpen = $leftFlanking && (!$rightFlanking || preg_match(RegexHelper::REGEX_PUNCTUATION, $charBefore)); $canClose = $rightFlanking && (!$leftFlanking || preg_match(RegexHelper::REGEX_PUNCTUATION, $charAfter)); } else { $canOpen = $leftFlanking; $canClose = $rightFlanking; } $inlineContext->getInlines()->add(new Text($cursor->getPreviousText(), array('delim' => true))); // Add entry to stack to this opener $delimiter = new Delimiter($character, $numDelims, $inlineContext->getInlines()->count() - 1, $canOpen, $canClose); $inlineContext->getDelimiterStack()->push($delimiter); return true; }
/** * @param ContextInterface $context * @param InlineParserContext $inlineContext * * @return bool */ public function parse(ContextInterface $context, 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 && ($character !== '’' || !$rightFlanking); $canClose = $rightFlanking; $inlineContext->getInlines()->add(new Text($character, ['delim' => true])); // Add entry to stack to this opener $delimiter = new Delimiter($character, 1, $inlineContext->getInlines()->count() - 1, $canOpen, $canClose); $inlineContext->getDelimiterStack()->push($delimiter); return true; }
/** * @param ContextInterface $context * @param InlineParserContext $inlineContext * * @return bool */ public function parse(ContextInterface $context, InlineParserContext $inlineContext) { if ($inlineContext->getCursor()->getCharacter() !== '[') { return false; } $inlineContext->getCursor()->advance(); $inlineContext->getInlines()->add(new Text('[', ['delim' => true])); // Add entry to stack for this opener $delimiter = new Delimiter('[', 1, $inlineContext->getInlines()->count() - 1, true, false, $inlineContext->getCursor()->getPosition()); $inlineContext->getDelimiterStack()->push($delimiter); return true; }
/** * @param ContextInterface $context * @param InlineParserContext $inlineContext * * @return bool */ public function parse(ContextInterface $context, InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); if ($cursor->peek() === '[') { $cursor->advanceBy(2); $inlineContext->getInlines()->add(new Text('![', array('delim' => true))); // Add entry to stack for this opener $delimiter = new Delimiter('!', 1, $inlineContext->getInlines()->count() - 1, true, false, $cursor->getPosition()); $inlineContext->getDelimiterStack()->push($delimiter); return true; } return false; }
/** * @param ContextInterface $context * @param InlineParserContext $inlineContext * * @return bool */ public function parse(ContextInterface $context, InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); if ($m = $cursor->match(self::EMAIL_REGEX)) { $email = substr($m, 1, -1); $inlineContext->getInlines()->add(new Link('mailto:' . UrlEncoder::unescapeAndEncode($email), $email)); return true; } elseif ($m = $cursor->match(self::OTHER_LINK_REGEX)) { $dest = substr($m, 1, -1); $inlineContext->getInlines()->add(new Link(UrlEncoder::unescapeAndEncode($dest), $dest)); return true; } return false; }
/** * Parse a line and determine if it contains an emoji. * * If it does, then we do the necessary. * * @return bool */ public function parse(ContextInterface $context, 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->getInlines()->add($inline); return true; }
/** * Parse @mentions from a string of text * https://github.com/thephpleague/commonmark/blob/gh-pages/customization/inline-parsing.md#example * * @param ContextInterface $context * @param InlineParserContext $inlineContext * @return bool */ public function parse(ContextInterface $context, InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); $previousChar = $cursor->peek(-1); if ($previousChar !== null && $previousChar !== ' ') { return false; } $previousState = $cursor->saveState(); $cursor->advance(); $handle = $cursor->match('/^\\w+/'); if (empty($handle)) { $cursor->restoreState($previousState); return false; } $userCache = new UserCache(); $usernames = $userCache->usernames(); $found = in_array($handle, $usernames, true); if (!$found) { $cursor->restoreState($previousState); return false; } $users = $userCache->users(); $userKey = $this->searchArray($users, $handle); $user = $users[$userKey]; $url = $user['url']; $inlineContext->getInlines()->add(new Link($url, '@' . $handle)); return true; }
/** * @param ContextInterface $context * @param InlineParserContext $inlineContext * * @return bool */ public function parse(ContextInterface $context, InlineParserContext $inlineContext) { if ($m = $inlineContext->getCursor()->match(RegexHelper::REGEX_ENTITY)) { $inlineContext->getInlines()->add(new Text(Html5Entities::decodeEntity($m))); return true; } return false; }
/** * @param ContextInterface $context * @param InlineParserContext $inlineContext * * @return bool */ public function parse(ContextInterface $context, InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); if ($m = $cursor->match(RegexHelper::getInstance()->getHtmlTagRegex())) { $inlineContext->getInlines()->add(new Html($m)); return true; } return false; }
/** * @param ContextInterface $context * @param InlineParserContext $inlineContext * @return bool */ public function parse(ContextInterface $context, InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); if ($cursor->getCharacter() !== '\\') { return false; } $nextChar = $cursor->peek(); if ($nextChar === "\n") { $cursor->advanceBy(2); $inlineContext->getInlines()->add(new Newline(Newline::HARDBREAK)); return true; } elseif ($nextChar !== null && preg_match('/' . RegexHelper::REGEX_ESCAPABLE . '/', $nextChar)) { $cursor->advanceBy(2); $inlineContext->getInlines()->add(new Text($nextChar)); return true; } $cursor->advance(); $inlineContext->getInlines()->add(new Text('\\')); return true; }
/** * @param ContextInterface $context * @param InlineParserContext $inlineContext * * @return bool */ public function parse(ContextInterface $context, InlineParserContext $inlineContext) { $inlineContext->getCursor()->advance(); // Check previous inline for trailing spaces $spaces = 0; $lastInline = $inlineContext->getInlines()->last(); if ($lastInline && $lastInline instanceof Text) { $trimmed = rtrim($lastInline->getContent(), ' '); $spaces = strlen($lastInline->getContent()) - strlen($trimmed); if ($spaces) { $lastInline->setContent($trimmed); } } if ($spaces >= 2) { $inlineContext->getInlines()->add(new Newline(Newline::HARDBREAK)); } else { $inlineContext->getInlines()->add(new Newline(Newline::SOFTBREAK)); } return true; }
/** * @param ContextInterface $context * @param InlineParserContext $inlineContext * * @return bool */ public function parse(ContextInterface $context, InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); $ticks = $cursor->match('/^`+/'); if ($ticks === '') { return false; } $previousState = $cursor->saveState(); while ($matchingTicks = $cursor->match('/`+/m')) { if ($matchingTicks === $ticks) { $code = substr($cursor->getLine(), $previousState->getCurrentPosition(), $cursor->getPosition() - $previousState->getCurrentPosition() - strlen($ticks)); $c = preg_replace('/[ \\n]+/', ' ', $code); $inlineContext->getInlines()->add(new Code(trim($c))); return true; } } // If we got here, we didn't match a closing backtick sequence $cursor->restoreState($previousState); $inlineContext->getInlines()->add(new Text($ticks)); return true; }
/** * @param ContextInterface $context * @param InlineParserContext $inlineContext * * @return bool */ public function parse(ContextInterface $context, InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); $startPos = $cursor->getPosition(); $previousState = $cursor->saveState(); // Look through stack of delimiters for a [ or ! $opener = $inlineContext->getDelimiterStack()->searchByCharacter(['[', '!']); if ($opener === null) { return false; } if (!$opener->isActive()) { // no matched opener; remove from emphasis stack $inlineContext->getDelimiterStack()->removeDelimiter($opener); return false; } $isImage = $opener->getChar() === '!'; // Instead of copying a slice, we null out the parts of inlines that don't correspond to linkText; later, we'll // collapse them. This is awkward, and could be simplified if we made inlines a linked list instead $inlines = $inlineContext->getInlines(); $labelInlines = new ArrayCollection($inlines->toArray()); $this->nullify($labelInlines, 0, $opener->getPos() + 1); $cursor->advance(); // Check to see if we have a link/image if (!($link = $this->tryParseLink($cursor, $context->getDocument()->getReferenceMap(), $opener, $startPos))) { // No match $inlineContext->getDelimiterStack()->removeDelimiter($opener); // Remove this opener from stack $cursor->restoreState($previousState); return false; } $delimiterStack = $inlineContext->getDelimiterStack(); $stackBottom = $opener->getPrevious(); foreach ($this->environment->getInlineProcessors() as $inlineProcessor) { $inlineProcessor->processInlines($labelInlines, $delimiterStack, $stackBottom); } if ($delimiterStack instanceof DelimiterStack) { $delimiterStack->removeAll($stackBottom); } // Remove the part of inlines that become link_text $this->nullify($inlines, $opener->getPos(), $inlines->count()); // processEmphasis will remove this and later delimiters. // Now, for a link, we also remove earlier link openers (no links in links) if (!$isImage) { $inlineContext->getDelimiterStack()->removeEarlierMatches('['); } $inlines->add($this->createInline($link['url'], $labelInlines, $link['title'], $isImage)); return true; }
public function parse(ContextInterface $context, 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; } $inlineContext->getInlines()->add(new InlineAttributes($attributes, ' ' === $char || '' === $char)); return true; }
/** * @param string $character * @param InlineParserContext $inlineParserContext */ private function addPlainText($character, InlineParserContext $inlineParserContext) { // We reach here if none of the parsers can handle the input // Attempt to match multiple non-special characters at once $text = $inlineParserContext->getCursor()->match($this->environment->getInlineParserCharacterRegex()); // This might fail if we're currently at a special character which wasn't parsed; if so, just add that character if ($text === null) { $inlineParserContext->getCursor()->advance(); $text = $character; } $lastInline = $inlineParserContext->getInlines()->last(); if ($lastInline instanceof Text && !isset($lastInline->data['delim'])) { $lastInline->append($text); } else { $inlineParserContext->getInlines()->add(new Text($text)); } }
/** * {@inheritdoc} */ public function parse(ContextInterface $context, InlineParserContext $inline_context) { $cursor = $inline_context->getCursor(); // The @ symbol must not have any other characters immediately prior. $previous_char = $cursor->peek(-1); if ($previous_char !== NULL && $previous_char !== ' ') { // 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. $previous_state = $cursor->saveState(); // Advance past the @ symbol to keep parsing simpler. $cursor->advance(); // Parse the handle. $text = $cursor->match('/^[^\\s]+/'); $url = ''; $title = ''; $type = $this->getSetting('type'); if ($type === 'user') { $user = user_load_by_name($text); if ((!$user || !$user->uid) && is_numeric($text)) { $user = user_load((int) $text); } if ($user && $user->uid) { $url = url("user/{$user->uid}", ['absolute' => TRUE]); $title = t('View user profile.'); if ($this->getSetting('format_username')) { $text = format_username($user); } } else { $text = FALSE; } } elseif ($type === 'url' && ($url = $this->getSetting('url')) && strpos($url, '[text]') !== FALSE) { $url = str_replace('[text]', $text, $url); } else { $text = FALSE; } // Regex failed to match; this isn't a valid @ handle. if (empty($text) || empty($url)) { $cursor->restoreState($previous_state); return FALSE; } $inline_context->getInlines()->add(new Link($url, '@' . $text, $title)); return TRUE; }
/** * Parse @mentions from a string of text * https://github.com/thephpleague/commonmark/blob/gh-pages/customization/inline-parsing.md#example * * @param ContextInterface $context * @param InlineParserContext $inlineContext * @return bool */ public function parse(ContextInterface $context, InlineParserContext $inlineContext) { $cursor = $inlineContext->getCursor(); $previousChar = $cursor->peek(-1); if ($previousChar !== null && $previousChar !== ' ') { return false; } $previousState = $cursor->saveState(); $cursor->advance(); $handle = $cursor->match('/^\\w+/'); if (empty($handle)) { $cursor->restoreState($previousState); return false; } $profileUrl = 'https://cribbb.com/' . $handle; $inlineContext->getInlines()->add(new Link($profileUrl, '@' . $handle)); return true; }
/** * {@inheritdoc} */ public function parse(ContextInterface $context, InlineParserContext $inline_context) { $cursor = $inline_context->getCursor(); // The # symbol must not have any other characters immediately prior. $previous_char = $cursor->peek(-1); if ($previous_char !== NULL && $previous_char !== ' ' && $previous_char !== '[') { // 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. $previous_state = $cursor->saveState(); // Advance past the # symbol to keep parsing simpler. $cursor->advance(); // Parse the handle. $text = $cursor->match('/^[^\\s\\]]+/'); $url = FALSE; $title = FALSE; $type = $this->getSetting('type'); if ($type === 'node' && is_numeric($text) && ($node = node_load($text))) { $url = url("node/{$node->nid}", ['absolute' => TRUE]); if ($this->getSetting('node_title') && !empty($node->title)) { $text = $node->title; } else { $text = "#{$text}"; } } elseif ($type === 'url' && ($url = $this->getSetting('url')) && strpos($url, '[text]') !== FALSE) { $url = str_replace('[text]', $text, $url); if ($this->getSetting('url_title') && ($title = $this->getUrlTitle($url))) { $text = $title; $title = FALSE; } } else { $text = FALSE; } // Regex failed to match; this isn't a valid @ handle. if (empty($text) || empty($url)) { $cursor->restoreState($previous_state); return FALSE; } $inline_context->getInlines()->add(new Link($url, $text, $title)); return TRUE; }
public function parse(ContextInterface $context, 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('/^\\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->getInlines()->add(new Link($profileUrl, '@' . $handle)); return true; }
/** * @param \League\CommonMark\Inline\Element\AbstractWebResource $element * @return bool */ protected function addElement($element) { $this->inlineContext->getInlines()->add($element); return true; }