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