summaryrefslogtreecommitdiff
path: root/IkiWiki/Plugin/highlight.pm
blob: 65e372db1336dbdedd6f1c5e256d4ef3b953aa2a (plain)
  1. #!/usr/bin/perl
  2. package IkiWiki::Plugin::highlight;
  3. # This has been tested with highlight 2.16 and highlight 3.2+svn19.
  4. # In particular version 3.2 won't work. It detects the different
  5. # versions by the presence of the the highlight::DataDir class.
  6. use warnings;
  7. use strict;
  8. use IkiWiki 3.00;
  9. use Encode;
  10. my $data_dir;
  11. sub import {
  12. hook(type => "getsetup", id => "highlight", call => \&getsetup);
  13. hook(type => "checkconfig", id => "highlight", call => \&checkconfig);
  14. # this hook is used by the format plugin
  15. hook(type => "htmlizeformat", id => "highlight",
  16. call => \&htmlizeformat, last => 1);
  17. }
  18. sub getsetup () {
  19. return
  20. plugin => {
  21. safe => 1,
  22. rebuild => 1, # format plugin
  23. section => "format",
  24. },
  25. tohighlight => {
  26. type => "string",
  27. example => ".c .h .cpp .pl .py Makefile:make",
  28. description => "types of source files to syntax highlight",
  29. safe => 1,
  30. rebuild => 1,
  31. },
  32. filetypes_conf => {
  33. type => "string",
  34. example => "/etc/highlight/filetypes.conf",
  35. description => "location of highlight's filetypes.conf",
  36. safe => 0,
  37. rebuild => undef,
  38. },
  39. langdefdir => {
  40. type => "string",
  41. example => "/usr/share/highlight/langDefs",
  42. description => "location of highlight's langDefs directory",
  43. safe => 0,
  44. rebuild => undef,
  45. },
  46. }
  47. sub checkconfig () {
  48. eval q{use highlight};
  49. if ($@) {
  50. print STDERR "Failed to load highlight. Configuring anyway.\n";
  51. };
  52. if (highlight::DataDir->can('new')){
  53. $data_dir=new highlight::DataDir();
  54. $data_dir->searchDataDir("");
  55. } else {
  56. $data_dir=undef;
  57. }
  58. if (! exists $config{filetypes_conf}) {
  59. $config{filetypes_conf}=
  60. ($data_dir ? $data_dir->getConfDir() : "/etc/highlight/")
  61. . "filetypes.conf";
  62. }
  63. if (! exists $config{langdefdir}) {
  64. $config{langdefdir}=
  65. ($data_dir ? $data_dir->getLangPath("")
  66. : "/usr/share/highlight/langDefs");
  67. }
  68. if (exists $config{tohighlight} && read_filetypes()) {
  69. foreach my $file (split ' ', $config{tohighlight}) {
  70. my @opts = $file=~s/^\.// ?
  71. (keepextension => 1) :
  72. (noextension => 1);
  73. my $ext = $file=~s/:(.*)// ? $1 : $file;
  74. my $langfile=ext2langfile($ext);
  75. if (! defined $langfile) {
  76. error(sprintf(gettext(
  77. "tohighlight contains unknown file type '%s'"),
  78. $ext));
  79. }
  80. hook(
  81. type => "htmlize",
  82. id => $file,
  83. call => sub {
  84. my %params=@_;
  85. highlight($langfile, $params{content});
  86. },
  87. longname => sprintf(gettext("Source code: %s"), $file),
  88. @opts,
  89. );
  90. }
  91. }
  92. }
  93. sub htmlizeformat {
  94. my $format=lc shift;
  95. my $langfile=ext2langfile($format);
  96. if (! defined $langfile) {
  97. return;
  98. }
  99. return Encode::decode_utf8(highlight($langfile, shift));
  100. }
  101. my %ext2lang;
  102. my $filetypes_read=0;
  103. my %highlighters;
  104. # Parse highlight's config file to get extension => language mappings.
  105. sub read_filetypes () {
  106. my $f;
  107. if (!open($f, $config{filetypes_conf})) {
  108. warn($config{filetypes_conf}.": ".$!);
  109. return 0;
  110. };
  111. local $/=undef;
  112. my $config=<$f>;
  113. close $f;
  114. # highlight >= 3.2 format (bind-style)
  115. while ($config=~m/Lang\s*=\s*\"([^"]+)\"[,\s]+Extensions\s*=\s*{([^}]+)}/sg) {
  116. my $lang=$1;
  117. foreach my $bit (split ',', $2) {
  118. $bit=~s/.*"(.*)".*/$1/s;
  119. $ext2lang{$bit}=$lang;
  120. }
  121. }
  122. # highlight < 3.2 format
  123. if (! keys %ext2lang) {
  124. foreach (split("\n", $config)) {
  125. if (/^\$ext\((.*)\)=(.*)$/) {
  126. $ext2lang{$_}=$1 foreach $1, split ' ', $2;
  127. }
  128. }
  129. }
  130. return $filetypes_read=1;
  131. }
  132. # Given a filename extension, determines the language definition to
  133. # use to highlight it.
  134. sub ext2langfile ($) {
  135. my $ext=shift;
  136. my $langfile="$config{langdefdir}/$ext.lang";
  137. return $langfile if exists $highlighters{$langfile};
  138. read_filetypes() unless $filetypes_read;
  139. if (exists $ext2lang{$ext}) {
  140. return "$config{langdefdir}/$ext2lang{$ext}.lang";
  141. }
  142. # If a language only has one common extension, it will not
  143. # be listed in filetypes, so check the langfile.
  144. elsif (-e $langfile) {
  145. return $langfile;
  146. }
  147. else {
  148. return undef;
  149. }
  150. }
  151. # Interface to the highlight C library.
  152. sub highlight ($$) {
  153. my $langfile=shift;
  154. my $input=shift;
  155. eval q{use highlight};
  156. if ($@) {
  157. print STDERR gettext("warning: highlight perl module not available; falling back to pass through");
  158. return $input;
  159. }
  160. my $gen;
  161. if (! exists $highlighters{$langfile}) {
  162. $gen = highlight::CodeGenerator::getInstance($highlight::XHTML);
  163. $gen->setFragmentCode(1); # generate html fragment
  164. $gen->setHTMLEnclosePreTag(1); # include stylish <pre>
  165. if ($data_dir){
  166. # new style, requires a real theme, but has no effect
  167. $gen->initTheme($data_dir->getThemePath("seashell.theme"));
  168. } else {
  169. # old style, anything works.
  170. $gen->initTheme("/dev/null");
  171. }
  172. $gen->loadLanguage($langfile); # must come after initTheme
  173. $gen->setEncoding("utf-8");
  174. $highlighters{$langfile}=$gen;
  175. }
  176. else {
  177. $gen=$highlighters{$langfile};
  178. }
  179. return $gen->generateString($input);
  180. }
  181. 1