WoWInterface SVN bdGrid

[/] [trunk/] [lib/] [oUF/] [utils/] [docs] - Blame information for rev 2

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 Blooblahguy-207965
#!/usr/bin/env lua
2 Blooblahguy-207965
-- docs
3 Blooblahguy-207965
-- oUF documentation generator
4 Blooblahguy-207965
--
5 Blooblahguy-207965
-- This is really just a quick and dirty way of generating documentation for
6 Blooblahguy-207965
-- oUF[1]. The syntax is inspired by TomDoc[2], but a lot of the non-oUF and
7 Blooblahguy-207965
-- non-Lua things aren't implemented.
8 Blooblahguy-207965
--
9 Blooblahguy-207965
-- Why implement my own documentation generator?
10 Blooblahguy-207965
-- It was mainly done because oUF is kind-of special, but also because the
11 Blooblahguy-207965
-- available alternatives aren't good enough or have issues I can't workaround.
12 Blooblahguy-207965
--
13 Blooblahguy-207965
-- Things that need fixing:
14 Blooblahguy-207965
--  - No highlighting of Lua code.
15 Blooblahguy-207965
--  - Doesn't validate that comments are documentation strings.
16 Blooblahguy-207965
--  - Doesn't parse its own documentation header.
17 Blooblahguy-207965
--  - Close to zero error handling.
18 Blooblahguy-207965
--
19 Blooblahguy-207965
-- Usage
20 Blooblahguy-207965
--
21 Blooblahguy-207965
-- docs [docs path] [file...]
22 Blooblahguy-207965
--
23 Blooblahguy-207965
-- Links
24 Blooblahguy-207965
--
25 Blooblahguy-207965
-- [1] https://github.com/haste/oUF
26 Blooblahguy-207965
-- [2] http://tomdoc.org/
27 Blooblahguy-207965
 
28 Blooblahguy-207965
local out
29 Blooblahguy-207965
local lines
30 Blooblahguy-207965
 
31 Blooblahguy-207965
local tisf = function(fmt, ...)
32 Blooblahguy-207965
        table.insert(out, fmt:format(...))
33 Blooblahguy-207965
end
34 Blooblahguy-207965
 
35 Blooblahguy-207965
local trim = function(str)
36 Blooblahguy-207965
        return str:match('^()%s*$') and '' or str:match('^%s*(.*%S)')
37 Blooblahguy-207965
end
38 Blooblahguy-207965
 
39 Blooblahguy-207965
local findNextEmpty = function(start, stop)
40 Blooblahguy-207965
        for i=start, stop or #lines do
41 Blooblahguy-207965
                if(lines[i] == '') then
42 Blooblahguy-207965
                        return i
43 Blooblahguy-207965
                end
44 Blooblahguy-207965
        end
45 Blooblahguy-207965
end
46 Blooblahguy-207965
 
47 Blooblahguy-207965
local findNextHeader = function(offest)
48 Blooblahguy-207965
        for i=offest, #lines do
49 Blooblahguy-207965
                local pre, header, post = unpack(lines, i, i + 2)
50 Blooblahguy-207965
                -- Single lines without punctuation are headers.
51 Blooblahguy-207965
                if(pre == '' and post == '' and not header:match'%.') then
52 Blooblahguy-207965
                        return i + 1
53 Blooblahguy-207965
                end
54 Blooblahguy-207965
        end
55 Blooblahguy-207965
end
56 Blooblahguy-207965
 
57 Blooblahguy-207965
local findNextArguent = function(start, stop, padding, pattern)
58 Blooblahguy-207965
        for i=start, stop do
59 Blooblahguy-207965
                local match, pad = lines[i]:match(pattern)
60 Blooblahguy-207965
                if(match and pad == padding) then
61 Blooblahguy-207965
                        return i
62 Blooblahguy-207965
                end
63 Blooblahguy-207965
        end
