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