| @@ 504-676 (lines=173) @@ | ||
| 501 | ) |
|
| 502 | ||
| 503 | ||
| 504 | class InlineLexer(object): |
|
| 505 | """Inline level lexer for inline grammars.""" |
|
| 506 | grammar_class = InlineGrammar |
|
| 507 | ||
| 508 | default_rules = [ |
|
| 509 | 'escape', 'inline_html', 'autolink', 'url', |
|
| 510 | 'footnote', 'link', 'reflink', 'nolink', |
|
| 511 | 'double_emphasis', 'emphasis', 'code', |
|
| 512 | 'linebreak', 'strikethrough', 'text', |
|
| 513 | ] |
|
| 514 | inline_html_rules = [ |
|
| 515 | 'escape', 'inline_html', 'autolink', 'url', 'link', 'reflink', |
|
| 516 | 'nolink', 'double_emphasis', 'emphasis', 'code', |
|
| 517 | 'linebreak', 'strikethrough', 'text', |
|
| 518 | ] |
|
| 519 | ||
| 520 | def __init__(self, renderer, rules=None, **kwargs): |
|
| 521 | self.renderer = renderer |
|
| 522 | self.links = {} |
|
| 523 | self.footnotes = {} |
|
| 524 | self.footnote_index = 0 |
|
| 525 | ||
| 526 | if not rules: |
|
| 527 | rules = self.grammar_class() |
|
| 528 | ||
| 529 | kwargs.update(self.renderer.options) |
|
| 530 | if kwargs.get('hard_wrap'): |
|
| 531 | rules.hard_wrap() |
|
| 532 | ||
| 533 | self.rules = rules |
|
| 534 | ||
| 535 | self._in_link = False |
|
| 536 | self._in_footnote = False |
|
| 537 | self._parse_inline_html = kwargs.get('parse_inline_html') |
|
| 538 | ||
| 539 | def __call__(self, text, rules=None): |
|
| 540 | return self.output(text, rules) |
|
| 541 | ||
| 542 | def setup(self, links, footnotes): |
|
| 543 | self.footnote_index = 0 |
|
| 544 | self.links = links or {} |
|
| 545 | self.footnotes = footnotes or {} |
|
| 546 | ||
| 547 | def output(self, text, rules=None): |
|
| 548 | text = text.rstrip('\n') |
|
| 549 | if not rules: |
|
| 550 | rules = list(self.default_rules) |
|
| 551 | ||
| 552 | if self._in_footnote and 'footnote' in rules: |
|
| 553 | rules.remove('footnote') |
|
| 554 | ||
| 555 | output = self.renderer.placeholder() |
|
| 556 | ||
| 557 | def manipulate(text): |
|
| 558 | for key in rules: |
|
| 559 | pattern = getattr(self.rules, key) |
|
| 560 | m = pattern.match(text) |
|
| 561 | if not m: |
|
| 562 | continue |
|
| 563 | self.line_match = m |
|
| 564 | out = getattr(self, 'output_%s' % key)(m) |
|
| 565 | if out is not None: |
|
| 566 | return m, out |
|
| 567 | return False # pragma: no cover |
|
| 568 | ||
| 569 | while text: |
|
| 570 | ret = manipulate(text) |
|
| 571 | if ret is not False: |
|
| 572 | m, out = ret |
|
| 573 | output += out |
|
| 574 | text = text[len(m.group(0)):] |
|
| 575 | continue |
|
| 576 | if text: # pragma: no cover |
|
| 577 | raise RuntimeError('Infinite loop at: %s' % text) |
|
| 578 | ||
| 579 | return output |
|
| 580 | ||
| 581 | def output_escape(self, m): |
|
| 582 | text = m.group(1) |
|
| 583 | return self.renderer.escape(text) |
|
| 584 | ||
| 585 | def output_autolink(self, m): |
|
| 586 | link = m.group(1) |
|
| 587 | if m.group(2) == '@': |
|
| 588 | is_email = True |
|
| 589 | else: |
|
| 590 | is_email = False |
|
| 591 | return self.renderer.autolink(link, is_email) |
|
| 592 | ||
| 593 | def output_url(self, m): |
|
| 594 | link = m.group(1) |
|
| 595 | if self._in_link: |
|
| 596 | return self.renderer.text(link) |
|
| 597 | return self.renderer.autolink(link, False) |
|
| 598 | ||
| 599 | def output_inline_html(self, m): |
|
| 600 | tag = m.group(1) |
|
| 601 | if self._parse_inline_html and tag in _inline_tags: |
|
| 602 | text = m.group(3) |
|
| 603 | if tag == 'a': |
|
| 604 | self._in_link = True |
|
| 605 | text = self.output(text, rules=self.inline_html_rules) |
|
| 606 | self._in_link = False |
|
| 607 | else: |
|
| 608 | text = self.output(text, rules=self.inline_html_rules) |
|
| 609 | extra = m.group(2) or '' |
|
| 610 | html = '<%s%s>%s</%s>' % (tag, extra, text, tag) |
|
| 611 | else: |
|
| 612 | html = m.group(0) |
|
| 613 | return self.renderer.inline_html(html) |
|
| 614 | ||
| 615 | def output_footnote(self, m): |
|
| 616 | key = _keyify(m.group(1)) |
|
| 617 | if key not in self.footnotes: |
|
| 618 | return None |
|
| 619 | if self.footnotes[key]: |
|
| 620 | return None |
|
| 621 | self.footnote_index += 1 |
|
| 622 | self.footnotes[key] = self.footnote_index |
|
| 623 | return self.renderer.footnote_ref(key, self.footnote_index) |
|
| 624 | ||
| 625 | def output_link(self, m): |
|
| 626 | return self._process_link(m, m.group(3), m.group(4)) |
|
| 627 | ||
| 628 | def output_reflink(self, m): |
|
| 629 | key = _keyify(m.group(2) or m.group(1)) |
|
| 630 | if key not in self.links: |
|
| 631 | return None |
|
| 632 | ret = self.links[key] |
|
| 633 | return self._process_link(m, ret['link'], ret['title']) |
|
| 634 | ||
| 635 | def output_nolink(self, m): |
|
| 636 | key = _keyify(m.group(1)) |
|
| 637 | if key not in self.links: |
|
| 638 | return None |
|
| 639 | ret = self.links[key] |
|
| 640 | return self._process_link(m, ret['link'], ret['title']) |
|
| 641 | ||
| 642 | def _process_link(self, m, link, title=None): |
|
| 643 | line = m.group(0) |
|
| 644 | text = m.group(1) |
|
| 645 | if line[0] == '!': |
|
| 646 | return self.renderer.image(link, title, text) |
|
| 647 | ||
| 648 | self._in_link = True |
|
| 649 | text = self.output(text) |
|
| 650 | self._in_link = False |
|
| 651 | return self.renderer.link(link, title, text) |
|
| 652 | ||
| 653 | def output_double_emphasis(self, m): |
|
| 654 | text = m.group(2) or m.group(1) |
|
| 655 | text = self.output(text) |
|
| 656 | return self.renderer.double_emphasis(text) |
|
| 657 | ||
| 658 | def output_emphasis(self, m): |
|
| 659 | text = m.group(2) or m.group(1) |
|
| 660 | text = self.output(text) |
|
| 661 | return self.renderer.emphasis(text) |
|
| 662 | ||
| 663 | def output_code(self, m): |
|
| 664 | text = m.group(2) |
|
| 665 | return self.renderer.codespan(text) |
|
| 666 | ||
| 667 | def output_linebreak(self, m): |
|
| 668 | return self.renderer.linebreak() |
|
| 669 | ||
| 670 | def output_strikethrough(self, m): |
|
| 671 | text = self.output(m.group(1)) |
|
| 672 | return self.renderer.strikethrough(text) |
|
| 673 | ||
| 674 | def output_text(self, m): |
|
| 675 | text = m.group(0) |
|
| 676 | return self.renderer.text(text) |
|
| 677 | ||
| 678 | ||
| 679 | class Renderer(object): |
|
| @@ 482-653 (lines=172) @@ | ||
| 479 | ) |
|
| 480 | ||
| 481 | ||
| 482 | class InlineLexer(object): |
|
| 483 | """Inline level lexer for inline grammars.""" |
|
| 484 | grammar_class = InlineGrammar |
|
| 485 | ||
| 486 | default_rules = [ |
|
| 487 | 'escape', 'inline_html', 'autolink', 'url', |
|
| 488 | 'footnote', 'link', 'reflink', 'nolink', |
|
| 489 | 'double_emphasis', 'emphasis', 'code', |
|
| 490 | 'linebreak', 'strikethrough', 'text', |
|
| 491 | ] |
|
| 492 | inline_html_rules = [ |
|
| 493 | 'escape', 'autolink', 'url', 'link', 'reflink', |
|
| 494 | 'nolink', 'double_emphasis', 'emphasis', 'code', |
|
| 495 | 'linebreak', 'strikethrough', 'text', |
|
| 496 | ] |
|
| 497 | ||
| 498 | def __init__(self, renderer, rules=None, **kwargs): |
|
| 499 | self.renderer = renderer |
|
| 500 | self.links = {} |
|
| 501 | self.footnotes = {} |
|
| 502 | self.footnote_index = 0 |
|
| 503 | ||
| 504 | if not rules: |
|
| 505 | rules = self.grammar_class() |
|
| 506 | ||
| 507 | self.rules = rules |
|
| 508 | ||
| 509 | self._in_link = False |
|
| 510 | self._in_footnote = False |
|
| 511 | ||
| 512 | kwargs.update(self.renderer.options) |
|
| 513 | self._parse_inline_html = kwargs.get('parse_inline_html') |
|
| 514 | ||
| 515 | def __call__(self, text, rules=None): |
|
| 516 | return self.output(text, rules) |
|
| 517 | ||
| 518 | def setup(self, links, footnotes): |
|
| 519 | self.footnote_index = 0 |
|
| 520 | self.links = links or {} |
|
| 521 | self.footnotes = footnotes or {} |
|
| 522 | ||
| 523 | def output(self, text, rules=None): |
|
| 524 | text = text.rstrip('\n') |
|
| 525 | if not rules: |
|
| 526 | rules = list(self.default_rules) |
|
| 527 | ||
| 528 | if self._in_footnote and 'footnote' in rules: |
|
| 529 | rules.remove('footnote') |
|
| 530 | ||
| 531 | output = self.renderer.placeholder() |
|
| 532 | ||
| 533 | def manipulate(text): |
|
| 534 | for key in rules: |
|
| 535 | pattern = getattr(self.rules, key) |
|
| 536 | m = pattern.match(text) |
|
| 537 | if not m: |
|
| 538 | continue |
|
| 539 | self.line_match = m |
|
| 540 | out = getattr(self, 'output_%s' % key)(m) |
|
| 541 | if out is not None: |
|
| 542 | return m, out |
|
| 543 | return False # pragma: no cover |
|
| 544 | ||
| 545 | self.line_started = False |
|
| 546 | while text: |
|
| 547 | ret = manipulate(text) |
|
| 548 | self.line_started = True |
|
| 549 | if ret is not False: |
|
| 550 | m, out = ret |
|
| 551 | output += out |
|
| 552 | text = text[len(m.group(0)):] |
|
| 553 | continue |
|
| 554 | if text: # pragma: no cover |
|
| 555 | raise RuntimeError('Infinite loop at: %s' % text) |
|
| 556 | ||
| 557 | return output |
|
| 558 | ||
| 559 | def output_escape(self, m): |
|
| 560 | return m.group(1) |
|
| 561 | ||
| 562 | def output_autolink(self, m): |
|
| 563 | link = m.group(1) |
|
| 564 | if m.group(2) == '@': |
|
| 565 | is_email = True |
|
| 566 | else: |
|
| 567 | is_email = False |
|
| 568 | return self.renderer.autolink(link, is_email) |
|
| 569 | ||
| 570 | def output_url(self, m): |
|
| 571 | link = m.group(1) |
|
| 572 | if self._in_link: |
|
| 573 | return self.renderer.text(link) |
|
| 574 | return self.renderer.autolink(link, False) |
|
| 575 | ||
| 576 | def output_inline_html(self, m): |
|
| 577 | tag = m.group(1) |
|
| 578 | if self._parse_inline_html and tag in _inline_tags: |
|
| 579 | text = m.group(3) |
|
| 580 | if tag == 'a': |
|
| 581 | self._in_link = True |
|
| 582 | text = self.output(text, rules=self.inline_html_rules) |
|
| 583 | self._in_link = False |
|
| 584 | else: |
|
| 585 | text = self.output(text, rules=self.inline_html_rules) |
|
| 586 | extra = m.group(2) or '' |
|
| 587 | html = '<%s%s>%s</%s>' % (tag, extra, text, tag) |
|
| 588 | else: |
|
| 589 | html = m.group(0) |
|
| 590 | return self.renderer.inline_html(html) |
|
| 591 | ||
| 592 | def output_footnote(self, m): |
|
| 593 | key = _keyify(m.group(1)) |
|
| 594 | if key not in self.footnotes: |
|
| 595 | return None |
|
| 596 | if self.footnotes[key]: |
|
| 597 | return None |
|
| 598 | self.footnote_index += 1 |
|
| 599 | self.footnotes[key] = self.footnote_index |
|
| 600 | return self.renderer.footnote_ref(key, self.footnote_index) |
|
| 601 | ||
| 602 | def output_link(self, m): |
|
| 603 | return self._process_link(m, m.group(3), m.group(4)) |
|
| 604 | ||
| 605 | def output_reflink(self, m): |
|
| 606 | key = _keyify(m.group(2) or m.group(1)) |
|
| 607 | if key not in self.links: |
|
| 608 | return None |
|
| 609 | ret = self.links[key] |
|
| 610 | return self._process_link(m, ret['link'], ret['title']) |
|
| 611 | ||
| 612 | def output_nolink(self, m): |
|
| 613 | key = _keyify(m.group(1)) |
|
| 614 | if key not in self.links: |
|
| 615 | return None |
|
| 616 | ret = self.links[key] |
|
| 617 | return self._process_link(m, ret['link'], ret['title']) |
|
| 618 | ||
| 619 | def _process_link(self, m, link, title=None): |
|
| 620 | line = m.group(0) |
|
| 621 | text = m.group(1) |
|
| 622 | if line[0] == '!': |
|
| 623 | return self.renderer.image(link, title, text) |
|
| 624 | ||
| 625 | self._in_link = True |
|
| 626 | text = self.output(text) |
|
| 627 | self._in_link = False |
|
| 628 | return self.renderer.link(link, title, text) |
|
| 629 | ||
| 630 | def output_double_emphasis(self, m): |
|
| 631 | text = m.group(2) or m.group(1) |
|
| 632 | text = self.output(text) |
|
| 633 | return self.renderer.double_emphasis(text) |
|
| 634 | ||
| 635 | def output_emphasis(self, m): |
|
| 636 | text = m.group(2) or m.group(1) |
|
| 637 | text = self.output(text) |
|
| 638 | return self.renderer.emphasis(text) |
|
| 639 | ||
| 640 | def output_code(self, m): |
|
| 641 | text = m.group(2) |
|
| 642 | return self.renderer.codespan(text) |
|
| 643 | ||
| 644 | def output_linebreak(self, m): |
|
| 645 | return self.renderer.linebreak() |
|
| 646 | ||
| 647 | def output_strikethrough(self, m): |
|
| 648 | text = self.output(m.group(1)) |
|
| 649 | return self.renderer.strikethrough(text) |
|
| 650 | ||
| 651 | def output_text(self, m): |
|
| 652 | text = m.group(0) |
|
| 653 | return self.renderer.text(text) |
|
| 654 | ||
| 655 | ||
| 656 | class Renderer(object): |
|