File Coverage

blib/lib/Perl/LanguageServer/SyntaxChecker.pm
Criterion Covered Total %
statement 30 141 21.2
branch 0 38 0.0
condition n/a
subroutine 10 18 55.5
pod 0 5 0.0
total 40 202 19.8


line stmt bran cond sub pod time code
1             package Perl::LanguageServer::SyntaxChecker ;
2              
3 1     1   1225 use Moose::Role ;
  1         5153  
  1         4  
4 1     1   5553 use strict ;
  1         2  
  1         65  
5              
6 1     1   7 use Coro ;
  1         3  
  1         75  
7 1     1   8 use Coro::AIO ;
  1         2  
  1         303  
8 1     1   467 use Coro::Channel ;
  1         1196  
  1         30  
9 1     1   7 use AnyEvent::Util ;
  1         2  
  1         73  
10 1     1   699 use File::Temp ;
  1         16577  
  1         92  
11 1     1   591 use Encode ;
  1         10249  
  1         87  
12              
13             #use Proc::FastSpawn;
14              
15 1     1   8 no warnings 'uninitialized' ;
  1         3  
  1         772  
16              
17             # ---------------------------------------------------------------------------
18              
19              
20             has 'infile' =>
21             (
22             is => 'rw',
23             isa => 'Str',
24             lazy_build => 1,
25             ) ;
26              
27             has 'outfile' =>
28             (
29             is => 'rw',
30             isa => 'Str',
31             lazy_build => 1,
32             ) ;
33              
34             has 'checker_channel' =>
35             (
36             is => 'ro',
37             isa => 'Coro::Channel',
38             default => sub { Coro::Channel -> new }
39             ) ;
40              
41             has 'checker2_channel' =>
42             (
43             is => 'ro',
44             isa => 'Coro::Channel',
45             default => sub { Coro::Channel -> new }
46             ) ;
47              
48             # ---------------------------------------------------------------------------
49              
50             sub _build_infile
51             {
52 0     0     my ($fh, $filename) = File::Temp::tempfile();
53 0           close $fh ;
54              
55 0           return $filename ;
56             }
57              
58             # ---------------------------------------------------------------------------
59              
60             sub _build_outfile
61             {
62 0     0     my ($fh, $filename) = File::Temp::tempfile();
63 0           close $fh ;
64              
65 0           return $filename ;
66             }
67              
68              
69             # ---------------------------------------------------------------------------
70              
71             sub check_perl_syntax
72             {
73 0     0 0   my ($self, $workspace, $uri, $text) = @_ ;
74              
75 0           $self -> checker_channel -> put ([$uri, $text]) ;
76             }
77              
78              
79             # ---------------------------------------------------------------------------
80              
81             sub run_win32
82             {
83 0     0 0   my ($self, $text, $inc) = @_ ;
84              
85              
86 0           return (0, undef, undef) ; # disable for now on windows
87              
88 0           my $infile = $self -> infile ;
89 0           my $outfile = $self -> outfile ;
90              
91 0           print STDERR "infile=$infile outfile=$outfile\n" ;
92 0 0         my $ifh = aio_open ($infile, IO::AIO::O_WRONLY | IO::AIO::O_TRUNC | IO::AIO::O_CREAT, 0600) or die "open $infile failed ($!)" ;
93 0           aio_write ($ifh, undef, undef, $text, 0) ;
94 0           aio_close ($ifh) ;
95              
96 0           print STDERR "run ", $self -> perlcmd . " -c @$inc $infile 2> $outfile", "\n" ;
97              
98             # use Win32::Process ;
99              
100             # my $cmd = $self -> perlcmd . " -c @$inc $infile" ;
101              
102             # print STDERR $cmd, "\n" ;
103              
104             # my $ProcessObj ;
105 0           my $rc ;
106             # Win32::Process::Create($ProcessObj,
107              
108             # $self -> perlcmd,
109             # $cmd,
110             # 0,
111             # NORMAL_PRIORITY_CLASS,
112             # ".");
113              
114             # print STDERR "wait\n" ;
115              
116             # $ProcessObj->Wait(5000) ;
117              
118 0           print STDERR "done\n" ;
119              
120 0           my $errout ;
121             my $out ;
122 0           aio_load ($outfile, $errout) ;
123 0           print STDERR "errout = $errout\n" ;
124              
125 0           return ($rc, $out, $errout) ;
126             }
127              
128              
129             # ---------------------------------------------------------------------------
130              
131             sub run_system
132             {
133 0     0 0   my ($self, $text, $inc) = @_ ;
134              
135 0           my $infile = $self -> infile ;
136 0           my $outfile = $self -> outfile ;
137              
138 0           local $SIG{CHLD} = 'DEFAULT' ;
139 0           local $SIG{PIPE} = 'DEFAULT' ;
140              
141 0           print STDERR "infile=$infile outfile=$outfile\n" ;
142 0 0         my $ifh = aio_open ($infile, IO::AIO::O_WRONLY | IO::AIO::O_TRUNC | IO::AIO::O_CREAT, 0600) or die "open $infile failed ($!)" ;
143 0           aio_write ($ifh, undef, undef, $text, 0) ;
144 0           aio_close ($ifh) ;
145              
146 0           print STDERR "run ", $self -> perlcmd . " -c @$inc $infile 2> $outfile", "\n" ;
147 0           my $rc = system ($self -> perlcmd . " -c @$inc $infile 2> $outfile") ;
148 0           print STDERR "done\n" ;
149              
150 0           my $errout ;
151             my $out ;
152 0           aio_load ($outfile, $errout) ;
153 0           print STDERR "errout = $errout\n" ;
154              
155 0           return ($rc, $out, $errout) ;
156             }
157              
158             # ---------------------------------------------------------------------------
159              
160             sub run_open3
161             {
162 0     0 0   my ($self, $text, $inc) = @_ ;
163              
164             #return (0, undef, undef) ;
165              
166 0           my($wtr, $rdr, $err);
167              
168 0           require IPC::Open3 ;
169 1     1   10 use Symbol 'gensym'; $err = gensym;
  1         2  
  1         1113  
  0            
170 0 0         $self -> logger ("open3\n") if ($Perl::LanguageServer::debug2) ;
171 0 0         my $pid = IPC::Open3::open3($wtr, $rdr, $err, $self -> perlcmd, '-c', @$inc) or die "Cannot run " . $self -> perlcmd ;
172 0 0         $self -> logger ("write start pid=$pid\n") if ($Perl::LanguageServer::debug2) ;
173 0           syswrite ($wtr, $text . "\n__END__\n") ;
174 0 0         $self -> logger ("close start\n") if ($Perl::LanguageServer::debug2) ;
175 0           close ($wtr) ;
176 0 0         $self -> logger ("write done\n") if ($Perl::LanguageServer::debug2) ;
177              
178 0           my $out ;
179 0           my $errout = join ('', <$err>) ;
180 0           close $err ;
181 0           close $rdr ;
182 0 0         $self -> logger ("closed\n") if ($Perl::LanguageServer::debug2) ;
183 0           waitpid( $pid, 0 );
184 0           my $rc = $? ;
185              
186 0           return ($rc, $out, $errout) ;
187             }
188              
189             # ---------------------------------------------------------------------------
190              
191             sub background_checker
192             {
193 0     0 0   my ($self, $server) = @_ ;
194              
195             async
196             {
197 0     0     my $channel1 = $self -> checker_channel ;
198 0           my $channel2 = $self -> checker2_channel ;
199              
200 0           my %timer ;
201 0           while (my $cmd = $channel1 -> get)
202             {
203 0           my ($uri, $text) = @$cmd ;
204              
205             $timer{$uri} = AnyEvent->timer (after => 1.5, cb => sub
206             {
207 0           delete $timer{$uri} ;
208 0           $channel2 -> put($cmd) ;
209 0           }) ;
210             }
211              
212 0           } ;
213              
214 0           my $channel = $self -> checker2_channel ;
215              
216 0           while (my $cmd = $channel -> get)
217             {
218 0           my ($uri, $text) = @$cmd ;
219              
220 0           $text = eval { Encode::encode ('utf-8', $text) ; } ;
  0            
221 0 0         $self -> logger ($@) if ($@) ;
222              
223 0           my $fn = $uri ;
224 0           $fn =~ s/^file:\/\/// ;
225 0           $fn = $self -> uri_client2server ($fn) ;
226 0           $text = "local \$0; BEGIN { \$0 = '$fn'; if (\$INC{'FindBin.pm'}) { FindBin->again(); } }\n# line 1 \"$fn\"\n" . $text;
227              
228 0           my $ret ;
229             my $errout ;
230 0           my $out ;
231 0           my $inc = $self -> perlinc ;
232 0           my @inc ;
233 0 0         @inc = map { ('-I', $_)} @$inc if ($inc) ;
  0            
234              
235 0 0         $self -> logger ("start perl -c for $uri\n") if ($Perl::LanguageServer::debug1) ;
236 0 0         if ($^O =~ /Win/)
237             {
238             # ($ret, $out, $errout) = $self -> run_open3 ($text, \@inc) ;
239 0           ($ret, $out, $errout) = $self -> run_win32 ($text, \@inc) ;
240             }
241             else
242             {
243 0           $ret = run_cmd ([$self -> perlcmd, '-c', @inc],
244             "<", \$text,
245             ">", \$out,
246             "2>", \$errout)
247             -> recv ;
248             }
249              
250 0           my $rc = $ret >> 8 ;
251 0 0         $self -> logger ("perl -c rc=$rc out=$out errout=$errout\n") if ($Perl::LanguageServer::debug1) ;
252              
253 0           my @messages ;
254 0 0         if ($rc != 0)
255             {
256 0           my $line ;
257 0           my @lines = split /\n/, $errout ;
258 0           my $lineno = 0 ;
259 0           my $filename ;
260 0           my $lastline = 1 ;
261 0           my $msg ;
262 0           my $severity = 1 ;
263 0           foreach $line (@lines)
264             {
265 0           $line =~ s/\s*$// ;
266             #print STDERR $line, "\n" ;
267 0 0         next if ($line =~ /had compilation errors/) ;
268 0 0         $filename = $1 if ($line =~ /at (.+?) line (\d+)[,.]/) ;
269 0 0         $lineno = $1 if ($line =~ / line (\d+)[,.]/) ;
270              
271             #print STDERR "line = $lineno file=$filename\n" ;
272 0           $msg .= $line ;
273 0 0         if ($lineno)
274             {
275 0 0         push @messages, [$filename, $lineno, $severity, $msg] if ($msg) ;
276 0           $lastline = $lineno ;
277 0           $lineno = 0 ;
278 0           $msg = '' ;
279             }
280             }
281             }
282              
283 0           $self -> add_diagnostic_messages ($server, $uri, 'perl syntax', \@messages) ;
284             }
285             }
286              
287             1;
288              
289             __END__
290              
291             sub xxxx
292             {
293              
294             my $infile = $self -> infile ;
295             my $outfile = $self -> outfile ;
296              
297             print STDERR "infile=$infile outfile=$outfile\n" ;
298             my $ifh = aio_open ($infile, IO::AIO::O_WRONLY | IO::AIO::O_TRUNC | IO::AIO::O_CREAT, 0600) or die "open $infile failed ($!)" ;
299             aio_write ($ifh, undef, undef, $text, 0) ;
300             aio_close ($ifh) ;
301              
302             # my $oldstderr ;
303             # open($oldstderr, ">&", \*STDERR) or die "Can't dup STDERR: $!";
304             # open(STDERR, '>', $outfile) or die "Can't redirect STDERR: $!";
305             # print STDERR "start\n" ;
306             # my $pid = spawnp "perl", ["perl", "-c", $infile];
307             # open(STDERR, ">&", $oldstderr) or die "Can't dup \$oldstderr: $!";
308              
309             #my $pid = spawnp "cmd", ["cmd", '/C', "perl -c $infile 2> $outfile"];
310             my $pid = spawnp $workspace -> perlcmd, [$workspace -> perlcmd, ]
311              
312             print STDERR "pid=$pid\n" ;
313              
314             my $w = AnyEvent->child (pid => $pid, cb => rouse_cb) ;
315             my $ret = rouse_wait ;
316             undef $w ;
317             #Coro::AnyEvent::sleep (1) ;
318             #print STDERR "wait\n" ;
319             #waitpid ($pid, 0) ;
320             #my $ret = $? ;
321             my $rc = $ret >> 8;
322             print STDERR "perl -c rc=$rc\n" ;
323              
324             #aio_slurp ($outfile, 0, 0, $errout) ;
325             aio_load ($outfile, $errout) ;
326             print STDERR "errout = $errout\n" ;
327              
328             #return ;
329              
330             #my ($rc, $diags) = rouse_wait ;
331             my $diags = [] ;
332              
333             print STDERR "---perl -c rc=$rc\n" ;
334              
335             return if ($rc == 0) ;
336              
337             my $result =
338             {
339             method => 'textDocument/publishDiagnostics',
340             params =>
341             {
342             uri => $uri,
343             diagnostics => $diags,
344             },
345             } ;
346              
347             $self -> send_notification ($result) ;
348             }
349              
350              
351              
352             # my $cv = run_cmd [$workspace -> perlcmd, '-c'],
353             # # "<", \$text,
354             # "2>", \$errout
355             # ;
356              
357             # $cv->cb (sub
358             # {
359             # shift->recv and die "perl -c failed";
360              
361             # print "-------->$errout\n";
362             # });
363              
364             # return ;
365              
366             AnyEvent::Util::fork_call (sub
367             {
368             print STDERR "open3 start c $$\n" ;
369             IO::AIO::reinit ;
370              
371             my($wtr, $rdr, $err);
372              
373             #return ;
374              
375             # use Symbol 'gensym'; $err = gensym;
376             my $pid = open3($wtr, $rdr, $err, $workspace -> perlcmd, '-c') or die "Cannot run " . $workspace -> perlcmd ;
377             #cede () ;
378             print STDERR "write start pid=$pid\n" ;
379             syswrite ($wtr, $text . "\n__END__\n") ;
380             print STDERR "close start\n" ;
381             close ($wtr) ;
382             print STDERR "write done\n" ;
383             #my $errout = unblock $err ;
384             my @diags ;
385             my $line ;
386             # while ($line = $errout -> readline)
387             while ($line = <$rdr>)
388             {
389             $line =~ s/\s*$// ;
390             print STDERR $line, "\n" ;
391             next if ($line =~ /had compilation errors/) ;
392             my $lineno = 0 ;
393             $lineno = $1 if ($line =~ / line (\d+),/) ;
394             my $diag =
395             {
396             # range: Range;
397             # severity?: number;
398             # code?: number | string;
399             # source?: string;
400             # message: string;
401             # relatedInformation?: DiagnosticRelatedInformation[];
402             range => { start => { line => $lineno-1, character => 0 }, end => { line => $lineno+0, character => 0 }},
403             message => $line,
404             } ;
405             push @diags, $diag ;
406             }
407              
408             print STDERR "EOF\n" ;
409              
410             waitpid( $pid, 0 );
411             my $rc = $? >> 8;
412             print STDERR "perl -c rc=$rc\n" ;
413             return ($rc, \@diags) ;
414             }, rouse_cb ) ;
415              
416             my ($rc, $diags) = rouse_wait ;
417              
418             print STDERR "---perl -c rc=$rc\n" ;
419              
420             return if ($rc == 0) ;
421              
422             my $result =
423             {
424             method => 'textDocument/publishDiagnostics',
425             params =>
426             {
427             uri => $uri,
428             diagnostics => $diags,
429             },
430             } ;
431              
432             $self -> send_notification ($result) ;
433             }
434              
435             1 ;