File Coverage

blib/lib/Perl/LanguageServer/SyntaxChecker.pm
Criterion Covered Total %
statement 30 145 20.6
branch 0 40 0.0
condition 0 3 0.0
subroutine 10 18 55.5
pod 0 5 0.0
total 40 211 18.9


line stmt bran cond sub pod time code
1             package Perl::LanguageServer::SyntaxChecker ;
2              
3 1     1   1169 use Moose::Role ;
  1         5466  
  1         8  
4 1     1   5815 use strict ;
  1         2  
  1         34  
5              
6 1     1   6 use Coro ;
  1         3  
  1         62  
7 1     1   5 use Coro::AIO ;
  1         2  
  1         296  
8 1     1   515 use Coro::Channel ;
  1         1322  
  1         32  
9 1     1   7 use AnyEvent::Util ;
  1         2  
  1         66  
10 1     1   799 use File::Temp ;
  1         17560  
  1         71  
11 1     1   637 use Encode ;
  1         10900  
  1         79  
12              
13             #use Proc::FastSpawn;
14              
15 1     1   8 no warnings 'uninitialized' ;
  1         2  
  1         825  
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   8 use Symbol 'gensym'; $err = gensym;
  1         10  
  1         1080  
  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 -> file_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           my @syntax_options ;
