aboutsummaryrefslogtreecommitdiffstats
path: root/lib/edoc/src/edoc.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/edoc/src/edoc.erl')
-rw-r--r--lib/edoc/src/edoc.erl771
1 files changed, 771 insertions, 0 deletions
diff --git a/lib/edoc/src/edoc.erl b/lib/edoc/src/edoc.erl
new file mode 100644
index 0000000000..ec452a5929
--- /dev/null
+++ b/lib/edoc/src/edoc.erl
@@ -0,0 +1,771 @@
+%% =====================================================================
+%% This library is free software; you can redistribute it and/or modify
+%% it under the terms of the GNU Lesser General Public License as
+%% published by the Free Software Foundation; either version 2 of the
+%% License, or (at your option) any later version.
+%%
+%% This library is distributed in the hope that it will be useful, but
+%% WITHOUT ANY WARRANTY; without even the implied warranty of
+%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+%% Lesser General Public License for more details.
+%%
+%% You should have received a copy of the GNU Lesser General Public
+%% License along with this library; if not, write to the Free Software
+%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+%% USA
+%%
+%% $Id$
+%%
+%% @copyright 2001-2007 Richard Carlsson
+%% @author Richard Carlsson <[email protected]>
+%% @version {@version}
+%% @end
+%% =====================================================================
+
+%% TODO: check weirdness in name generation for @spec f(TypeName, ...) -> ...
+%% TODO: option for ignoring functions matching some pattern ('..._test_'/0)
+%% TODO: @private_type tag, opaque unless generating private docs?
+%% TODO: document the record type syntax
+%% TODO: some 'skip' option for ignoring particular modules/packages?
+%% TODO: intermediate-level packages: document even if no local sources.
+%% TODO: multiline comment support (needs modified comment representation)
+%% TODO: config-file for default settings
+%% TODO: config: locations of all local docdirs; generate local doc-index page
+%% TODO: config: URL:s of offline packages/apps
+%% TODO: config: default stylesheet
+%% TODO: config: default header/footer, etc.
+%% TODO: offline linkage
+%% TODO: including source code, explicitly and/or automatically
+
+%% @doc EDoc - the Erlang program documentation generator.
+%%
+%% This module provides the main user interface to EDoc.
+%% <ul>
+%% <li><a href="overview-summary.html">EDoc User Manual</a></li>
+%% <li><a href="overview-summary.html#Running_EDoc">Running EDoc</a></li>
+%% </ul>
+
+-module(edoc).
+
+-export([packages/1, packages/2, files/1, files/2,
+ application/1, application/2, application/3,
+ toc/1, toc/2, toc/3,
+ run/3,
+ file/1, file/2,
+ read/1, read/2,
+ layout/1, layout/2,
+ get_doc/1, get_doc/2, get_doc/3,
+ read_comments/1, read_comments/2,
+ read_source/1, read_source/2]).
+
+-import(edoc_report, [report/2, report/3, error/1, error/3]).
+
+-include("edoc.hrl").
+
+
+%% @spec (Name::filename()) -> ok
+%% @equiv file(Name, [])
+%% @deprecated See {@link file/2} for details.
+
+file(Name) ->
+ file(Name, []).
+
+%% @spec file(filename(), proplist()) -> ok
+%%
+%% @type filename() = //kernel/file:filename()
+%% @type proplist() = [term()]
+%%
+%% @deprecated This is part of the old interface to EDoc and is mainly
+%% kept for backwards compatibility. The preferred way of generating
+%% documentation is through one of the functions {@link application/2},
+%% {@link packages/2} and {@link files/2}.
+%%
+%% @doc Reads a source code file and outputs formatted documentation to
+%% a corresponding file.
+%%
+%% Options:
+%% <dl>
+%% <dt>{@type {dir, filename()@}}
+%% </dt>
+%% <dd>Specifies the output directory for the created file. (By
+%% default, the output is written to the directory of the source
+%% file.)
+%% </dd>
+%% <dt>{@type {source_suffix, string()@}}
+%% </dt>
+%% <dd>Specifies the expected suffix of the input file. The default
+%% value is `".erl"'.
+%% </dd>
+%% <dt>{@type {file_suffix, string()@}}
+%% </dt>
+%% <dd>Specifies the suffix for the created file. The default value is
+%% `".html"'.
+%% </dd>
+%% </dl>
+%%
+%% See {@link get_doc/2} and {@link layout/2} for further
+%% options.
+%%
+%% For running EDoc from a Makefile or similar, see
+%% {@link edoc_run:file/1}.
+%%
+%% @see read/2
+
+%% NEW-OPTIONS: source_suffix, file_suffix, dir
+%% INHERIT-OPTIONS: read/2
+
+file(Name, Options) ->
+ Text = read(Name, Options),
+ SrcSuffix = proplists:get_value(source_suffix, Options,
+ ?DEFAULT_SOURCE_SUFFIX),
+ BaseName = filename:basename(Name, SrcSuffix),
+ Suffix = proplists:get_value(file_suffix, Options,
+ ?DEFAULT_FILE_SUFFIX),
+ Dir = proplists:get_value(dir, Options, filename:dirname(Name)),
+ edoc_lib:write_file(Text, Dir, BaseName ++ Suffix).
+
+
+%% TODO: better documentation of files/1/2, packages/1/2, application/1/2/3
+
+%% @spec (Files::[filename() | {package(), [filename()]}]) -> ok
+%% @equiv packages(Packages, [])
+
+files(Files) ->
+ files(Files, []).
+
+%% @spec (Files::[filename() | {package(), [filename()]}],
+%% Options::proplist()) -> ok
+%% @doc Runs EDoc on a given set of source files. See {@link run/3} for
+%% details, including options.
+%% @equiv run([], Files, Options)
+
+files(Files, Options) ->
+ run([], Files, Options).
+
+%% @spec (Packages::[package()]) -> ok
+%% @equiv packages(Packages, [])
+
+packages(Packages) ->
+ packages(Packages, []).
+
+%% @spec (Packages::[package()], Options::proplist()) -> ok
+%% @type package() = atom() | string()
+%%
+%% @doc Runs EDoc on a set of packages. The `source_path' option is used
+%% to locate the files; see {@link run/3} for details, including
+%% options. This function automatically appends the current directory to
+%% the source path.
+%%
+%% @equiv run(Packages, [], Options)
+
+packages(Packages, Options) ->
+ run(Packages, [], Options ++ [{source_path, [?CURRENT_DIR]}]).
+
+%% @spec (Application::atom()) -> ok
+%% @equiv application(Application, [])
+
+application(App) ->
+ application(App, []).
+
+%% @spec (Application::atom(), Options::proplist()) -> ok
+%% @doc Run EDoc on an application in its default app-directory. See
+%% {@link application/3} for details.
+%% @see application/1
+
+application(App, Options) when is_atom(App) ->
+ case code:lib_dir(App) of
+ Dir when is_list(Dir) ->
+ application(App, Dir, Options);
+ _ ->
+ report("cannot find application directory for '~s'.",
+ [App]),
+ exit(error)
+ end.
+
+%% @spec (Application::atom(), Dir::filename(), Options::proplist())
+%% -> ok
+%% @doc Run EDoc on an application located in the specified directory.
+%% Tries to automatically set up good defaults. Unless the user
+%% specifies otherwise:
+%% <ul>
+%% <li>The `doc' subdirectory will be used as the target directory, if
+%% it exists; otherwise the application directory is used.
+%% </li>
+%% <li>The source code is assumed to be located in the `src'
+%% subdirectory, if it exists, or otherwise in the application
+%% directory itself.
+%% </li>
+%% <li>The {@link run/3. `subpackages'} option is turned on. All found
+%% source files will be processed.
+%% </li>
+%% <li>The `include' subdirectory is automatically added to the
+%% include path. (Only important if {@link read_source/2.
+%% preprocessing} is turned on.)
+%% </li>
+%% </ul>
+%%
+%% See {@link run/3} for details, including options.
+%%
+%% @see application/2
+
+application(App, Dir, Options) when is_atom(App) ->
+ Src = edoc_lib:try_subdir(Dir, ?SOURCE_DIR),
+ Overview = filename:join(edoc_lib:try_subdir(Dir, ?EDOC_DIR),
+ ?OVERVIEW_FILE),
+ Opts = Options ++ [{source_path, [Src]},
+ subpackages,
+ {title, io_lib:fwrite("The ~s application", [App])},
+ {overview, Overview},
+ {dir, filename:join(Dir, ?EDOC_DIR)},
+ {includes, [filename:join(Dir, "include")]}],
+ Opts1 = set_app_default(App, Dir, Opts),
+ %% Recursively document all subpackages of '' - i.e., everything.
+ run([''], [], [{application, App} | Opts1]).
+
+%% Try to set up a default application base URI in a smart way if the
+%% user has not specified it explicitly.
+
+set_app_default(App, Dir0, Opts) ->
+ case proplists:get_value(app_default, Opts) of
+ undefined ->
+ AppName = atom_to_list(App),
+ Dir = edoc_lib:simplify_path(filename:absname(Dir0)),
+ AppDir = case filename:basename(Dir) of
+ AppName ->
+ filename:dirname(Dir);
+ _ ->
+ ?APP_DEFAULT
+ end,
+ [{app_default, AppDir} | Opts];
+ _ ->
+ Opts
+ end.
+
+%% If no source files are found for a (specified) package, no package
+%% documentation will be generated either (even if there is a
+%% package-documentation file). This is the way it should be. For
+%% specified files, use empty package (unless otherwise specified). The
+%% assumed package is always used for creating the output. If the actual
+%% module or package of the source differs from the assumption gathered
+%% from the path and file name, a warning should be issued (since links
+%% are likely to be incorrect).
+
+opt_defaults() ->
+ [packages].
+
+opt_negations() ->
+ [{no_preprocess, preprocess},
+ {no_subpackages, subpackages},
+ {no_packages, packages}].
+
+%% @spec run(Packages::[package()],
+%% Files::[filename() | {package(), [filename()]}],
+%% Options::proplist()) -> ok
+%% @doc Runs EDoc on a given set of source files and/or packages. Note
+%% that the doclet plugin module has its own particular options; see the
+%% `doclet' option below.
+%%
+%% Also see {@link layout/2} for layout-related options, and
+%% {@link get_doc/2} for options related to reading source
+%% files.
+%%
+%% Options:
+%% <dl>
+%% <dt>{@type {app_default, string()@}}
+%% </dt>
+%% <dd>Specifies the default base URI for unknown applications.
+%% </dd>
+%% <dt>{@type {application, App::atom()@}}
+%% </dt>
+%% <dd>Specifies that the generated documentation describes the
+%% application `App'. This mainly affects generated references.
+%% </dd>
+%% <dt>{@type {dir, filename()@}}
+%% </dt>
+%% <dd>Specifies the target directory for the generated documentation.
+%% </dd>
+%% <dt>{@type {doc_path, [string()]@}}
+%% </dt>
+%% <dd>Specifies a list of URI:s pointing to directories that contain
+%% EDoc-generated documentation. URI without a `scheme://' part are
+%% taken as relative to `file://'. (Note that such paths must use
+%% `/' as separator, regardless of the host operating system.)
+%% </dd>
+%% <dt>{@type {doclet, Module::atom()@}}
+%% </dt>
+%% <dd>Specifies a callback module to be used for creating the
+%% documentation. The module must export a function `run(Cmd, Ctxt)'.
+%% The default doclet module is {@link edoc_doclet}; see {@link
+%% edoc_doclet:run/2} for doclet-specific options.
+%% </dd>
+%% <dt>{@type {exclude_packages, [package()]@}}
+%% </dt>
+%% <dd>Lists packages to be excluded from the documentation. Typically
+%% used in conjunction with the `subpackages' option.
+%% </dd>
+%% <dt>{@type {file_suffix, string()@}}
+%% </dt>
+%% <dd>Specifies the suffix used for output files. The default value is
+%% `".html"'. Note that this also affects generated references.
+%% </dd>
+%% <dt>{@type {new, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', any existing `edoc-info' file in the
+%% target directory will be ignored and overwritten. The default
+%% value is `false'.
+%% </dd>
+%% <dt>{@type {packages, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', it it assumed that packages (module
+%% namespaces) are being used, and that the source code directory
+%% structure reflects this. The default value is `true'. (Usually,
+%% this does the right thing even if all the modules belong to the
+%% top-level "empty" package.) `no_packages' is an alias for
+%% `{packages, false}'. See the `subpackages' option below for
+%% further details.
+%%
+%% If the source code is organized in a hierarchy of
+%% subdirectories although it does not use packages, use
+%% `no_packages' together with the recursive-search `subpackages'
+%% option (on by default) to automatically generate documentation
+%% for all the modules.
+%% </dd>
+%% <dt>{@type {source_path, [filename()]@}}
+%% </dt>
+%% <dd>Specifies a list of file system paths used to locate the source
+%% code for packages.
+%% </dd>
+%% <dt>{@type {source_suffix, string()@}}
+%% </dt>
+%% <dd>Specifies the expected suffix of input files. The default
+%% value is `".erl"'.
+%% </dd>
+%% <dt>{@type {subpackages, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', all subpackages of specified packages
+%% will also be included in the documentation. The default value is
+%% `false'. `no_subpackages' is an alias for `{subpackages,
+%% false}'. See also the `exclude_packages' option.
+%%
+%% Subpackage source files are found by recursively searching
+%% for source code files in subdirectories of the known source code
+%% root directories. (Also see the `source_path' option.) Directory
+%% names must begin with a lowercase letter and contain only
+%% alphanumeric characters and underscore, or they will be ignored.
+%% (For example, a subdirectory named `test-files' will not be
+%% searched.)
+%% </dd>
+%% </dl>
+%%
+%% @see files/2
+%% @see packages/2
+%% @see application/2
+
+%% NEW-OPTIONS: source_path, application
+%% INHERIT-OPTIONS: init_context/1
+%% INHERIT-OPTIONS: expand_sources/2
+%% INHERIT-OPTIONS: target_dir_info/5
+%% INHERIT-OPTIONS: edoc_lib:find_sources/3
+%% INHERIT-OPTIONS: edoc_lib:run_doclet/2
+%% INHERIT-OPTIONS: edoc_lib:get_doc_env/4
+
+run(Packages, Files, Opts0) ->
+ Opts = expand_opts(Opts0),
+ Ctxt = init_context(Opts),
+ Dir = Ctxt#context.dir,
+ Path = proplists:append_values(source_path, Opts),
+ Ss = sources(Path, Packages, Opts),
+ {Ss1, Ms} = expand_sources(expand_files(Files) ++ Ss, Opts),
+ Ps = [P || {_, P, _, _} <- Ss1],
+ App = proplists:get_value(application, Opts, ?NO_APP),
+ {App1, Ps1, Ms1} = target_dir_info(Dir, App, Ps, Ms, Opts),
+ %% The "empty package" is never included in the list of packages.
+ Ps2 = edoc_lib:unique(lists:sort(Ps1)) -- [''],
+ Ms2 = edoc_lib:unique(lists:sort(Ms1)),
+ Fs = package_files(Path, Ps2),
+ Env = edoc_lib:get_doc_env(App1, Ps2, Ms2, Opts),
+ Ctxt1 = Ctxt#context{env = Env},
+ Cmd = #doclet_gen{sources = Ss1,
+ app = App1,
+ packages = Ps2,
+ modules = Ms2,
+ filemap = Fs
+ },
+ F = fun (M) ->
+ M:run(Cmd, Ctxt1)
+ end,
+ edoc_lib:run_doclet(F, Opts).
+
+expand_opts(Opts0) ->
+ proplists:substitute_negations(opt_negations(),
+ Opts0 ++ opt_defaults()).
+
+%% NEW-OPTIONS: dir
+%% DEFER-OPTIONS: run/3
+
+init_context(Opts) ->
+ #context{dir = proplists:get_value(dir, Opts, ?CURRENT_DIR),
+ opts = Opts
+ }.
+
+%% INHERIT-OPTIONS: edoc_lib:find_sources/3
+
+sources(Path, Packages, Opts) ->
+ lists:foldl(fun (P, Xs) ->
+ edoc_lib:find_sources(Path, P, Opts) ++ Xs
+ end,
+ [], Packages).
+
+package_files(Path, Packages) ->
+ Name = ?PACKAGE_FILE, % this is hard-coded for now
+ D = lists:foldl(fun (P, D) ->
+ F = edoc_lib:find_file(Path, P, Name),
+ dict:store(P, F, D)
+ end,
+ dict:new(), Packages),
+ fun (P) ->
+ case dict:find(P, D) of
+ {ok, F} -> F;
+ error -> ""
+ end
+ end.
+
+%% Expand user-specified sets of files.
+
+expand_files([{P, Fs1} | Fs]) ->
+ [{P, filename:basename(F), filename:dirname(F)} || F <- Fs1]
+ ++ expand_files(Fs);
+expand_files([F | Fs]) ->
+ [{'', filename:basename(F), filename:dirname(F)} |
+ expand_files(Fs)];
+expand_files([]) ->
+ [].
+
+%% Create the (assumed) full module names. Keep only the first source
+%% for each module, but preserve the order of the list.
+
+%% NEW-OPTIONS: source_suffix, packages
+%% DEFER-OPTIONS: run/3
+
+expand_sources(Ss, Opts) ->
+ Suffix = proplists:get_value(source_suffix, Opts,
+ ?DEFAULT_SOURCE_SUFFIX),
+ Ss1 = case proplists:get_bool(packages, Opts) of
+ true -> Ss;
+ false -> [{'',F,D} || {_P,F,D} <- Ss]
+ end,
+ expand_sources(Ss1, Suffix, sets:new(), [], []).
+
+expand_sources([{P, F, D} | Fs], Suffix, S, As, Ms) ->
+ M = list_to_atom(packages:concat(P, filename:rootname(F, Suffix))),
+ case sets:is_element(M, S) of
+ true ->
+ expand_sources(Fs, Suffix, S, As, Ms);
+ false ->
+ S1 = sets:add_element(M, S),
+ expand_sources(Fs, Suffix, S1, [{M, P, F, D} | As],
+ [M | Ms])
+ end;
+expand_sources([], _Suffix, _S, As, Ms) ->
+ {lists:reverse(As), lists:reverse(Ms)}.
+
+%% NEW-OPTIONS: new
+
+target_dir_info(Dir, App, Ps, Ms, Opts) ->
+ case proplists:get_bool(new, Opts) of
+ true ->
+ {App, Ps, Ms};
+ false ->
+ {App1, Ps1, Ms1} = edoc_lib:read_info_file(Dir),
+ {if App == ?NO_APP -> App1;
+ true -> App
+ end,
+ Ps ++ Ps1,
+ Ms ++ Ms1}
+ end.
+
+
+%% @hidden Not official yet
+
+toc(Dir) ->
+ toc(Dir, []).
+
+%% @equiv toc(Dir, Paths, [])
+%% @hidden Not official yet
+
+%% NEW-OPTIONS: doc_path
+
+toc(Dir, Opts) ->
+ Paths = proplists:append_values(doc_path, Opts)
+ ++ edoc_lib:find_doc_dirs(),
+ toc(Dir, Paths, Opts).
+
+%% @doc Create a meta-level table of contents.
+%% @hidden Not official yet
+
+%% INHERIT-OPTIONS: init_context/1
+%% INHERIT-OPTIONS: edoc_lib:run_doclet/2
+%% INHERIT-OPTIONS: edoc_lib:get_doc_env/4
+
+toc(Dir, Paths, Opts0) ->
+ Opts = expand_opts(Opts0 ++ [{dir, Dir}]),
+ Ctxt = init_context(Opts),
+ Env = edoc_lib:get_doc_env('', [], [], Opts),
+ Ctxt1 = Ctxt#context{env = Env},
+ F = fun (M) ->
+ M:run(#doclet_toc{paths=Paths}, Ctxt1)
+ end,
+ edoc_lib:run_doclet(F, Opts).
+
+
+%% @spec read(File::filename()) -> string()
+%% @equiv read(File, [])
+
+read(File) ->
+ read(File, []).
+
+%% @spec read(File::filename(), Options::proplist()) -> string()
+%%
+%% @doc Reads and processes a source file and returns the resulting
+%% EDoc-text as a string. See {@link get_doc/2} and {@link layout/2} for
+%% options.
+%%
+%% @see file/2
+
+%% INHERIT-OPTIONS: get_doc/2, layout/2
+
+read(File, Opts) ->
+ {_ModuleName, Doc} = get_doc(File, Opts),
+ layout(Doc, Opts).
+
+
+%% @spec layout(Doc::edoc_module()) -> string()
+%% @equiv layout(Doc, [])
+
+layout(Doc) ->
+ layout(Doc, []).
+
+%% @spec layout(Doc::edoc_module(), Options::proplist()) -> string()
+%%
+%% @doc Transforms EDoc module documentation data to text. The default
+%% layout creates an HTML document.
+%%
+%% Options:
+%% <dl>
+%% <dt>{@type {layout, Module::atom()@}}
+%% </dt>
+%% <dd>Specifies a callback module to be used for formatting. The
+%% module must export a function `module(Doc, Options)'. The
+%% default callback module is {@link edoc_layout}; see {@link
+%% edoc_layout:module/2} for layout-specific options.
+%% </dd>
+%% </dl>
+%%
+%% @see layout/1
+%% @see run/3
+%% @see read/2
+%% @see file/2
+
+%% INHERIT-OPTIONS: edoc_lib:run_layout/2
+
+layout(Doc, Opts) ->
+ F = fun (M) ->
+ M:module(Doc, Opts)
+ end,
+ edoc_lib:run_layout(F, Opts).
+
+
+%% @spec (File) -> [comment()]
+%% @equiv read_comments(File, [])
+
+read_comments(File) ->
+ read_comments(File, []).
+
+%% @spec read_comments(File::filename(), Options::proplist()) ->
+%% [comment()]
+%% where
+%% comment() = {Line, Column, Indentation, Text},
+%% Line = integer(),
+%% Column = integer(),
+%% Indentation = integer(),
+%% Text = [string()]
+%%
+%% @doc Extracts comments from an Erlang source code file. See the
+%% module {@link //syntax_tools/erl_comment_scan} for details on the
+%% representation of comments. Currently, no options are avaliable.
+
+read_comments(File, _Opts) ->
+ erl_comment_scan:file(File).
+
+
+%% @spec (File) -> [syntaxTree()]
+%% @equiv read_source(File, [])
+
+read_source(Name) ->
+ read_source(Name, []).
+
+%% @spec read_source(File::filename(), Options::proplist()) ->
+%% [syntaxTree()]
+%%
+%% @type syntaxTree() = //syntax_tools/erl_syntax:syntaxTree()
+%%
+%% @doc Reads an Erlang source file and returns the list of "source code
+%% form" syntax trees.
+%%
+%% Options:
+%% <dl>
+%% <dt>{@type {preprocess, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', the source file will be read via the
+%% Erlang preprocessor (`epp'). The default value is `false'.
+%% `no_preprocess' is an alias for `{preprocess, false}'.
+%%
+%% Normally, preprocessing is not necessary for EDoc to work, but
+%% if a file contains too exotic definitions or uses of macros, it
+%% will not be possible to read it without preprocessing. <em>Note:
+%% comments in included files will not be available to EDoc, even
+%% with this option enabled.</em>
+%% </dd>
+%% <dt>{@type {includes, Path::[string()]@}}
+%% </dt>
+%% <dd>Specifies a list of directory names to be searched for include
+%% files, if the `preprocess' option is turned on. Also used with
+%% the `@headerfile' tag. The default value is the empty list. The
+%% directory of the source file is always automatically appended to
+%% the search path.
+%% </dd>
+%% <dt>{@type {macros, [{atom(), term()@}]@}}
+%% </dt>
+%% <dd>Specifies a list of pre-defined Erlang preprocessor (`epp')
+%% macro definitions, used if the `preprocess' option is turned on.
+%% The default value is the empty list.</dd>
+%% </dl>
+%%
+%% @see get_doc/2
+%% @see //syntax_tools/erl_syntax
+
+%% NEW-OPTIONS: [no_]preprocess (preprocess -> includes, macros)
+
+read_source(Name, Opts0) ->
+ Opts = expand_opts(Opts0),
+ case read_source_1(Name, Opts) of
+ {ok, Forms} ->
+ check_forms(Forms, Name),
+ Forms;
+ {error, R} ->
+ error({"error reading file '~s'.",
+ [edoc_lib:filename(Name)]}),
+ exit({error, R})
+ end.
+
+read_source_1(Name, Opts) ->
+ case proplists:get_bool(preprocess, Opts) of
+ true ->
+ read_source_2(Name, Opts);
+ false ->
+ epp_dodger:quick_parse_file(Name, Opts ++ [{no_fail, false}])
+ end.
+
+read_source_2(Name, Opts) ->
+ Includes = proplists:append_values(includes, Opts)
+ ++ [filename:dirname(Name)],
+ Macros = proplists:append_values(macros, Opts),
+ epp:parse_file(Name, Includes, Macros).
+
+check_forms(Fs, Name) ->
+ Fun = fun (F) ->
+ case erl_syntax:type(F) of
+ error_marker ->
+ case erl_syntax:error_marker_info(F) of
+ {L, M, D} ->
+ error(L, Name, {format_error, M, D});
+
+ Other ->
+ report(Name, "unknown error in "
+ "source code: ~w.", [Other])
+ end,
+ exit(error);
+ _ ->
+ ok
+ end
+ end,
+ lists:foreach(Fun, Fs).
+
+
+%% @spec get_doc(File::filename()) -> {ModuleName, edoc_module()}
+%% @equiv get_doc(File, [])
+
+get_doc(File) ->
+ get_doc(File, []).
+
+%% @spec get_doc(File::filename(), Options::proplist()) ->
+%% {ModuleName, edoc_module()}
+%% ModuleName = atom()
+%%
+%% @type edoc_module(). The EDoc documentation data for a module,
+%% expressed as an XML document in {@link //xmerl. XMerL} format. See
+%% the file <a href="../priv/edoc.dtd">`edoc.dtd'</a> for details.
+%%
+%% @doc Reads a source code file and extracts EDoc documentation data.
+%% Note that without an environment parameter (see {@link get_doc/3}),
+%% hypertext links may not be correct.
+%%
+%% Options:
+%% <dl>
+%% <dt>{@type {def, Macros@}}
+%% </dt>
+%% <dd><ul>
+%% <li>`Macros' = {@type Macro | [Macro]}</li>
+%% <li>`Macro' = {@type {Name::atom(), Text::string()@}}</li>
+%% </ul>
+%% Specifies a set of EDoc macro definitions. See
+%% <a href="overview-summary.html#Macro_expansion">Inline macro expansion</a>
+%% for details.
+%% </dd>
+%% <dt>{@type {hidden, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', documentation of hidden functions will
+%% also be included. The default value is `false'.
+%% </dd>
+%% <dt>{@type {private, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', documentation of private functions will
+%% also be included. The default value is `false'.
+%% </dd>
+%% <dt>{@type {todo, bool()@}}
+%% </dt>
+%% <dd>If the value is `true', To-Do notes written using `@todo' or
+%% `@TODO' tags will be included in the documentation. The default
+%% value is `false'.
+%% </dd>
+%% </dl>
+%%
+%% See {@link read_source/2}, {@link read_comments/2} and {@link
+%% edoc_lib:get_doc_env/4} for further options.
+%%
+%% @see get_doc/3
+%% @see run/3
+%% @see edoc_extract:source/5
+%% @see read/2
+%% @see layout/2
+
+%% INHERIT-OPTIONS: get_doc/3
+%% INHERIT-OPTIONS: edoc_lib:get_doc_env/4
+
+get_doc(File, Opts) ->
+ Env = edoc_lib:get_doc_env(Opts),
+ get_doc(File, Env, Opts).
+
+%% @spec get_doc(File::filename(), Env::edoc_lib:edoc_env(),
+%% Options::proplist()) -> {ModuleName, edoc_module()}
+%% ModuleName = atom()
+%%
+%% @doc Like {@link get_doc/2}, but for a given environment
+%% parameter. `Env' is an environment created by {@link
+%% edoc_lib:get_doc_env/4}.
+
+%% INHERIT-OPTIONS: read_source/2, read_comments/2, edoc_extract:source/5
+%% DEFER-OPTIONS: get_doc/2
+
+get_doc(File, Env, Opts) ->
+ edoc_extract:source(File, Env, Opts).