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