File Coverage

blib/lib/Syntax/Highlight/Engine/Simple.pm
Criterion Covered Total %
statement 138 148 93.2
branch 33 42 78.5
condition 6 7 85.7
subroutine 19 20 95.0
pod 8 8 100.0
total 204 225 90.6


line stmt bran cond sub pod time code
1             package Syntax::Highlight::Engine::Simple;
2 4     4   276976 use warnings;
  4         41  
  4         147  
3 4     4   24 use strict;
  4         11  
  4         90  
4 4     4   20 use Carp;
  4         7  
  4         3840  
5             our $VERSION = '0.101';
6              
7             ### ---
8             ### constructor
9             ### ---
10             sub new {
11            
12 6     6 1 3127 my $class = shift;
13 6         45 my $self = bless {type => undef, syntax => undef, @_}, $class;
14            
15 6         43 $self->setParams(@_);
16            
17 6 50       32 if ($self->{type}) {
18            
19 0         0 my $class = "Syntax::Highlight::Engine::Simple::". $self->{type};
20            
21 0         0 require $class;
22 0         0 $class->setSyntax();
23            
24 0         0 return $self;
25             }
26            
27 6         36 $self->setSyntax();
28            
29 6         116 return $self;
30             }
31            
32             ### ---
33             ### set params
34             ### ---
35             sub setParams {
36            
37 6     6 1 17 my $self = shift;
38            
39 6         34 my %args = (
40             html_escape_code_ref => \&_html_escape,
41             @_);
42            
43 6         40 $self->{html_escape_code_ref} = $args{html_escape_code_ref};
44             }
45            
46             ### ---
47             ### set syntax
48             ### ---
49             sub setSyntax {
50            
51 8     8 1 55 my $self = shift;
52 8         27 my %args = (syntax => [], @_);
53            
54 8         34 $self->{syntax} = $args{syntax};
55             }
56            
57             ### ---
58             ### append syntax
59             ### ---
60             sub appendSyntax {
61            
62 4     4 1 1532 my $self = shift;
63 4         27 my %args = (
64             syntax => {
65             regexp => '',
66             class => '',
67             container => undef,
68             }, @_);
69            
70 4         14 push(@{$self->{syntax}}, $args{syntax});
  4         138  
71             }
72            
73             ### ---
74             ### Highlight multi Line
75             ### ---
76             sub doStr{
77            
78 13     13 1 2140 my $self = shift;
79 13         67 my %args = (str => '', tab_width => -1, @_);
80            
81 13 50       58 defined $args{str} or croak 'doStr method got undefined value';
82            
83 13 100       46 if ($args{tab_width} > 0) {
84            
85 4         12 my $tabed = '';
86            
87 4         75 foreach my $line (split(/\r\n|\r|\n/, $args{str})) {
88            
89             $tabed .=
90 31         99 &_tab2space($line, $args{tab_width}). "\n";
91             }
92            
93 4         19 $args{str} = $tabed;
94             }
95            
96 13         68 return $self->_doLine($args{str});
97             }
98            
99             ### ---
100             ### Highlight file
101             ### ---
102             sub doFile {
103            
104 4     4 1 2743 my $self = shift;
105 4         37 my %args = (
106             file => '',
107             tab_width => -1,
108             encode => 'utf8',
109             @_);
110            
111 4         14 my $str = '';
112            
113 4         99 require 5.005;
114            
115 4 50       193 open(my $filehandle, '<'. $args{file}) or croak 'File open failed';
116 4     2   119 binmode($filehandle, ":encoding($args{encode})");
  2         18  
  2         6  
  2         19  
117            
118 4         36727 while (my $line = <$filehandle>) {
119 513 50       1183 if ($args{tab_width} > 0) {
120 513         921 $line = &_tab2space($line, $args{tab_width});
121             }
122 513         1750 $str .= $line;
123             }
124            
125 4         51 close($filehandle);
126            
127 4         31 return $self->_doLine($str);
128             }
129            
130             ### ---
131             ### Highlight single line
132             ### ---
133             sub _doLine {
134            
135 17     17   62 my ($self, $str) = @_;
136            
137 17         122 $str =~ s/\r\n|\r/\n/g;
138            
139 17         71 $self->{_markup_map} = [];
140            
141             ### make markup map
142 17         70 foreach my $i (0 .. $#{$self->{syntax}}) {
  17         94  
143 88         364 $self->_makeAllowHash($i);
144 88         284 $self->_make_map($str, $i);
145             }
146            
147 17         53 my $outstr = '';
148 17         44 my $last_pos = 0;
149            
150             ### Apply the map to string
151 17         82 foreach my $pos ($self->_restracture_map()) {
152            
153 1216         2264 my $str_left = substr($str, $last_pos, $$pos[0] - $last_pos);
154            
155 1216         1932 $outstr .= $self->{html_escape_code_ref}->($str_left);
156            
157 1216 100       2092 if (defined $$pos[1]) {
158 608         1210 $outstr .= sprintf("", $$pos[1]->{class});
159             } else {
160 608         781 $outstr .= '';
161             }
162 1216         1777 $last_pos = $$pos[0];
163             }
164            
165 17         289 return $outstr. $self->{html_escape_code_ref}->(substr($str, $last_pos));
166             }
167            
168             ### ---
169             ### Prepare hash for container matching
170             ### ---
171             sub _makeAllowHash {
172            
173 88     88   210 my $self = shift;
174            
175 88 100       350 if (! exists $self->{syntax}->[$_[0]]->{container} ) {
176 70         190 return;
177             }
178            
179 18         63 my $allowed = $self->{syntax}->[$_[0]]->{container};
180            
181 18 100       88 if (ref $allowed eq 'ARRAY') {
    50          
182 1         5 foreach my $class ( @$allowed ) {
183 2         9 $self->{syntax}->[$_[0]]->{_cont_hash}->{$class} = 0;
184             }
185             } elsif ($allowed) {
186 17         83 $self->{syntax}->[$_[0]]->{_cont_hash}->{$allowed} = 0;
187             }
188             }
189            
190             ### ---
191             ### Make markup map
192             ### ---------------------------------------
193             ### | open_pos | close_pos | syntax index
194             ### | open_pos | close_pos | syntax index
195             ### | open_pos | close_pos | syntax index
196             ### ---------------------------------------
197             ### ---
198             sub _make_map {
199            
200 4     4   37 no warnings; ### Avoid Deep Recursion warning
  4         9  
  4         2185  
201            
202 834     834   2127 my ($self, $str, $index, $pos) = @_;
203 834   100     1915 $pos ||= 0;
204            
205 834         1481 my $map_ref = $self->{_markup_map};
206            
207 834         39497 my @scraps =
208             split(/$self->{syntax}->[$index]->{regexp}/, $str, 2);
209            
210 834 100       2687 if ((scalar @scraps) >= 2) {
    100          
211            
212 746         1700 my $rest = pop(@scraps);
213 746         2670 my $ins_pos0 = $pos + length($scraps[0]);
214 746         54482 my $ins_pos1 = $pos + (length($str) - length($rest));
215            
216             ### Add markup position
217 746         2224 push(@$map_ref, [
218             $ins_pos0,
219             $ins_pos1,
220             $index,
221             ]
222             );
223            
224             ### Recurseion for rest
225 746         2697 $self->_make_map($rest, $index, $ins_pos1);
226             }
227            
228             ### Follow up process
229             elsif (@$map_ref) {
230            
231             @$map_ref = sort {
232 59 50 100     423 $$a[0] <=> $$b[0] or
  9211         19224  
233             $$b[1] <=> $$a[1] or
234             $$a[2] <=> $$b[2]
235             } @$map_ref;
236             }
237            
238 834         3127 return;
239             }
240            
241             ### ---
242             ### restracture the map data into following format
243             ### ------------------------
244             ### | open_pos | syntax ref
245             ### | close_pos |
246             ### | open_pos | syntax ref
247             ### | close_pos |
248             ### ------------------------
249             ### ---
250             sub _restracture_map {
251            
252 17     17   43 my $self = shift;
253 17         45 my $map_ref = $self->{_markup_map};
254 17         38 my @out_array;
255 17         41 my @root = ();
256            
257 17         80 REGLOOP: for (my $i = 0; $i < scalar @$map_ref; $i++) {
258            
259             ### vacuum @root
260 746         1332 for (my $j = 0; $j < scalar @root; $j++) {
261 859 100       1779 if ($root[$j]->[1] <= $$map_ref[$i]->[0]) {
262 589         1114 splice(@root, $j--, 1);
263             }
264             }
265            
266 746         1168 my $syntax_ref = $self->{syntax}->[$$map_ref[$i]->[2]];
267 746         941 my $ok = 0;
268            
269             ### no container restriction
270 746 100       1179 if (! exists $$syntax_ref{container}) {
271 657 100       1091 if (!scalar @root) {
272 525         729 $ok = 1;
273             }
274             } else {
275            
276             ### Search for container
277 89         190 BACKWARD: for (my $j = scalar @root - 1; $j >= 0; $j--) {
278            
279             ### overlap?
280 88 50       165 if ($root[$j]->[1] > $$map_ref[$i]->[0]) {
281            
282             ### contained?
283 88 50       164 if ($root[$j]->[1] >= $$map_ref[$i]->[1]) {
284            
285             my $root_class =
286 88         151 $self->{syntax}->[$root[$j]->[2]]->{class};
287            
288 88 100       173 if (exists $$syntax_ref{_cont_hash}->{$root_class}) {
289 83         124 $ok = 1; last BACKWARD; # allowed
  83         157  
290             }
291 5         15 last BACKWARD; # container not allowed
292             }
293 0         0 last BACKWARD; # illigal overlap
294             }
295 0         0 splice(@root, $j, 1);
296             }
297             }
298            
299 746 100       1236 if (! $ok) {
300 138         217 splice(@$map_ref, $i--, 1);
301 138         313 next REGLOOP;
302             }
303            
304 608         930 push(@root, $$map_ref[$i]);
305            
306 608         1664 push(
307             @out_array,
308             [$$map_ref[$i]->[0], $syntax_ref],
309             [$$map_ref[$i]->[1]]
310             );
311             }
312 17         93 @out_array = sort {$$a[0] <=> $$b[0]} @out_array;
  1777         2570  
313 17         114 return @out_array;
314             }
315            
316             ### ---
317             ### replace tabs to spaces
318             ### ---
319             sub _tab2space {
320            
321 4     4   30 no warnings 'recursion';
  4         10  
  4         1162  
322            
323 950     950   1877 my ($str, $width) = @_;
324 950   50     1727 $str ||= '';
325 950 50       1633 $width = defined $width ? $width : 4;
326 950         2400 my @scraps = split(/\t/, $str, 2);
327            
328 950 100       2103 if (scalar @scraps == 2) {
329            
330 406         856 my $num = $width - (length($scraps[0]) % $width);
331 406         745 my $right_str = &_tab2space($scraps[1], $width);
332            
333 406         1428 return ($scraps[0]. ' ' x $num. $right_str);
334             }
335            
336 544         1274 return $str;
337             }
338            
339             ### ---
340             ### convert array to regexp
341             ### ---
342             sub array2regexp {
343            
344 5     5 1 904 my $self = shift;
345            
346 5         122 return sprintf('\\b(?:%s)\\b', join('|', @_));
347             }
348            
349             ### ---
350             ### Return Class names
351             ### ---
352             sub getClassNames {
353            
354 0     0 1 0 return map {${$_}{class}} @{shift->{syntax}}
  0         0  
  0         0  
  0         0  
355             }
356            
357             ### ---
358             ### HTML escape
359             ### ---
360             sub _html_escape {
361            
362 1233     1233   1877 my ($str) = @_;
363            
364 1233         1829 $str =~ s/&/&/g;
365 1233         1734 $str =~ s/
366 1233         1697 $str =~ s/>/>/g;
367            
368 1233         2331 return $str;
369             }
370              
371             1;
372              
373             __END__