]> git.vanrenterghem.biz Git - git.ikiwiki.info.git/blob - IkiWiki/Plugin/highlight.pm
Add jmtd's permalink plugin.
[git.ikiwiki.info.git] / IkiWiki / Plugin / highlight.pm
1 #!/usr/bin/perl
2 package IkiWiki::Plugin::highlight;
4 # This has been tested with highlight 2.16 and highlight 3.2+svn19.
5 # In particular version 3.2 won't work. It detects the different
6 # versions by the presence of the the highlight::DataDir class.
8 use warnings;
9 use strict;
10 use IkiWiki 3.00;
11 use Encode;
13 my $data_dir;
15 sub import {
16         hook(type => "getsetup", id => "highlight",  call => \&getsetup);
17         hook(type => "checkconfig", id => "highlight", call => \&checkconfig);
18         # this hook is used by the format plugin
19         hook(type => "htmlizeformat", id => "highlight", 
20                 call => \&htmlizeformat, last => 1);
21 }
23 sub getsetup () {
24         return
25                 plugin => {
26                         safe => 1,
27                         rebuild => 1, # format plugin
28                         section => "format",
29                 },
30                 tohighlight => {
31                         type => "string",
32                         example => ".c .h .cpp .pl .py Makefile:make",
33                         description => "types of source files to syntax highlight",
34                         safe => 1,
35                         rebuild => 1,
36                 },
37                 filetypes_conf => {
38                         type => "string",
39                         example => "/etc/highlight/filetypes.conf",
40                         description => "location of highlight's filetypes.conf",
41                         safe => 0,
42                         rebuild => undef,
43                 },
44                 langdefdir => {
45                         type => "string",
46                         example => "/usr/share/highlight/langDefs",
47                         description => "location of highlight's langDefs directory",
48                         safe => 0,
49                         rebuild => undef,
50                 },
51 }
53 sub checkconfig () {
54         eval q{use highlight};
55         if (highlight::DataDir->can('new')) {
56                 $data_dir=new highlight::DataDir();
57                 if ( $data_dir->can('initSearchDirectories') ) {
58                         # 4.0+
59                         $data_dir -> initSearchDirectories("");
60                 } else {
61                         # pre-4.0
62                         $data_dir -> searchDataDir("");
63                 }
64         } else {
65                 $data_dir=undef;
66         }
68         if (! exists $config{filetypes_conf}) {
69           if (! $data_dir ) {
70                 $config{filetypes_conf}= "/etc/highlight/filetypes.conf";
71               } elsif ( $data_dir -> can('getFiletypesConfPath') ) {
72                 # 3.14 +
73                 $config{filetypes_conf}=
74                   $data_dir -> getFiletypesConfPath("filetypes");
75               } else {
76                 # 3.9 +
77                 $config{filetypes_conf}=
78                   $data_dir -> getConfDir() . "/filetypes.conf";
79               }
80         }
81         # note that this is only used for old versions of highlight
82         # where $data_dir will not be defined.
83         if (! exists $config{langdefdir}) {
84                 $config{langdefdir}= "/usr/share/highlight/langDefs";
86         }
87         if (exists $config{tohighlight} && read_filetypes()) {
88                 foreach my $file (split ' ', $config{tohighlight}) {
89                         my @opts = $file=~s/^\.// ?
90                                 (keepextension => 1) :
91                                 (noextension => 1);
92                         my $ext = $file=~s/:(.*)// ? $1 : $file;
93                 
94                         my $langfile=ext2langfile($ext);
95                         if (! defined $langfile) {
96                                 error(sprintf(gettext(
97                                         "tohighlight contains unknown file type '%s'"),
98                                         $ext));
99                         }
100         
101                         hook(
102                                 type => "htmlize",
103                                 id => $file,
104                                 call => sub {
105                                         my %params=@_;
106                                         highlight($langfile, $file, $params{content});
107                                 },
108                                 longname => sprintf(gettext("Source code: %s"), $file),
109                                 @opts,
110                         );
111                 }
112         }
115 sub htmlizeformat {
116         my $format=lc shift;
117         my $langfile=ext2langfile($format);
119         if (! defined $langfile) {
120                 return;
121         }
123         return Encode::decode_utf8(highlight($langfile, $format, shift));
126 my %ext2lang;
127 my $filetypes_read=0;
128 my %highlighters;
130 # Parse highlight's config file to get extension => language mappings.
131 sub read_filetypes () {
132         my $f;
133         if (!open($f, $config{filetypes_conf})) {
134                 warn($config{filetypes_conf}.": ".$!);
135                 return 0;
136         };
138         local $/=undef;
139         my $config=<$f>;
140         close $f;
142         # highlight >= 3.2 format (bind-style)
143         while ($config=~m/Lang\s*=\s*\"([^"]+)\"[,\s]+Extensions\s*=\s*{([^}]+)}/sg) {
144                 my $lang=$1;
145                 foreach my $bit (split ',', $2) {
146                         $bit=~s/.*"(.*)".*/$1/s;
147                         $ext2lang{$bit}=$lang;
148                 }
149         }
151         # highlight < 3.2 format
152         if (! keys %ext2lang) {
153                 foreach (split("\n", $config)) {
154                         if (/^\$ext\((.*)\)=(.*)$/) {
155                                 $ext2lang{$_}=$1 foreach $1, split ' ', $2;
156                         }
157                 }
158         }
160         return $filetypes_read=1;
164 sub searchlangdef {
165   my $lang=shift;
167   if ($data_dir) {
168     return $data_dir->getLangPath($lang . ".lang");
169   } else {
170     return "$config{langdefdir}/$lang.lang";
171   }
174 # Given a filename extension, determines the language definition to
175 # use to highlight it.
176 sub ext2langfile ($) {
177         my $ext=shift;
179         my $langfile=searchlangdef($ext);
180         return $langfile if exists $highlighters{$langfile};
182         read_filetypes() unless $filetypes_read;
183         if (exists $ext2lang{$ext}) {
184                 return searchlangdef($ext2lang{$ext});
185         }
186         # If a language only has one common extension, it will not
187         # be listed in filetypes, so check the langfile.
188         elsif (-e $langfile) {
189                 return $langfile;
190         }
191         else {
192                 return undef;
193         }
196 # Interface to the highlight C library.
197 sub highlight ($$) {
198         my $langfile=shift;
199         my $extorfile=shift;
200         my $input=shift;
202         eval q{use highlight};
203         if ($@) {
204                 print STDERR gettext("warning: highlight perl module not available; falling back to pass through");
205                 return $input;
206         }
208         my $gen;
209         if (! exists $highlighters{$langfile}) {
210                 no warnings 'once';
211                 $gen = highlight::CodeGenerator::getInstance($highlight::XHTML);
212                 use warnings;
213                 $gen->setFragmentCode(1); # generate html fragment
214                 $gen->setHTMLEnclosePreTag(1); # include stylish <pre>
215                 if ($data_dir){
216                         # new style, requires a real theme, but has no effect
217                         $gen->initTheme($data_dir->getThemePath("seashell.theme"));
218                 } else {
219                         # old style, anything works.
220                         $gen->initTheme("/dev/null");
221                 }
222                 $gen->loadLanguage($langfile); # must come after initTheme
223                 $gen->setEncoding("utf-8");
224                 $highlighters{$langfile}=$gen;
225         }
226         else {          
227                 $gen=$highlighters{$langfile};
228         }
230         return "<div class=\"highlight-$extorfile\">".$gen->generateString($input)."</div>";