File Coverage

blib/lib/PPIx/EditorTools/Lexer.pm
Criterion Covered Total %
statement 55 58 94.8
branch 25 34 73.5
condition 4 6 66.6
subroutine 9 9 100.0
pod 0 2 0.0
total 93 109 85.3


line stmt bran cond sub pod time code
1             package PPIx::EditorTools::Lexer;
2              
3             # ABSTRACT: Simple Lexer used for syntax highlighting
4              
5 2     2   281160 use 5.008;
  2         7  
  2         84  
6 2     2   11 use strict;
  2         9  
  2         72  
7 2     2   10 use warnings;
  2         4  
  2         289  
8 2     2   13 use Carp;
  2         9  
  2         147  
9              
10 2     2   10 use base 'PPIx::EditorTools';
  2         4  
  2         676  
11 2     2   14 use Class::XSAccessor accessors => {};
  2         3  
  2         17  
12              
13 2     2   1730 use PPI;
  2         4  
  2         1512  
14              
15             our $VERSION = '0.18';
16              
17             =pod
18              
19             =head1 NAME
20              
21             PPIx::EditorTools::Lexer - Simple Lexer used for syntax highlighting
22              
23             =head1 SYNOPSIS
24              
25             PPIx::EditorTools::Lexer->new->lexer(
26             code => "package TestPackage;\nsub x { 1;\n",
27             highlighter => sub {
28             my ( $css, $row, $rowchar, $len ) = @_;
29             ...
30             },
31             );
32              
33             =head1 DESCRIPTION
34              
35             Go over the various interesting elements of a give piece
36             of code or an already process PPI tree.
37             For each token call the user supplied 'highlighter' function with
38             the follow values:
39              
40             $css - The keyword that can be used for colouring.
41             $row - The row number where the token starts
42             $rowchar - The character within that row where the token starts
43             $len - The length of the token
44              
45             =head1 METHODS
46              
47             =over 4
48              
49             =item new()
50              
51             Constructor. Generally shouldn't be called with any arguments.
52              
53             =item find( ppi => PPI::Document $ppi, highlighter => sub {...} )
54             =item find( code => Str $code, highlighter => sub ...{} )
55              
56             Accepts either a C to process or a string containing
57             the code (which will be converted into a C) to process.
58             Return a reference to an array.
59              
60             =back
61              
62             =cut
63              
64             sub lexer {
65 8     8 0 32299 my ( $self, %args ) = @_;
66 8         18 my $markup = delete $args{highlighter};
67              
68 8         36 $self->process_doc(%args);
69              
70 8         24 my $ppi = $self->ppi;
71              
72 8 50       26 return [] unless defined $ppi;
73 8         27 $ppi->index_locations;
74              
75 8         2492 my @tokens = $ppi->tokens;
76              
77 8         288 foreach my $t (@tokens) {
78              
79 91         375 my ( $row, $rowchar, $col ) = @{ $t->location };
  91         223  
80              
81 91         911 my $css = class_to_css($t);
82              
83 91         237 my $len = $t->length;
84              
85 91         360 $markup->( $css, $row, $rowchar, $len );
86             }
87             }
88              
89              
90             sub class_to_css {
91 91     91 0 98 my $Token = shift;
92              
93 91 100       364 if ( $Token->isa('PPI::Token::Word') ) {
94              
95             # There are some words we can be very confident are
96             # being used as keywords
97 37 50 66     91 unless ( $Token->snext_sibling and $Token->snext_sibling->content eq '=>' ) {
98 37 100       1524 if ( $Token->content =~ /^(?:sub|return)$/ ) {
    100          
99 3         23 return 'keyword';
100             } elsif ( $Token->content =~ /^(?:undef|shift|defined|bless)$/ ) {
101 4         49 return 'core';
102             }
103             }
104              
105 30 50 66     306 if ( $Token->previous_sibling and $Token->previous_sibling->content eq '->' ) {
106 0 0       0 if ( $Token->content =~ /^(?:new)$/ ) {
107 0         0 return 'core';
108             }
109             }
110              
111 30 100       1008 if ( $Token->parent->isa('PPI::Statement::Include') ) {
    100          
    100          
    100          
    50          
112 8 100       49 if ( $Token->content =~ /^(?:use|no)$/ ) {
113 5         74 return 'keyword';
114             }
115 3 100       18 if ( $Token->content eq $Token->parent->pragma ) {
116 2         80 return 'pragma';
117             }
118             } elsif ( $Token->parent->isa('PPI::Statement::Variable') ) {
119 4 50       57 if ( $Token->content =~ /^(?:my|local|our)$/ ) {
120 4         32 return 'keyword';
121             }
122             } elsif ( $Token->parent->isa('PPI::Statement::Compound') ) {
123 8 50       141 if ( $Token->content =~ /^(?:if|else|elsif|unless|for|foreach|while|my)$/ ) {
124 8         51 return 'keyword';
125             }
126             } elsif ( $Token->parent->isa('PPI::Statement::Package') ) {
127 1 50       26 if ( $Token->content eq 'package' ) {
128 1         9 return 'keyword';
129             }
130             } elsif ( $Token->parent->isa('PPI::Statement::Scheduled') ) {
131 0         0 return 'keyword';
132             }
133             }
134              
135             # Normal coloring
136 64         443 my $css = ref $Token;
137 64         231 $css =~ s/^.+:://;
138 64         128 $css;
139             }
140              
141              
142             1;
143              
144             __END__