summaryrefslogtreecommitdiff
path: root/IkiWiki/Plugin/highlight.pm
blob: e517ac5c06aa425771dcac7256a8e3127da8d3bb (plain)
  1. #!/usr/bin/perl
  2. package IkiWiki::Plugin::highlight;
  3. use warnings;
  4. use strict;
  5. use IkiWiki 3.00;
  6. use Encode;
  7. # locations of highlight's files
  8. my $filetypes="/etc/highlight/filetypes.conf";
  9. my $langdefdir="/usr/share/highlight/langDefs";
  10. sub import {
  11. hook(type => "getsetup", id => "highlight", call => \&getsetup);
  12. hook(type => "checkconfig", id => "highlight", call => \&checkconfig);
  13. # this hook is used by the format plugin
  14. hook(type => "htmlizefallback", id => "highlight", call =>
  15. \&htmlizefallback);
  16. }
  17. sub getsetup () {
  18. return
  19. plugin => {
  20. safe => 1,
  21. rebuild => 1, # format plugin
  22. section => "format",
  23. },
  24. tohighlight => {
  25. type => "string",
  26. example => ".c .h .cpp .pl .py Makefile:make",
  27. description => "types of source files to syntax highlight",
  28. safe => 1,
  29. rebuild => 1,
  30. },
  31. }
  32. sub checkconfig () {
  33. if (exists $config{tohighlight}) {
  34. foreach my $file (split ' ', $config{tohighlight}) {
  35. my @opts = $file=~s/^\.// ?
  36. (keepextension => 1) :
  37. (noextension => 1);
  38. my $ext = $file=~s/:(.*)// ? $1 : $file;
  39. my $langfile=ext2langfile($ext);
  40. if (! defined $langfile) {
  41. error(sprintf(gettext(
  42. "tohighlight contains unknown file type '%s'"),
  43. $ext));
  44. }
  45. hook(
  46. type => "htmlize",
  47. id => $file,
  48. call => sub {
  49. my %params=@_;
  50. highlight($langfile, $params{content});
  51. },
  52. longname => sprintf(gettext("Source code: %s"), $file),
  53. @opts,
  54. );
  55. }
  56. }
  57. }
  58. sub htmlizefallback {
  59. my $format=lc shift;
  60. my $langfile=ext2langfile($format);
  61. if (! defined $langfile) {
  62. return;
  63. }
  64. return Encode::decode_utf8(highlight($langfile, shift));
  65. }
  66. my %ext2lang;
  67. my $filetypes_read=0;
  68. my %highlighters;
  69. # Parse highlight's config file to get extension => language mappings.
  70. sub read_filetypes () {
  71. open (IN, $filetypes) || error("$filetypes: $!");
  72. while (<IN>) {
  73. chomp;
  74. if (/^\$ext\((.*)\)=(.*)$/) {
  75. $ext2lang{$_}=$1 foreach $1, split ' ', $2;
  76. }
  77. }
  78. close IN;
  79. $filetypes_read=1;
  80. }
  81. # Given a filename extension, determines the language definition to
  82. # use to highlight it.
  83. sub ext2langfile ($) {
  84. my $ext=shift;
  85. my $langfile="$langdefdir/$ext.lang";
  86. return $langfile if exists $highlighters{$langfile};
  87. read_filetypes() unless $filetypes_read;
  88. if (exists $ext2lang{$ext}) {
  89. return "$langdefdir/$ext2lang{$ext}.lang";
  90. }
  91. # If a language only has one common extension, it will not
  92. # be listed in filetypes, so check the langfile.
  93. elsif (-e $langfile) {
  94. return $langfile;
  95. }
  96. else {
  97. return undef;
  98. }
  99. }
  100. # Interface to the highlight C library.
  101. sub highlight ($$) {
  102. my $langfile=shift;
  103. my $input=shift;
  104. eval q{use highlight};
  105. if ($@) {
  106. print STDERR gettext("warning: highlight perl module not available; falling back to pass through");
  107. return $input;
  108. }
  109. my $gen;
  110. if (! exists $highlighters{$langfile}) {
  111. $gen = highlightc::CodeGenerator_getInstance($highlightc::XHTML);
  112. $gen->setFragmentCode(1); # generate html fragment
  113. $gen->setHTMLEnclosePreTag(1); # include stylish <pre>
  114. $gen->initTheme("/dev/null"); # theme is not needed because CSS is not emitted
  115. $gen->initLanguage($langfile); # must come after initTheme
  116. $gen->setEncoding("utf-8");
  117. $highlighters{$langfile}=$gen;
  118. }
  119. else {
  120. $gen=$highlighters{$langfile};
  121. }
  122. return $gen->generateString($input);
  123. }
  124. 1