Index: cortav.lua ================================================================== --- cortav.lua +++ cortav.lua @@ -141,49 +141,71 @@ return arg else return sp end end) end - if not id:find'%.' then - local rid = self.sec.refs[id] - if rid then - return rid, id, self.sec - end - - --nothing in the current section, but this ID could be looked up in the context of a macro expansion. if so, check section of the site of invocation as well - if self.invocation then + + local function checkFromSec(sec,doc) + if sec and not id:find'%.' then + local rid = sec.refs[id] + if rid then + return rid, id, sec + end + + if doc.sections[rid] then + return nil, id, doc.sections[rid] + end + else + local secid, ref = string.match(id, "(.-)%.(.+)") + local s + s = s or doc.sections[secid] + if s then + if s.refs[ref] then + return s.refs[ref], ref, s + end + end + end + end + + local function scanParents(doc) + for i, p in ipairs(doc.parents) do + -- TODO figure out a way to ref the embedding section + local o,i,s = checkFromSec(nil, p) + if o or s then return o,i,s end + end + -- breadth-first search + for i, p in ipairs(doc.parents) do + local o,i,s = scanParents(p) + if o or s then return o,i,s end + end + end + + local o,i,s = checkFromSec(self.sec, self.doc) + + if o or s then return o,i,s end + + --nothing in the current section, but this ID could be looked up in the context of a macro expansion. if so, check section of the site of invocation as well + if self.invocation then + local dp = id:find'%.' + if dp == 1 then + local s = self.invocation.origin.sec + local ref = id:sub(2) + if s and s.refs[ref] then + return s.refs[ref], ref, s + end + elseif not dp then rid = self.invocation.origin:ref(id) if rid then return rid, id, self.invocation.origin.sec end end + end - self:fail("no such ref %s in current section", id or '') - else - local sec, ref = string.match(id, "(.-)%.(.+)") - local s - if sec == '' then - if self.invocation == nil then - self:fail('site-of-invocation IDs can only be dereferenced in a macro expansion (offending ID: “%s”)', id) - end - s = self.invocation.origin.sec - end - s = s or self.doc.sections[sec] - if not s then -- fall back on inheritance tree - for i, p in ipairs(self.doc.parents) do - if p.sections[sec] then - s = p.sections[sec] - break - end - end - end - if s then - if s.refs[ref] then - return s.refs[ref], ref, sec - else self:fail("no such ref %s in section %s", ref, sec) end - else self:fail("no such section %s", sec) end - end + o,i,s = scanParents(doc) + if o or s then return o,i,s end + + self:fail("ID “%s” does not name an object or section", id) end }; } ct.sec = declare { @@ -1051,10 +1073,21 @@ table.insert(c.sec.blocks, tbl) j:hook('block_table_insert', c, tbl, l) j:hook('block_table_row_insert', c, tbl, tbl.rows[1], l) end end + +local function insert_link_block(seq) + return blockwrap(function(s,c) + local r = s:sub(#seq):gsub('^%s+','') -- chomp + local uri, txt = r:match('^([^%s]*)%s*(.*)$') + return { + uri = ss.uri(uri); + label = ct.parse_span(txt, c); + } + end) +end ct.ctlseqs = { {seq = '.', fn = insert_paragraph}; {seq = '¶', fn = insert_paragraph}; {seq = '❡', fn = insert_paragraph}; @@ -1255,10 +1288,12 @@ return true end end; fn = blockwrap(function() return { kind = 'horiz-rule' } end)}; + {seq='=>', fn = insert_link_block '=>'}; + {seq='⇒', fn = insert_link_block '⇒'}; {seq='@', fn=function(s,c,j,d) local function mirror(b) local ch = {} local rev = { ['['] = ']'; [']'] = '['; Index: render/html.lua ================================================================== --- render/html.lua +++ render/html.lua @@ -615,11 +615,11 @@ end function span_renderers.link(sp,b,s) local href if b.origin.doc.sections[sp.ref] then - href = '#' .. sp.ref + href = '#' .. getSafeID(sp) else if sp.addr then href = sp.addr else local r = b.origin:ref(sp.ref) if type(r) == 'table' then href = '#' .. getSafeID(r)