630c6c475d732e968a9bf585702fc98becb907d0
[project/luci.git] / contrib / luadoc / lua / luadoc / doclet / html.lua
1 -------------------------------------------------------------------------------
2 -- Doclet that generates HTML output. This doclet generates a set of html files
3 -- based on a group of templates. The main templates are:
4 -- <ul>
5 -- <li>index.lp: index of modules and files;</li>
6 -- <li>file.lp: documentation for a lua file;</li>
7 -- <li>module.lp: documentation for a lua module;</li>
8 -- <li>function.lp: documentation for a lua function. This is a
9 -- sub-template used by the others.</li>
10 -- </ul>
11 --
12 -- @release $Id: html.lua,v 1.29 2007/12/21 17:50:48 tomas Exp $
13 -------------------------------------------------------------------------------
14
15 local assert, getfenv, ipairs, loadstring, pairs, setfenv, tostring, tonumber, type = assert, getfenv, ipairs, loadstring, pairs, setfenv, tostring, tonumber, type
16 local io = require"io"
17 local posix = require "posix"
18 local lp = require "luadoc.lp"
19 local luadoc = require"luadoc"
20 local package = package
21 local string = require"string"
22 local table = require"table"
23 local luciutil = require "luci.util"
24
25 module "luadoc.doclet.html"
26
27 -------------------------------------------------------------------------------
28 -- Looks for a file `name' in given path. Removed from compat-5.1
29 -- @param path String with the path.
30 -- @param name String with the name to look for.
31 -- @return String with the complete path of the file found
32 --      or nil in case the file is not found.
33
34 local function search (path, name)
35   for c in string.gfind(path, "[^;]+") do
36     c = string.gsub(c, "%?", name)
37     local f = io.open(c)
38     if f then   -- file exist?
39       f:close()
40       return c
41     end
42   end
43   return nil    -- file not found
44 end
45
46 -------------------------------------------------------------------------------
47 -- Include the result of a lp template into the current stream.
48
49 function include (template, env)
50         -- template_dir is relative to package.path
51         local templatepath = options.template_dir .. template
52
53         -- search using package.path (modified to search .lp instead of .lua
54         local search_path = string.gsub(package.path, "%.lua", "")
55         local templatepath = search(search_path, templatepath)
56         assert(templatepath, string.format("template `%s' not found", template))
57
58         env = env or {}
59         env.table = table
60         env.io = io
61         env.lp = lp
62         env.ipairs = ipairs
63         env.tonumber = tonumber
64         env.tostring = tostring
65         env.type = type
66         env.luadoc = luadoc
67         env.options = options
68
69         return lp.include(templatepath, env)
70 end
71
72 -------------------------------------------------------------------------------
73 -- Returns a link to a html file, appending "../" to the link to make it right.
74 -- @param html Name of the html file to link to
75 -- @return link to the html file
76
77 function link (html, from)
78         local h = html
79         from = from or ""
80         string.gsub(from, "/", function () h = "../" .. h end)
81         return h
82 end
83
84 -------------------------------------------------------------------------------
85 -- Returns the name of the html file to be generated from a module.
86 -- Files with "lua" or "luadoc" extensions are replaced by "html" extension.
87 -- @param modulename Name of the module to be processed, may be a .lua file or
88 -- a .luadoc file.
89 -- @return name of the generated html file for the module
90
91 function module_link (modulename, doc, from)
92         -- TODO: replace "." by "/" to create directories?
93         -- TODO: how to deal with module names with "/"?
94         assert(modulename)
95         assert(doc)
96         from = from or ""
97
98         if doc.modules[modulename] == nil then
99 --              logger:error(string.format("unresolved reference to module `%s'", modulename))
100                 return
101         end
102
103         local href = "modules/" .. modulename .. ".html"
104         string.gsub(from, "/", function () href = "../" .. href end)
105         return href
106 end
107
108 -------------------------------------------------------------------------------
109 -- Returns the name of the html file to be generated from a lua(doc) file.
110 -- Files with "lua" or "luadoc" extensions are replaced by "html" extension.
111 -- @param to Name of the file to be processed, may be a .lua file or
112 -- a .luadoc file.
113 -- @param from path of where am I, based on this we append ..'s to the
114 -- beginning of path
115 -- @return name of the generated html file
116
117 function file_link (to, from)
118         assert(to)
119         from = from or ""
120
121         local href = to
122         href = string.gsub(href, "lua$", "html")
123         href = string.gsub(href, "luadoc$", "html")
124         href = "files/" .. href
125         string.gsub(from, "/", function () href = "../" .. href end)
126         return href
127 end
128
129 -------------------------------------------------------------------------------
130 -- Returns a link to a function or to a table
131 -- @param fname name of the function or table to link to.
132 -- @param doc documentation table
133 -- @param kind String specying the kinf of element to link ("functions" or "tables").
134
135 function link_to (fname, doc, module_doc, file_doc, from, kind)
136         assert(fname)
137         assert(doc)
138         from = from or ""
139         kind = kind or "functions"
140
141         --luciutil.dumptable( module_doc )
142
143         if file_doc then
144                 for _, func_name in pairs(file_doc[kind]) do
145                         if func_name == fname then
146                                 return file_link(file_doc.name, from) .. "#" .. fname
147                         end
148                 end
149         end
150
151         if module_doc and module_doc[kind] then
152                 for func_name, tbl in pairs(module_doc[kind]) do
153                         if func_name == fname then
154                                 return "#" .. fname
155                         end
156                 end
157         end
158
159         local _, _, modulename, fname = string.find(fname, "^(.-)[%.%:]?([^%.%:]*)$")
160         assert(fname)
161
162         -- if fname does not specify a module, use the module_doc
163         if string.len(modulename) == 0 and module_doc then
164                 modulename = module_doc.name
165         end
166
167         local module_doc = doc.modules[modulename]
168         if not module_doc then
169 --              logger:error(string.format("unresolved reference to function `%s': module `%s' not found", fname, modulename))
170                 return
171         end
172
173         for _, func_name in pairs(module_doc[kind]) do
174                 if func_name == fname then
175                         return module_link(modulename, doc, from) .. "#" .. fname
176                 end
177         end
178
179 --      logger:error(string.format("unresolved reference to function `%s' of module `%s'", fname, modulename))
180 end
181
182 -------------------------------------------------------------------------------
183 -- Make a link to a file, module or function
184
185 function symbol_link (symbol, doc, module_doc, file_doc, from)
186         assert(symbol)
187         assert(doc)
188
189         local href =
190 --              file_link(symbol, from) or
191                 module_link(symbol, doc, from) or
192                 link_to(symbol, doc, module_doc, file_doc, from, "functions") or
193                 link_to(symbol, doc, module_doc, file_doc, from, "tables")
194
195         if not href then
196                 logger:error(string.format("unresolved reference to symbol `%s'", symbol))
197         end
198
199         return href or ""
200 end
201
202 -------------------------------------------------------------------------------
203 -- Assembly the output filename for an input file.
204 -- TODO: change the name of this function
205 function out_file (filename)
206         local h = filename
207         h = string.gsub(h, "lua$", "html")
208         h = string.gsub(h, "luadoc$", "html")
209         h = "files/" .. h
210 --      h = options.output_dir .. string.gsub (h, "^.-([%w_]+%.html)$", "%1")
211         h = options.output_dir .. h
212         return h
213 end
214
215 -------------------------------------------------------------------------------
216 -- Assembly the output filename for a module.
217 -- TODO: change the name of this function
218 function out_module (modulename)
219         local h = modulename .. ".html"
220         h = "modules/" .. h
221         h = options.output_dir .. h
222         return h
223 end
224
225 -----------------------------------------------------------------
226 -- Generate the output.
227 -- @param doc Table with the structured documentation.
228
229 function start (doc)
230         -- Generate index file
231         if (#doc.files > 0 or #doc.modules > 0) and (not options.noindexpage) then
232                 local filename = options.output_dir.."index.html"
233                 logger:info(string.format("generating file `%s'", filename))
234                 local f = posix.open(filename, "w")
235                 assert(f, string.format("could not open `%s' for writing", filename))
236                 io.output(f)
237                 include("index.lp", { doc = doc })
238                 f:close()
239         end
240
241         -- Process modules
242         if not options.nomodules then
243                 for _, modulename in ipairs(doc.modules) do
244                         local module_doc = doc.modules[modulename]
245                         -- assembly the filename
246                         local filename = out_module(modulename)
247                         logger:info(string.format("generating file `%s'", filename))
248
249                         local f = posix.open(filename, "w")
250                         assert(f, string.format("could not open `%s' for writing", filename))
251                         io.output(f)
252                         include("module.lp", { doc = doc, module_doc = module_doc })
253                         f:close()
254                 end
255         end
256
257         -- Process files
258         if not options.nofiles then
259                 for _, filepath in ipairs(doc.files) do
260                         local file_doc = doc.files[filepath]
261                         -- assembly the filename
262                         local filename = out_file(file_doc.name)
263                         logger:info(string.format("generating file `%s'", filename))
264
265                         local f = posix.open(filename, "w")
266                         assert(f, string.format("could not open `%s' for writing", filename))
267                         io.output(f)
268                         include("file.lp", { doc = doc, file_doc = file_doc} )
269                         f:close()
270                 end
271         end
272
273         -- copy extra files
274         local f = posix.open(options.output_dir.."luadoc.css", "w")
275         io.output(f)
276         include("luadoc.css")
277         f:close()
278 end