236 0 0         if ($self -> use_taint_for_syntax_check) {
237 0           @syntax_options = ('-T') ;
238             }
239              
240 0 0         $self -> logger ("start perl @syntax_options -c @inc for $uri\n") if ($Perl::LanguageServer::debug1) ;
241 0 0         if ($^O =~ /Win/)
242             {
243             # ($ret, $out, $errout) = $self -> run_open3 ($text, \@inc) ;
244 0           ($ret, $out, $errout) = $self -> run_win32 ($text, \@inc) ;
245             }
246             else
247             {
248 0           $ret = run_cmd ([$self -> perlcmd, @syntax_options, '-c', @inc],
249             "<", \$text,
250             ">", \$out,
251             "2>", \$errout)
252             -> recv ;
253             }
254              
255 0           my $rc = $ret >> 8 ;
256 0 0         $self -> logger ("perl -c rc=$rc out=$out errout=$errout\n") if ($Perl::LanguageServer::debug1) ;
257              
258 0           my @messages ;
259 0 0         if ($rc != 0)
260             {
261 0           my $line ;
262 0           my @lines = split /\n/, $errout ;
263 0           my $lineno = 0 ;
264 0           my $filename ;
265 0           my $lastline = 1 ;
266 0           my $msg ;
267 0           my $severity = 1 ;
268 0           foreach $line (@lines)
269             {
270 0           $line =~ s/\s*$// ;
271             #print STDERR $line, "\n" ;
272 0 0         next if ($line =~ /had compilation errors/) ;
273 0 0         $filename = $1 if ($line =~ /at (.+?) line (\d+)[,.]/) ;
274             #print STDERR "line = $lineno file=$filename fn=$fn\n" ;
275 0   0       $filename ||= $fn ;
276 0 0         $lineno = $1 if ($line =~ / line (\d+)[,.]/) ;
277              
278 0           $msg .= $line ;
279 0 0         if ($lineno)
280             {
281 0 0         push @messages, [$filename, $lineno, $severity, $msg] if ($msg) ;
282 0           $lastline = $lineno ;
283 0           $lineno = 0 ;
284 0           $msg = '' ;
285             }
286             }
287             }
288              
289 0           $self -> add_diagnostic_messages ($server, $uri, 'perl syntax', \@messages) ;
290             }
291             }
292              
293             1;
294              
295             __END__
296              
297             sub xxxx
298             {
299              
300             my $infile = $self -> infile ;
301             my $outfile = $self -> outfile ;
302              
303             print STDERR "infile=$infile outfile=$outfile\n" ;
304             my $ifh = aio_open ($infile, IO::AIO::O_WRONLY | IO::AIO::O_TRUNC | IO::AIO::O_CREAT, 0600) or die "open $infile failed ($!)" ;
305             aio_write ($ifh, undef, undef, $text, 0) ;
306             aio_close ($ifh) ;
307              
308             # my $oldstderr ;
309             # open($oldstderr, ">&", \*STDERR) or die "Can't dup STDERR: $!";
310             # open(STDERR, '>', $outfile) or die "Can't redirect STDERR: $!";
311             # print STDERR "start\n" ;
312             # my $pid = spawnp "perl", ["perl", "-c", $infile];
313             # open(STDERR, ">&", $oldstderr) or die "Can't dup \$oldstderr: $!";
314              
315             #my $pid = spawnp "cmd", ["cmd", '/C', "perl -c $infile 2> $outfile"];
316             my $pid = spawnp $workspace -> perlcmd, [$workspace -> perlcmd, ]
317              
318             print STDERR "pid=$pid\n" ;
319              
320             my $w = AnyEvent->child (pid => $pid, cb => rouse_cb) ;
321             my $ret = rouse_wait ;
322             undef $w ;
323             #Coro::AnyEvent::sleep (1) ;
324             #print STDERR "wait\n" ;
325             #waitpid ($pid, 0) ;
326             #my $ret = $? ;
327             my $rc = $ret >> 8;
328             print STDERR "perl -c rc=$rc\n" ;
329              
330             #aio_slurp ($outfile, 0, 0, $errout) ;
331             aio_load ($outfile, $errout) ;
332             print STDERR "errout = $errout\n" ;
333              
334             #return ;
335              
336             #my ($rc, $diags) = rouse_wait ;
337             my $diags = [] ;
338              
339             print STDERR "---perl -c rc=$rc\n" ;
340              
341             return if ($rc == 0) ;
342              
343             my $result =
344             {
345             method => 'textDocument/publishDiagnostics',
346             params =>
347             {
348             uri => $uri,
349             diagnostics => $diags,
350             },
351             } ;
352              
353             $self -> send_notification ($result) ;
354             }
355              
356              
357              
358             # my $cv = run_cmd [$workspace -> perlcmd, '-c'],
359             # # "<", \$text,
360             # "2>", \$errout
361             # ;
362              
363             # $cv->cb (sub
364             # {
365             # shift->recv and die "perl -c failed";
366              
367             # print "-------->$errout\n";
368             # });
369              
370             # return ;
371              
372             AnyEvent::Util::fork_call (sub
373             {
374             print STDERR "open3 start c $$\n" ;
375             IO::AIO::reinit ;
376              
377             my($wtr, $rdr, $err);
378              
379             #return ;
380              
381             # use Symbol 'gensym'; $err = gensym;
382             my $pid = open3($wtr, $rdr, $err, $workspace -> perlcmd, '-c') or die "Cannot run " . $workspace -> perlcmd ;
383             #cede () ;
384             print STDERR "write start pid=$pid\n" ;
385             syswrite ($wtr, $text . "\n__END__\n") ;
386             print STDERR "close start\n" ;
387             close ($wtr) ;
388             print STDERR "write done\n" ;
389             #my $errout = unblock $err ;
390             my @diags ;
391             my $line ;
392             # while ($line = $errout -> readline)
393             while ($line = <$rdr>)
394             {
395             $line =~ s/\s*$// ;
396             print STDERR $line, "\n" ;
397             next if ($line =~ /had compilation errors/) ;
398             my $lineno = 0 ;
399             $lineno = $1 if ($line =~ / line (\d+),/) ;
400             my $diag =
401             {
402             # range: Range;
403             # severity?: number;
404             # code?: number | string;
405             # source?: string;
406             # message: string;
407             # relatedInformation?: DiagnosticRelatedInformation[];
408             range => { start => { line => $lineno-1, character => 0 }, end => { line => $lineno+0, character => 0 }},
409             message => $line,
410             } ;
411             push @diags, $diag ;
412             }
413              
414             print STDERR "EOF\n" ;
415              
416             waitpid( $pid, 0 );
417             my $rc = $? >> 8;
418             print STDERR "perl -c rc=$rc\n" ;
419             return ($rc, \@diags) ;
420             }, rouse_cb ) ;
421              
422             my ($rc, $diags) = rouse_wait ;
423              
424             print STDERR "---perl -c rc=$rc\n" ;
425              
426             return if ($rc == 0) ;
427              
428             my $result =
429             {
430             method => 'textDocument/publishDiagnostics',
431             params =>
432             {
433             uri => $uri,
434             diagnostics => $diags,
435             },
436             } ;
437              
438             $self -> send_notification ($result) ;
439             }
440              
441             1 ;