Merge tag '1.3.13'
nil-description error patch
Julian Wollrath
10 years ago
395 | 395 | -- @class module |
396 | 396 | -- @name simple |
397 | 397 | |
398 | becomes: | |
399 | ||
398 | 400 | ------------ |
399 | 401 | -- a simple module. |
400 | 402 | -- (LDoc) |
556 | 558 | |
557 | 559 | An LDoc feature which is particularly useful for C extensions is _module merging_. If several |
558 | 560 | files are all marked as `@module lib` then a single module `lib` is generated, containing all |
559 | the docs from the separate files. | |
561 | the docs from the separate files. For this, use `merge=true`. | |
560 | 562 | |
561 | 563 | See 'tests/examples/mylib.c' for the full example. |
562 | 564 | |
565 | 567 | For example, to process all files in the 'lua' directory: |
566 | 568 | |
567 | 569 | $ ldoc lua |
568 | output written to docs/ | |
569 | ||
570 | Thereafter the `docs` directory will contain `index.html` which points to individual modules | |
570 | output written to doc/ | |
571 | ||
572 | Thereafter the `doc` directory will contain `index.html` which points to individual modules | |
571 | 573 | in the `modules` subdirectory. The `--dir` flag can specify where the output is generated, |
572 | 574 | and will ensure that the directory exists. The output structure is like LuaDoc: there is an |
573 | 575 | `index.html` and the individual modules are in the `modules` subdirectory. This applies to |
589 | 591 | has an explicit `@module PACKAGE.NAME`. If it does not, then `ldoc` will still attempt to |
590 | 592 | deduce the module name, but may need help with `--package/-b` as above. |
591 | 593 | |
594 | A special case is if you simply say 'ldoc .'. Then there _must_ be a `config.ld` file | |
595 | available in the directory, and it can specify the file: | |
596 | ||
597 | file = "mymod.lua" | |
598 | title = "mymod documentation" | |
599 | description = "mymod does some simple but useful things" | |
600 | ||
601 | `file` can of course point to a directory, just as with the `--file` option. This mode makes | |
602 | it particularly easy for the user to build the documentation, by allowing you to specify | |
603 | everything explicitly in the configuration. | |
604 | ||
605 | In `config.ld`, `file` may be a Lua table, containing file names or directories; if it has | |
606 | an `exclude` field then that will be used to exclude files from the list, for example | |
607 | `{'examples', exclude = {'examples/slow.lua'}}`. | |
608 | ||
609 | A particular configuration file can be specified with the `-c` flag. Configuration files don't | |
610 | _have_ to contain a `file` field, but in that case LDoc does need an explicit file on the command | |
611 | line. This is useful if you have some defaults you wish to apply to all of your docs. | |
612 | ||
613 | ## Markdown Support | |
614 | ||
592 | 615 | `format = 'markdown'` can be used in your `config.ld` and will be used to process summaries |
593 | and descriptions. This requires [markdown.lua](http://www.frykholm.se/files/markdown.lua) by | |
594 | Niklas Frykholm to be installed (this can be most easily done with `luarocks install | |
595 | markdown`.) A much faster alternative is | |
596 | [lua-discount](http://asbradbury.org/projects/lua-discount/) which you can use by setting | |
597 | `format` to 'discount' after installing using `luarocks install lua-discount`) The | |
598 | [discount](http://www.pell.portland.or.us/~orc/Code/discount/) Markdown processor | |
599 | additionally has more features than the pure Lua version, such as PHP-Extra style tables. | |
600 | As a special case, LDoc will fall back to using `markdown.lua` if it cannot find `discount`. | |
601 | ||
602 | `format = 'markdown'` can be used in your `config.ld` and will be used to process summaries | |
603 | and descriptions. This requires a markdown processor. | |
616 | and descriptions; you can also use the `-f` flag. This requires a markdown processor. | |
604 | 617 | LDoc knows how to use: |
605 | 618 | |
606 | 619 | - [markdown.lua](http://www.frykholm.se/files/markdown.lua) a pure Lua processor by |
607 | Niklas Frykholm (this can be installed easily with `luarocks install markdown`.) | |
620 | Niklas Frykholm. For convenience, LDoc comes with a copy of markdown.lua. | |
608 | 621 | - [lua-discount](http://asbradbury.org/projects/lua-discount/), a faster alternative |
609 | 622 | (installed with `luarocks install lua-discount`). lua-discount uses the C |
610 | 623 | [discount](http://www.pell.portland.or.us/~orc/Code/discount/) Markdown processor which has |
614 | 627 | |
615 | 628 | You can request the processor you like with `format = 'markdown|discount|lunamark'`, and |
616 | 629 | LDoc will attempt to use it. If it can't find it, it will look for one of the other |
617 | markdown processors. If it can't find any markdown processer, it will fall back to text | |
618 | processing. | |
619 | ||
620 | ||
621 | A special case is if you simply say 'ldoc .'. Then there _must_ be a `config.ld` file | |
622 | available in the directory, and it can specify the file: | |
623 | ||
624 | file = "mymod.lua" | |
625 | title = "mymod documentation" | |
626 | description = "mymod does some simple but useful things" | |
627 | ||
628 | `file` can of course point to a directory, just as with the `--file` option. This mode makes | |
629 | it particularly easy for the user to build the documentation, by allowing you to specify | |
630 | everything explicitly in the configuration. | |
631 | ||
632 | In `config.ld`, `file` may be a Lua table, containing file names or directories; if it has | |
633 | an `exclude` field then that will be used to exclude files from the list, for example | |
634 | `{'examples', exclude = {'examples/slow.lua'}}`. | |
635 | ||
630 | markdown processors. | |
631 | ||
632 | Even with the default of 'plain' some minimal processing takes place, in particular empty lines | |
633 | are treated as line breaks. | |
634 | ||
635 | This formatting applies to all of a project, including any readmes and so forth. You may want | |
636 | Markdown for this 'narrative' documentation, but not for your code comments. `plain=true` will | |
637 | switch off formatting for code. | |
636 | 638 | |
637 | 639 | ## Processing Single Modules |
638 | 640 | |
640 | 642 | special case when a single module file is specified. Here an index would be redundant, so |
641 | 643 | the single HTML file generated contains the module documentation. |
642 | 644 | |
643 | $ ldoc mylib.lua --> results in docs/index.html | |
644 | $ ldoc --output mylib mylib.lua --> results in docs/mylib.html | |
645 | $ ldoc mylib.lua --> results in doc/index.html | |
646 | $ ldoc --output mylib mylib.lua --> results in doc/mylib.html | |
645 | 647 | $ ldoc --output mylib --dir html mylib.lua --> results in html/mylib.html |
646 | 648 | |
647 | 649 | The default sections used by LDoc are 'Functions', 'Tables' and 'Fields', corresponding to |
784 | 786 | |
785 | 787 | ## Customizing the Page |
786 | 788 | |
789 | A basic customization is to override the default UTF-8 encoding using `charset`. For instance, | |
790 | Brazillian software would find it useful to put `charset='ISO-8859-1'` in `config.ld`, or use | |
791 | the @charset tag for individual files. | |
792 | ||
787 | 793 | Setting `no_return_or_parms` to `true` will suppress the display of 'param' and 'return' |
788 | 794 | tags. This may appeal to programmers who dislike the traditional @tag soup xDoc style and |
789 | 795 | prefer to comment functions just with a description. This is particularly useful when using |
810 | 816 | no_return_or_parms = true |
811 | 817 | format = 'discount' |
812 | 818 | |
813 | ||
814 | 819 | Generally, using Markdown gives you the opportunity to structure your documentation in any |
815 | 820 | way you want; particularly if using lua-discount and its [table |
816 | 821 | syntax](http://michelf.com/projects/php-markdown/extra/#table); the desired result can often |
833 | 838 | examples = {'examples', exclude = {'examples/slow.lua'}} |
834 | 839 | |
835 | 840 | That is, all files in the `examples` folder are to be pretty-printed, except for `slow.lua` |
836 | which is meant to be called from one of the examples. The see-reference to `testu.lua` | |
841 | which is meant to be called from one of the examples. Now a see-reference to `testu.lua` | |
837 | 842 | resolves to 'examples/testu.lua.html'. |
838 | 843 | |
839 | 844 | Examples may link back to the API documentation, for instance the example `input.lua` has a |
840 | 845 | @\{spawn_process} inline reference. |
846 | ||
847 | By default, LDoc uses a built-in Lua code 'prettifier'. See-references are allowed in comments, | |
848 | and also in code if they're enclosed in backticks. | |
849 | ||
850 | [lxsh](https://github.com/xolox/lua-lxsh) | |
851 | can be used (available from LuaRocks) if you want support for C as well. `pretty='lxsh'` will | |
852 | cause `lxsh` to be used, if available. | |
841 | 853 | |
842 | 854 | ## Readme files |
843 | 855 | |
857 | 869 | Another name for `readme` is `topics`, which is more descriptive. From LDoc 1.2, |
858 | 870 | `readme/topics` can be a list of documents. These act as a top-level table-of-contents for |
859 | 871 | your documentation. Currently, if you want them in a particular order, then use names like |
860 | `01-introduction.md` etc which sort appropriately. | |
872 | `01-introduction.md` etc, which sort appropriately. | |
861 | 873 | |
862 | 874 | The first line of a document may be a Markdown `#` title. If so, then LDoc will regard the |
863 | 875 | next level as the subheadings, normally second-level `##`. But if the title is already |
974 | 986 | - `all` show local functions, etc as well in the docs |
975 | 987 | - `format` markup processor, can be 'plain' (default), 'markdown' or 'discount' |
976 | 988 | - `output` output name (default 'index') |
977 | - `dir` directory for output files (default 'docs') | |
989 | - `dir` directory for output files (default 'doc') | |
990 | - `colon` use colon style, instead of @ tag style | |
991 | - `boilerplate` ignore first comment in all source files (e.g. license comments) | |
978 | 992 | - `ext` extension for output (default 'html') |
979 | 993 | - `one` use a one-column layout |
980 | 994 | - `style`, `template`: together these specify the directories for the style and and the |
981 | 995 | template. In `config.ld` they may also be `true`, meaning use the same directory as the |
982 | 996 | configuration file. |
997 | - `merge` allow documentation from different files to be merged into modules without | |
998 | explicit @submodule tag | |
983 | 999 | |
984 | 1000 | These only appear in `config.ld`: |
985 | 1001 | |
986 | 1002 | - `description` a project description used under the project title |
987 | 1003 | - `examples` a directory or file: can be a table |
988 | - `readme` name of readme file (to be processed with Markdown) | |
1004 | - `readme` or `topics` readme files (to be processed with Markdown) | |
1005 | - `pretty` code prettify 'lua' (default) or 'lxsh' | |
1006 | - `charset` use if you want to override the UTF-8 default (also @charset in files) | |
989 | 1007 | - `no_return_or_parms` don't show parameters or return values in output |
990 | 1008 | - `backtick_references` whether references in backticks will be resolved |
1009 | - `plain` set to true if `format` is set but you don't want code comments processed | |
1010 | - `wrap` ?? | |
991 | 1011 | - `manual_url` point to an alternative or local location for the Lua manual, e.g. |
992 | 1012 | 'file:///D:/dev/lua/projects/lua-5.1.4/doc/manual.html' |
993 | - `one` use a one-column output format | |
994 | 1013 | - `no_summary` suppress the Contents summary |
1014 | - `custom_see_handler` function that filters see-references | |
1015 | - `not_luadoc` set to `true` if the docs break LuaDoc compatibility | |
995 | 1016 | |
996 | 1017 | Available functions are: |
997 | 1018 |
114 | 114 | if tags.summary ~= '' then return false end |
115 | 115 | for tag, value in pairs(tags) do |
116 | 116 | if known_tags._annotation_tags[tag] then |
117 | tags.class = 'annotation' | |
118 | tags.summary = value | |
117 | tags:add('class','annotation') | |
118 | tags:add('summary',value) | |
119 | 119 | local item_name = last_item and last_item.tags.name or '?' |
120 | tags.name = item_name..'-'..tag..acount | |
120 | tags:add('name',item_name..'-'..tag..acount) | |
121 | 121 | acount = acount + 1 |
122 | 122 | return true |
123 | 123 | end |
219 | 219 | if mod then |
220 | 220 | print('found master module',mf) |
221 | 221 | this_mod = mod |
222 | if this_mod.section then | |
223 | print '***closing section from master module***' | |
224 | this_mod.section = nil | |
225 | end | |
222 | 226 | submodule = true |
223 | 227 | end |
224 | 228 | end |
263 | 267 | -- add the item to the module's item list |
264 | 268 | if this_mod then |
265 | 269 | -- new-style modules will have qualified names like 'mod.foo' |
270 | --require 'pl.pretty'.dump(item.tags) | |
266 | 271 | local mod,fname = split_dotted_name(item.name) |
267 | 272 | -- warning for inferred unqualified names in new style modules |
268 | 273 | -- (retired until we handle methods like Set:unset() properly) |
314 | 319 | end |
315 | 320 | end |
316 | 321 | end |
317 | section_description = this_section.summary..' '..this_section.description | |
322 | section_description = this_section.summary..' '..(this_section.description or '') | |
318 | 323 | elseif item.tags.within then |
319 | 324 | section_description = item.tags.within |
320 | 325 | item.section = section_description |
383 | 388 | end |
384 | 389 | end |
385 | 390 | |
391 | function Item:trailing_warning (kind,tag,rest) | |
392 | if type(rest)=='string' and #rest > 0 then | |
393 | Item.warning(self,kind.." tag: '"..tag..'" has trailing text; use no_luadoc=true\n'..rest) | |
394 | end | |
395 | end | |
396 | ||
386 | 397 | function Item:set_tag (tag,value) |
387 | 398 | local ttype = known_tags[tag] |
399 | local args = self.file.args | |
388 | 400 | |
389 | 401 | if ttype == TAG_MULTI or ttype == TAG_MULTI_LINE then -- value is always a List! |
390 | 402 | if getmetatable(value) ~= List then |
391 | 403 | value = List{value} |
392 | 404 | end |
393 | if ttype ~= TAG_MULTI_LINE then | |
405 | if ttype ~= TAG_MULTI_LINE and args and args.not_luadoc then | |
394 | 406 | local last = value[#value] |
395 | 407 | if type(last) == 'string' and last:match '\n' then |
396 | 408 | local line,rest = last:match('([^\n]+)(.*)') |
409 | 421 | value = value[1] |
410 | 422 | modifiers = value.modifiers |
411 | 423 | end |
424 | if value == nil then self:error("Tag without value: "..tag) end | |
412 | 425 | local id, rest = tools.extract_identifier(value) |
413 | 426 | self.tags[tag] = id |
414 | self:add_to_description(rest) | |
427 | if args and args.not_luadoc then | |
428 | self:add_to_description(rest) | |
429 | else | |
430 | self:trailing_warning('id',tag,rest) | |
431 | end | |
415 | 432 | elseif ttype == TAG_SINGLE then |
416 | 433 | self.tags[tag] = value |
417 | 434 | elseif ttype == TAG_FLAG then |
418 | 435 | self.tags[tag] = true |
419 | self:add_to_description(value) | |
436 | if args.not_luadoc then | |
437 | self:add_to_description(value) | |
438 | else | |
439 | self:trailing_warning('flag',tag,value) | |
440 | end | |
420 | 441 | else |
421 | 442 | Item.warning(self,"unknown tag: '"..tag.."' "..tostring(ttype)) |
422 | 443 | end |
741 | 762 | end |
742 | 763 | |
743 | 764 | Module.warning, Module.error = Item.warning, Item.error |
744 | ||
745 | 765 | |
746 | 766 | -------- Resolving References ----------------- |
747 | 767 |
23 | 23 | # local use_li = ldoc.use_li |
24 | 24 | # local display_name = ldoc.display_name |
25 | 25 | # local iter = ldoc.modules.iter |
26 | # local M = ldoc.markup | |
26 | # ---local M = ldoc.markup | |
27 | # local function M(txt,item) return ldoc.markup(txt,item,ldoc.plain) end | |
27 | 28 | # local nowrap = ldoc.wrap and '' or 'nowrap' |
28 | 29 | |
29 | 30 | <!-- Menu --> |
248 | 249 | </div> <!-- id="content" --> |
249 | 250 | </div> <!-- id="main" --> |
250 | 251 | <div id="about"> |
251 | <i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.3.11</a></i> | |
252 | <i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.3.12</a></i> | |
252 | 253 | </div> <!-- id="about" --> |
253 | 254 | </div> <!-- id="container" --> |
254 | 255 | </body> |
16 | 16 | -- iden n |
17 | 17 | -- keyword do |
18 | 18 | -- </pre> |
19 | -- See the Guide for further <a href="../../index.html#lexer">discussion</a> <br> | |
20 | -- @class module | |
21 | -- @name pl.lexer | |
19 | -- | |
20 | -- Based on pl.lexer from Penlight | |
22 | 21 | |
23 | 22 | local strfind = string.find |
24 | 23 | local strsub = string.sub |
25 | 24 | local append = table.insert |
26 | --[[ | |
27 | module ('pl.lexer',utils._module) | |
28 | ]] | |
29 | 25 | |
30 | 26 | local function assert_arg(idx,val,tp) |
31 | 27 | if type(val) ~= tp then |
67 | 63 | tok = tok:sub(2,-2) |
68 | 64 | end |
69 | 65 | return "string",tok |
66 | end | |
67 | ||
68 | -- strings enclosed in back ticks | |
69 | local function bdump(tok,options) | |
70 | if options and options.string then | |
71 | tok = tok:sub(2,-2) | |
72 | end | |
73 | return "backtick",tok | |
70 | 74 | end |
71 | 75 | |
72 | 76 | -- long Lua strings need extra work to get rid of the quotes |
171 | 175 | if file then |
172 | 176 | s = file:read() |
173 | 177 | if not s then return nil end -- empty file |
178 | if s:match '^\239\187' then -- UTF-8 BOM Abomination | |
179 | s = s:sub(4) | |
180 | end | |
174 | 181 | s = s ..'\n' |
175 | 182 | end |
176 | 183 | local sz = #s |
294 | 301 | {STRING3,sdump}, |
295 | 302 | {STRING1,sdump}, |
296 | 303 | {STRING2,sdump}, |
304 | {'^`[^`]+`',bdump}, | |
297 | 305 | {'^%-%-%[(=*)%[.-%]%1%]',cdump}, |
298 | 306 | {'^%-%-.-\n',cdump}, |
299 | 307 | {'^%[(=*)%[.-%]%1%]',sdump_l}, |
4 | 4 | |
5 | 5 | <http://www.frykholm.se/files/markdown.lua> |
6 | 6 | |
7 | **Author:** Niklas Frykholm, <niklas@frykholm.se> | |
7 | **Author:** Niklas Frykholm, <niklas@frykholm.se> | |
8 | 8 | **Date:** 31 May 2008 |
9 | 9 | |
10 | 10 | This is an implementation of the popular text markup language Markdown in pure Lua. |
46 | 46 | software and associated documentation files (the "Software"), to deal in the Software |
47 | 47 | without restriction, including without limitation the rights to use, copy, modify, merge, |
48 | 48 | publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
49 | to whom the Software is furnished to do so, subject to the following conditions: | |
49 | to whom the Software is furnished to do so, subject to the following conditions: | |
50 | 50 | |
51 | 51 | The above copyright notice and this permission notice shall be included in all copies |
52 | or substantial portions of the Software. | |
52 | or substantial portions of the Software. | |
53 | 53 | |
54 | 54 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
55 | 55 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
129 | 129 | -- Locks table t from changes, writes an error if someone attempts to change the table. |
130 | 130 | -- This is useful for detecting variables that have "accidently" been made global. Something |
131 | 131 | -- I tend to do all too much. |
132 | function lock(t) | |
133 | function lock_new_index(t, k, v) | |
132 | function M.lock(t) | |
133 | local function lock_new_index(t, k, v) | |
134 | 134 | error("module has been locked -- " .. k .. " must be declared local", 2) |
135 | 135 | end |
136 | 136 | |
137 | 137 | local mt = {__newindex = lock_new_index} |
138 | if getmetatable(t) then mt.__index = getmetatable(t).__index end | |
138 | if getmetatable(t) then | |
139 | mt.__index = getmetatable(t).__index | |
140 | end | |
139 | 141 | setmetatable(t, mt) |
140 | 142 | end |
141 | 143 | |
142 | 144 | -- Returns the result of mapping the values in table t through the function f |
143 | function map(t, f) | |
145 | local function map(t, f) | |
144 | 146 | local out = {} |
145 | 147 | for k,v in pairs(t) do out[k] = f(v,k) end |
146 | 148 | return out |
147 | 149 | end |
148 | 150 | |
149 | 151 | -- The identity function, useful as a placeholder. |
150 | function identity(text) return text end | |
152 | local function identity(text) return text end | |
151 | 153 | |
152 | 154 | -- Functional style if statement. (NOTE: no short circuit evaluation) |
153 | function iff(t, a, b) if t then return a else return b end end | |
155 | local function iff(t, a, b) if t then return a else return b end end | |
154 | 156 | |
155 | 157 | -- Splits the text into an array of separate lines. |
156 | function split(text, sep) | |
158 | local function split(text, sep) | |
157 | 159 | sep = sep or "\n" |
158 | 160 | local lines = {} |
159 | 161 | local pos = 1 |
167 | 169 | end |
168 | 170 | |
169 | 171 | -- Converts tabs to spaces |
170 | function detab(text) | |
172 | local function detab(text) | |
171 | 173 | local tab_width = 4 |
172 | 174 | local function rep(match) |
173 | 175 | local spaces = -match:len() |
179 | 181 | end |
180 | 182 | |
181 | 183 | -- Applies string.find for every pattern in the list and returns the first match |
182 | function find_first(s, patterns, index) | |
184 | local function find_first(s, patterns, index) | |
183 | 185 | local res = {} |
184 | 186 | for _,p in ipairs(patterns) do |
185 | 187 | local match = {s:find(p, index)} |
191 | 193 | -- If a replacement array is specified, the range [start, stop] in the array is replaced |
192 | 194 | -- with the replacement array and the resulting array is returned. Without a replacement |
193 | 195 | -- array the section of the array between start and stop is returned. |
194 | function splice(array, start, stop, replacement) | |
196 | local function splice(array, start, stop, replacement) | |
195 | 197 | if replacement then |
196 | 198 | local n = stop - start + 1 |
197 | 199 | while n > 0 do |
212 | 214 | end |
213 | 215 | |
214 | 216 | -- Outdents the text one step. |
215 | function outdent(text) | |
217 | local function outdent(text) | |
216 | 218 | text = "\n" .. text |
217 | 219 | text = text:gsub("\n ? ? ?", "\n") |
218 | 220 | text = text:sub(2) |
220 | 222 | end |
221 | 223 | |
222 | 224 | -- Indents the text one step. |
223 | function indent(text) | |
225 | local function indent(text) | |
224 | 226 | text = text:gsub("\n", "\n ") |
225 | 227 | return text |
226 | 228 | end |
227 | 229 | |
228 | -- Does a simple tokenization of html data. Returns the data as a list of tokens. | |
230 | -- Does a simple tokenization of html data. Returns the data as a list of tokens. | |
229 | 231 | -- Each token is a table with a type field (which is either "tag" or "text") and |
230 | 232 | -- a text field (which contains the original token data). |
231 | function tokenize_html(html) | |
233 | local function tokenize_html(html) | |
232 | 234 | local tokens = {} |
233 | 235 | local pos = 1 |
234 | 236 | while true do |
238 | 240 | break |
239 | 241 | end |
240 | 242 | if start ~= pos then table.insert(tokens, {type="text", text = html:sub(pos, start-1)}) end |
241 | ||
243 | ||
242 | 244 | local _, stop |
243 | 245 | if html:match("^<!%-%-", start) then |
244 | 246 | _,stop = html:find("%-%->", start) |
248 | 250 | _,stop = html:find("%b<>", start) |
249 | 251 | end |
250 | 252 | if not stop then |
251 | -- error("Could not match html tag " .. html:sub(start,start+30)) | |
253 | -- error("Could not match html tag " .. html:sub(start,start+30)) | |
252 | 254 | table.insert(tokens, {type="text", text=html:sub(start, start)}) |
253 | 255 | pos = start + 1 |
254 | 256 | else |
271 | 273 | local HASH = { |
272 | 274 | -- Has the hash been inited. |
273 | 275 | inited = false, |
274 | ||
276 | ||
275 | 277 | -- The unique string prepended to all hash values. This is to ensure |
276 | 278 | -- that hash values do not accidently coincide with an actual existing |
277 | 279 | -- string in the document. |
278 | 280 | identifier = "", |
279 | ||
281 | ||
280 | 282 | -- Counter that counts up for each new hash instance. |
281 | 283 | counter = 0, |
282 | ||
284 | ||
283 | 285 | -- Hash table. |
284 | 286 | table = {} |
285 | 287 | } |
286 | 288 | |
287 | 289 | -- Inits hashing. Creates a hash_identifier that doesn't occur anywhere |
288 | 290 | -- in the text. |
289 | function init_hash(text) | |
291 | local function init_hash(text) | |
290 | 292 | HASH.inited = true |
291 | 293 | HASH.identifier = "" |
292 | 294 | HASH.counter = 0 |
293 | 295 | HASH.table = {} |
294 | ||
296 | ||
295 | 297 | local s = "HASH" |
296 | 298 | local counter = 0 |
297 | 299 | local id |
304 | 306 | end |
305 | 307 | |
306 | 308 | -- Returns the hashed value for s. |
307 | function hash(s) | |
309 | local function hash(s) | |
308 | 310 | assert(HASH.inited) |
309 | 311 | if not HASH.table[s] then |
310 | 312 | HASH.counter = HASH.counter + 1 |
319 | 321 | ---------------------------------------------------------------------- |
320 | 322 | |
321 | 323 | -- The protection module is used to "protect" parts of a document |
322 | -- so that they are not modified by subsequent processing steps. | |
324 | -- so that they are not modified by subsequent processing steps. | |
323 | 325 | -- Protected parts are saved in a table for later unprotection |
324 | 326 | |
325 | 327 | -- Protection data |
341 | 343 | -- Nested data. |
342 | 344 | -- </div> |
343 | 345 | -- </div> |
344 | function block_pattern(tag) | |
346 | local function block_pattern(tag) | |
345 | 347 | return "\n<" .. tag .. ".-\n</" .. tag .. ">[ \t]*\n" |
346 | 348 | end |
347 | 349 | |
348 | 350 | -- Pattern for matching a block tag that begins and ends with a newline |
349 | function line_pattern(tag) | |
351 | local function line_pattern(tag) | |
350 | 352 | return "\n<" .. tag .. ".-</" .. tag .. ">[ \t]*\n" |
351 | 353 | end |
352 | 354 | |
353 | 355 | -- Protects the range of characters from start to stop in the text and |
354 | 356 | -- returns the protected string. |
355 | function protect_range(text, start, stop) | |
357 | local function protect_range(text, start, stop) | |
356 | 358 | local s = text:sub(start, stop) |
357 | 359 | local h = hash(s) |
358 | 360 | PD.blocks[h] = s |
362 | 364 | |
363 | 365 | -- Protect every part of the text that matches any of the patterns. The first |
364 | 366 | -- matching pattern is protected first, etc. |
365 | function protect_matches(text, patterns) | |
367 | local function protect_matches(text, patterns) | |
366 | 368 | while true do |
367 | 369 | local start, stop = find_first(text, patterns) |
368 | 370 | if not start then break end |
372 | 374 | end |
373 | 375 | |
374 | 376 | -- Protects blocklevel tags in the specified text |
375 | function protect(text) | |
377 | local function protect(text) | |
376 | 378 | -- First protect potentially nested block tags |
377 | 379 | text = protect_matches(text, map(PD.tags, block_pattern)) |
378 | 380 | -- Then protect block tags at the line level. |
384 | 386 | end |
385 | 387 | |
386 | 388 | -- Returns true if the string s is a hash resulting from protection |
387 | function is_protected(s) | |
389 | local function is_protected(s) | |
388 | 390 | return PD.blocks[s] |
389 | 391 | end |
390 | 392 | |
391 | 393 | -- Unprotects the specified text by expanding all the nonces |
392 | function unprotect(text) | |
394 | local function unprotect(text) | |
393 | 395 | for k,v in pairs(PD.blocks) do |
394 | 396 | v = v:gsub("%%", "%%%%") |
395 | 397 | text = text:gsub(k, v) |
409 | 411 | -- Returns true if the line is a ruler of (char) characters. |
410 | 412 | -- The line must contain at least three char characters and contain only spaces and |
411 | 413 | -- char characters. |
412 | function is_ruler_of(line, char) | |
414 | local function is_ruler_of(line, char) | |
413 | 415 | if not line:match("^[ %" .. char .. "]*$") then return false end |
414 | 416 | if not line:match("%" .. char .. ".*%" .. char .. ".*%" .. char) then return false end |
415 | 417 | return true |
416 | 418 | end |
417 | 419 | |
418 | 420 | -- Identifies the block level formatting present in the line |
419 | function classify(line) | |
421 | local function classify(line) | |
420 | 422 | local info = {line = line, text = line} |
421 | ||
423 | ||
422 | 424 | if line:match("^ ") then |
423 | 425 | info.type = "indented" |
424 | 426 | info.outdented = line:sub(5) |
425 | 427 | return info |
426 | 428 | end |
427 | ||
429 | ||
428 | 430 | for _,c in ipairs({'*', '-', '_', '='}) do |
429 | 431 | if is_ruler_of(line, c) then |
430 | 432 | info.type = "ruler" |
432 | 434 | return info |
433 | 435 | end |
434 | 436 | end |
435 | ||
437 | ||
436 | 438 | if line == "" then |
437 | 439 | info.type = "blank" |
438 | 440 | return info |
439 | 441 | end |
440 | ||
442 | ||
441 | 443 | if line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") then |
442 | 444 | local m1, m2 = line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") |
443 | 445 | info.type = "header" |
445 | 447 | info.text = m2 |
446 | 448 | return info |
447 | 449 | end |
448 | ||
450 | ||
449 | 451 | if line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") then |
450 | 452 | local number, text = line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") |
451 | 453 | info.type = "list_item" |
454 | 456 | info.text = text |
455 | 457 | return info |
456 | 458 | end |
457 | ||
459 | ||
458 | 460 | if line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") then |
459 | 461 | local bullet, text = line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") |
460 | 462 | info.type = "list_item" |
463 | 465 | info.text= text |
464 | 466 | return info |
465 | 467 | end |
466 | ||
468 | ||
467 | 469 | if line:match("^>[ \t]?(.*)") then |
468 | 470 | info.type = "blockquote" |
469 | 471 | info.text = line:match("^>[ \t]?(.*)") |
470 | 472 | return info |
471 | 473 | end |
472 | ||
474 | ||
473 | 475 | if is_protected(line) then |
474 | 476 | info.type = "raw" |
475 | 477 | info.html = unprotect(line) |
476 | 478 | return info |
477 | 479 | end |
478 | ||
480 | ||
479 | 481 | info.type = "normal" |
480 | 482 | return info |
481 | 483 | end |
482 | 484 | |
483 | 485 | -- Find headers constisting of a normal line followed by a ruler and converts them to |
484 | 486 | -- header entries. |
485 | function headers(array) | |
487 | local function headers(array) | |
486 | 488 | local i = 1 |
487 | 489 | while i <= #array - 1 do |
488 | if array[i].type == "normal" and array[i+1].type == "ruler" and | |
490 | if array[i].type == "normal" and array[i+1].type == "ruler" and | |
489 | 491 | (array[i+1].ruler_char == "-" or array[i+1].ruler_char == "=") then |
490 | 492 | local info = {line = array[i].line} |
491 | 493 | info.text = info.line |
499 | 501 | return array |
500 | 502 | end |
501 | 503 | |
504 | local block_transform, blocks_to_html, encode_code, span_transform, encode_backslash_escapes | |
505 | ||
502 | 506 | -- Find list blocks and convert them to protected data blocks |
503 | function lists(array, sublist) | |
507 | local function lists(array, sublist) | |
504 | 508 | local function process_list(arr) |
505 | 509 | local function any_blanks(arr) |
506 | 510 | for i = 1, #arr do |
508 | 512 | end |
509 | 513 | return false |
510 | 514 | end |
511 | ||
515 | ||
512 | 516 | local function split_list_items(arr) |
513 | 517 | local acc = {arr[1]} |
514 | 518 | local res = {} |
523 | 527 | table.insert(res, acc) |
524 | 528 | return res |
525 | 529 | end |
526 | ||
530 | ||
527 | 531 | local function process_list_item(lines, block) |
528 | 532 | while lines[#lines].type == "blank" do |
529 | 533 | table.remove(lines) |
530 | 534 | end |
531 | ||
535 | ||
532 | 536 | local itemtext = lines[1].text |
533 | 537 | for i=2,#lines do |
534 | 538 | itemtext = itemtext .. "\n" .. outdent(lines[i].line) |
547 | 551 | return " <li>" .. itemtext .. "</li>" |
548 | 552 | end |
549 | 553 | end |
550 | ||
554 | ||
551 | 555 | local block_list = any_blanks(arr) |
552 | 556 | local items = split_list_items(arr) |
553 | 557 | local out = "" |
560 | 564 | return "<ul>\n" .. out .. "</ul>" |
561 | 565 | end |
562 | 566 | end |
563 | ||
567 | ||
564 | 568 | -- Finds the range of lines composing the first list in the array. A list |
565 | 569 | -- starts with (^ list_item) or (blank list_item) and ends with |
566 | 570 | -- (blank* $) or (blank normal). |
585 | 589 | local function find_list_end(array, start) |
586 | 590 | local pos = #array |
587 | 591 | for i = start, #array-1 do |
588 | if array[i].type == "blank" and array[i+1].type ~= "list_item" | |
592 | if array[i].type == "blank" and array[i+1].type ~= "list_item" | |
589 | 593 | and array[i+1].type ~= "indented" and array[i+1].type ~= "blank" then |
590 | 594 | pos = i-1 |
591 | 595 | break |
596 | 600 | end |
597 | 601 | return pos |
598 | 602 | end |
599 | ||
603 | ||
600 | 604 | local start = find_list_start(array, sublist) |
601 | 605 | if not start then return nil end |
602 | 606 | return start, find_list_end(array, start) |
603 | 607 | end |
604 | ||
608 | ||
605 | 609 | while true do |
606 | 610 | local start, stop = find_list(array, sublist) |
607 | 611 | if not start then break end |
613 | 617 | } |
614 | 618 | array = splice(array, start, stop, {info}) |
615 | 619 | end |
616 | ||
620 | ||
617 | 621 | -- Convert any remaining list items to normal |
618 | 622 | for _,line in ipairs(array) do |
619 | 623 | if line.type == "list_item" then line.type = "normal" end |
620 | 624 | end |
621 | ||
625 | ||
622 | 626 | return array |
623 | 627 | end |
624 | 628 | |
625 | 629 | -- Find and convert blockquote markers. |
626 | function blockquotes(lines) | |
630 | local function blockquotes(lines) | |
627 | 631 | local function find_blockquote(lines) |
628 | 632 | local start |
629 | 633 | for i,line in ipairs(lines) do |
633 | 637 | end |
634 | 638 | end |
635 | 639 | if not start then return nil end |
636 | ||
640 | ||
637 | 641 | local stop = #lines |
638 | 642 | for i = start+1, #lines do |
639 | 643 | if lines[i].type == "blank" or lines[i].type == "blockquote" then |
646 | 650 | while lines[stop].type == "blank" do stop = stop - 1 end |
647 | 651 | return start, stop |
648 | 652 | end |
649 | ||
653 | ||
650 | 654 | local function process_blockquote(lines) |
651 | 655 | local raw = lines[1].text |
652 | 656 | for i = 2,#lines do |
657 | 661 | return "<blockquote>\n " .. bt .. |
658 | 662 | "\n</blockquote>" |
659 | 663 | end |
660 | ||
664 | ||
661 | 665 | while true do |
662 | 666 | local start, stop = find_blockquote(lines) |
663 | 667 | if not start then break end |
673 | 677 | end |
674 | 678 | |
675 | 679 | -- Find and convert codeblocks. |
676 | function codeblocks(lines) | |
680 | local function codeblocks(lines) | |
677 | 681 | local function find_codeblock(lines) |
678 | 682 | local start |
679 | 683 | for i,line in ipairs(lines) do |
680 | 684 | if line.type == "indented" then start = i break end |
681 | 685 | end |
682 | 686 | if not start then return nil end |
683 | ||
687 | ||
684 | 688 | local stop = #lines |
685 | 689 | for i = start+1, #lines do |
686 | 690 | if lines[i].type ~= "indented" and lines[i].type ~= "blank" then |
691 | 695 | while lines[stop].type == "blank" do stop = stop - 1 end |
692 | 696 | return start, stop |
693 | 697 | end |
694 | ||
698 | ||
695 | 699 | local function process_codeblock(lines) |
696 | 700 | local raw = detab(encode_code(outdent(lines[1].line))) |
697 | 701 | for i = 2,#lines do |
699 | 703 | end |
700 | 704 | return "<pre><code>" .. raw .. "\n</code></pre>" |
701 | 705 | end |
702 | ||
706 | ||
703 | 707 | while true do |
704 | 708 | local start, stop = find_codeblock(lines) |
705 | 709 | if not start then break end |
726 | 730 | table.insert(out, line.html) |
727 | 731 | elseif line.type == "normal" then |
728 | 732 | local s = line.line |
729 | ||
733 | ||
730 | 734 | while i+1 <= #lines and lines[i+1].type == "normal" do |
731 | 735 | i = i + 1 |
732 | 736 | s = s .. "\n" .. lines[i].line |
733 | 737 | end |
734 | ||
738 | ||
735 | 739 | if no_paragraphs then |
736 | 740 | table.insert(out, span_transform(s)) |
737 | 741 | else |
763 | 767 | |
764 | 768 | -- Debug function for printing a line array to see the result |
765 | 769 | -- of partial transforms. |
766 | function print_lines(lines) | |
770 | local function print_lines(lines) | |
767 | 771 | for i, line in ipairs(lines) do |
768 | 772 | print(i, line.type, line.text or line.line) |
769 | 773 | end |
777 | 781 | |
778 | 782 | -- These characters may need to be escaped because they have a special |
779 | 783 | -- meaning in markdown. |
780 | escape_chars = "'\\`*_{}[]()>#+-.!'" | |
781 | escape_table = {} | |
782 | ||
783 | function init_escape_table() | |
784 | local escape_chars = "'\\`*_{}[]()>#+-.!'" | |
785 | local escape_table = {} | |
786 | ||
787 | local function init_escape_table() | |
784 | 788 | escape_table = {} |
785 | 789 | for i = 1,#escape_chars do |
786 | 790 | local c = escape_chars:sub(i,i) |
789 | 793 | end |
790 | 794 | |
791 | 795 | -- Adds a new escape to the escape table. |
792 | function add_escape(text) | |
796 | local function add_escape(text) | |
793 | 797 | if not escape_table[text] then |
794 | 798 | escape_table[text] = hash(text) |
795 | 799 | end |
796 | 800 | return escape_table[text] |
797 | end | |
801 | end | |
798 | 802 | |
799 | 803 | -- Escape characters that should not be disturbed by markdown. |
800 | function escape_special_chars(text) | |
804 | local function escape_special_chars(text) | |
801 | 805 | local tokens = tokenize_html(text) |
802 | ||
806 | ||
803 | 807 | local out = "" |
804 | 808 | for _, token in ipairs(tokens) do |
805 | 809 | local t = token.text |
825 | 829 | end |
826 | 830 | |
827 | 831 | -- Unescape characters that have been encoded. |
828 | function unescape_special_chars(t) | |
832 | local function unescape_special_chars(t) | |
829 | 833 | local tin = t |
830 | 834 | for k,v in pairs(escape_table) do |
831 | 835 | k = k:gsub("%%", "%%%%") |
849 | 853 | end |
850 | 854 | |
851 | 855 | -- Handle backtick blocks. |
852 | function code_spans(s) | |
856 | local function code_spans(s) | |
853 | 857 | s = s:gsub("\\\\", escape_table["\\"]) |
854 | 858 | s = s:gsub("\\`", escape_table["`"]) |
855 | 859 | |
879 | 883 | end |
880 | 884 | |
881 | 885 | -- Encode alt text... enodes &, and ". |
882 | function encode_alt(s) | |
886 | local function encode_alt(s) | |
883 | 887 | if not s then return s end |
884 | 888 | s = s:gsub('&', '&') |
885 | 889 | s = s:gsub('"', '"') |
887 | 891 | return s |
888 | 892 | end |
889 | 893 | |
894 | local link_database | |
895 | ||
890 | 896 | -- Handle image references |
891 | function images(text) | |
897 | local function images(text) | |
892 | 898 | local function reference_link(alt, id) |
893 | 899 | alt = encode_alt(alt:match("%b[]"):sub(2,-2)) |
894 | 900 | id = id:match("%[(.*)%]"):lower() |
901 | 907 | if title then title = " title=\"" .. title .. "\"" else title = "" end |
902 | 908 | return add_escape ('<img src="' .. url .. '" alt="' .. alt .. '"' .. title .. "/>") |
903 | 909 | end |
904 | ||
910 | ||
905 | 911 | local function inline_link(alt, link) |
906 | 912 | alt = encode_alt(alt:match("%b[]"):sub(2,-2)) |
907 | 913 | local url, title = link:match("%(<?(.-)>?[ \t]*['\"](.+)['\"]") |
914 | 920 | return add_escape('<img src="' .. url .. '" alt="' .. alt .. '"/>') |
915 | 921 | end |
916 | 922 | end |
917 | ||
923 | ||
918 | 924 | text = text:gsub("!(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) |
919 | 925 | text = text:gsub("!(%b[])(%b())", inline_link) |
920 | 926 | return text |
921 | 927 | end |
922 | 928 | |
923 | 929 | -- Handle anchor references |
924 | function anchors(text) | |
930 | local function anchors(text) | |
925 | 931 | local function reference_link(text, id) |
926 | 932 | text = text:match("%b[]"):sub(2,-2) |
927 | 933 | id = id:match("%b[]"):sub(2,-2):lower() |
934 | 940 | if title then title = " title=\"" .. title .. "\"" else title = "" end |
935 | 941 | return add_escape("<a href=\"" .. url .. "\"" .. title .. ">") .. text .. add_escape("</a>") |
936 | 942 | end |
937 | ||
943 | ||
938 | 944 | local function inline_link(text, link) |
939 | 945 | text = text:match("%b[]"):sub(2,-2) |
940 | 946 | local url, title = link:match("%(<?(.-)>?[ \t]*['\"](.+)['\"]") |
947 | 953 | return add_escape("<a href=\"" .. url .. "\">") .. text .. add_escape("</a>") |
948 | 954 | end |
949 | 955 | end |
950 | ||
956 | ||
951 | 957 | text = text:gsub("(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) |
952 | 958 | text = text:gsub("(%b[])(%b())", inline_link) |
953 | 959 | return text |
954 | 960 | end |
955 | 961 | |
956 | 962 | -- Handle auto links, i.e. <http://www.google.com/>. |
957 | function auto_links(text) | |
963 | local function auto_links(text) | |
958 | 964 | local function link(s) |
959 | 965 | return add_escape("<a href=\"" .. s .. "\">") .. s .. "</a>" |
960 | 966 | end |
968 | 974 | local plain = {code = function(c) return c end, count = 0, rate = 0.1} |
969 | 975 | local codes = {hex, dec, plain} |
970 | 976 | local function swap(t,k1,k2) local temp = t[k2] t[k2] = t[k1] t[k1] = temp end |
971 | ||
977 | ||
972 | 978 | local out = "" |
973 | 979 | for i = 1,s:len() do |
974 | 980 | for _,code in ipairs(codes) do code.count = code.count + code.rate end |
975 | 981 | if codes[1].count < codes[2].count then swap(codes,1,2) end |
976 | 982 | if codes[2].count < codes[3].count then swap(codes,2,3) end |
977 | 983 | if codes[1].count < codes[2].count then swap(codes,1,2) end |
978 | ||
984 | ||
979 | 985 | local code = codes[1] |
980 | 986 | local c = s:sub(i,i) |
981 | 987 | -- Force encoding of "@" to make email address more invisible. |
982 | 988 | if c == "@" and code == plain then code = codes[2] end |
983 | 989 | out = out .. code.code(c) |
984 | 990 | code.count = code.count - 1 |
985 | end | |
991 | end | |
986 | 992 | return out |
987 | 993 | end |
988 | 994 | local function mail(s) |
994 | 1000 | -- links |
995 | 1001 | text = text:gsub("<(https?:[^'\">%s]+)>", link) |
996 | 1002 | text = text:gsub("<(ftp:[^'\">%s]+)>", link) |
997 | ||
1003 | ||
998 | 1004 | |
999 | 1005 | text = text:gsub("<mailto:([^'\">%s]+)>", mail) |
1000 | 1006 | text = text:gsub("<([-.%w]+%@[-.%w]+)>", mail) |
1003 | 1009 | |
1004 | 1010 | -- Encode free standing amps (&) and angles (<)... note that this does not |
1005 | 1011 | -- encode free >. |
1006 | function amps_and_angles(s) | |
1012 | local function amps_and_angles(s) | |
1007 | 1013 | -- encode amps not part of &..; expression |
1008 | 1014 | local pos = 1 |
1009 | 1015 | while true do |
1018 | 1024 | pos = amp+1 |
1019 | 1025 | end |
1020 | 1026 | end |
1021 | ||
1027 | ||
1022 | 1028 | -- encode naked <'s |
1023 | 1029 | s = s:gsub("<([^a-zA-Z/?$!])", "<%1") |
1024 | 1030 | s = s:gsub("<$", "<") |
1025 | ||
1031 | ||
1026 | 1032 | -- what about >, nothing done in the original markdown source to handle them |
1027 | 1033 | return s |
1028 | 1034 | end |
1029 | 1035 | |
1030 | 1036 | -- Handles emphasis markers (* and _) in the text. |
1031 | function emphasis(text) | |
1037 | local function emphasis(text) | |
1032 | 1038 | for _, s in ipairs {"%*%*", "%_%_"} do |
1033 | 1039 | text = text:gsub(s .. "([^%s][%*%_]?)" .. s, "<strong>%1</strong>") |
1034 | 1040 | text = text:gsub(s .. "([^%s][^<>]-[^%s][%*%_]?)" .. s, "<strong>%1</strong>") |
1043 | 1049 | end |
1044 | 1050 | |
1045 | 1051 | -- Handles line break markers in the text. |
1046 | function line_breaks(text) | |
1052 | local function line_breaks(text) | |
1047 | 1053 | return text:gsub(" +\n", " <br/>\n") |
1048 | 1054 | end |
1049 | 1055 | |
1066 | 1072 | |
1067 | 1073 | -- Cleanup the text by normalizing some possible variations to make further |
1068 | 1074 | -- processing easier. |
1069 | function cleanup(text) | |
1075 | local function cleanup(text) | |
1070 | 1076 | -- Standardize line endings |
1071 | 1077 | text = text:gsub("\r\n", "\n") -- DOS to UNIX |
1072 | 1078 | text = text:gsub("\r", "\n") -- Mac to UNIX |
1073 | ||
1079 | ||
1074 | 1080 | -- Convert all tabs to spaces |
1075 | 1081 | text = detab(text) |
1076 | ||
1082 | ||
1077 | 1083 | -- Strip lines with only spaces and tabs |
1078 | 1084 | while true do |
1079 | 1085 | local subs |
1080 | 1086 | text, subs = text:gsub("\n[ \t]+\n", "\n\n") |
1081 | 1087 | if subs == 0 then break end |
1082 | 1088 | end |
1083 | ||
1089 | ||
1084 | 1090 | return "\n" .. text .. "\n" |
1085 | 1091 | end |
1086 | 1092 | |
1087 | 1093 | -- Strips link definitions from the text and stores the data in a lookup table. |
1088 | function strip_link_definitions(text) | |
1094 | local function strip_link_definitions(text) | |
1089 | 1095 | local linkdb = {} |
1090 | ||
1096 | ||
1091 | 1097 | local function link_def(id, url, title) |
1092 | 1098 | id = id:match("%[(.+)%]"):lower() |
1093 | 1099 | linkdb[id] = linkdb[id] or {} |
1100 | 1106 | local def_title1 = def_no_title .. "[ \t]+\n?[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" |
1101 | 1107 | local def_title2 = def_no_title .. "[ \t]*\n[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" |
1102 | 1108 | local def_title3 = def_no_title .. "[ \t]*\n?[ \t]+[\"'(]([^\n]+)[\"')][ \t]*" |
1103 | ||
1109 | ||
1104 | 1110 | text = text:gsub(def_title1, link_def) |
1105 | 1111 | text = text:gsub(def_title2, link_def) |
1106 | 1112 | text = text:gsub(def_title3, link_def) |
1111 | 1117 | link_database = {} |
1112 | 1118 | |
1113 | 1119 | -- Main markdown processing function |
1114 | function markdown(text) | |
1120 | local function markdown(text) | |
1115 | 1121 | init_hash(text) |
1116 | 1122 | init_escape_table() |
1117 | ||
1123 | ||
1118 | 1124 | text = cleanup(text) |
1119 | 1125 | text = protect(text) |
1120 | 1126 | text, link_database = strip_link_definitions(text) |
1131 | 1137 | M.lock(M) |
1132 | 1138 | |
1133 | 1139 | -- Expose markdown function to the world |
1134 | markdown = M.markdown | |
1140 | _G.markdown = M.markdown | |
1135 | 1141 | |
1136 | 1142 | -- Class for parsing command-line options |
1137 | 1143 | local OptionParser = {} |
1167 | 1173 | -- where successfully parsed and false otherwise. |
1168 | 1174 | function OptionParser:run(args) |
1169 | 1175 | local pos = 1 |
1176 | local param | |
1170 | 1177 | while pos <= #args do |
1171 | 1178 | local arg = args[pos] |
1172 | 1179 | if arg == "--" then |
1224 | 1231 | if not options.wrap_header then return s end |
1225 | 1232 | local header = "" |
1226 | 1233 | if options.header then |
1227 | local f = io.open(options.header) or error("Could not open file: " .. options.header) | |
1234 | local f = io.open(options.header) or error("Could not open file: " .. options.header) | |
1228 | 1235 | header = f:read("*a") |
1229 | 1236 | f:close() |
1230 | 1237 | else |
1238 | 1245 | </head> |
1239 | 1246 | <body> |
1240 | 1247 | ]] |
1241 | local title = options.title or s:match("<h1>(.-)</h1>") or s:match("<h2>(.-)</h2>") or | |
1248 | local title = options.title or s:match("<h1>(.-)</h1>") or s:match("<h2>(.-)</h2>") or | |
1242 | 1249 | s:match("<h3>(.-)</h3>") or "Untitled" |
1243 | 1250 | header = header:gsub("TITLE", title) |
1244 | 1251 | if options.inline_style then |
1245 | 1252 | local style = "" |
1246 | 1253 | local f = io.open(options.stylesheet) |
1247 | if f then | |
1248 | style = f:read("*a") f:close() | |
1254 | if f then | |
1255 | style = f:read("*a") f:close() | |
1249 | 1256 | else |
1250 | 1257 | error("Could not include style sheet " .. options.stylesheet .. ": File not found") |
1251 | 1258 | end |
1264 | 1271 | end |
1265 | 1272 | return header .. s .. footer |
1266 | 1273 | end |
1267 | ||
1268 | -- Generate output path name from input path name given options. | |
1274 | ||
1275 | -- Generate output path name from input path name given options. | |
1269 | 1276 | local function outpath(path, options) |
1270 | 1277 | if options.append then return path .. ".html" end |
1271 | 1278 | local m = path:match("^(.+%.html)[^/\\]+$") if m then return m end |
1272 | 1279 | m = path:match("^(.+%.)[^/\\]*$") if m and path ~= m .. "html" then return m .. "html" end |
1273 | 1280 | return path .. ".html" |
1274 | 1281 | end |
1275 | ||
1282 | ||
1276 | 1283 | -- Default commandline options |
1277 | 1284 | local options = { |
1278 | 1285 | wrap_header = true, |
1315 | 1322 | op:param("s", "style", function(x) options.stylesheet = x end) |
1316 | 1323 | op:flag("l", "inline-style", function(x) options.inline_style = true end) |
1317 | 1324 | op:flag("a", "append", function() options.append = true end) |
1318 | op:flag("t", "test", function() | |
1325 | op:flag("t", "test", function() | |
1319 | 1326 | local n = arg[0]:gsub("markdown.lua", "markdown-tests.lua") |
1320 | 1327 | local f = io.open(n) |
1321 | if f then | |
1322 | f:close() dofile(n) | |
1328 | if f then | |
1329 | f:close() dofile(n) | |
1323 | 1330 | else |
1324 | 1331 | error("Cannot find markdown-tests.lua") |
1325 | 1332 | end |
1326 | run_stdin = false | |
1333 | run_stdin = false | |
1327 | 1334 | end) |
1328 | 1335 | op:flag("h", "help", function() print(help) run_stdin = false end) |
1329 | op:arg(function(path) | |
1336 | op:arg(function(path) | |
1330 | 1337 | local file = io.open(path) or error("Could not open file: " .. path) |
1331 | 1338 | local s = file:read("*a") |
1332 | 1339 | file:close() |
1337 | 1344 | run_stdin = false |
1338 | 1345 | end |
1339 | 1346 | ) |
1340 | ||
1347 | ||
1341 | 1348 | if not op:run(arg) then |
1342 | 1349 | print(help) |
1343 | 1350 | run_stdin = false |
1349 | 1356 | io.write(s) |
1350 | 1357 | end |
1351 | 1358 | end |
1352 | ||
1359 | ||
1353 | 1360 | -- If we are being run from the command-line, act accordingly |
1354 | 1361 | if arg and arg[0]:find("markdown%.lua$") then |
1355 | 1362 | run_command_line(arg) |
1356 | 1363 | else |
1357 | 1364 | return markdown |
1358 | end⏎ | |
1365 | end |
232 | 232 | end |
233 | 233 | end |
234 | 234 | |
235 | ||
236 | 235 | local function text_processor(ldoc) |
237 | 236 | return function(txt,item) |
238 | 237 | if txt == nil then return '' end |
242 | 241 | end |
243 | 242 | end |
244 | 243 | |
244 | local plain_processor | |
245 | 245 | |
246 | 246 | local function markdown_processor(ldoc, formatter) |
247 | return function (txt,item) | |
247 | return function (txt,item,plain) | |
248 | 248 | if txt == nil then return '' end |
249 | if plain then | |
250 | if not plain_processor then | |
251 | plain_processor = text_processor(ldoc) | |
252 | end | |
253 | return plain_processor(txt,item) | |
254 | end | |
249 | 255 | if utils.is_type(item,doc.File) then |
250 | 256 | txt = process_multiline_markdown(ldoc, txt, item) |
251 | 257 | else |
256 | 262 | return (txt:gsub('^%s*<p>(.+)</p>%s*$','%1')) |
257 | 263 | end |
258 | 264 | end |
259 | ||
260 | 265 | |
261 | 266 | local function get_processor(ldoc, format) |
262 | 267 | if format == 'plain' then return text_processor(ldoc) end |
69 | 69 | return preamble,tag_items |
70 | 70 | end |
71 | 71 | |
72 | -- Tags are stored as an ordered map | |
72 | 73 | local Tags = {} |
73 | 74 | Tags.__index = Tags |
74 | 75 | |
78 | 79 | end |
79 | 80 | |
80 | 81 | function Tags:add (tag,value) |
81 | self[tag] = value | |
82 | --print('adding',tag,value) | |
82 | rawset(self,tag,value) | |
83 | 83 | self._order:append(tag) |
84 | 84 | end |
85 | 85 | |
210 | 210 | else |
211 | 211 | mod,t,v = lang:parse_module_call(tok,t,v) |
212 | 212 | if mod ~= '...' then |
213 | add_module({summary='(no description)'},mod,true) | |
213 | add_module(Tags.new{summary='(no description)'},mod,true) | |
214 | 214 | first_comment = false |
215 | 215 | module_found = true |
216 | 216 | end |
334 | 334 | end |
335 | 335 | end |
336 | 336 | if is_local or tags['local'] then |
337 | tags['local'] = true | |
337 | tags:add('local',true) | |
338 | 338 | end |
339 | 339 | if tags.name then |
340 | 340 | current_item = F:new_item(tags,line) |
23 | 23 | return ('<span class="%s">%s</span>'):format(t,val) |
24 | 24 | end |
25 | 25 | |
26 | local spans = {keyword=true,number=true,string=true,comment=true,global=true} | |
26 | local spans = {keyword=true,number=true,string=true,comment=true,global=true,backtick=true} | |
27 | 27 | |
28 | 28 | function prettify.lua (fname, code, initial_lineno, pre) |
29 | 29 | local res = List() |
46 | 46 | t = 'global' |
47 | 47 | end |
48 | 48 | if spans[t] then |
49 | if t == 'comment' then -- may contain @{ref} | |
49 | if t == 'comment' or t == 'backtick' then -- may contain @{ref} or `..` | |
50 | 50 | val = prettify.resolve_inline_references(val,error_reporter) |
51 | 51 | end |
52 | 52 | res:append(span(t,val)) |
0 | package = "ldoc" | |
1 | version = "1.3.12-1" | |
2 | ||
3 | source = { | |
4 | dir="ldoc", | |
5 | url = "http://stevedonovan.github.com/files/ldoc-1.3.12.zip" | |
6 | } | |
7 | ||
8 | description = { | |
9 | summary = "A Lua Documentation Tool", | |
10 | detailed = [[ | |
11 | LDoc is a LuaDoc-compatible documentation generator which can also | |
12 | process C extension source. Markdown may be optionally used to | |
13 | render comments, as well as integrated readme documentation and | |
14 | pretty-printed example files | |
15 | ]], | |
16 | homepage='http://stevedonovan.github.com/ldoc', | |
17 | maintainer='steve.j.donovan@gmail.com', | |
18 | license = "MIT/X11", | |
19 | } | |
20 | ||
21 | ||
22 | dependencies = { | |
23 | "penlight","markdown" | |
24 | } | |
25 | ||
26 | build = { | |
27 | type = "builtin", | |
28 | modules = { | |
29 | ["ldoc.tools"] = "ldoc/tools.lua", | |
30 | ["ldoc.lang"] = "ldoc/lang.lua", | |
31 | ["ldoc.parse"] = "ldoc/parse.lua", | |
32 | ["ldoc.html"] = "ldoc/html.lua", | |
33 | ["ldoc.lexer"] = "ldoc/lexer.lua", | |
34 | ["ldoc.markup"] = "ldoc/markup.lua", | |
35 | ["ldoc.prettify"] = "ldoc/prettify.lua", | |
36 | ["ldoc.doc"] = "ldoc/doc.lua", | |
37 | ["ldoc.html.ldoc_css"] = "ldoc/html/ldoc_css.lua", | |
38 | ["ldoc.html.ldoc_ltp"] = "ldoc/html/ldoc_ltp.lua", | |
39 | ["ldoc.html.ldoc_one_css"] = "ldoc/html/ldoc_one_css.lua", | |
40 | ["ldoc.builtin.globals"] = "ldoc/builtin/globals.lua", | |
41 | ["ldoc.builtin.coroutine"] = "ldoc/builtin/coroutine.lua", | |
42 | ["ldoc.builtin.global"] = "ldoc/builtin/global.lua", | |
43 | ["ldoc.builtin.debug"] = "ldoc/builtin/debug.lua", | |
44 | ["ldoc.builtin.io"] = "ldoc/builtin/io.lua", | |
45 | ["ldoc.builtin.lfs"] = "ldoc/builtin/lfs.lua", | |
46 | ["ldoc.builtin.lpeg"] = "ldoc/builtin/lpeg.lua", | |
47 | ["ldoc.builtin.math"] = "ldoc/builtin/math.lua", | |
48 | ["ldoc.builtin.os"] = "ldoc/builtin/os.lua", | |
49 | ["ldoc.builtin.package"] = "ldoc/builtin/package.lua", | |
50 | ["ldoc.builtin.string"] = "ldoc/builtin/string.lua", | |
51 | ["ldoc.builtin.table"] = "ldoc/builtin/table.lua", | |
52 | }, | |
53 | install = { | |
54 | bin = { | |
55 | ldoc = "ldoc.lua" | |
56 | } | |
57 | } | |
58 | } | |
59 | ||
60 |
0 | package = "ldoc" | |
1 | version = "1.3.8-2" | |
2 | ||
3 | source = { | |
4 | dir="ldoc", | |
5 | url = "http://stevedonovan.github.com/files/ldoc-1.3.8.zip" | |
6 | } | |
7 | ||
8 | description = { | |
9 | summary = "A Lua Documentation Tool", | |
10 | detailed = [[ | |
11 | LDoc is a LuaDoc-compatible documentation generator which can also | |
12 | process C extension source. Markdown may be optionally used to | |
13 | render comments, as well as integrated readme documentation and | |
14 | pretty-printed example files | |
15 | ]], | |
16 | homepage='http://stevedonovan.github.com/ldoc', | |
17 | maintainer='steve.j.donovan@gmail.com', | |
18 | license = "MIT/X11", | |
19 | } | |
20 | ||
21 | ||
22 | dependencies = { | |
23 | "penlight","markdown" | |
24 | } | |
25 | ||
26 | build = { | |
27 | type = "builtin", | |
28 | modules = { | |
29 | ["ldoc.tools"] = "ldoc/tools.lua", | |
30 | ["ldoc.lang"] = "ldoc/lang.lua", | |
31 | ["ldoc.parse"] = "ldoc/parse.lua", | |
32 | ["ldoc.html"] = "ldoc/html.lua", | |
33 | ["ldoc.lexer"] = "ldoc/lexer.lua", | |
34 | ["ldoc.markup"] = "ldoc/markup.lua", | |
35 | ["ldoc.prettify"] = "ldoc/prettify.lua", | |
36 | ["ldoc.doc"] = "ldoc/doc.lua", | |
37 | ["ldoc.html.ldoc_css"] = "ldoc/html/ldoc_css.lua", | |
38 | ["ldoc.html.ldoc_ltp"] = "ldoc/html/ldoc_ltp.lua", | |
39 | ["ldoc.html.ldoc_one_css"] = "ldoc/html/ldoc_one_css.lua", | |
40 | ["ldoc.builtin.globals"] = "ldoc/builtin/globals.lua", | |
41 | ["ldoc.builtin.coroutine"] = "ldoc/builtin/coroutine.lua", | |
42 | ["ldoc.builtin.global"] = "ldoc/builtin/global.lua", | |
43 | ["ldoc.builtin.debug"] = "ldoc/builtin/debug.lua", | |
44 | ["ldoc.builtin.io"] = "ldoc/builtin/io.lua", | |
45 | ["ldoc.builtin.lfs"] = "ldoc/builtin/lfs.lua", | |
46 | ["ldoc.builtin.lpeg"] = "ldoc/builtin/lpeg.lua", | |
47 | ["ldoc.builtin.math"] = "ldoc/builtin/math.lua", | |
48 | ["ldoc.builtin.os"] = "ldoc/builtin/os.lua", | |
49 | ["ldoc.builtin.package"] = "ldoc/builtin/package.lua", | |
50 | ["ldoc.builtin.string"] = "ldoc/builtin/string.lua", | |
51 | ["ldoc.builtin.table"] = "ldoc/builtin/table.lua", | |
52 | }, | |
53 | install = { | |
54 | bin = { | |
55 | "ldoc.lua" | |
56 | } | |
57 | } | |
58 | } | |
59 | ||
60 |
34 | 34 | |
35 | 35 | --- @usage |
36 | 36 | local usage = [[ |
37 | ldoc, a documentation generator for Lua, vs 1.3.11 | |
38 | -d,--dir (default docs) output directory | |
37 | ldoc, a documentation generator for Lua, vs 1.3.12 | |
38 | -d,--dir (default doc) output directory | |
39 | 39 | -o,--output (default 'index') output name |
40 | 40 | -v,--verbose verbose |
41 | 41 | -a,--all show local functions, etc, in docs |
62 | 62 | <file> (string) source file or directory containing source |
63 | 63 | |
64 | 64 | `ldoc .` reads options from an `config.ld` file in same directory; |
65 | `ldoc -c path/to/myconfig.ld .` reads options from `path/to/myconfig.ld` | |
65 | `ldoc -c path/to/myconfig.ld <file>` reads options from `path/to/myconfig.ld` | |
66 | and processes <file> if 'file' was not defined in the ld file. | |
66 | 67 | ]] |
67 | 68 | local args = lapp(usage) |
68 | 69 | local lfs = require 'lfs' |
183 | 184 | local ldoc_contents = { |
184 | 185 | 'alias','add_language_extension','new_type','add_section', 'tparam_alias', |
185 | 186 | 'file','project','title','package','format','output','dir','ext', 'topics', |
186 | 'one','style','template','description','examples', 'pretty', 'charset', | |
187 | 'readme','all','manual_url', 'ignore', 'colon','boilerplate','merge', 'wrap', | |
187 | 'one','style','template','description','examples', 'pretty', 'charset', 'plain', | |
188 | 'readme','all','manual_url', 'ignore', 'colon', | |
189 | 'boilerplate','merge', 'wrap', 'not_luadoc', | |
188 | 190 | 'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler', |
189 | 191 | } |
190 | 192 | ldoc_contents = tablex.makeset(ldoc_contents) |
360 | 362 | |
361 | 363 | override 'colon' |
362 | 364 | override 'merge' |
365 | override 'not_luadoc' | |
363 | 366 | |
364 | 367 | if type(args.file) == 'table' then |
365 | 368 | -- this can only be set from config file so we can assume it's already read |
0 | --- simplified LDoc style | |
0 | --- simplified LDoc colon style. | |
1 | -- You have to use -C flag or 'colon=true' for this one! | |
1 | 2 | module 'easy' |
2 | 3 | |
3 | 4 | --- First one. |
0 | 0 | ------------ |
1 | 1 | -- Yet another module. |
2 | -- Description can continue after simple tags, if you | |
3 | -- like - but to keep backwards compatibility, say 'not_luadoc=true' | |
2 | 4 | -- @module four |
3 | -- Description can continue after simple tags, if you | |
4 | -- like | |
5 | 5 | -- @author bob, james |
6 | 6 | -- @license MIT |
7 | 7 | -- @copyright InfoReich 2013 |
1 | 1 | A non-doc comment |
2 | 2 | multi-line |
3 | 3 | probably containing license information! |
4 | Doesn't use module(), but module name is inferred from file name | |
4 | Doesn't use module(), but module name is inferred from file name. | |
5 | If you have initial licence comments that look like doc comments, | |
6 | then set `boilerplate=true` | |
5 | 7 | ]] |
6 | 8 | ------------ |
7 | 9 | -- Test module, |
1 | 1 | -- Alternative to no-magic style. |
2 | 2 | -- Description here |
3 | 3 | ---- |
4 | ||
5 | --- documented, but private | |
6 | local function question () | |
7 | end | |
4 | 8 | |
5 | 9 | --- answer to everything. |
6 | 10 | -- @return magic number |
10 | 10 | -- suppress @params and the summary at the top |
11 | 11 | no_return_or_parms=true |
12 | 12 | no_summary=true |
13 | not_luadoc=true | |
14 |
69 | 69 | |
70 | 70 | --[[----------------- |
71 | 71 | @table Vector.Opts |
72 | Options table format for `Vector.options` | |
72 | Options table format for `Vector:options` | |
73 | 73 | |
74 | autoconvert: try to convert strings to numbers | |
74 | * `autoconvert`: try to convert strings to numbers | |
75 | * `adder`: function used to perform addition and subtraction | |
76 | * `multiplier`: function used to perform multiplication and division | |
75 | 77 | |
76 | adder: function used to perform addition and subtraction | |
77 | ||
78 | multiplier: function used to perform multiplication and division | |
79 | 78 | @usage |
80 | 79 | v = Vector {{1},{2}} |
81 | 80 | v:options {adder = function(x,y) return {x[1]+y[1]} end} |