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