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   282125 use warnings;
  4         37  
  4         142  
3 4     4   22 use strict;
  4         8  
  4         94  
4 4     4   22 use Carp;
  4         8  
  4         4405  
5             our $VERSION = '0.102';
6              
7             ### ---
8             ### constructor
9             ### ---
10             sub new {
11            
12 6     6 1 2382 my $class = shift;
13 6         28 my $self = bless {type => undef, syntax => undef, @_}, $class;
14            
15 6         30 $self->setParams(@_);
16            
17 6 50       55 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         28 $self->setSyntax();
28            
29 6         79 return $self;
30             }
31            
32             ### ---
33             ### set params
34             ### ---
35             sub setParams {
36            
37 6     6 1 15 my $self = shift;
38            
39 6         26 my %args = (
40             html_escape_code_ref => \&_html_escape,
41             @_);
42            
43 6         28 $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 41 my $self = shift;
52 8         22 my %args = (syntax => [], @_);
53            
54 8         27 $self->{syntax} = $args{syntax};
55             }
56            
57             ### ---
58             ### append syntax
59             ### ---
60             sub appendSyntax {
61            
62 4     4 1 1063 my $self = shift;
63 4         17 my %args = (
64             syntax => {
65             regexp => '',
66             class => '',
67             container => undef,
68             }, @_);
69            
70 4         10 push(@{$self->{syntax}}, $args{syntax});
  4         15  
71             }
72            
73             ### ---
74             ### Highlight multi Line
75             ### ---
76             sub doStr{
77            
78 13     13 1 1198 my $self = shift;
79 13         49 my %args = (str => '', tab_width => -1, @_);
80            
81 13 50       42 defined $args{str} or croak 'doStr method got undefined value';
82            
83 13 100       41 if ($args{tab_width} > 0) {
84            
85 4         10 my $tabed = '';
86            
87 4         56 foreach my $line (split(/\r\n|\r|\n/, $args{str})) {
88            
89             $tabed .=
90 31         74 &_tab2space($line, $args{tab_width}). "\n";
91             }
92            
93 4         12 $args{str} = $tabed;
94             }
95            
96 13         49 return $self->_doLine($args{str});
97             }
98            
99             ### ---
100             ### Highlight file
101             ### ---
102             sub doFile {
103            
104 4     4 1 1731 my $self = shift;
105 4         23 my %args = (
106             file => '',
107             tab_width => -1,
108             encode => 'utf8',
109             @_);
110            
111 4         11 my $str = '';
112            
113 4         71 require 5.005;
114            
115 4 50       209 open(my $filehandle, '<'. $args{file}) or croak 'File open failed';
116 4     2   110 binmode($filehandle, ":encoding($args{encode})");
  2         13  
  2         6  
  2         14  
117            
118 4         33344 while (my $line = <$filehandle>) {
119 513 50       1417 if ($args{tab_width} > 0) {
120 513         1068 $line = &_tab2space($line, $args{tab_width});
121             }
122 513         1816 $str .= $line;
123             }
124            
125 4         71 close($filehandle);
126            
127 4         32 return $self->_doLine($str);
128             }
129            
130             ### ---
131             ### Highlight single line
132             ### ---
133             sub _doLine {
134            
135 17     17   44 my ($self, $str) = @_;
136            
137 17         132 $str =~ s/\r\n|\r/\n/g;
138            
139 17         54 $self->{_markup_map} = [];
140            
141             ### make markup map
142 17         55 foreach my $i (0 .. $#{$self->{syntax}}) {
  17         68  
143 88         267 $self->_makeAllowHash($i);
144 88         205 $self->_make_map($str, $i);
145             }
146            
147 17         40 my $outstr = '';
148 17         36 my $last_pos = 0;
149            
150             ### Apply the map to string
151 17         64 foreach my $pos ($self->_restracture_map()) {
152            
153 1216         2596 my $str_left = substr($str, $last_pos, $$pos[0] - $last_pos);
154            
155 1216         2373 $outstr .= $self->{html_escape_code_ref}->($str_left);
156            
157 1216 100       2521 if (defined $$pos[1]) {
158 608         1491 $outstr .= sprintf("", $$pos[1]->{class});
159             } else {
160 608         1040 $outstr .= '';
161             }
162 1216         2290 $last_pos = $$pos[0];
163             }
164            
165 17         198 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   158 my $self = shift;
174            
175 88 100       260 if (! exists $self->{syntax}->[$_[0]]->{container} ) {
176 70         151 return;
177             }
178            
179 18         49 my $allowed = $self->{syntax}->[$_[0]]->{container};
180            
181 18 100       76 if (ref $allowed eq 'ARRAY') {
    50          
182 1         2 foreach my $class ( @$allowed ) {
183 2         7 $self->{syntax}->[$_[0]]->{_cont_hash}->{$class} = 0;
184             }
185             } elsif ($allowed) {
186 17         60 $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   41 no warnings; ### Avoid Deep Recursion warning
  4         9  
  4         2422  
201            
202 834     834   1985 my ($self, $str, $index, $pos) = @_;
203 834   100     2091 $pos ||= 0;
204            
205 834         1577 my $map_ref = $self->{_markup_map};
206            
207 834         47580 my @scraps =
208             split(/$self->{syntax}->[$index]->{regexp}/, $str, 2);
209            
210 834 100       2661 if ((scalar @scraps) >= 2) {
    100          
211            
212 746         1528 my $rest = pop(@scraps);
213 746         2699 my $ins_pos0 = $pos + length($scraps[0]);
214 746         60286 my $ins_pos1 = $pos + (length($str) - length($rest));
215            
216             ### Add markup position
217 746         1932 push(@$map_ref, [
218             $ins_pos0,
219             $ins_pos1,
220             $index,
221             ]
222             );
223            
224             ### Recurseion for rest
225 746         2785 $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     275 $$a[0] <=> $$b[0] or
  9211         22711  
233             $$b[1] <=> $$a[1] or
234             $$a[2] <=> $$b[2]
235             } @$map_ref;
236             }
237            
238 834         1960 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   29 my $self = shift;
253 17         34 my $map_ref = $self->{_markup_map};
254 17         33 my @out_array;
255 17         33 my @root = ();
256            
257 17         56 REGLOOP: for (my $i = 0; $i < scalar @$map_ref; $i++) {
258            
259             ### vacuum @root
260 746         1743 for (my $j = 0; $j < scalar @root; $j++) {
261 859 100       2244 if ($root[$j]->[1] <= $$map_ref[$i]->[0]) {
262 589         1478 splice(@root, $j--, 1);
263             }
264             }
265            
266 746         1455 my $syntax_ref = $self->{syntax}->[$$map_ref[$i]->[2]];
267 746         1289 my $ok = 0;
268            
269             ### no container restriction
270 746 100       1440 if (! exists $$syntax_ref{container}) {
271 657 100       1428 if (!scalar @root) {
272 525         838 $ok = 1;
273             }
274             } else {
275            
276             ### Search for container
277 89         240 BACKWARD: for (my $j = scalar @root - 1; $j >= 0; $j--) {
278            
279             ### overlap?
280 88 50       211 if ($root[$j]->[1] > $$map_ref[$i]->[0]) {
281            
282             ### contained?
283 88 50       209 if ($root[$j]->[1] >= $$map_ref[$i]->[1]) {
284            
285             my $root_class =
286 88         175 $self->{syntax}->[$root[$j]->[2]]->{class};
287            
288 88 100       211 if (exists $$syntax_ref{_cont_hash}->{$root_class}) {
289 83         134 $ok = 1; last BACKWARD; # allowed
  83         161  
290             }
291 5         10 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       1640 if (! $ok) {
300 138         254 splice(@$map_ref, $i--, 1);
301 138         395 next REGLOOP;
302             }
303            
304 608         1062 push(@root, $$map_ref[$i]);
305            
306 608         2055 push(
307             @out_array,
308             [$$map_ref[$i]->[0], $syntax_ref],
309             [$$map_ref[$i]->[1]]
310             );
311             }
312 17         70 @out_array = sort {$$a[0] <=> $$b[0]} @out_array;
  1777         2936  
313 17         106 return @out_array;
314             }
315            
316             ### ---
317             ### replace tabs to spaces
318             ### ---
319             sub _tab2space {
320            
321 4     4   34 no warnings 'recursion';
  4         9  
  4         1312  
322            
323 950     950   2007 my ($str, $width) = @_;
324 950   50     2023 $str ||= '';
325 950 50       1870 $width = defined $width ? $width : 4;
326 950         2529 my @scraps = split(/\t/, $str, 2);
327            
328 950 100       2309 if (scalar @scraps == 2) {
329            
330 406         885 my $num = $width - (length($scraps[0]) % $width);
331 406         812 my $right_str = &_tab2space($scraps[1], $width);
332            
333 406         1467 return ($scraps[0]. ' ' x $num. $right_str);
334             }
335            
336 544         1305 return $str;
337             }
338            
339             ### ---
340             ### convert array to regexp
341             ### ---
342             sub array2regexp {
343            
344 5     5 1 740 my $self = shift;
345            
346 5         100 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   2418 my ($str) = @_;
363            
364 1233         2311 $str =~ s/&/&/g;
365 1233         2009 $str =~ s/
366 1233         2122 $str =~ s/>/>/g;
367            
368 1233         2741 return $str;
369             }
370              
371             1;
372              
373             __END__