@@ 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): |