-
Notifications
You must be signed in to change notification settings - Fork 0
/
ldoc.lua
907 lines (782 loc) · 26.6 KB
/
ldoc.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
#!/usr/bin/env lua
---------------
-- ## ldoc, a Lua documentation generator.
--
-- Compatible with luadoc-style annotations, but providing
-- easier customization options.
--
-- C/C++ support for Lua extensions is provided.
--
-- Available from LuaRocks as 'ldoc' and as a [Zip file](https://github.com/lunarmodules/ldoc/archive/refs/tags/v1.5.0.zip)
--
-- [Github Page](https://github.com/lunarmodules/ldoc)
--
-- @author Steve Donovan
-- @copyright 2011
-- @license MIT/X11
-- @script ldoc
local class = require 'pl.class'
local app = require 'pl.app'
local path = require 'pl.path'
local dir = require 'pl.dir'
local utils = require 'pl.utils'
local List = require 'pl.List'
local stringx = require 'pl.stringx'
local tablex = require 'pl.tablex'
-- Penlight compatibility
utils.unpack = utils.unpack or unpack or table.unpack
local lapp = require 'pl.lapp'
local version = '1.5.0'
-- so we can find our private modules
app.require_here()
--- @usage
local usage = [[
ldoc, a documentation generator for Lua, v]]..version..[[
Invocation:
ldoc [options] <file>
ldoc --version
Options:
-d,--dir (default doc) output directory
-o,--output (default 'index') output name
-v,--verbose verbose
-a,--all show local functions, etc, in docs
-q,--quiet suppress output
-m,--module module docs as text
-s,--style (default !) directory for style sheet (ldoc.css)
-l,--template (default !) directory for template (ldoc.ltp)
-p,--project (default ldoc) project name
-t,--title (default Reference) page title
-f,--format (default plain) formatting - can be markdown, discount or plain
-b,--package (default .) top-level package basename (needed for module(...))
-x,--ext (default html) output file extension
-c,--config (default config.ld) configuration name
-u,--unqualified don't show package name in sidebar links
-i,--ignore ignore any 'no doc comment or no module' warnings
-X,--not_luadoc break LuaDoc compatibility. Descriptions may continue after tags.
-D,--define (default none) set a flag to be used in config.ld
-C,--colon use colon style
--lls use lua-language-server style
-N,--no_args_infer don't infer arguments from source
-B,--boilerplate ignore first comment in source files
-M,--merge allow module merging
-S,--simple no return or params, no summary
-O,--one one-column output layout
-V,--version show version information
--date (default system) use this date in generated doc, set to empty string to skip the timestamp
--dump debug output dump
--filter (default none) filter output as Lua data (e.g pl.pretty.dump)
--tags (default none) show all references to given tags, comma-separated
--fatalwarnings non-zero exit status on any warning
--testing reproducible build; no date or version on output
--icon (default none) an image that will be displayed under the project name on all pages
--multimodule allow using @module, @script, @file, etc. multiple times in a file
--unsafe_no_sandbox disables sandbox and allows using `import` command in config.ld
<file> (optional string) source file or directory containing source
`ldoc .` reads options from an `config.ld` file in same directory;
`ldoc -c path/to/myconfig.ld <file>` reads options from `path/to/myconfig.ld`
and processes <file> if 'file' was not defined in the ld file.
]]
local args = lapp(usage)
local lfs = require 'lfs'
local doc = require 'ldoc.doc'
local lang = require 'ldoc.lang'
local tools = require 'ldoc.tools'
local global = require 'ldoc.builtin.globals'
local markup = require 'ldoc.markup'
local parse = require 'ldoc.parse'
local KindMap = tools.KindMap
local Item,File = doc.Item,doc.File
local quit = utils.quit
if args.version then
print('LDoc v' .. version)
os.exit(0)
end
local isdir_old = path.isdir
path.isdir = function(p)
p = tools.trim_path_slashes(p)
return isdir_old(p)
end
local ModuleMap = class(KindMap)
doc.ModuleMap = ModuleMap
function ModuleMap:_init ()
self.klass = ModuleMap
self.fieldname = 'section'
end
local ProjectMap = class(KindMap)
ProjectMap.project_level = true
function ProjectMap:_init ()
self.klass = ProjectMap
self.fieldname = 'type'
end
local lua, cc = lang.lua, lang.cc
local file_types = {
['.lua'] = lua,
['.ldoc'] = lua,
['.luadoc'] = lua,
['.c'] = cc,
['.h'] = cc,
['.cpp'] = cc,
['.cxx'] = cc,
['.C'] = cc,
['.mm'] = cc,
['.cs'] = cc,
['.moon'] = lang.moon,
}
------- ldoc external API ------------
-- the ldoc table represents the API available in `config.ld`.
local ldoc = { charset = 'UTF-8', version = version }
local known_types, kind_names = {}
local function lookup (itype,igroup,isubgroup)
local kn = kind_names[itype]
known_types[itype] = true
if kn then
if type(kn) == 'string' then
igroup = kn
else
igroup = kn[1]
isubgroup = kn[2]
end
end
return itype, igroup, isubgroup
end
local function setup_kinds ()
kind_names = ldoc.kind_names or {}
ModuleMap:add_kind(lookup('function','Functions','Parameters'))
ModuleMap:add_kind(lookup('table','Tables','Fields'))
ModuleMap:add_kind(lookup('field','Fields'))
ModuleMap:add_kind(lookup('type','Types'))
ModuleMap:add_kind(lookup('lfunction','Local Functions','Parameters'))
ModuleMap:add_kind(lookup('annotation','Issues'))
ProjectMap:add_kind(lookup('module','Modules'))
ProjectMap:add_kind(lookup('script','Scripts'))
ProjectMap:add_kind(lookup('classmod','Classes'))
ProjectMap:add_kind(lookup('topic','Topics'))
ProjectMap:add_kind(lookup('example','Examples'))
ProjectMap:add_kind(lookup('file','Source'))
for k in pairs(kind_names) do
if not known_types[k] then
quit("unknown item type "..tools.quote(k).." in kind_names")
end
end
end
-- hacky way for doc module to be passed options...
doc.ldoc = ldoc
-- if the corresponding argument was the default, then any ldoc field overrides
local function override (field,defval)
defval = defval or false
if args[field] == defval and ldoc[field] ~= nil then args[field] = ldoc[field] end
end
-- aliases to existing tags can be defined. E.g. just 'p' for 'param'
function ldoc.alias (a,tag)
doc.add_alias(a,tag)
end
-- standard aliases --
ldoc.alias('tparam',{'param',modifiers={type="$1"}})
ldoc.alias('treturn',{'return',modifiers={type="$1"}})
ldoc.alias('tfield',{'field',modifiers={type="$1"}})
function ldoc.tparam_alias (name,type)
type = type or name
ldoc.alias(name,{'param',modifiers={type=type}})
end
ldoc.alias ('error',doc.error_macro)
ldoc.tparam_alias 'string'
ldoc.tparam_alias 'number'
ldoc.tparam_alias('int', 'integer')
ldoc.tparam_alias('bool', 'boolean')
ldoc.tparam_alias('func', 'function')
ldoc.tparam_alias('tab', 'table')
ldoc.tparam_alias 'thread'
function ldoc.add_language_extension(ext, lang)
lang = (lang=='c' and cc) or (lang=='lua' and lua) or quit('unknown language')
if ext:sub(1,1) ~= '.' then ext = '.'..ext end
file_types[ext] = lang
end
function ldoc.add_section (name, title, subname)
ModuleMap:add_kind(name,title,subname)
end
-- new tags can be added, which can be on a project level.
function ldoc.new_type (tag, header, project_level,subfield)
doc.add_tag(tag,doc.TAG_TYPE,project_level)
if project_level then
ProjectMap:add_kind(tag,header,subfield)
else
ModuleMap:add_kind(tag,header,subfield)
end
end
function ldoc.manual_url (url)
global.set_manual_url(url)
end
function ldoc.custom_see_handler(pat, handler)
doc.add_custom_see_handler(pat, handler)
end
local ldoc_contents = {
'alias','add_language_extension','custom_tags','new_type','add_section', 'tparam_alias',
'file','project','title','package','icon','favicon','format','output','dir','ext', 'topics',
'one','style','template','description','examples', 'pretty', 'charset', 'plain',
'readme','all','manual_url', 'ignore', 'colon', 'sort', 'module_file','vars',
'boilerplate','merge', 'wrap', 'not_luadoc', 'template_escape','merge_error_groups',
'no_return_or_parms','no_summary','full_description','backtick_references', 'custom_see_handler',
'no_space_before_args','parse_extra','no_lua_ref','sort_modules','use_markdown_titles',
'unqualified', 'custom_display_name_handler', 'kind_names', 'custom_references',
'dont_escape_underscore','global_lookup','prettify_files','convert_opt', 'user_keywords',
'postprocess_html',
'custom_css','version',
'no_args_infer', 'multimodule', 'import'
}
if args.unsafe_no_sandbox then
local select_locals = {
["args"] = args,
}
function ldoc.import(t, env)
local retval = select_locals[t]
if not retval then
retval = _G[t]
end
if env then
env[t] = retval
end
return retval
end
table.insert(ldoc_contents, 'import')
end
ldoc_contents = tablex.makeset(ldoc_contents)
local function loadstr (ldoc,txt)
local chunk, err
-- Penlight's Lua 5.2 compatibility has wobbled over the years...
if not rawget(_G,'loadin') then -- Penlight 0.9.5
-- Penlight 0.9.7; no more global load() override
chunk,err = utils.load(txt,'config',nil,ldoc)
else
-- luacheck: push ignore 113
chunk,err = loadin(ldoc,txt)
-- luacheck: pop
end
return chunk, err
end
-- any file called 'config.ld' found in the source tree will be
-- handled specially. It will be loaded using 'ldoc' as the environment.
local function read_ldoc_config (fname)
local directory = path.dirname(fname)
if directory == '' then
directory = '.'
end
local chunk, err, _
if args.filter == 'none' then
print('reading configuration from '..fname)
end
local txt,not_found = utils.readfile(fname)
if txt then
chunk, err = loadstr(ldoc,txt)
if chunk then
if args.define ~= 'none' then ldoc[args.define] = true end
_,err = pcall(chunk)
end
end
if err then quit('error loading config file '..fname..': '..err) end
for k in pairs(ldoc) do
if not ldoc_contents[k] then
quit("this config file field/function is unrecognized: "..k)
end
end
return directory, not_found
end
local quote = tools.quote
--- processing command line and preparing for output ---
local file_list = List()
File.list = file_list
local config_dir
local ldoc_dir = arg[0]:gsub('[^/\\]+$','')
local doc_path = ldoc_dir..'/ldoc/builtin/?.lua'
-- ldoc -m is expecting a Lua package; this converts this to a file path
if args.module then
-- first check if we've been given a global Lua lib function
if args.file:match '^%a+$' and global.functions[args.file] then
args.file = 'global.'..args.file
end
local fullpath,mod,_ = tools.lookup_existing_module_or_function (args.file, doc_path)
if not fullpath then
quit(mod)
else
args.file = fullpath
args.module = mod
end
end
local abspath = tools.abspath
-- trim trailing forward slash
if args.file then
args.file = tools.trim_path_slashes(args.file)
end
-- a special case: 'ldoc .' can get all its parameters from config.ld
if args.file == '.' then
local err
config_dir,err = read_ldoc_config(args.config)
if err then quit("no "..quote(args.config).." found") end
local config_path = path.dirname(args.config)
if config_path ~= '' then
print('changing to directory',config_path)
lfs.chdir(config_path)
end
args.file = ldoc.file or '.'
if args.file == '.' then
args.file = lfs.currentdir()
elseif type(args.file) == 'table' then
for i,f in ipairs(args.file) do
args.file[i] = abspath(f)
end
else
args.file = abspath(args.file)
end
else
-- user-provided config file
if args.config ~= 'config.ld' then
local err
config_dir,err = read_ldoc_config(args.config)
if err then quit("no "..quote(args.config).." found") end
end
-- with user-provided file
if args.file == nil then
lapp.error('missing required parameter: file')
end
args.file = abspath(args.file)
end
local source_dir = args.file
-- override "file" from config
if ldoc.file then
args.file = ldoc.file
end
if type(ldoc.custom_tags) == 'table' then -- custom tags
for i, custom in ipairs(ldoc.custom_tags) do
if type(custom) == 'string' then
custom = {custom}
ldoc.custom_tags[i] = custom
end
doc.add_tag(custom[1], 'ML')
end
end -- custom tags
if type(source_dir) == 'table' then
source_dir = source_dir[1]
end
if type(source_dir) == 'string' and path.isfile(source_dir) then
source_dir = path.splitpath(source_dir)
end
source_dir = source_dir:gsub('[/\\]%.$','')
---------- specifying the package for inferring module names --------
-- If you use module(...), or forget to explicitly use @module, then
-- ldoc has to infer the module name. There are three sensible values for
-- `args.package`:
--
-- * '.' the actual source is in an immediate subdir of the path given
-- * '..' the path given points to the source directory
-- * 'NAME' explicitly give the base module package name
--
override ('package','.')
local function setup_package_base()
if ldoc.package then args.package = ldoc.package end
if args.package == '.' then
args.package = source_dir
elseif args.package == '..' then
args.package = path.splitpath(source_dir)
elseif not args.package:find '[\\/]' then
local subdir,dir = path.splitpath(source_dir)
if dir == args.package then
args.package = subdir
elseif path.isdir(path.join(source_dir,args.package)) then
args.package = source_dir
else
quit("args.package is not the name of the source directory")
end
end
end
--------- processing files ---------------------
-- ldoc may be given a file, or a directory. `args.file` may also be specified in config.ld
-- where it is a list of files or directories. If specified on the command-line, we have
-- to find an optional associated config.ld, if not already loaded.
if ldoc.ignore then args.ignore = true end
local function process_file (f, flist)
local ext = path.extension(f)
local ftype = file_types[ext]
if ftype then
if args.verbose then print(f) end
ftype.extra = ldoc.parse_extra or {}
local F,err = parse.file(f,ftype,args)
if err then
if F then
F:warning("internal LDoc error")
end
quit(err)
end
flist:append(F)
end
end
local process_file_list = tools.process_file_list
setup_package_base()
override 'no_args_infer'
override 'colon'
override 'merge'
override 'not_luadoc'
override 'module_file'
override 'boilerplate'
override 'all'
override 'multimodule'
setup_kinds()
-- LDoc is doing plain ole C, don't want random Lua references!
if ldoc.parse_extra and ldoc.parse_extra.C then
ldoc.no_lua_ref = true
end
if ldoc.merge_error_groups == nil then
ldoc.merge_error_groups = 'Error Message'
end
-- ldoc.module_file establishes a partial ordering where the
-- master module files are processed first.
local function reorder_module_file ()
if args.module_file then
local mf = {}
for mname, f in pairs(args.module_file) do
local fullpath = abspath(f)
mf[fullpath] = true
end
return function(x,y)
return mf[x] and not mf[y]
end
end
end
-- process files, optionally in order that respects master module files
local function process_all_files(files)
local sortfn = reorder_module_file()
local files = tools.expand_file_list(files,'*.*')
if sortfn then files:sort(sortfn) end
for f in files:iter() do
process_file(f, file_list)
end
if #file_list == 0 then quit "no source files found" end
end
if type(args.file) == 'table' then
-- this can only be set from config file so we can assume config is already read
process_all_files(args.file)
elseif path.isdir(args.file) then
-- use any configuration file we find, if not already specified
if not config_dir then
local files = List(dir.getallfiles(args.file,'*.*'))
local config_files = files:filter(function(f)
return path.basename(f) == args.config
end)
if #config_files > 0 then
config_dir = read_ldoc_config(config_files[1])
if #config_files > 1 then
print('warning: other config files found: '..config_files[2])
end
end
end
process_all_files({args.file})
elseif path.isfile(args.file) then
-- a single file may be accompanied by a config.ld in the same dir
if not config_dir then
config_dir = path.dirname(args.file)
if config_dir == '' then config_dir = '.' end
local config = path.join(config_dir,args.config)
if path.isfile(config) then
read_ldoc_config(config)
end
end
process_file(args.file, file_list)
if #file_list == 0 then quit "unsupported file extension" end
else
quit ("file or directory does not exist: "..quote(args.file))
end
-- create the function that renders text (descriptions and summaries)
-- (this also will initialize the code prettifier used)
override ('format','plain')
override 'pretty'
ldoc.markup = markup.create(ldoc, args.format, args.pretty, ldoc.user_keywords)
------ 'Special' Project-level entities ---------------------------------------
-- Examples and Topics do not contain code to be processed for doc comments.
-- Instead, they are intended to be rendered nicely as-is, whether as pretty-lua
-- or as Markdown text. Treating them as 'modules' does stretch the meaning of
-- of the term, but allows them to be treated much as modules or scripts.
-- They define an item 'body' field (containing the file's text) and a 'postprocess'
-- field which is used later to convert them into HTML. They may contain @{ref}s.
local function add_special_project_entity (f,tags,process)
local F = File(f)
tags.name = path.basename(f)
local text = utils.readfile(f)
local item = F:new_item(tags,1)
if process then
text = process(F, text)
end
F:finish()
file_list:append(F)
item.body = text
return item, F
end
local function prettify_source_files(files,class,linemap)
local prettify = require 'ldoc.prettify'
process_file_list (files, '*.*', function(f)
local ext = path.extension(f)
local ftype = file_types[ext]
if ftype then
local item = add_special_project_entity(f,{
class = class,
})
-- wrap prettify for this example so it knows which file to blame
-- if there's a problem
local lang = ext:sub(2)
item.postprocess = function(code)
return '<h2>'..path.basename(f)..'</h2>\n' ..
prettify.lua(lang,f,code,0,true,linemap and linemap[f])
end
end
end)
end
if type(ldoc.examples) == 'string' then
ldoc.examples = {ldoc.examples}
end
if type(ldoc.examples) == 'table' then
prettify_source_files(ldoc.examples,"example")
end
ldoc.is_file_prettified = {}
if ldoc.prettify_files then
local files = List()
local linemap = {}
for F in file_list:iter() do
files:append(F.filename)
local mod = F.modules[1]
if mod then
local ls = List()
for item in mod.items:iter() do
ls:append(item.lineno)
end
linemap[F.filename] = ls
end
end
if type(ldoc.prettify_files) == 'table' then
files = tools.expand_file_list(ldoc.prettify_files, '*.*')
elseif type(ldoc.prettify_files) == 'string' then
-- the gotcha is that if the person has a folder called 'show', only the contents
-- of that directory will be converted. So, we warn of this amibiguity
if ldoc.prettify_files == 'show' then
-- just fall through with all module files collected above
if path.exists 'show' then
print("Notice: if you only want to prettify files in `show`, then set prettify_files to `show/`")
end
else
files = tools.expand_file_list({ldoc.prettify_files}, '*.*')
end
end
ldoc.is_file_prettified = tablex.makeset(files)
prettify_source_files(files,"file",linemap)
end
if args.simple then
ldoc.no_return_or_parms=true
ldoc.no_summary=true
end
ldoc.readme = ldoc.readme or ldoc.topics
if type(ldoc.readme) == 'string' then
ldoc.readme = {ldoc.readme}
end
if type(ldoc.readme) == 'table' then
process_file_list(ldoc.readme, '*.md', function(f)
local item, F = add_special_project_entity(f,{
class = 'topic'
}, markup.add_sections)
-- add_sections above has created sections corresponding to the 2nd level
-- headers in the readme, which are attached to the File. So
-- we pass the File to the postprocesser, which will insert the section markers
-- and resolve inline @ references.
if ldoc.use_markdown_titles then
item.display_name = F.display_name
end
item.postprocess = function(txt) return ldoc.markup(txt,F) end
end)
end
-- extract modules from the file objects, resolve references and sort appropriately ---
local first_module
local project = ProjectMap()
local module_list = List()
module_list.by_name = {}
local modcount = 0
for F in file_list:iter() do
for mod in F.modules:iter() do
if not first_module then first_module = mod end
if doc.code_tag(mod.type) then modcount = modcount + 1 end
module_list:append(mod)
module_list.by_name[mod.name] = mod
end
end
for mod in module_list:iter() do
if not args.module then -- no point if we're just showing docs on the console
mod:resolve_references(module_list)
end
project:add(mod,module_list)
end
if ldoc.sort_modules then
table.sort(module_list,function(m1,m2)
return m1.name < m2.name
end)
end
ldoc.single = modcount == 1 and first_module or nil
--do return end
-------- three ways to dump the object graph after processing -----
-- ldoc -m will give a quick & dirty dump of the module's documentation;
-- using -v will make it more verbose
if args.module then
if #module_list == 0 then quit("no modules found") end
if args.module == true then
file_list[1]:dump(args.verbose)
else
local M,name = module_list[1], args.module
local fun = M.items.by_name[name]
if not fun then
fun = M.items.by_name[M.mod_name..':'..name]
end
if not fun then quit(quote(name).." is not part of "..quote(args.file)) end
fun:dump(true)
end
return
end
-- ldoc --dump will do the same as -m, except for the currently specified files
if args.dump then
for mod in module_list:iter() do
mod:dump(true)
end
os.exit()
end
if args.tags ~= 'none' then
local tagset = {}
for t in stringx.split(args.tags,','):iter() do
tagset[t] = true
end
for mod in module_list:iter() do
mod:dump_tags(tagset)
end
os.exit()
end
-- ldoc --filter mod.name will load the module `mod` and pass the object graph
-- to the function `name`. As a special case --filter dump will use pl.pretty.dump.
if args.filter ~= 'none' then
doc.filter_objects_through_function(args.filter, module_list)
os.exit()
end
-- can specify format, output, dir and ext in config.ld
override ('output','index')
override ('dir','doc')
override ('ext','html')
override 'one'
-- handling styling and templates --
ldoc.css, ldoc.templ = 'ldoc.css','ldoc.ltp'
-- special case: user wants to generate a .md file from a .lua file
if args.ext == 'md' then
if #module_list ~= 1 then
quit("can currently only generate Markdown output from one module only")
end
if not ldoc.template or ldoc.template == '!' then
ldoc.template = '!md'
end
args.output = module_list[1].name
args.dir = '.'
ldoc.template_escape = '>'
ldoc.style = false
args.ext = '.md'
end
local function match_bang (s)
if type(s) ~= 'string' then return end
return s:match '^!(.*)'
end
local function style_dir (sname)
local style = ldoc[sname]
local dir
if style==false and sname == 'style' then
args.style = false
ldoc.css = false
end
if style then
if style == true then
dir = config_dir
elseif type(style) == 'string' and (path.isdir(style) or match_bang(style)) then
dir = style
else
quit(quote(tostring(style)).." is not a directory")
end
args[sname] = dir
end
end
-- the directories for template and stylesheet can be specified
-- either by command-line '--template','--style' arguments or by 'template and
-- 'style' fields in config.ld.
-- The assumption here is that if these variables are simply true then the directory
-- containing config.ld contains a ldoc.css and a ldoc.ltp respectively. Otherwise
-- they must be a valid subdirectory.
style_dir 'style'
style_dir 'template'
if not args.ext:find '^%.' then
args.ext = '.'..args.ext
end
if args.one then
ldoc.style = '!one'
end
local builtin_style, builtin_template = match_bang(args.style),match_bang(args.template)
if builtin_style or builtin_template then
-- '!' here means 'use built-in templates'
local user = path.expanduser('~'):gsub('[/\\: ]','_')
local tmpdir = path.join(path.is_windows and os.getenv('TMP') or (os.getenv('TMPDIR') or '/tmp'),'ldoc'..user)
if not path.isdir(tmpdir) then
lfs.mkdir(tmpdir)
end
local function tmpwrite (name)
local ok,text = pcall(require,'ldoc.html.'..name:gsub('%.','_'))
if not ok then
quit("cannot find builtin template "..name.." ("..text..")")
end
if not utils.writefile(path.join(tmpdir,name),text) then
quit("cannot write to temp directory "..tmpdir)
end
end
if builtin_style then
if builtin_style ~= '' then
ldoc.css = 'ldoc_'..builtin_style..'.css'
end
tmpwrite(ldoc.css)
args.style = tmpdir
end
if builtin_template then
if builtin_template ~= '' then
ldoc.templ = 'ldoc_'..builtin_template..'.ltp'
end
tmpwrite(ldoc.templ)
args.template = tmpdir
end
end
-- default icon to nil
if args.icon == 'none' then args.icon = nil end
ldoc.log = print
ldoc.kinds = project
ldoc.modules = module_list
ldoc.title = ldoc.title or args.title
ldoc.project = ldoc.project or args.project
ldoc.package = args.package:match '%a+' and args.package or nil
ldoc.icon = ldoc.icon or args.icon
ldoc.multimodule = ldoc.multimodule or args.multimodule
if ldoc.icon then
ldoc.icon_basename = path.basename(ldoc.icon)
end
local source_date_epoch = os.getenv("SOURCE_DATE_EPOCH")
if args.testing then
ldoc.updatetime = "2015-01-01 12:00:00"
ldoc.version = 'TESTING'
elseif source_date_epoch == nil then
if args.date == 'system' then
ldoc.updatetime = os.date("%Y-%m-%d %H:%M:%S")
else
ldoc.updatetime = args.date
end
else
ldoc.updatetime = os.date("!%Y-%m-%d %H:%M:%S",source_date_epoch)
end
local html = require 'ldoc.html'
html.generate_output(ldoc, args, project)
if args.verbose then
print 'modules'
for k in pairs(module_list.by_name) do print(k) end
end
if args.fatalwarnings and Item.had_warning then
os.exit(1)
end