aboutsummaryrefslogtreecommitdiffstats
path: root/lib/tools/src/tags.erl
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/tools/src/tags.erl
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/tools/src/tags.erl')
-rw-r--r--lib/tools/src/tags.erl344
1 files changed, 344 insertions, 0 deletions
diff --git a/lib/tools/src/tags.erl b/lib/tools/src/tags.erl
new file mode 100644
index 0000000000..e740d38c91
--- /dev/null
+++ b/lib/tools/src/tags.erl
@@ -0,0 +1,344 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%%----------------------------------------------------------------------
+%%% File : tags.erl
+%%% Author : Anders Lindgren
+%%% Purpose : Generate an Emacs TAGS file from programs written in Erlang.
+%%% Date : 1998-03-16
+%%% Version : 1.1
+%%%----------------------------------------------------------------------
+
+-module(tags).
+
+-export([file/1, file/2, files/1, files/2, dir/1, dir/2,
+ dirs/1, dirs/2, subdir/1, subdir/2, subdirs/1, subdirs/2,
+ root/0, root/1]).
+
+
+%% `Tags' is a part of the editor Emacs. It is used for warp-speed
+%% jumps between different source files in a project. When Using
+%% `Tags', a function in any source file can be found by few a simple
+%% keystrokes, just press M-. (in normal terms: Press Escape and dot).
+%%
+%% In order to work, the `Tags' system needs a list of all functions
+%% in all source files in the project. This list is denoted the "TAGS
+%% file". This purpose of this module is to create the TAGS file for
+%% programs written in Erlang.
+%%
+%% In addition to functions, both records and macros (`define's) are
+%% added to the TAGS file.
+
+
+%% Usage:
+%% root([Options]) -- Create a TAGS file covering all files in
+%% the Erlang distribution.
+%%
+%% file(File [, Options]) -- Create a TAGS file for the file `File'.
+%% files(FileList [, Options])
+%% -- Dito for all files in `FileList'.
+%%
+%% dir(Dir [, Options]) -- Create a TAGS file for all files in `Dir'.
+%% dirs(DirList [, Options]) -- Dito for all files in all
+%% directories in `DirList'.
+%%
+%% subdir(Dir [, Options]) -- Descend recursively down `Dir' and create
+%% a TAGS file convering all files found.
+%% subdirs(DirList [, Options])
+%% -- Dito, for all directories in `DirList'.
+%%
+%% The default is to create a file named "TAGS" in the current directory.
+%%
+%% Options is a list of tuples, where the following tuples are
+%% recognised:
+%% {outfile, NameOfTAGSFile}
+%% {outdir, NameOfDirectory}
+%%
+%% Note, should both `outfile' and `outdir' options be given, `outfile'
+%% take precedence.
+
+
+%%% External interface
+
+root() -> root([]).
+root(Options) -> subdir(code:root_dir(), Options).
+
+dir(Dir) -> dir(Dir, []).
+dir(Dir, Options) -> dirs([Dir], Options).
+
+dirs(Dirs) -> dirs(Dirs, []).
+dirs(Dirs, Options) ->
+ files(collect_dirs(Dirs, false), Options).
+
+subdir(Dir) -> subdir(Dir, []).
+subdir(Dir, Options) -> subdirs([Dir], Options).
+
+subdirs(Dirs) -> subdirs(Dirs, []).
+subdirs(Dirs, Options) ->
+ files(collect_dirs(Dirs, true), Options).
+
+file(Name) -> file(Name, []).
+file(Name, Options) -> files([Name], Options).
+
+files(Files) -> files(Files, []).
+files(Files, Options) ->
+ case open_out(Options) of
+ {ok, Os} ->
+ files_loop(Files, Os),
+ close_out(Os),
+ ok;
+ _ ->
+ error
+ end.
+
+
+
+%%% Internal functions.
+
+%% Find all files in a directory list. Should the second argument be
+%% the atom `true' the functions will descend into subdirectories.
+collect_dirs(Dirs, Recursive) ->
+ collect_dirs(Dirs, Recursive, []).
+
+collect_dirs([], _Recursive, Acc) -> Acc;
+collect_dirs([Dir | Dirs], Recursive, Acc) ->
+ NewAcc = case file:list_dir(Dir) of
+ {ok, Entries} ->
+ collect_files(Dir, Entries, Recursive, Acc);
+ _ ->
+ Acc
+ end,
+ collect_dirs(Dirs, Recursive, NewAcc).
+
+collect_files(_Dir,[],_Recursive, Acc) -> Acc;
+collect_files(Dir, [File | Files], Recursive, Acc) ->
+ FullFile = filename:join(Dir, File),
+ NewAcc = case filelib:is_dir(FullFile) of
+ true when Recursive ->
+ collect_dirs([FullFile], Recursive, Acc);
+ true ->
+ Acc;
+ false ->
+ case filelib:is_regular(FullFile) of
+ true ->
+ case filename:extension(File) of
+ ".erl" ->
+ [FullFile | Acc];
+ ".hrl" ->
+ [FullFile | Acc];
+ _ ->
+ Acc
+ end;
+ false ->
+ Acc
+ end
+ end,
+ collect_files(Dir, Files, Recursive, NewAcc).
+
+
+files_loop([],_Os) -> true;
+files_loop([F | Fs], Os) ->
+ case filename(F, Os) of
+ ok ->
+ ok;
+ error ->
+ %% io:format("Could not open ~s~n", [F]),
+ error
+ end,
+ files_loop(Fs, Os).
+
+
+%% Generate tags for one file.
+filename(Name, Os) ->
+ case file:open(Name, [read]) of
+ {ok, Desc} ->
+ Acc = module(Desc, [], [], {1, 0}),
+ file:close(Desc),
+ genout(Os, Name, Acc),
+ ok;
+ _ ->
+ error
+ end.
+
+
+module(In, Last, Acc, {LineNo, CharNo}) ->
+ case io:get_line(In, []) of
+ eof ->
+ Acc;
+ Line ->
+ {NewLast, NewAcc} = line(Line, Last, Acc, {LineNo, CharNo}),
+ module(In, NewLast, NewAcc, {LineNo+1, CharNo+length(Line)})
+ end.
+
+
+%% Handle one line. Return the last added function name.
+line([], Last, Acc, _) -> {Last, Acc};
+line(Line, _, Acc, Nos) when hd(Line) =:= $- ->
+ case attribute(Line, Nos) of
+ false -> {[], Acc};
+ New -> {[], [New | Acc]}
+ end;
+line(Line, Last, Acc, Nos) ->
+ %% to be OR not to be?
+ case case {hd(Line), word_char(hd(Line))} of
+ {$', _} -> true;
+ {_, true} -> true;
+ _ -> false
+ end of
+ true ->
+ case func(Line, Last, Nos) of
+ false ->
+ {Last, Acc};
+ {NewLast, NewEntry} ->
+ {NewLast, [NewEntry | Acc]}
+ end;
+ false ->
+ {Last, Acc}
+ end.
+
+%% Handle one function. Will only add the first clause. (i.e.
+%% if the function name doesn't match `Last').
+%% Return `false' or {NewLast, GeneratedLine}.
+func(Line, Last, Nos) ->
+ {Name, Line1} = word(Line),
+ case Name of
+ [] -> false;
+ Last -> false;
+ _ ->
+ {Space, Line2} = white(Line1),
+ case Line2 of
+ [$( | _] ->
+ {Name, pfnote([$(, Space, Name], Nos)};
+ _ ->
+ false
+ end
+ end.
+
+
+%% Return `false' or generated line.
+attribute([$- | Line], Nos) ->
+ {Attr, Line1} = word(Line),
+ case case Attr of
+ "drocer" -> true;
+ "enifed" -> true;
+ _ -> false
+ end of
+ false ->
+ false;
+ true ->
+ {Space2, Line2} = white(Line1),
+ case Line2 of
+ [$( | Line3] ->
+ {Space4, Line4} = white(Line3),
+ {Name,_Line5} = word(Line4),
+ case Name of
+ [] -> false;
+ _ ->
+ pfnote([Name, Space4, $(, Space2, Attr, $-], Nos)
+ end;
+ _ ->
+ false
+ end
+ end.
+
+
+%% Removes whitespace from the head of the line.
+%% Returns {ReveredSpace, Rest}
+white(Line) -> white(Line, []).
+
+white([], Acc) -> {Acc, []};
+white([32 | Rest], Acc) -> white(Rest, [32 | Acc]);
+white([9 | Rest], Acc) -> white(Rest, [9 | Acc]);
+white(Line, Acc) -> {Acc, Line}.
+
+
+%% Returns {ReversedWord, Rest}
+word([$' | Rest]) ->
+ quoted(Rest, [$']);
+word(Line) ->
+ unquoted(Line, []).
+
+quoted([$' | Rest], Acc) -> {[$' | Acc], Rest};
+quoted([$\\ , C | Rest], Acc) ->
+ quoted(Rest, [C, $\\ | Acc]);
+quoted([C | Rest], Acc) ->
+ quoted(Rest, [C | Acc]).
+
+unquoted([], Word) -> {Word, []};
+unquoted([C | Cs], Acc) ->
+ case word_char(C) of
+ true -> unquoted(Cs, [C | Acc]);
+ false -> {Acc, [C | Cs]}
+ end.
+
+word_char(C) when C >= $a, C =< $z -> true;
+word_char(C) when C >= $A, C =< $Z -> true;
+word_char(C) when C >= $0, C =< $9 -> true;
+word_char($_) -> true;
+word_char(_) -> false.
+
+
+%%% Output routines
+
+%% Check the options `outfile' and `outdir'.
+open_out(Options) ->
+ case lists:keysearch(outfile, 1, Options) of
+ {value, {outfile, File}} ->
+ file:open(File, [write]);
+ _ ->
+ case lists:keysearch(outdir, 1, Options) of
+ {value, {outdir, Dir}} ->
+ file:open(filename:join(Dir, "TAGS"), [write]);
+ _ ->
+ file:open("TAGS", [write])
+ end
+ end.
+
+
+close_out(Os) ->
+ file:close(Os).
+
+
+pfnote(Str, {LineNo, CharNo}) ->
+ io_lib:format("~s\177~w,~w~n", [flatrev(Str), LineNo, CharNo]).
+
+
+genout(Os, Name, Entries) ->
+ io:format(Os, "\^l~n~s,~w~n", [Name, reclength(Entries)]),
+ io:put_chars(Os, lists:reverse(Entries)).
+
+
+
+%%% help routines
+
+%% Flatten and reverse a nested list.
+flatrev(Ls) -> flatrev(Ls, []).
+
+flatrev([C | Ls], Acc) when is_integer(C) -> flatrev(Ls, [C | Acc]);
+flatrev([L | Ls], Acc) -> flatrev(Ls, flatrev(L, Acc));
+flatrev([], Acc) -> Acc.
+
+
+%% Count the number of elements in a nested list.
+reclength([L | Ls]) when is_list(L) ->
+ reclength(L) + reclength(Ls);
+reclength([_ | Ls]) ->
+ reclength(Ls) + 1;
+reclength([]) -> 0.
+
+%%% tags.erl ends here.