]> sipb.mit.edu Git - ikiwiki.git/blob - IkiWiki/Plugin/highlight.pm
fix uninitilized value warning on bad page name
[ikiwiki.git] / IkiWiki / Plugin / highlight.pm
1 #!/usr/bin/perl
2 package IkiWiki::Plugin::highlight;
3
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.
7
8 use warnings;
9 use strict;
10 use IkiWiki 3.00;
11 use Encode;
12
13 my $data_dir;
14
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 }
22
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 }
52
53 sub checkconfig () {
54
55         eval q{use highlight};
56         if ($@) {
57                 print STDERR "Failed to load highlight. Configuring anyway.\n";
58         };
59
60         if (highlight::DataDir->can('new')){
61                 $data_dir=new highlight::DataDir();
62                 $data_dir->searchDataDir("");
63         } else {
64                 $data_dir=undef;
65         }
66
67         if (! exists $config{filetypes_conf}) {
68                 $config{filetypes_conf}= 
69                      ($data_dir ? $data_dir->getConfDir() : "/etc/highlight/")
70                           . "filetypes.conf";
71         }
72         if (! exists $config{langdefdir}) {
73                 $config{langdefdir}=
74                      ($data_dir ? $data_dir->getLangPath("")
75                       : "/usr/share/highlight/langDefs");
76
77         }
78         if (exists $config{tohighlight} && read_filetypes()) {
79                 foreach my $file (split ' ', $config{tohighlight}) {
80                         my @opts = $file=~s/^\.// ?
81                                 (keepextension => 1) :
82                                 (noextension => 1);
83                         my $ext = $file=~s/:(.*)// ? $1 : $file;
84                 
85                         my $langfile=ext2langfile($ext);
86                         if (! defined $langfile) {
87                                 error(sprintf(gettext(
88                                         "tohighlight contains unknown file type '%s'"),
89                                         $ext));
90                         }
91         
92                         hook(
93                                 type => "htmlize",
94                                 id => $file,
95                                 call => sub {
96                                         my %params=@_;
97                                         highlight($langfile, $params{content});
98                                 },
99                                 longname => sprintf(gettext("Source code: %s"), $file),
100                                 @opts,
101                         );
102                 }
103         }
104 }
105
106 sub htmlizeformat {
107         my $format=lc shift;
108         my $langfile=ext2langfile($format);
109
110         if (! defined $langfile) {
111                 return;
112         }
113
114         return Encode::decode_utf8(highlight($langfile, shift));
115 }
116
117 my %ext2lang;
118 my $filetypes_read=0;
119 my %highlighters;
120
121 # Parse highlight's config file to get extension => language mappings.
122 sub read_filetypes () {
123         my $f;
124         if (!open($f, $config{filetypes_conf})) {
125                 warn($config{filetypes_conf}.": ".$!);
126                 return 0;
127         };
128
129         local $/=undef;
130         my $config=<$f>;
131         close $f;
132
133         # highlight >= 3.2 format (bind-style)
134         while ($config=~m/Lang\s*=\s*\"([^"]+)\"[,\s]+Extensions\s*=\s*{([^}]+)}/sg) {
135                 my $lang=$1;
136                 foreach my $bit (split ',', $2) {
137                         $bit=~s/.*"(.*)".*/$1/s;
138                         $ext2lang{$bit}=$lang;
139                 }
140         }
141
142         # highlight < 3.2 format
143         if (! keys %ext2lang) {
144                 foreach (split("\n", $config)) {
145                         if (/^\$ext\((.*)\)=(.*)$/) {
146                                 $ext2lang{$_}=$1 foreach $1, split ' ', $2;
147                         }
148                 }
149         }
150
151         return $filetypes_read=1;
152 }
153
154
155 # Given a filename extension, determines the language definition to
156 # use to highlight it.
157 sub ext2langfile ($) {
158         my $ext=shift;
159
160         my $langfile="$config{langdefdir}/$ext.lang";
161         return $langfile if exists $highlighters{$langfile};
162
163         read_filetypes() unless $filetypes_read;
164         if (exists $ext2lang{$ext}) {
165                 return "$config{langdefdir}/$ext2lang{$ext}.lang";
166         }
167         # If a language only has one common extension, it will not
168         # be listed in filetypes, so check the langfile.
169         elsif (-e $langfile) {
170                 return $langfile;
171         }
172         else {
173                 return undef;
174         }
175 }
176
177 # Interface to the highlight C library.
178 sub highlight ($$) {
179         my $langfile=shift;
180         my $input=shift;
181
182         eval q{use highlight};
183         if ($@) {
184                 print STDERR gettext("warning: highlight perl module not available; falling back to pass through");
185                 return $input;
186         }
187
188         my $gen;
189         if (! exists $highlighters{$langfile}) {
190                 $gen = highlight::CodeGenerator::getInstance($highlight::XHTML);
191                 $gen->setFragmentCode(1); # generate html fragment
192                 $gen->setHTMLEnclosePreTag(1); # include stylish <pre>
193                 if ($data_dir){
194                         # new style, requires a real theme, but has no effect
195                         $gen->initTheme($data_dir->getThemePath("seashell.theme"));
196                 } else {
197                         # old style, anything works.
198                         $gen->initTheme("/dev/null");
199                 }
200                 $gen->loadLanguage($langfile); # must come after initTheme
201                 $gen->setEncoding("utf-8");
202                 $highlighters{$langfile}=$gen;
203         }
204         else {          
205                 $gen=$highlighters{$langfile};
206         }
207
208         return $gen->generateString($input);
209 }
210
211 1