64 Blooblahguy-207965
end
65 Blooblahguy-207965
 
66 Blooblahguy-207965
local replaceMarkup = function(str)
67 Blooblahguy-207965
        return str
68 Blooblahguy-207965
        -- `in-line code` to <code>in-line code</code>
69 Blooblahguy-207965
        :gsub('`([^`]+)`', '<code>%1</code>')
70 Blooblahguy-207965
        -- [Link](http://example.com) to <a href="http://example.com">Link</a>
71 Blooblahguy-207965
        :gsub('%[([^%]]+)%]%(([^)]+)%)', '<a href="%2">%1</a>')
72 Blooblahguy-207965
end
73 Blooblahguy-207965
 
74 Blooblahguy-207965
local handleArguments = function(start, stop, pattern)
75 Blooblahguy-207965
        tisf('<dl>')
76 Blooblahguy-207965
        repeat
77 Blooblahguy-207965
                -- Tear out the argument name and offset of where the description begins.
78 Blooblahguy-207965
                local def, padding, offset = lines[start]:match(pattern)
79 Blooblahguy-207965
                tisf('<dt>%s</dt>', def)
80 Blooblahguy-207965
 
81 Blooblahguy-207965
                -- Insert the first line of the description.
82 Blooblahguy-207965
                tisf('<dd>')
83 Blooblahguy-207965
                tisf(lines[start]:sub(offset))
84 Blooblahguy-207965
 
85 Blooblahguy-207965
                -- Find the next argument in the list or continue to the end of the
86 Blooblahguy-207965
                -- current section.
87 Blooblahguy-207965
                local nextarg = findNextArguent(start + 1, stop, padding, pattern)
88 Blooblahguy-207965
                nextarg = (nextarg or stop + 1) - 1
89 Blooblahguy-207965
                for i=start + 1, nextarg do
90 Blooblahguy-207965
                        tisf(trim(lines[i]))
91 Blooblahguy-207965
                end
92 Blooblahguy-207965
                tisf('</dd>')
93 Blooblahguy-207965
 
94 Blooblahguy-207965
                start = nextarg + 1
95 Blooblahguy-207965
        until start > stop
96 Blooblahguy-207965
        tisf('</dl>')
97 Blooblahguy-207965
end
98 Blooblahguy-207965
 
99 Blooblahguy-207965
local handleExamples = function(start, stop)
100 Blooblahguy-207965
        -- An extra line gets added if we don't do this.
101 Blooblahguy-207965
        tisf('<pre><code>%s', lines[start]:sub(3))
102 Blooblahguy-207965
        for i=start + 1, stop  do
103 Blooblahguy-207965
                tisf(lines[i]:sub(3))
104 Blooblahguy-207965
        end
105 Blooblahguy-207965
        tisf('</code></pre>')
106 Blooblahguy-207965
end
107 Blooblahguy-207965
 
108 Blooblahguy-207965
local handleParagraph = function(start, stop)
109 Blooblahguy-207965
        tisf('<p>')
110 Blooblahguy-207965
        for i=start, stop do
111 Blooblahguy-207965
                tisf(trim(lines[i]))
112 Blooblahguy-207965
        end
113 Blooblahguy-207965
        tisf('</p>')
114 Blooblahguy-207965
end
115 Blooblahguy-207965
 
116 Blooblahguy-207965
local handleSection = function(start, stop)
117 Blooblahguy-207965
        while(start) do
118 Blooblahguy-207965
                -- Find the next empty line or continue until the end of the section.
119 Blooblahguy-207965
                -- findNextEmpty() returns the position of the empty line, so we need to
120 Blooblahguy-207965
                -- subtract one from it.
121 Blooblahguy-207965
                local nextEmpty = findNextEmpty(start + 1, stop)
122 Blooblahguy-207965
                if(nextEmpty) then
123 Blooblahguy-207965
                        nextEmpty = nextEmpty - 1
124 Blooblahguy-207965
                else
125 Blooblahguy-207965
                        nextEmpty = stop
126 Blooblahguy-207965
                end
127 Blooblahguy-207965
 
128 Blooblahguy-207965
                local line = lines[start]
129 Blooblahguy-207965
                if(not line) then
130 Blooblahguy-207965
                        return
131 Blooblahguy-207965
                elseif(line:match('^%S+%s*%- ')) then
132 Blooblahguy-207965
                        handleArguments(start, nextEmpty, '(%S+)%s*()%- ()')
133 Blooblahguy-207965
                elseif(line:sub(1, 2) == '  ') then
134 Blooblahguy-207965
                        handleExamples(start, nextEmpty)
135 Blooblahguy-207965
                else
136 Blooblahguy-207965
                        handleParagraph(start, nextEmpty)
137 Blooblahguy-207965
                end
138 Blooblahguy-207965
 
139 Blooblahguy-207965
                start = findNextEmpty(nextEmpty, stop)
140 Blooblahguy-207965
                if(start) then start = start + 1 end
141 Blooblahguy-207965
        end
142 Blooblahguy-207965
end
143 Blooblahguy-207965
 
144 Blooblahguy-207965
local generateDocs = function(str, level)
145 Blooblahguy-207965
        lines = {}
146 Blooblahguy-207965
        out = {}
147 Blooblahguy-207965
 
148 Blooblahguy-207965
        for line in str:gmatch('([^\n]*)\n') do
149 Blooblahguy-207965
                table.insert(lines, line:gsub('\t*', ''):sub(2))
150 Blooblahguy-207965
        end
151 Blooblahguy-207965
 
152 Blooblahguy-207965
        -- The first line is always the main header.
153 Blooblahguy-207965
        tisf('<h%d>%s</h%d>', level, lines[1], level)
154 Blooblahguy-207965
 
155 Blooblahguy-207965
        -- Then comes the main description.
156 Blooblahguy-207965
        local offset = findNextHeader(1)
157 Blooblahguy-207965
 
158 Blooblahguy-207965
        -- Continue until two lines before the header or to the end of the comment.
159 Blooblahguy-207965
        if(offset) then
160 Blooblahguy-207965
                offset = offset - 2
161 Blooblahguy-207965
        else
162 Blooblahguy-207965
                offset = #lines
163 Blooblahguy-207965
        end
164 Blooblahguy-207965
 
165 Blooblahguy-207965
        local init = findNextEmpty(1) + 1
166 Blooblahguy-207965
        if(init > offset) then
167 Blooblahguy-207965
                init = 2
168 Blooblahguy-207965
        end
169 Blooblahguy-207965
 
170 Blooblahguy-207965
        handleSection(init, offset)
171 Blooblahguy-207965
 
172 Blooblahguy-207965
        while(true) do
173 Blooblahguy-207965
                offset = findNextHeader(offset)
174 Blooblahguy-207965
                if(not offset) then break end
175 Blooblahguy-207965
 
176 Blooblahguy-207965
                -- Every section has a header.
177 Blooblahguy-207965
                tisf('<h%d>%s</h%d>', level + 1, lines[offset], level + 1)
178 Blooblahguy-207965
 
179 Blooblahguy-207965
                -- Find out the size of the section.
180 Blooblahguy-207965
                local start = findNextEmpty(offset) + 1
181 Blooblahguy-207965
                if(not lines[start]) then
182 Blooblahguy-207965
                        -- There's no section here, only a headline.
183 Blooblahguy-207965
                        break
184 Blooblahguy-207965
                end
185 Blooblahguy-207965
 
186 Blooblahguy-207965
                local stop
187 Blooblahguy-207965
                local nextHeader = findNextHeader(start)
188 Blooblahguy-207965
                if(nextHeader) then
189 Blooblahguy-207965
                        stop = nextHeader - 2
190 Blooblahguy-207965
                else
191 Blooblahguy-207965
                        local nextEmpty = findNextEmpty(start)
192 Blooblahguy-207965
                        if(nextEmpty) then
193 Blooblahguy-207965
                                stop = nextEmpty - 1
194 Blooblahguy-207965
                        else
195 Blooblahguy-207965
                                stop = #lines
196 Blooblahguy-207965
                        end
197 Blooblahguy-207965
                end
198 Blooblahguy-207965
 
199 Blooblahguy-207965
                handleSection(start, stop)
200 Blooblahguy-207965
                offset = stop + 1
201 Blooblahguy-207965
        end
202 Blooblahguy-207965
 
203 Blooblahguy-207965
        return table.concat(out, '\n')
204 Blooblahguy-207965
end
205 Blooblahguy-207965
 
206 Blooblahguy-207965
local handleFile = function(path)
207 Blooblahguy-207965
        local file = io.open(path, 'r')
208 Blooblahguy-207965
        local content = file:read'*a'
209 Blooblahguy-207965
        file:close()
210 Blooblahguy-207965
 
211 Blooblahguy-207965
        local out = {}
212 Blooblahguy-207965
        local init = 1
213 Blooblahguy-207965
        repeat
214 Blooblahguy-207965
                local _, comStart, depth = content:find('%-%-%[(=*)%[ ', init)
215 Blooblahguy-207965
                if(comStart) then
216 Blooblahguy-207965
                        local comEnd = content:find('%]' .. depth .. '%]', comStart)
217 Blooblahguy-207965
                        local comment = content:sub(comStart, comEnd - 1)
218 Blooblahguy-207965
 
219 Blooblahguy-207965
                        -- Convert markup to html.
220 Blooblahguy-207965
                        comment = replaceMarkup(comment)
221 Blooblahguy-207965
 
222 Blooblahguy-207965
                        -- The first comment uses h1 and h2, while the subsequent ones uses h3
223 Blooblahguy-207965
                        -- and h4.
224 Blooblahguy-207965
                        local level = init == 1 and 1 or 3
225 Blooblahguy-207965
                        table.insert(out, generateDocs(comment, init == 1 and 1 or 3))
226 Blooblahguy-207965
 
227 Blooblahguy-207965
                        init = comEnd
228 Blooblahguy-207965
                end
229 Blooblahguy-207965
        until not comStart
230 Blooblahguy-207965
 
231 Blooblahguy-207965
        return table.concat(out, '\n')
232 Blooblahguy-207965
end
233 Blooblahguy-207965
 
234 Blooblahguy-207965
local destination = (...)
235 Blooblahguy-207965
for i=2, select('#', ...) do
236 Blooblahguy-207965
        local file = select(i, ...)
237 Blooblahguy-207965
        local path, filename = file:match('(.+)/(.+)$')
238 Blooblahguy-207965
 
239 Blooblahguy-207965
        if(path:sub(1,3) == '../') then
240 Blooblahguy-207965
                path = path:sub(4)
241 Blooblahguy-207965
        end
242 Blooblahguy-207965
 
243 Blooblahguy-207965
        if(#path == 0) then path = nil end
244 Blooblahguy-207965
        filename = filename:gsub('lua', 'html')
245 Blooblahguy-207965
 
246 Blooblahguy-207965
        local doc = handleFile(file)
247 Blooblahguy-207965
        if(doc) then
248 Blooblahguy-207965
                local dfPath = string.format('%s/%s', destination, path or '')
249 Blooblahguy-207965
                os.execute(string.format('mkdir -p %s', dfPath))
250 Blooblahguy-207965
                local docFile = io.open(string.format('%s/%s', dfPath, filename), 'w+')
251 Blooblahguy-207965
                docFile:write(doc)
252 Blooblahguy-207965
                docFile:close()
253 Blooblahguy-207965
        end
254 Blooblahguy-207965
end