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 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
|
# Leave the first line of this file blank!
# This is a Perl script; the following two lines allow us to avoid
# embedding the path of the perl interpreter in the script.
eval "exec perl -S $0 $*"
if $running_under_some_shell;
#---------------------------------------------------------------------------#
# Copyright (C) 1994-1999 The University of Melbourne.
# This file may only be copied under the terms of the GNU General
# Public License - see the file COPYING in the Mercury distribution.
#---------------------------------------------------------------------------#
$usage = "\
Usage: mtags [<options>] <source files>
Use \`mtags --help' for help.";
$help = "\
Usage:
mtags [<options>] <source files>
Description:
This script creates tags files for Mercury programs that can be
used with Vi, Vim, Elvis or Emacs (depending on the options
specified). It takes a list of filenames from the command line
and produces a tags file for the Mercury declarations in those
files.
Options:
With no options specified, mtags defaults to creating a vi-style
tags file. If multiple identical tags are found, only the first
occurrence of the tag is placed in the tags file.
-e, --emacs
Produce an emacs-style TAGS file.
--vim
Produce a dumbed-down vi-style tags file that will work
with versions of vim prior to 5.0, and versions of elvis
prior to 2.1.
--ext
Produce a tags file in the extended format supported by
vim 5.0+. Duplicate tags are allowed in the tags file.
Extra attributes are added to each tag to say whether it
is in the implementation or interface of the source file
and to describe the kind of tag. Tag kinds used are:
\`pred' for predicate declarations
\`func' for function declarations
\`type' for type definitions
\`cons' for type constructors
\`inst' for inst definitions
\`mode' for mode definitions
\`tc' for typeclass declarations
\`tci' for typeclass instance declarations
\`tcm' for typeclass methods
\`tcim' for typeclass instance methods
(Vim assumes that the \`kind' attribute has at most 4
characters.)
--elvis
Without \`--ext', works the same as \`--vim' and supports
versions of elvis prior to 2.1. When used in
conjunction with \`--ext', produces an extended tags file
in a format that will work with elvis 2.1+.
--keep-duplicates
By default, mtags removes duplicate tags from the tags
file. With this option, duplicate tags are not removed.
Also, with this option, tags are created for typeclass
instances. This option is implied by \`--emacs' and by
\`--ext'.
-h, --help
Dislay this help message and exit.
--
Treat all remaining arguments as source file names. This is
useful if you have file names starting with \`-'.
";
$warnings = 0;
$emacs = 0;
$vim = 0;
$ext = 0;
$elvis = 0;
$keep_dups = 0;
OPTION:
while ($#ARGV >= 0 && $ARGV[0] =~ /^-/) {
if ($ARGV[0] eq "-e" || $ARGV[0] eq "--emacs") {
$emacs = 1;
$keep_dups = 1;
shift(ARGV);
next OPTION;
}
if ($ARGV[0] eq "--vim") {
$vim = 1;
$elvis = 0;
shift(ARGV);
next OPTION;
}
if ($ARGV[0] eq "--ext") {
$ext = 1;
$keep_dups = 1;
shift(ARGV);
next OPTION;
}
if ($ARGV[0] eq "--elvis") {
$elvis = 1;
$vim = 0;
shift(ARGV);
next OPTION;
}
if ($ARGV[0] eq "--keep-duplicates") {
$keep_dups = 1;
shift(ARGV);
next OPTION;
}
if ($ARGV[0] eq "-h" || $ARGV[0] eq "--help") {
print "$help";
exit(0);
}
if ($ARGV[0] eq "--") {
shift(ARGV);
break;
}
die "mtags: unrecognized option \`$ARGV[0]'\n" .
"Use \`mtags --help' for help.\n";
}
die $usage if $#ARGV < 0;
#---------------------------------------------------------------------------#
sub output_name {
# figure out the part of the body that is the name
$name =~ s/^[ \t]*//;
if ($name =~ /^\(/) {
$name =~ s/\(//;
$name =~ s/\).*//;
} else {
$name =~ s/\.$//;
$name =~ s/\(.*//;
$name =~ s/ .*//;
}
$match_line = $_;
$match_line =~ s|\\|\\\\|g; # replace `\' with `\\'
$match_line =~ s|/|\\/|g; # replace `/' with `\/'
if (!$emacs && !$keep_dups && $seen{$name}) {
if ($warnings &&
$file ne $prev_file{$name} &&
$. != $prev_line{$name})
{
printf STDOUT "%s:%03d: Warning: ignoring duplicate defn " .
"for `$name'\n", $file, $., $name;
printf STDOUT
"%s:%03d: (previous definition of `%s' was here).\n",
$prev_file{$name}, $prev_line{$name}, $name;
}
} else {
if ($emacs) {
printf out "%s\177%s\001%d,%d\n",
$_, $name, $., $.;
} elsif ($ext) {
# In ``ext'' mode, print the extra attributes used by
# vim 5.0+ and elvis 2.1+.
if ($context =~ /implementation/) {
$static = "\tfile:";
$sfile = $file;
} else {
$static = "";
$sfile = "";
}
if ($elvis) {
# Elvis 2.1+
# Elvis (as of 2.1i) seems to require `[' to be escaped
# in tag patterns, even though they are supposed to use
# `nomagic' mode.
$match_line =~ s/\[/\\\[/g;
# Elvis allows only a single search pattern or line
# number rather than an arbitrary sequence of
# semicolon-separated ex commands.
printf out "%s\t%s\t/^%s\$/;\"\tkind:%s%s%s\n",
$name, $file, $match_line, $kind, $static, $sfile;
} else {
# Vim 5.0+
# Vim 5.0, like vi, allows an arbitrary number of
# colon-separated ex commands. However if more than
# one command is given, it seems to ignore the extra
# tag attributes. For now, we only output a single
# search command so that vim will recognise the
# extra attributes. If you would prefer the more
# complex command used for vi (see below) instead of
# the extra attributes, use `mtags --keep-duplicates'
# instead of `mtags --ext'.
printf out "%s\t%s\t/^%s\$/;\"\tkind:%s%s\n",
$name, $file, $match_line, $kind, $static;
}
} elsif ($vim || $elvis) {
# Works with any version of vim, elvis or vi.
printf out "%s\t%s\t/^%s\$/\n",
$name, $file, $match_line;
} else {
# Works with vi or vim 5.0+. The ex command searches
# for the matching line and then places the tag in the
# search buffer so that if this is a pred/func
# declaration you can do `n' to go to the pred/func
# body.
printf out "%s\t%s\t/^%s\$/;-;/%s/\n",
$name, $file, $match_line, $name;
}
$seen{$name} = 1;
$prev_file{$name} = $file;
$prev_line{$name} = $.;
}
}
#---------------------------------------------------------------------------#
if ($emacs) {
open(out, "> TAGS") || die "mtags: error opening TAGS: $!\n";
} elsif ($keep_dups) {
# Vim 5.0+ and elvis 2.1+ allow multiple matches for a tag, so don't
# remove duplicate tags.
# Vim and elvis expect the tags file to be sorted so they can do
# binary search.
open(out, "| sort > tags") ||
die "mtags: error opening pipe: $!\n";
} else {
# Remove duplicate tags for vi.
open(out, "| sort -u +0 -1 > tags") ||
die "mtags: error opening pipe: $!\n";
}
$context = "implementation";
while ($#ARGV >= 0)
{
$file = shift(ARGV);
open(srcfile, $file) || die "mtags: can't open $file: $!\n";
if ($emacs) {
close(out) || die "mtags: error closing TAGS: $!\n";
open(out, ">> TAGS") || die "mtags: error opening TAGS: $!\n";
printf out "\f\n%s,%d\n", $file, 0;
close(out) || die "mtags: error closing TAGS: $!\n";
# open(out, "| sort -u +0 -1 >> TAGS") ||
open(out, ">> TAGS") ||
die "mtags: error opening pipe: $!\n";
}
while ($_ = <srcfile>)
{
# skip lines which are not declarations
next unless ($_ =~ /^:- /);
chop;
($cmd, $decl, @rest) = split;
$body = join(' ', @rest);
# Remove `impure' and `semipure' declarations.
if ($decl eq "impure" || $decl eq "semipure") {
($decl, @rest) = split /\s+/, $body;
$body = join(' ', @rest);
}
# Is this an "interface" or "implementation" declaration?
# If so, change context.
if ($decl =~ "\binterface\b" || $decl =~ "\bimplementation\b") {
$context = $decl;
}
# Skip lines which are not pred, func, type, inst, mode,
# typeclass or instance declarations.
# Also skip instance declarations if we're producing a normal vi
# tags file since vi doesn't allow duplicate tags and the
# typeclass tags are probably more important than the instance
# tags.
next unless (
$decl eq "pred" ||
$decl eq "func" ||
$decl eq "type" ||
$decl eq "inst" ||
($decl eq "mode" && $body =~ /::/) ||
$decl eq "typeclass" ||
($decl eq "instance" && $keep_dups)
);
# skip declarations which are not definitions
next unless (
# pred, func, and typeclass declarations are always definitions
$decl eq "pred" ||
$decl eq "func" ||
$decl eq "typeclass" ||
# if it doesn't end in a `.' (i.e if it doesn't fit on one line),
# then it's probably a definition
($body !~ /\.[ \t]*$/ && $body !~ /\.[ \t]*%.*$/) ||
# if it contains `--->', `=', or `::', it's probably a
# definition.
$body =~ /--->/ ||
$body =~ /=/ ||
$body =~ /::/
);
$name = $body;
$kind = $decl;
# Shorten $kind for typeclass and instance so they display better in
# vim which assumes the kind attribute has at most 4 chars.
if ($kind eq "typeclass") { $kind = "tc"; }
if ($kind eq "instance") { $kind = "tci"; }
do output_name();
# for everything except type, typeclass and instance declarations,
# we're done
next unless ($decl eq "type" || $decl eq "typeclass" ||
$decl eq "instance");
if ($decl eq "type") {
# make sure we're at the line with the `--->'
if ($body !~ /--->/) {
next if $_ =~ /\.[ \t]*$/ || $_ =~ /\.[ \t]*%.*$/;
$_ = <srcfile>;
chop;
$body = $_;
}
next unless ($body =~ /--->/);
# replace everything up to the `--->' with `;'
$body =~ s/.*--->/;/;
for(;;) {
# if the body starts with `;', we assume it must be the
# start of a constructor definition
if ($body =~ /^[ \t]*;/) {
# delete the leading `;'
$body =~ s/[^;]*;[ \t]*//;
if ($body =~ /^[ \t]*$/) {
$_ = <srcfile> || last;
chop;
$body = $_;
}
$name = $body;
$name =~ s/[;.%].*//;
$kind = "cons";
do output_name();
# if there are more constructor definitions on the
# same line, process the next one
if ($body =~ /;/) {
$body =~ s/[^;]*;/;/;
next;
}
}
last if $_ =~ /\.[ \t]*$/ || $_ =~ /\.[ \t]*%.*$/;
$_ = <srcfile> || last;
chop;
$body = $_;
}
} elsif ($decl eq "typeclass") {
for(;;) {
# Assume each method declaration starts on a new line.
if ($body =~ /^.*\b(pred|func)[ \t]*/) {
$body =~ s/^.*\b(pred|func)[ \t]*//;
if ($body =~ /^[ \t]*$/) {
$_ = <srcfile> || last;
chop;
$body = $_;
}
$name = $body;
$name =~ s/[(,%].*//;
$kind = "tcm"; # tcm == type class method
do output_name();
}
last if $_ =~ /\.[ \t]*$/ || $_ =~ /\]/;
$_ = <srcfile> || last;
chop;
$body = $_;
}
} else { # instance declaration
for(;;) {
# Assume each method declaration starts on a new line.
if ($body =~ /^.*\b(pred\(|func\()/) {
$body =~ s/.*\b(pred\(|func\()//;
if ($body =~ /^[ \t]*$/) {
$_ = <srcfile> || last;
chop;
$body = $_;
}
$name = $body;
$name =~ s/[\/)].*//;
$kind = "tcim"; # tcim == type class instance method
do output_name();
}
last if $_ =~ /\.[ \t]*$/ || $_ =~ /\]/;
$_ = <srcfile> || last;
chop;
$body = $_;
}
}
}
close(srcfile) || die "mtags: error closing `$file': $!\n";
}
close(out) || die "mtags: error closing pipe: $!\n";
|