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