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