markdown-rule.pl 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #! /usr/bin/perl
  2. # Copyright (C) 2014 Alex Schroeder <alex@gnu.org>
  3. # This program is free software: you can redistribute it and/or modify it under
  4. # the terms of the GNU General Public License as published by the Free Software
  5. # Foundation, either version 3 of the License, or (at your option) any later
  6. # version.
  7. #
  8. # This program is distributed in the hope that it will be useful, but WITHOUT
  9. # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  10. # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  11. #
  12. # You should have received a copy of the GNU General Public License along with
  13. # this program. If not, see <http://www.gnu.org/licenses/>.
  14. use strict;
  15. use v5.10;
  16. AddModuleDescription('markdown-rule.pl', 'Markdown Rule Extension');
  17. our ($q, $bol, %RuleOrder, @MyRules, $UrlProtocols, $FullUrlPattern);
  18. push(@MyRules, \&MarkdownRule);
  19. # Since we want this package to be a simple add-on, we try and avoid
  20. # all conflicts by going *last*. The use of # for numbered lists by
  21. # Usemod conflicts with the use of # for headings, for example.
  22. $RuleOrder{\&MarkdownRule} = 200;
  23. # http://daringfireball.net/projects/markdown/syntax
  24. # https://help.github.com/articles/markdown-basics
  25. # https://help.github.com/articles/github-flavored-markdown
  26. sub MarkdownRule {
  27. # atx headers
  28. if ($bol and m~\G(\s*\n)*(#{1,6})[ \t]*~cg) {
  29. my $header_depth = length($2);
  30. return CloseHtmlEnvironments()
  31. . AddHtmlEnvironment("h" . $header_depth);
  32. }
  33. # end atx header at a newline
  34. elsif ((InElement('h1') or InElement('h2') or InElement('h3') or
  35. InElement('h4') or InElement('h5') or InElement('h6'))
  36. and m/\G\n/cg) {
  37. return CloseHtmlEnvironments()
  38. . AddHtmlEnvironment("p");
  39. }
  40. # setext headers
  41. elsif ($bol and m/\G((\s*\n)*(.+?)[ \t]*\n(-+|=+)[ \t]*\n)/cg) {
  42. return CloseHtmlEnvironments()
  43. . (substr($4,0,1) eq '=' ? $q->h2($3) : $q->h3($3))
  44. . AddHtmlEnvironment('p');
  45. }
  46. # > blockquote
  47. # with continuation
  48. elsif ($bol and m/\G&gt;/cg) {
  49. return CloseHtmlEnvironments()
  50. . AddHtmlEnvironment('blockquote');
  51. }
  52. # ***bold and italic***
  53. elsif (not InElement('strong') and not InElement('em') and m/\G\*\*\*/cg) {
  54. return AddHtmlEnvironment('em') . AddHtmlEnvironment('strong');
  55. }
  56. # **bold**
  57. elsif (m/\G\*\*/cg) {
  58. return AddOrCloseHtmlEnvironment('strong');
  59. }
  60. # *italic*
  61. elsif (m/\G\*/cg) {
  62. return AddOrCloseHtmlEnvironment('em');
  63. }
  64. # ~~strikethrough~~ (deleted)
  65. elsif (m/\G~~/cg) {
  66. return AddOrCloseHtmlEnvironment('del');
  67. }
  68. # - bullet list
  69. elsif ($bol and m/\G(\s*\n)*-[ \t]*/cg
  70. or InElement('li') and m/\G(\s*\n)+-[ \t]*/cg) {
  71. return CloseHtmlEnvironment('li')
  72. . OpenHtmlEnvironment('ul',1) . AddHtmlEnvironment('li');
  73. }
  74. # 1. numbered list
  75. elsif ($bol and m/\G(\s*\n)*\d+\.[ \t]*/cg
  76. or InElement('li') and m/\G(\s*\n)+\d+\.[ \t]*/cg) {
  77. return CloseHtmlEnvironment('li')
  78. . OpenHtmlEnvironment('ol',1) . AddHtmlEnvironment('li');
  79. }
  80. # beginning of a table
  81. elsif ($bol and !InElement('table') and m/\G\|/cg) {
  82. # warn pos . " beginning of a table";
  83. return OpenHtmlEnvironment('table',1)
  84. . AddHtmlEnvironment('tr')
  85. . AddHtmlEnvironment('th');
  86. }
  87. # end of a row and beginning of a new row
  88. elsif (InElement('table') and m/\G\|?\n\|/cg) {
  89. # warn pos . " end of a row and beginning of a new row";
  90. return CloseHtmlEnvironment('tr')
  91. . AddHtmlEnvironment('tr')
  92. . AddHtmlEnvironment('td');
  93. }
  94. # otherwise the table ends
  95. elsif (InElement('table') and m/\G\|?(\n|$)/cg) {
  96. # warn pos . " otherwise the table ends";
  97. return CloseHtmlEnvironment('table')
  98. . AddHtmlEnvironment('p');
  99. }
  100. # continuation of the first row
  101. elsif (InElement('th') and m/\G\|/cg) {
  102. # warn pos . " continuation of the first row";
  103. return CloseHtmlEnvironment('th')
  104. . AddHtmlEnvironment('th');
  105. }
  106. # continuation of other rows
  107. elsif (InElement('td') and m/\G\|/cg) {
  108. # warn pos . " continuation of other rows";
  109. return CloseHtmlEnvironment('td')
  110. . AddHtmlEnvironment('td');
  111. }
  112. # whitespace indentation = code
  113. elsif ($bol and m/\G(\s*\n)*( .+)\n?/cg) {
  114. my $str = substr($2, 4);
  115. while (m/\G( .*)\n?/cg) {
  116. $str .= "\n" . substr($1, 4);
  117. }
  118. return OpenHtmlEnvironment('pre',1) . $str; # always level 1
  119. }
  120. # ``` = code
  121. elsif ($bol and m/\G```[ \t]*\n(.*?)\n```[ \t]*(\n|$)/cgs) {
  122. return CloseHtmlEnvironments() . $q->pre($1)
  123. . AddHtmlEnvironment("p");
  124. }
  125. # [an example](http://example.com/ "Title")
  126. elsif (m/\G\[(.+?)\]\($FullUrlPattern(\s+"(.+?)")?\)/cg) {
  127. my ($text, $url, $title) = ($1, $2, $4);
  128. $url =~ /^($UrlProtocols)/;
  129. my %params;
  130. $params{-href} = $url;
  131. $params{-class} = "url $1";
  132. $params{-title} = $title if $title;
  133. return $q->a(\%params, $text);
  134. }
  135. return;
  136. }