a technical notebook

render.pl 2.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. #!/usr/bin/env perl
  2. use strict;
  3. use warnings;
  4. use 5.10.0;
  5. use feature "state";
  6. use Cwd;
  7. use File::Basename;
  8. use HTML::Entities;
  9. use Text::Markdown::Discount;
  10. # Enable html5 block-level tags:
  11. Text::Markdown::Discount::with_html5_tags();
  12. my $flags = Text::Markdown::Discount::MKD_EXTRA_FOOTNOTE() | Text::Markdown::Discount::MKD_AUTOLINK();
  13. my $markdown = Text::Markdown::Discount->new;
  14. my $cwd = Cwd::getcwd();
  15. my $full_source = '';
  16. my $context = slurp("$cwd/context.md");
  17. while (my $source = get_input()) {
  18. # A simple preprocessor:
  19. my ($basename, $dir) = fileparse($ARGV); # get path of target file
  20. chdir $dir;
  21. $source =~ s{<!-- exec -->(.*?)<!-- end -->}{handle_block($1);}egs;
  22. chdir $cwd;
  23. # $context here is for stuff like links that should be available to any
  24. # markdown file that gets interpreted
  25. $full_source .= "\n\n<article>\n\n"
  26. . $markdown->markdown($context . $source, $flags)
  27. . "\n\n</article>\n\n";
  28. }
  29. print replace_some_stuff($full_source);
  30. sub get_input {
  31. local $/ = undef;
  32. my $source = <>;
  33. return $source;
  34. }
  35. sub handle_block {
  36. my ($block) = @_;
  37. my $cmd;
  38. if ($block =~ m/\$ (.*?)$/m) {
  39. $cmd = $1;
  40. } else {
  41. die "bogus cmd";
  42. }
  43. my $result = `$cmd`;
  44. # indent 4 spaces so we get a code block:
  45. $result =~ s/^/ /gm;
  46. return "<!-- exec -->\n\n \$ " . $cmd . "\n" . $result . "\n<!-- end -->";
  47. }
  48. # super cheeseball, man
  49. sub replace_some_stuff {
  50. my ($markup) = @_;
  51. my @contents;
  52. my $a_name_prefix_for_h2;
  53. # insert anchors in headers, accumulate a table of contents
  54. $markup =~ s{<(h[12])(.*?)>(.*?)</h[12]>}{
  55. my ($tag, $attributes, $text) = ($1, $2, $3);
  56. my $a_name = $text;
  57. $a_name =~ s/[^a-z0-9]+/-/ig;
  58. $a_name =~ s/^-|-$//g;
  59. if ($tag eq 'h1') {
  60. $a_name_prefix_for_h2 = $a_name;
  61. } else {
  62. $a_name = $a_name_prefix_for_h2 . '-' . $a_name;
  63. }
  64. push @contents, make_contents_link($tag, $a_name, $text);
  65. "<$tag$attributes><a name=$a_name href=#$a_name>#</a> $text</$tag>";
  66. }iesg;
  67. my $contents_text = '<div class=contents>'
  68. . $markdown->markdown((join "\n", @contents), $flags)
  69. . '</div>';
  70. $markup =~ s/{{contents}}/$contents_text/g;
  71. return $markup;
  72. }
  73. # make an individual element of a markdown list
  74. sub make_contents_link {
  75. my ($tag, $a_name, $text) = @_;
  76. if ($tag eq 'h2') {
  77. # indented bullet
  78. return " * [$text](#$a_name)";
  79. } elsif ($tag eq 'h1') {
  80. # top-level ordered-list item
  81. return "1. [$text](#$a_name)";
  82. }
  83. }
  84. # Get contents of a file as a string, because a File::Slurp dependency seemed
  85. # like overkill:
  86. sub slurp {
  87. my ($file) = @_;
  88. my $everything;
  89. open my $fh, '<', $file
  90. or die "Couldn't open $file: $!\n";
  91. # line separator:
  92. local $/ = undef;
  93. $everything = <$fh>;
  94. close $fh
  95. or die "Couldn't close $file: $!";
  96. return $everything;
  97. }