Merge branch 'es/chainlint-lineno'
Teach chainlint.pl to show corresponding line numbers when printing the source of a test. * es/chainlint-lineno: chainlint: prefix annotated test definition with line numbers chainlint: latch line numbers at which each token starts and ends chainlint: sidestep impoverished macOS "terminfo"
This commit is contained in:
commit
cf9721cc46
@ -94,7 +94,7 @@ check-chainlint:
|
|||||||
done \
|
done \
|
||||||
} >'$(CHAINLINTTMP_SQ)'/expect && \
|
} >'$(CHAINLINTTMP_SQ)'/expect && \
|
||||||
$(CHAINLINT) --emit-all '$(CHAINLINTTMP_SQ)'/tests | \
|
$(CHAINLINT) --emit-all '$(CHAINLINTTMP_SQ)'/tests | \
|
||||||
grep -v '^[ ]*$$' >'$(CHAINLINTTMP_SQ)'/actual && \
|
sed -e 's/^[1-9][0-9]* //;/^[ ]*$$/d' >'$(CHAINLINTTMP_SQ)'/actual && \
|
||||||
if test -f ../GIT-BUILD-OPTIONS; then \
|
if test -f ../GIT-BUILD-OPTIONS; then \
|
||||||
. ../GIT-BUILD-OPTIONS; \
|
. ../GIT-BUILD-OPTIONS; \
|
||||||
fi && \
|
fi && \
|
||||||
|
@ -67,6 +67,7 @@ sub new {
|
|||||||
bless {
|
bless {
|
||||||
parser => $parser,
|
parser => $parser,
|
||||||
buff => $s,
|
buff => $s,
|
||||||
|
lineno => 1,
|
||||||
heretags => []
|
heretags => []
|
||||||
} => $class;
|
} => $class;
|
||||||
}
|
}
|
||||||
@ -97,7 +98,9 @@ sub scan_op {
|
|||||||
sub scan_sqstring {
|
sub scan_sqstring {
|
||||||
my $self = shift @_;
|
my $self = shift @_;
|
||||||
${$self->{buff}} =~ /\G([^']*'|.*\z)/sgc;
|
${$self->{buff}} =~ /\G([^']*'|.*\z)/sgc;
|
||||||
return "'" . $1;
|
my $s = $1;
|
||||||
|
$self->{lineno} += () = $s =~ /\n/sg;
|
||||||
|
return "'" . $s;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub scan_dqstring {
|
sub scan_dqstring {
|
||||||
@ -115,7 +118,7 @@ sub scan_dqstring {
|
|||||||
if ($c eq '\\') {
|
if ($c eq '\\') {
|
||||||
$s .= '\\', last unless $$b =~ /\G(.)/sgc;
|
$s .= '\\', last unless $$b =~ /\G(.)/sgc;
|
||||||
$c = $1;
|
$c = $1;
|
||||||
next if $c eq "\n"; # line splice
|
$self->{lineno}++, next if $c eq "\n"; # line splice
|
||||||
# backslash escapes only $, `, ", \ in dq-string
|
# backslash escapes only $, `, ", \ in dq-string
|
||||||
$s .= '\\' unless $c =~ /^[\$`"\\]$/;
|
$s .= '\\' unless $c =~ /^[\$`"\\]$/;
|
||||||
$s .= $c;
|
$s .= $c;
|
||||||
@ -123,6 +126,7 @@ sub scan_dqstring {
|
|||||||
}
|
}
|
||||||
die("internal error scanning dq-string '$c'\n");
|
die("internal error scanning dq-string '$c'\n");
|
||||||
}
|
}
|
||||||
|
$self->{lineno} += () = $s =~ /\n/sg;
|
||||||
return $s;
|
return $s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,6 +141,7 @@ sub scan_balanced {
|
|||||||
$depth--;
|
$depth--;
|
||||||
last if $depth == 0;
|
last if $depth == 0;
|
||||||
}
|
}
|
||||||
|
$self->{lineno} += () = $s =~ /\n/sg;
|
||||||
return $s;
|
return $s;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,8 +168,11 @@ sub swallow_heredocs {
|
|||||||
my $b = $self->{buff};
|
my $b = $self->{buff};
|
||||||
my $tags = $self->{heretags};
|
my $tags = $self->{heretags};
|
||||||
while (my $tag = shift @$tags) {
|
while (my $tag = shift @$tags) {
|
||||||
|
my $start = pos($$b);
|
||||||
my $indent = $tag =~ s/^\t// ? '\\s*' : '';
|
my $indent = $tag =~ s/^\t// ? '\\s*' : '';
|
||||||
$$b =~ /(?:\G|\n)$indent\Q$tag\E(?:\n|\z)/gc;
|
$$b =~ /(?:\G|\n)$indent\Q$tag\E(?:\n|\z)/gc;
|
||||||
|
my $body = substr($$b, $start, pos($$b) - $start);
|
||||||
|
$self->{lineno} += () = $body =~ /\n/sg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,11 +180,12 @@ sub scan_token {
|
|||||||
my $self = shift @_;
|
my $self = shift @_;
|
||||||
my $b = $self->{buff};
|
my $b = $self->{buff};
|
||||||
my $token = '';
|
my $token = '';
|
||||||
my $start;
|
my ($start, $startln);
|
||||||
RESTART:
|
RESTART:
|
||||||
|
$startln = $self->{lineno};
|
||||||
$$b =~ /\G[ \t]+/gc; # skip whitespace (but not newline)
|
$$b =~ /\G[ \t]+/gc; # skip whitespace (but not newline)
|
||||||
$start = pos($$b) || 0;
|
$start = pos($$b) || 0;
|
||||||
return ["\n", $start, pos($$b)] if $$b =~ /\G#[^\n]*(?:\n|\z)/gc; # comment
|
$self->{lineno}++, return ["\n", $start, pos($$b), $startln, $startln] if $$b =~ /\G#[^\n]*(?:\n|\z)/gc; # comment
|
||||||
while (1) {
|
while (1) {
|
||||||
# slurp up non-special characters
|
# slurp up non-special characters
|
||||||
$token .= $1 if $$b =~ /\G([^\\;&|<>(){}'"\$\s]+)/gc;
|
$token .= $1 if $$b =~ /\G([^\\;&|<>(){}'"\$\s]+)/gc;
|
||||||
@ -188,20 +197,20 @@ RESTART:
|
|||||||
$token .= $self->scan_sqstring(), next if $c eq "'";
|
$token .= $self->scan_sqstring(), next if $c eq "'";
|
||||||
$token .= $self->scan_dqstring(), next if $c eq '"';
|
$token .= $self->scan_dqstring(), next if $c eq '"';
|
||||||
$token .= $c . $self->scan_dollar(), next if $c eq '$';
|
$token .= $c . $self->scan_dollar(), next if $c eq '$';
|
||||||
$self->swallow_heredocs(), $token = $c, last if $c eq "\n";
|
$self->{lineno}++, $self->swallow_heredocs(), $token = $c, last if $c eq "\n";
|
||||||
$token = $self->scan_op($c), last if $c =~ /^[;&|<>]$/;
|
$token = $self->scan_op($c), last if $c =~ /^[;&|<>]$/;
|
||||||
$token = $c, last if $c =~ /^[(){}]$/;
|
$token = $c, last if $c =~ /^[(){}]$/;
|
||||||
if ($c eq '\\') {
|
if ($c eq '\\') {
|
||||||
$token .= '\\', last unless $$b =~ /\G(.)/sgc;
|
$token .= '\\', last unless $$b =~ /\G(.)/sgc;
|
||||||
$c = $1;
|
$c = $1;
|
||||||
next if $c eq "\n" && length($token); # line splice
|
$self->{lineno}++, next if $c eq "\n" && length($token); # line splice
|
||||||
goto RESTART if $c eq "\n"; # line splice
|
$self->{lineno}++, goto RESTART if $c eq "\n"; # line splice
|
||||||
$token .= '\\' . $c;
|
$token .= '\\' . $c;
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
die("internal error scanning character '$c'\n");
|
die("internal error scanning character '$c'\n");
|
||||||
}
|
}
|
||||||
return length($token) ? [$token, $start, pos($$b)] : undef;
|
return length($token) ? [$token, $start, pos($$b), $startln, $self->{lineno}] : undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
# ShellParser parses POSIX shell scripts (with minor extensions for Bash). It
|
# ShellParser parses POSIX shell scripts (with minor extensions for Bash). It
|
||||||
@ -605,6 +614,7 @@ sub check_test {
|
|||||||
my $problems = $parser->{problems};
|
my $problems = $parser->{problems};
|
||||||
return unless $emit_all || @$problems;
|
return unless $emit_all || @$problems;
|
||||||
my $c = main::fd_colors(1);
|
my $c = main::fd_colors(1);
|
||||||
|
my $lineno = $_[1]->[3];
|
||||||
my $start = 0;
|
my $start = 0;
|
||||||
my $checked = '';
|
my $checked = '';
|
||||||
for (sort {$a->[1]->[2] <=> $b->[1]->[2]} @$problems) {
|
for (sort {$a->[1]->[2] <=> $b->[1]->[2]} @$problems) {
|
||||||
@ -614,10 +624,12 @@ sub check_test {
|
|||||||
$start = $pos;
|
$start = $pos;
|
||||||
}
|
}
|
||||||
$checked .= substr($body, $start);
|
$checked .= substr($body, $start);
|
||||||
$checked =~ s/^\n//;
|
$checked =~ s/^/$lineno++ . ' '/mge;
|
||||||
|
$checked =~ s/^\d+ \n//;
|
||||||
$checked =~ s/(\s) \?!/$1?!/mg;
|
$checked =~ s/(\s) \?!/$1?!/mg;
|
||||||
$checked =~ s/\?! (\s)/?!$1/mg;
|
$checked =~ s/\?! (\s)/?!$1/mg;
|
||||||
$checked =~ s/(\?![^?]+\?!)/$c->{rev}$c->{red}$1$c->{reset}/mg;
|
$checked =~ s/(\?![^?]+\?!)/$c->{rev}$c->{red}$1$c->{reset}/mg;
|
||||||
|
$checked =~ s/^\d+/$c->{dim}$&$c->{reset}/mg;
|
||||||
$checked .= "\n" unless $checked =~ /\n$/;
|
$checked .= "\n" unless $checked =~ /\n$/;
|
||||||
push(@{$self->{output}}, "$c->{blue}# chainlint: $title$c->{reset}\n$checked");
|
push(@{$self->{output}}, "$c->{blue}# chainlint: $title$c->{reset}\n$checked");
|
||||||
}
|
}
|
||||||
@ -649,25 +661,39 @@ if (eval {require Time::HiRes; Time::HiRes->import(); 1;}) {
|
|||||||
# thread and ignore %ENV changes in subthreads.
|
# thread and ignore %ENV changes in subthreads.
|
||||||
$ENV{TERM} = $ENV{USER_TERM} if $ENV{USER_TERM};
|
$ENV{TERM} = $ENV{USER_TERM} if $ENV{USER_TERM};
|
||||||
|
|
||||||
my @NOCOLORS = (bold => '', rev => '', reset => '', blue => '', green => '', red => '');
|
my @NOCOLORS = (bold => '', rev => '', dim => '', reset => '', blue => '', green => '', red => '');
|
||||||
my %COLORS = ();
|
my %COLORS = ();
|
||||||
sub get_colors {
|
sub get_colors {
|
||||||
return \%COLORS if %COLORS;
|
return \%COLORS if %COLORS;
|
||||||
if (exists($ENV{NO_COLOR}) ||
|
if (exists($ENV{NO_COLOR})) {
|
||||||
system("tput sgr0 >/dev/null 2>&1") != 0 ||
|
|
||||||
system("tput bold >/dev/null 2>&1") != 0 ||
|
|
||||||
system("tput rev >/dev/null 2>&1") != 0 ||
|
|
||||||
system("tput setaf 1 >/dev/null 2>&1") != 0) {
|
|
||||||
%COLORS = @NOCOLORS;
|
%COLORS = @NOCOLORS;
|
||||||
return \%COLORS;
|
return \%COLORS;
|
||||||
}
|
}
|
||||||
%COLORS = (bold => `tput bold`,
|
if ($ENV{TERM} =~ /xterm|xterm-\d+color|xterm-new|xterm-direct|nsterm|nsterm-\d+color|nsterm-direct/) {
|
||||||
rev => `tput rev`,
|
%COLORS = (bold => "\e[1m",
|
||||||
reset => `tput sgr0`,
|
rev => "\e[7m",
|
||||||
blue => `tput setaf 4`,
|
dim => "\e[2m",
|
||||||
green => `tput setaf 2`,
|
reset => "\e[0m",
|
||||||
red => `tput setaf 1`);
|
blue => "\e[34m",
|
||||||
chomp(%COLORS);
|
green => "\e[32m",
|
||||||
|
red => "\e[31m");
|
||||||
|
return \%COLORS;
|
||||||
|
}
|
||||||
|
if (system("tput sgr0 >/dev/null 2>&1") == 0 &&
|
||||||
|
system("tput bold >/dev/null 2>&1") == 0 &&
|
||||||
|
system("tput rev >/dev/null 2>&1") == 0 &&
|
||||||
|
system("tput dim >/dev/null 2>&1") == 0 &&
|
||||||
|
system("tput setaf 1 >/dev/null 2>&1") == 0) {
|
||||||
|
%COLORS = (bold => `tput bold`,
|
||||||
|
rev => `tput rev`,
|
||||||
|
dim => `tput dim`,
|
||||||
|
reset => `tput sgr0`,
|
||||||
|
blue => `tput setaf 4`,
|
||||||
|
green => `tput setaf 2`,
|
||||||
|
red => `tput setaf 1`);
|
||||||
|
return \%COLORS;
|
||||||
|
}
|
||||||
|
%COLORS = @NOCOLORS;
|
||||||
return \%COLORS;
|
return \%COLORS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user