File Coverage

blib/lib/PLS/Parser/DocumentSymbols.pm
Criterion Covered Total %
statement 21 77 27.2
branch 0 28 0.0
condition 0 15 0.0
subroutine 7 10 70.0
pod 0 2 0.0
total 28 132 21.2


line stmt bran cond sub pod time code
1             package PLS::Parser::DocumentSymbols;
2              
3 11     11   101 use strict;
  11         148  
  11         609  
4 11     11   341 use warnings;
  11         204  
  11         737  
5              
6 11     11   238 use feature 'state';
  11         57  
  11         2181  
7              
8 11     11   114 use IO::Async::Function;
  11         243  
  11         613  
9 11     11   87 use IO::Async::Loop;
  11         46  
  11         617  
10 11     11   91 use Scalar::Util qw(blessed);
  11         127  
  11         4578  
11              
12             use constant {
13 11         26415 PACKAGE => 4,
14             FUNCTION => 12,
15             VARIABLE => 13,
16             CONSTANT => 14
17 11     11   98 };
  11         37  
18              
19             =head1 NAME
20              
21             PLS::Parser::DocumentSymbols
22              
23             =head1 DESCRIPTION
24              
25             This class parses a document to find all symbols.
26             It returns a hierachy of the symbols, so that a tree structure can be displayed.
27              
28             =cut
29              
30             sub get_all_document_symbols_async
31             {
32 0     0 0   my ($class, $uri) = @_;
33              
34 0           state $function;
35              
36 0 0         if (ref $function ne 'IO::Async::Function')
37             {
38 0           $function = IO::Async::Function->new(code => \&get_all_document_symbols);
39 0           IO::Async::Loop->new->add($function);
40             }
41              
42 0           require PLS::Parser::Document;
43 0           my $text = PLS::Parser::Document->text_from_uri($uri);
44 0           return $function->call(args => [$class, $uri, $text]);
45             } ## end sub get_all_document_symbols_async
46              
47             sub get_all_document_symbols
48             {
49 0     0 0   my ($class, $uri, $text) = @_;
50              
51 0           require PLS::Parser::Document;
52 0           require PLS::Parser::Element;
53              
54 0           my $document = PLS::Parser::Document->new(uri => $uri, text => $text);
55 0 0         return [] if (ref $document ne 'PLS::Parser::Document');
56              
57 0           my @roots;
58 0           $class->_get_all_document_symbols($document, $document->{document}, \@roots);
59              
60 0           my @package_roots;
61              
62 0           my $packages = $document->get_packages();
63              
64 0           foreach my $index (0 .. $#{$packages})
  0            
65             {
66 0           my $line_start = $packages->[$index]->lsp_line_number;
67 0 0         my $line_end = $index == $#{$packages} ? undef : $packages->[$index + 1]->lsp_line_number;
  0            
68 0           my $range = $packages->[$index]->range();
69              
70             push @package_roots,
71             {
72             name => $packages->[$index]->name,
73             kind => PACKAGE,
74             range => $range,
75             selectionRange => $range,
76 0 0 0       children => [grep { $_->{range}{start}{line} > $line_start and (not defined $line_end or $_->{range}{end}{line} < $line_end) } @roots]
  0            
77             };
78             } ## end foreach my $index (0 .. $#{...})
79              
80 0 0         if (not scalar @package_roots)
81             {
82 0           my $range = PLS::Parser::Element->new(element => $document->{document})->range();
83              
84 0           push @package_roots,
85             {
86             name => 'main',
87             kind => PACKAGE,
88             range => $range,
89             selectionRange => $range,
90             children => \@roots
91             };
92             } ## end if (not scalar @package_roots...)
93              
94 0           return \@package_roots;
95             } ## end sub get_all_document_symbols
96              
97             sub _get_all_document_symbols
98             {
99 0     0     my ($class, $document, $scope, $roots, $current) = @_;
100              
101 0           require PLS::Parser::Element;
102              
103 0 0         my $array = ref $current eq 'HASH' ? $current->{children} : $roots;
104 0 0         return unless blessed($scope);
105              
106 0 0 0       if ($scope->isa('PPI::Document') or $scope->isa('PPI::Structure::Block'))
    0 0        
    0 0        
    0 0        
107             {
108 0           foreach my $child ($scope->children)
109             {
110 0           $class->_get_all_document_symbols($document, $child, $roots, $current);
111             }
112             } ## end if ($scope->isa('PPI::Document'...))
113             elsif ($scope->isa('PPI::Statement::Sub') or $scope->isa('PPI::Statement::Scheduled'))
114             {
115             # Don't show subroutine forward declarations
116 0 0         return unless blessed($scope->block);
117 0 0         return unless $scope->block->isa('PPI::Structure::Block');
118 0           my $range = PLS::Parser::Element->new(element => $scope)->range();
119              
120 0 0         $current = {
121             name => $scope->isa('PPI::Statement::Sub') ? $scope->name : $scope->type,
122             kind => FUNCTION,
123             range => $range,
124             selectionRange => $range,
125             children => []
126             };
127              
128 0           push @{$array}, $current;
  0            
129              
130 0           $class->_get_all_document_symbols($document, $scope->block, $roots, $current);
131             } ## end elsif ($scope->isa('PPI::Statement::Sub'...))
132             elsif ($scope->isa('PPI::Statement::Variable'))
133             {
134 0           foreach my $statement (@{$document->get_variable_statements($scope)})
  0            
135             {
136 0           foreach my $symbol (@{$statement->symbols})
  0            
137             {
138 0           my $range = $symbol->range();
139              
140 0           push @{$array},
  0            
141             {
142             name => $symbol->name,
143             kind => VARIABLE,
144             range => $range,
145             selectionRange => $range
146             };
147             } ## end foreach my $symbol (@{$statement...})
148             } ## end foreach my $statement (@{$document...})
149             } ## end elsif ($scope->isa('PPI::Statement::Variable'...))
150             elsif ($scope->isa('PPI::Statement::Include') and $scope->type eq 'use' and $scope->pragma eq 'constant')
151             {
152 0           foreach my $constant (@{$document->get_constants($scope)})
  0            
153             {
154 0           my $range = $constant->range();
155              
156 0           push @{$array},
  0            
157             {
158             name => $constant->name,
159             kind => CONSTANT,
160             range => $range,
161             selectionRange => $range
162             };
163             } ## end foreach my $constant (@{$document...})
164             } ## end elsif ($scope->isa('PPI::Statement::Include'...))
165              
166 0           return;
167             } ## end sub _get_all_document_symbols
168              
169             1;