-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathfixup-doc-links.pl
executable file
·187 lines (163 loc) · 5.24 KB
/
fixup-doc-links.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#!/usr/bin/perl -w
# Version: 2020-07-16
#
# This code assumes that it owns all the `[...]: ...` definitions at
# the end of each doc comment block. It deletes them and rewrites
# them according to the links found in the comment block. (Any other
# links must be made inline to avoid being deleted.)
#
# Links in certain fixed formats are recognised and converted into
# links within this same crate or to other crates in the generated
# documentation. A warning is given if the corresponding
# documentation files are not found, so this requires that
# documentation has already been generated.
#
# Recognises structs, enums, methods and macros, optionally with a
# path. By checking generated documentation files, guesses when a
# path refers to an enum rather than a struct, or another crate rather
# than this one. For example:
#
# [`Actor`]
# [`Actor::apply`]
# [`actor!`]
# [`mio::Poll`]
# [`mio::Poll::poll`]
# [`mio::net::TcpStream`]
# [`stakker::actor!`]
use Cwd qw(cwd);
my $CRATE = cwd();
$CRATE =~ s|^.*/([^/]+)/?|$1|;
$CRATE =~ s/-/_/g;
my $TARGET = $ENV{CARGO_TARGET_DIR};
$TARGET = "./target" unless defined $TARGET;
my $DOCDIR = "$TARGET/doc/$CRATE";
sub readfile {
my $fn = shift;
die "Can't open file: $fn" unless open READFILE, "<$fn";
local $/ = undef;
my $data = <READFILE>;
die unless close READFILE;
return $data;
}
my $inplace = 1;
my @rs = ();
push @rs, glob('src/*.rs');
push @rs, glob('src/*/*.rs');
push @rs, glob('src/*/*/*.rs');
my %unknown = ();
my @curr = ();
my $prefix = '';
sub process_section {
if ($prefix eq '') {
print OUT @curr;
return;
}
# Drop existing link-defs, and any trailing blank lines
while (@curr > 0 && $curr[@curr-1] =~ m|^\s*//[/!]\s*\[.*\]:|) {
pop @curr;
}
while (@curr > 0 && $curr[@curr-1] =~ m|^\s*//[/!]\s*$|) {
pop @curr;
}
# Make a list of [`...`] links
my %links = ();
my $data = join('', @curr);
$data =~ s/```.*?```//sg;
while ($data =~ /(\[`[^`]+`\])[^(]/g) {
$links{$1} = 1;
}
my @linkdefs = ();
# Generate defs for them
for my $link (sort keys %links) {
my $fn;
my $id;
my $href;
my $path = '';
my $tmp = $link;
while ($tmp =~ s/^\[`([a-z][a-z0-9_]*)::/[`/) {
$path = "$path$1/";
}
my @href = ();
if ($tmp =~ /^\[`([A-Z][a-zA-Z0-9_]+)`\]$/) {
push @href, "${path}struct.$1.html";
push @href, "../${path}struct.$1.html" if $path ne '';
push @href, "${path}enum.$1.html";
push @href, "../${path}enum.$1.html" if $path ne '';
} elsif ($tmp =~ /^\[`([A-Z][a-zA-Z0-9_]+)::([a-z][a-z0-9_]+)`\]$/) {
push @href, "${path}struct.$1.html#method.$2";
push @href, "../${path}struct.$1.html#method.$2" if $path ne '';
} elsif ($tmp =~ /^\[`([A-Z][a-zA-Z0-9_]+)::([A-Z][a-zA-Z0-9_]+)`\]$/) {
push @href, "${path}enum.$1.html#variant.$2";
push @href, "../${path}enum.$1.html#variant.$2" if $path ne '';
} elsif ($tmp =~ /^\[`([a-z][a-z0-9_]+)!`\]$/) {
push @href, "${path}macro.$1.html";
push @href, "../${path}macro.$1.html" if $path ne '';
} else {
$unknown{$link} = 1;
}
my $found = 0;
for my $href (@href) {
my $fn = $href;
my $id;
$id = $1 if $fn =~ s/#([^#]*)$//;
my $fnpath = "$DOCDIR/$fn";
if (-f $fnpath) {
push @linkdefs, "$link: $href";
if (defined $id) {
my $html = readfile($fnpath);
print "WARNING: Missing anchor '$id' in file: $fnpath\n"
unless $html =~ /['"]$id['"]/;
}
$found = 1;
last;
}
}
if (!$found) {
print("WARNING: Doc file not found in any of these locations:\n " . join("\n ", @href) . "\n");
}
}
# Append
if (@linkdefs) {
my $linepre = $curr[0];
$linepre =~ s|^(\s*//[/!]).*$|$1|s;
push @curr, "$linepre\n";
push @curr, "$linepre $_\n" for (@linkdefs);
}
print OUT @curr;
}
for my $fnam (@rs) {
my $ofnam = "$fnam-NEW";
@curr = ();
$prefix = '';
die "Can't open file: $fnam" unless open IN, "<$fnam";
die "Can't create file: $ofnam" unless open OUT, ">$ofnam";
while (<IN>) {
my $pre = '';
$pre = $1 if m|^\s*(//[/!])|s;
if ($pre ne $prefix) {
process_section();
@curr = ();
$prefix = $pre;
}
push @curr, $_;
}
process_section() if @curr;
die "Failed reading file: $fnam" unless close IN;
die "Failed writing file: $ofnam" unless close OUT;
if ($inplace) {
my $f1 = readfile($fnam);
my $f2 = readfile($ofnam);
if ($f1 ne $f2) {
if (rename $ofnam, $fnam) {
print "Updated $fnam\n";
} else {
print "Failed to rename file $ofnam to $fnam\n";
}
} else {
unlink $ofnam;
}
}
}
for (sort keys %unknown) {
print "WARNING: Link format not known: $_\n";
}