Overview
Comment: | add path class; add URI support for HTML link output |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
8c11f3b6696754197f3fd2223687309f |
User & Date: | lexi on 2022-09-10 01:02:12 |
Other Links: | manifest | tags |
Context
2022-09-10
| ||
03:11 | add xref blocks, minor refactors check-in: 76fe4885f4 user: lexi tags: trunk | |
01:02 | add path class; add URI support for HTML link output check-in: 8c11f3b669 user: lexi tags: trunk | |
2022-09-09
| ||
23:22 | ref now recurses into parents properly check-in: 47905a08e6 user: lexi tags: trunk | |
Changes
Modified cortav.lua from [00eceed9de] to [08a1a9b804].
141 141 return arg 142 142 else return sp end 143 143 end) 144 144 145 145 end 146 146 147 147 local function checkFromSec(sec,doc) 148 - if sec and not id:find'%.' then 149 - local rid = sec.refs[id] 150 - if rid then 151 - return rid, id, sec 148 + if not id:find'%.' then 149 + if sec then 150 + local rid = sec.refs[id] 151 + if rid then 152 + return rid, id, sec 153 + end 152 154 end 153 155 154 - if doc.sections[rid] then 155 - return nil, id, doc.sections[rid] 156 + if doc.sections[id] then 157 + return nil, id, doc.sections[id] 156 158 end 157 159 else 158 160 local secid, ref = string.match(id, "(.-)%.(.+)") 159 161 local s 160 162 s = s or doc.sections[secid] 161 163 if s then 162 164 if s.refs[ref] then ................................................................................ 196 198 rid = self.invocation.origin:ref(id) 197 199 if rid then 198 200 return rid, id, self.invocation.origin.sec 199 201 end 200 202 end 201 203 end 202 204 203 - o,i,s = scanParents(doc) 205 + o,i,s = scanParents(self.doc) 204 206 if o or s then return o,i,s end 205 207 206 208 self:fail("ID “%s” does not name an object or section", id) 207 209 end 208 210 }; 209 211 } 210 212
Modified render/html.lua from [f159ef53c2] to [78b49cf252].
611 611 end 612 612 613 613 function span_renderers.raw(v,b,s) 614 614 return htmlSpan(v.spans, b, s) 615 615 end 616 616 617 617 function span_renderers.link(sp,b,s) 618 + local dest_o, _, dest_s = b.origin:ref(sp.ref) 618 619 local href 619 - if b.origin.doc.sections[sp.ref] then 620 - href = '#' .. getSafeID(sp) 620 + if dest_o == nil then 621 + -- link is to the section itself 622 + href = '#' .. getSafeID(dest_s) 621 623 else 622 - if sp.addr then href = sp.addr else 623 - local r = b.origin:ref(sp.ref) 624 - if type(r) == 'table' then 625 - href = '#' .. getSafeID(r) 626 - else href = r end 624 +-- if sp.addr then href = sp.addr else 625 + if type(dest_o) == 'table' then 626 + href = '#' .. getSafeID(dest_o) 627 + else -- URI in reference 628 + local uri = ss.uri(dest_o) 629 + if uri.class[1] == 'file' 630 + or uri.class[1] == 'asset' then 631 + if uri.namespace == 'localhost' then 632 + -- emit an actual file url 633 + href = 'file://' .. uri:construct('path','frag') 634 + elseif uri.namespace == nil then 635 + -- this is gonna be tricky. first we establish the location 636 + -- of the CWD/asset base relative to the output file (if any; 637 + -- assume equivalent otherwise) then express the difference 638 + -- as a directory prefix. 639 + -- jk tho for now we just emit the path+frag sadlol TODO 640 + href = uri:construct('path','frag') 641 + else 642 + b.origin:fail('file: URI namespace must be empty or “localhost” for HTML links; others are not meaningful (offending URI: “%s”)', dest_o) 643 + end 644 + elseif uri:canfetch() == 'http' then 645 + local sc = 'http' 646 + if uri.class[1] == 'https' or uri.class[2] == 'tls' then 647 + sc = 'https' 648 + end 649 + if uri.namespace == nil and uri.auth == nil and uri.svc == nil then 650 + -- omit the scheme so we can use a relative path 651 + href = uri:construct('path','query','frag') 652 + else 653 + uri.class = {sc} 654 + href = tostring(uri) 655 + end 656 + else href = tostring(uri) end 627 657 end 628 658 end 629 659 return tag('a',{href=href},next(sp.spans) and htmlSpan(sp.spans,b,s) or href) 630 660 end 631 661 632 662 span_renderers['line-break'] = function(sp,b,s) 633 663 return elt('br')
Modified sirsem.lua from [dec9af930b] to [9b28e1fde4].
761 761 return str 762 762 end 763 763 }; 764 764 } 765 765 766 766 function ss.classinstance(o) 767 767 local g = getmetatable(o) 768 - if not o then return nil end 768 + if not g then return nil end 769 769 local mm = getmetatable(g) 770 - if not o then return nil end 770 + if not mm then return nil end 771 771 if mm.__name == 'class' then 772 772 return g 773 773 else 774 774 return nil 775 775 end 776 776 end 777 777 ................................................................................ 1214 1214 if #a ~= #b then return false end 1215 1215 eq = eq or function(p,q) return p == q end 1216 1216 for i = 1, #a do 1217 1217 if not eq(a[i],b[i]) then return false end 1218 1218 end 1219 1219 return true 1220 1220 end 1221 + 1222 +ss.os = {} 1223 +function ss.os.getcwd() 1224 + return os.getenv 'PWD' -- :((( HAX 1225 +end 1226 + 1227 +ss.path = ss.declare { 1228 + ident = 'path'; 1229 + mk = function() return { 1230 + relative = true; 1231 + elts = {}; 1232 + } end; 1233 + construct = function(me, o, rel) 1234 + if type(o) == 'string' then 1235 + if o:sub(1,1) == '/' then 1236 + me.relative = false 1237 + end 1238 + me.elts = ss.str.split(ss.str.enc.ascii, o, '/') 1239 + elseif type(o) == 'table' then 1240 + me.elts = o 1241 + me.relative = rel 1242 + end 1243 + end; 1244 + clonesetup = function(me) 1245 + me.elts = ss.clone(me.elts) 1246 + end; 1247 + cast = { 1248 + string = function(me) 1249 + return (me.relative and '' or '/') .. 1250 + table.concat(me.elts, '/') 1251 + end; 1252 + }; 1253 + op = { 1254 + sub = function(a,b) 1255 + if a.relative ~= b.relative then 1256 + return nil 1257 + end 1258 + 1259 + local np = ss.path({}, true) 1260 + local brk = false 1261 + for i=1, math.max(#a.elts,#b.elts) do 1262 + if not brk then 1263 + if a.elts[i] ~= b.elts[i] then 1264 + brk = true 1265 + end 1266 + end 1267 + if brk then 1268 + table.insert(np.elts, b.elts[i]) 1269 + end 1270 + end 1271 + 1272 + return np 1273 + end; 1274 + sum = function(a,b) 1275 + if b.relative == false then 1276 + return nil 1277 + end 1278 + local n = a:clone() 1279 + local i = #n.elts 1280 + for j, v in ipairs(b.elts) do 1281 + n.elts[i+j] = v 1282 + end 1283 + return n 1284 + end; 1285 + lt = function(a,b) 1286 + -- '/a/b/c' < '/a/b/c/d' 1287 + -- 'q/f/g' < 'q/f/g/p/d' 1288 + -- '/a' !< '/b', '/a' !< 'a' 1289 + if a.relative ~= b.relative then 1290 + return false 1291 + end 1292 + if #a.elts > #b.elts then return false end 1293 + for i=1, #a.elts do 1294 + if a.elts[i] ~= b.elts[i] then 1295 + return false 1296 + end 1297 + end 1298 + return true 1299 + end; 1300 + }; 1301 + fns = { 1302 + dir = function(me) 1303 + local n = ss.copy(me.elts) 1304 + n[#n] = nil 1305 + local p = ss.path(n, me.relative) 1306 + end; 1307 + normalize = function(me) 1308 + local np = ss.path({}, me.relative) 1309 + for i, e in ipairs(me.elts) do 1310 + if e == '..' then 1311 + if me.relative and ( 1312 + next(np.elts) == nil or 1313 + np.elts[#np.elts] == '..' 1314 + ) then 1315 + table.insert(np.elts, '..') 1316 + else 1317 + table.remove(np.elts) 1318 + end 1319 + elseif e ~= '.' then 1320 + table.insert(np.elts, e) 1321 + end 1322 + end 1323 + return np 1324 + end 1325 + }; 1326 + cfns = { 1327 + cwd = function() 1328 + return ss.path(ss.os.getcwd()) 1329 + end; 1330 + }; 1331 +} 1221 1332 1222 1333 ss.uri = ss.declare { 1223 1334 ident = 'uri'; 1224 1335 mk = function() return { 1225 1336 class = nil; 1226 1337 namespace = nil; 1227 1338 path = nil; ................................................................................ 1242 1353 local s,r = rem:match '^([^:]+):(.*)$' 1243 1354 s_class, rem = s,r 1244 1355 end 1245 1356 if not rem then 1246 1357 ss.uri.exn('invalid URI “%s”', str):throw() 1247 1358 end 1248 1359 local s_ns do 1249 - local s,r = rem:match '^//([^/]*)(.*)$' 1360 + local s,r = rem:match '^//([^/?#]*)(.*)$' 1250 1361 if s then s_ns, rem = s,r end 1251 1362 end 1252 1363 local h_query 1253 1364 local s_frag 1254 1365 local s_path if rem ~= '' then 1255 1366 local s,q,r = rem:match '^([^?#]*)([?#]?)(.*)$' 1256 1367 if s == '' then s = nil end ................................................................................ 1303 1414 me.namespace = dec(s_ns) 1304 1415 me.path = dec(s_path) 1305 1416 me.query = dec(s_query) 1306 1417 me.frag = dec(s_frag) 1307 1418 end; 1308 1419 cast = { 1309 1420 string = function(me) 1421 + return me:construct('class','namespace','path','query','frag') 1422 + end; 1423 + }; 1424 + fns = { 1425 + canfetch = function(me) 1426 + for id, pr in pairs(fetchableProtocols) do 1427 + for _, p in ipairs(pr.proto) do 1428 + if ss.match(me.class, p) then return id end 1429 + end 1430 + end 1431 + return false 1432 + end; 1433 + construct = function(me, ...) 1434 + local parts = {} 1435 + local function add(n, ...) 1436 + if n == nil then return end 1437 + table.insert(parts, me:part(n)) 1438 + add(...) 1439 + end 1440 + add(...) 1441 + return table.concat(parts) 1442 + end; 1443 + part = function(me, p) 1310 1444 local function san(str, chars) 1311 1445 -- TODO IRI support 1312 1446 chars = chars or '' 1313 1447 local ptn = '-a-zA-Z0-9_.,;' 1314 1448 ptn = ptn .. chars 1315 1449 return (str:gsub('[^'..ptn..']', function(c) 1316 1450 if c == ' ' then return '+' end 1317 1451 return string.format('%%%02X', string.byte(c)) 1318 1452 end)) 1319 1453 end 1320 - if me.class == nil or next(me.class) == nil then 1321 - return 'none:' 1322 - end 1323 - local parts = { 1324 - table.concat(ss.map(san,me.class), '+') .. ':'; 1325 - } 1326 - if me.namespace or me.auth or me.svc then 1327 - table.insert(parts, '//') 1328 - if me.auth then 1329 - table.insert(parts, san(me.auth,':') .. '@') 1454 + if p == 'class' then 1455 + if me.class == nil or next(me.class) == nil then 1456 + return 'none:' 1330 1457 end 1331 - if me.namespace then 1332 - table.insert(parts, san(me.namespace)) 1333 - end 1334 - if me.svc then 1335 - table.insert(parts, ':' .. san(me.svc)) 1336 - end 1337 - if me.path and not ss.str.begins(me.path, '/') then 1338 - table.insert(parts, '/') 1458 + return table.concat(ss.map(san,me.class), '+') .. ':'; 1459 + else 1460 + if me[p] == nil then return '' end 1461 + if p == 'namespace' then 1462 + local parts = {} 1463 + if me.namespace or me.auth or me.svc then 1464 + table.insert(parts, '//') 1465 + if me.auth then 1466 + table.insert(parts, san(me.auth,':') .. '@') 1467 + end 1468 + if me.namespace then 1469 + table.insert(parts, san(me.namespace)) 1470 + end 1471 + if me.svc then 1472 + table.insert(parts, ':' .. san(me.svc)) 1473 + end 1474 + if me.path and not ss.str.begins(me.path, '/') then 1475 + table.insert(parts, '/') 1476 + end 1477 + end 1478 + return table.concat(parts) 1479 + elseif p == 'path' then 1480 + return san(me.path,'+/=&') 1481 + elseif p == 'query' then 1482 + return '?' .. san(me.query,'?+/=&') 1483 + elseif p == 'frag' then 1484 + return '#' .. san(me.frag,'+/=&') 1339 1485 end 1340 1486 end 1341 - if me.path then 1342 - table.insert(parts, san(me.path,'+/=&')) 1343 - end 1344 - if me.query then 1345 - table.insert(parts, '?' .. san(me.query,'?+/=&')) 1346 - end 1347 - if me.frag then 1348 - table.insert(parts, '#' .. san(me.frag,'+/=&')) 1349 - end 1350 - return table.concat(parts) 1351 - end; 1352 - }; 1353 - fns = { 1354 - canfetch = function(me) 1355 - for id, pr in pairs(fetchableProtocols) do 1356 - for _, p in ipairs(pr.proto) do 1357 - if ss.match(me.class, p) then return id end 1358 - end 1359 - end 1360 - return false 1361 1487 end; 1362 1488 fetch = function(me, env) 1363 1489 local pid = me:canfetch() 1364 1490 if (not pid) or fetchableProtocols[pid].fetch == nil then 1365 1491 ss.uri.exn("URI “%s” is unfetchable", tostring(me)):throw() 1366 1492 end 1367 1493 local proto = fetchableProtocols[pid]