Skip to content

Commit cf9721c

Browse files
committed
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"
2 parents ff84d03 + 48d69d8 commit cf9721c

File tree

2 files changed

+49
-23
lines changed

2 files changed

+49
-23
lines changed

t/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ check-chainlint:
9494
done \
9595
} >'$(CHAINLINTTMP_SQ)'/expect && \
9696
$(CHAINLINT) --emit-all '$(CHAINLINTTMP_SQ)'/tests | \
97-
grep -v '^[ ]*$$' >'$(CHAINLINTTMP_SQ)'/actual && \
97+
sed -e 's/^[1-9][0-9]* //;/^[ ]*$$/d' >'$(CHAINLINTTMP_SQ)'/actual && \
9898
if test -f ../GIT-BUILD-OPTIONS; then \
9999
. ../GIT-BUILD-OPTIONS; \
100100
fi && \

t/chainlint.pl

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ sub new {
6767
bless {
6868
parser => $parser,
6969
buff => $s,
70+
lineno => 1,
7071
heretags => []
7172
} => $class;
7273
}
@@ -97,7 +98,9 @@ sub scan_op {
9798
sub scan_sqstring {
9899
my $self = shift @_;
99100
${$self->{buff}} =~ /\G([^']*'|.*\z)/sgc;
100-
return "'" . $1;
101+
my $s = $1;
102+
$self->{lineno} += () = $s =~ /\n/sg;
103+
return "'" . $s;
101104
}
102105

103106
sub scan_dqstring {
@@ -115,14 +118,15 @@ sub scan_dqstring {
115118
if ($c eq '\\') {
116119
$s .= '\\', last unless $$b =~ /\G(.)/sgc;
117120
$c = $1;
118-
next if $c eq "\n"; # line splice
121+
$self->{lineno}++, next if $c eq "\n"; # line splice
119122
# backslash escapes only $, `, ", \ in dq-string
120123
$s .= '\\' unless $c =~ /^[\$`"\\]$/;
121124
$s .= $c;
122125
next;
123126
}
124127
die("internal error scanning dq-string '$c'\n");
125128
}
129+
$self->{lineno} += () = $s =~ /\n/sg;
126130
return $s;
127131
}
128132

@@ -137,6 +141,7 @@ sub scan_balanced {
137141
$depth--;
138142
last if $depth == 0;
139143
}
144+
$self->{lineno} += () = $s =~ /\n/sg;
140145
return $s;
141146
}
142147

@@ -163,20 +168,24 @@ sub swallow_heredocs {
163168
my $b = $self->{buff};
164169
my $tags = $self->{heretags};
165170
while (my $tag = shift @$tags) {
171+
my $start = pos($$b);
166172
my $indent = $tag =~ s/^\t// ? '\\s*' : '';
167173
$$b =~ /(?:\G|\n)$indent\Q$tag\E(?:\n|\z)/gc;
174+
my $body = substr($$b, $start, pos($$b) - $start);
175+
$self->{lineno} += () = $body =~ /\n/sg;
168176
}
169177
}
170178

171179
sub scan_token {
172180
my $self = shift @_;
173181
my $b = $self->{buff};
174182
my $token = '';
175-
my $start;
183+
my ($start, $startln);
176184
RESTART:
185+
$startln = $self->{lineno};
177186
$$b =~ /\G[ \t]+/gc; # skip whitespace (but not newline)
178187
$start = pos($$b) || 0;
179-
return ["\n", $start, pos($$b)] if $$b =~ /\G#[^\n]*(?:\n|\z)/gc; # comment
188+
$self->{lineno}++, return ["\n", $start, pos($$b), $startln, $startln] if $$b =~ /\G#[^\n]*(?:\n|\z)/gc; # comment
180189
while (1) {
181190
# slurp up non-special characters
182191
$token .= $1 if $$b =~ /\G([^\\;&|<>(){}'"\$\s]+)/gc;
@@ -188,20 +197,20 @@ sub scan_token {
188197
$token .= $self->scan_sqstring(), next if $c eq "'";
189198
$token .= $self->scan_dqstring(), next if $c eq '"';
190199
$token .= $c . $self->scan_dollar(), next if $c eq '$';
191-
$self->swallow_heredocs(), $token = $c, last if $c eq "\n";
200+
$self->{lineno}++, $self->swallow_heredocs(), $token = $c, last if $c eq "\n";
192201
$token = $self->scan_op($c), last if $c =~ /^[;&|<>]$/;
193202
$token = $c, last if $c =~ /^[(){}]$/;
194203
if ($c eq '\\') {
195204
$token .= '\\', last unless $$b =~ /\G(.)/sgc;
196205
$c = $1;
197-
next if $c eq "\n" && length($token); # line splice
198-
goto RESTART if $c eq "\n"; # line splice
206+
$self->{lineno}++, next if $c eq "\n" && length($token); # line splice
207+
$self->{lineno}++, goto RESTART if $c eq "\n"; # line splice
199208
$token .= '\\' . $c;
200209
next;
201210
}
202211
die("internal error scanning character '$c'\n");
203212
}
204-
return length($token) ? [$token, $start, pos($$b)] : undef;
213+
return length($token) ? [$token, $start, pos($$b), $startln, $self->{lineno}] : undef;
205214
}
206215

207216
# ShellParser parses POSIX shell scripts (with minor extensions for Bash). It
@@ -605,6 +614,7 @@ sub check_test {
605614
my $problems = $parser->{problems};
606615
return unless $emit_all || @$problems;
607616
my $c = main::fd_colors(1);
617+
my $lineno = $_[1]->[3];
608618
my $start = 0;
609619
my $checked = '';
610620
for (sort {$a->[1]->[2] <=> $b->[1]->[2]} @$problems) {
@@ -614,10 +624,12 @@ sub check_test {
614624
$start = $pos;
615625
}
616626
$checked .= substr($body, $start);
617-
$checked =~ s/^\n//;
627+
$checked =~ s/^/$lineno++ . ' '/mge;
628+
$checked =~ s/^\d+ \n//;
618629
$checked =~ s/(\s) \?!/$1?!/mg;
619630
$checked =~ s/\?! (\s)/?!$1/mg;
620631
$checked =~ s/(\?![^?]+\?!)/$c->{rev}$c->{red}$1$c->{reset}/mg;
632+
$checked =~ s/^\d+/$c->{dim}$&$c->{reset}/mg;
621633
$checked .= "\n" unless $checked =~ /\n$/;
622634
push(@{$self->{output}}, "$c->{blue}# chainlint: $title$c->{reset}\n$checked");
623635
}
@@ -649,25 +661,39 @@ package main;
649661
# thread and ignore %ENV changes in subthreads.
650662
$ENV{TERM} = $ENV{USER_TERM} if $ENV{USER_TERM};
651663

652-
my @NOCOLORS = (bold => '', rev => '', reset => '', blue => '', green => '', red => '');
664+
my @NOCOLORS = (bold => '', rev => '', dim => '', reset => '', blue => '', green => '', red => '');
653665
my %COLORS = ();
654666
sub get_colors {
655667
return \%COLORS if %COLORS;
656-
if (exists($ENV{NO_COLOR}) ||
657-
system("tput sgr0 >/dev/null 2>&1") != 0 ||
658-
system("tput bold >/dev/null 2>&1") != 0 ||
659-
system("tput rev >/dev/null 2>&1") != 0 ||
660-
system("tput setaf 1 >/dev/null 2>&1") != 0) {
668+
if (exists($ENV{NO_COLOR})) {
661669
%COLORS = @NOCOLORS;
662670
return \%COLORS;
663671
}
664-
%COLORS = (bold => `tput bold`,
665-
rev => `tput rev`,
666-
reset => `tput sgr0`,
667-
blue => `tput setaf 4`,
668-
green => `tput setaf 2`,
669-
red => `tput setaf 1`);
670-
chomp(%COLORS);
672+
if ($ENV{TERM} =~ /xterm|xterm-\d+color|xterm-new|xterm-direct|nsterm|nsterm-\d+color|nsterm-direct/) {
673+
%COLORS = (bold => "\e[1m",
674+
rev => "\e[7m",
675+
dim => "\e[2m",
676+
reset => "\e[0m",
677+
blue => "\e[34m",
678+
green => "\e[32m",
679+
red => "\e[31m");
680+
return \%COLORS;
681+
}
682+
if (system("tput sgr0 >/dev/null 2>&1") == 0 &&
683+
system("tput bold >/dev/null 2>&1") == 0 &&
684+
system("tput rev >/dev/null 2>&1") == 0 &&
685+
system("tput dim >/dev/null 2>&1") == 0 &&
686+
system("tput setaf 1 >/dev/null 2>&1") == 0) {
687+
%COLORS = (bold => `tput bold`,
688+
rev => `tput rev`,
689+
dim => `tput dim`,
690+
reset => `tput sgr0`,
691+
blue => `tput setaf 4`,
692+
green => `tput setaf 2`,
693+
red => `tput setaf 1`);
694+
return \%COLORS;
695+
}
696+
%COLORS = @NOCOLORS;
671697
return \%COLORS;
672698
}
673699

0 commit comments

Comments
 (0)