Differences From
Artifact [fc129ed31b]:
150 150 fns = {
151 151 throw = function(me) error(me) end;
152 152 }
153 153 }
154 154 ct.exnkind = declare {
155 155 ident = 'exn-kind';
156 156 mk = function(desc, report)
157 - return { desc = desc, report = report }
157 + return {
158 + desc = desc;
159 + report = report or function(msg,...)
160 + return string.format(msg,...)
161 + end;
162 + }
158 163 end;
159 164 call = function(me, ...)
160 165 return ct.exn(me, ...)
161 166 end;
162 167 }
163 168
164 169 ct.exns = {
165 170 tx = ct.exnkind('translation error', function(msg,...)
166 171 return string.format("(%s:%u) "..msg, ...)
167 - end)
172 + end);
173 + io = ct.exnkind('IO error', function(msg, ...)
174 + return string.format("<%s %s> "..msg, ...)
175 + end);
176 + cli = ct.exnkind 'command line parse error';
177 + mode = ct.exnkind('bad mode', function(msg, ...)
178 + return string.format("mode “%s” "..msg, ...)
179 + end);
168 180 }
169 181
170 182 ct.ctx = declare {
171 183 mk = function(src) return {src = src} end;
172 184 ident = 'context';
173 185 cast = {
174 186 string = function(me)
................................................................................
390 402 end
391 403
392 404 local span_renderers = {}
393 405 local function htmlSpan(spans, block, sec)
394 406 local text = {}
395 407 for k,v in pairs(spans) do
396 408 if type(v) == 'string' then
397 - table.insert(text,(v:gsub('[<>&]',
409 + table.insert(text,(v:gsub('[<>&"]',
398 410 function(x)
399 - return string.format('&#%02u', string.byte(x))
411 + return string.format('&#%02u;', string.byte(x))
400 412 end)))
401 413 else
402 414 table.insert(text, span_renderers[v.kind](v, block, sec))
403 415 end
404 416 end
405 417 return table.concat(text)
406 418 end
407 419
408 420 function span_renderers.format(sp)
409 421 local tags = { strong = 'strong', emph = 'em', strike = 'del', insert = 'ins', literal = 'code' }
410 - if sp.style == 'literal' then
422 + if sp.style == 'literal' and not opts['fossil-uv'] then
411 423 stylesNeeded.code = true
412 424 end
413 425 if sp.style == 'del' or sp.style == 'ins' then
414 426 stylesNeeded.editors_markup = true
415 427 end
416 428 return tag(tags[sp.style],nil,htmlSpan(sp.spans))
417 429 end
................................................................................
677 689
678 690 local styles = {}
679 691 for k in pairs(stylesNeeded) do
680 692 table.insert(styles, (stylesets[k]:gsub('%s+',' ')))
681 693 end
682 694
683 695 local head = {}
696 + local styletag = ''
697 + if opts['link-css'] then
698 + local css = opts['link-css']
699 + if type(css) ~= 'string' then ct.exns.mode('must be a string', 'html:link-css'):throw() end
700 + styletag = styletag .. elt('link',{rel='stylesheet',type='text/css',href=opts['link-css']})
701 + end
684 702 if next(styles) then
685 - table.insert(head, tag('style',{type='text/css'},table.concat(styles)))
703 + if opts['gen-styles'] then
704 + styletag = styletag .. tag('style',{type='text/css'},table.concat(styles))
705 + end
706 + table.insert(head, styletag)
686 707 end
687 708
688 - if opts.snippet then
689 - return body
709 + if opts['fossil-uv'] then
710 + return tag('div',{class='fossil-doc',['data-title']=doctitle},styletag .. body)
711 + elseif opts.snippet then
712 + return styletag .. body
690 713 else
691 714 return dr.htmlDoc(doctitle, next(head) and table.concat(head), body)
692 715 end
693 716 end
694 717
695 718 local function
696 719 startswith(str, pfx)
................................................................................
929 952 if a1 == ':' and a2 ~= ':' then
930 953 align = 'left'
931 954 elseif a1 == ':' and a2 == ':' then
932 955 align = 'center'
933 956 elseif a1 ~= ':' and a2 == ':' then
934 957 align = 'right'
935 958 end
959 + text = text:match '^%s*(.-)%s*$'
936 960 table.insert(row, {
937 961 spans = ct.parse_span(text, c);
938 962 align = align;
939 963 header = header;
940 964 })
941 965 end
942 966 if #c.sec.blocks > 1 and c.sec.blocks[#c.sec.blocks].kind == 'table' then
................................................................................
1100 1124 end
1101 1125 end
1102 1126
1103 1127 return ctx.doc
1104 1128 end
1105 1129
1106 1130 local default_mode = {
1107 - format = 'html';
1131 + ['render:format'] = 'html';
1132 + ['html:gen-styles'] = true;
1108 1133 }
1134 +
1135 +local function filter(list, fn)
1136 + local new = {}
1137 + for i, v in ipairs(list) do
1138 + if fn(v,i) then table.insert(new, v) end
1139 + end
1140 + return new
1141 +end
1142 +
1143 +local function kmap(fn, list)
1144 + local new = {}
1145 + for k, v in pairs(list) do
1146 + local nk,nv = fn(k,v)
1147 + new[nk or k] = nv or v
1148 + end
1149 + return new
1150 +end
1151 +local function kfilter(list, fn)
1152 + local new = {}
1153 + for k, v in pairs(list) do
1154 + if fn(k,v) then new[k] = v end
1155 + end
1156 + return new
1157 +end
1109 1158
1110 1159 local function main(input, output, log, mode, vars)
1111 1160 local doc = ct.parse(input.stream, input.src)
1112 1161 input.stream:close()
1113 - if mode['show-tree'] then
1162 + if mode['parse:show-tree'] then
1114 1163 log:write(dump(doc))
1115 1164 end
1116 1165
1117 - if not mode.format then
1166 + if not mode['render:format'] then
1118 1167 error 'what output format should i translate the input to?'
1119 1168 end
1120 - if not ct.render[mode.format] then
1121 - error(string.format('output format “%s” unsupported', mode.format))
1169 + if not ct.render[mode['render:format']] then
1170 + error(string.format('output format “%s” unsupported', mode['render:format']))
1122 1171 end
1123 1172
1124 - output:write(ct.render[mode.format](doc, {}))
1173 + local render_opts = kmap(function(k,v)
1174 + return k:sub(2+#mode['render:format'])
1175 + end, kfilter(mode, function(m)
1176 + return startswith(m, mode['render:format']..':')
1177 + end))
1178 +
1179 + output:write(ct.render[mode['render:format']](doc, render_opts))
1125 1180 end
1126 1181
1127 1182 local inp,outp,log = io.stdin, io.stdout, io.stderr
1128 1183
1129 1184 local function entry_cli()
1130 1185 local mode, vars, input = default_mode, {}, {
1131 1186 stream = inp;
1132 1187 src = {
1133 1188 file = '(stdin)';
1134 1189 }
1135 1190 }
1136 1191
1137 - if arg[1] and arg[1] ~= '' then
1192 + local optnparams = function(o)
1193 + local param_opts = {
1194 + out = 1;
1195 + log = 1;
1196 + define = 2; -- key value
1197 + ['mode-set'] = 1;
1198 + ['mode-clear'] = 1;
1199 + mode = 2;
1200 + }
1201 + return param_opts[o] or 0
1202 + end
1203 +
1204 + local optmap = {
1205 + o = 'out';
1206 + l = 'log';
1207 + d = 'define';
1208 + V = 'version';
1209 + h = 'help';
1210 + y = 'mode-set', n = 'mode-clear';
1211 + m = 'mode';
1212 + }
1213 +
1214 + local checkmodekey = function(key)
1215 + if not key:match '[^:]+:.+' then
1216 + ct.exns.cli('invalid mode key %s', key):throw()
1217 + end
1218 + return key
1219 + end
1220 + local onswitch = {
1221 + out = function(file)
1222 + local nf = io.open(file,'wb')
1223 + if nf then outp:close() outp = nf else
1224 + ct.exns.io('could not open output file for writing', 'open',file):throw()
1225 + end
1226 + end;
1227 + log = function(file)
1228 + local nf = io.open(file,'wb')
1229 + if nf then log:close() log = nf else
1230 + ct.exns.io('could not open log file for writing', 'open',file):throw()
1231 + end
1232 + end;
1233 + define = function(key,value)
1234 + -- set context key
1235 + end;
1236 + mode = function(key,value) mode[checkmodekey(key)] = value end;
1237 + ['mode-set'] = function(key) mode[checkmodekey(key)] = true end;
1238 + ['mode-clear'] = function(key) mode[checkmodekey(key)] = false end;
1239 + }
1240 +
1241 + local args = {}
1242 + local keepParsing = true
1243 + do local i = 1 while i <= #arg do local v = arg[i]
1244 + local execLongOpt = function(longopt)
1245 + if not onswitch[longopt] then
1246 + ct.exns.cli('switch --%s unrecognized', longopt):throw()
1247 + end
1248 + local nargs = optnparams(longopt)
1249 +
1250 + if nargs > 1 then
1251 + if i + nargs > #arg then
1252 + ct.exns.cli('not enough arguments for switch --%s (%u expected)', longopt, nargs):throw()
1253 + end
1254 + local nt = {}
1255 + for j = i+1, i+nargs do
1256 + table.insert(nt, arg[j])
1257 + end
1258 + onswitch[longopt](table.unpack(nt))
1259 + elseif nargs == 1 then
1260 + onswitch[longopt](arg[i+1])
1261 + end
1262 + i = i + nargs
1263 + end
1264 + if v == '--' then
1265 + keepParsing = false
1266 + else
1267 + local longopt = v:match '^%-%-(.+)$'
1268 + if keepParsing and longopt then
1269 + execLongOpt(longopt)
1270 + else
1271 + if keepParsing and v:sub(1,1) == '-' then
1272 + for c,p in eachcode(v:sub(2)) do
1273 + if optmap[c] then
1274 + execLongOpt(optmap[c])
1275 + else
1276 + ct.exns.cli('switch -%i unrecognized', c):throw()
1277 + end
1278 + end
1279 + else
1280 + table.insert(args, v)
1281 + end
1282 + end
1283 +
1284 + end
1285 + i = i + 1 end end
1286 +
1287 + if args[1] and args[1] ~= '' then
1138 1288 local file = io.open(arg[1], "rb")
1139 - if not file then error('unable to load file ' .. arg[1]) end
1289 + if not file then error('unable to load file ' .. args[1]) end
1140 1290 input.stream = file
1141 - input.src.file = arg[1]
1291 + input.src.file = args[1]
1142 1292 end
1143 1293
1144 1294 main(input, outp, log, mode, vars)
1145 1295 end
1146 1296
1147 1297 -- local ok, e = pcall(entry_cli)
1148 1298 local ok, e = true, entry_cli()