Overview
Comment: | add xref blocks, minor refactors |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
76fe4885f4e440299021f7299bc42f59 |
User & Date: | lexi on 2022-09-10 03:11:19 |
Other Links: | manifest | tags |
Context
2022-09-10
| ||
03:16 | defuck xref structure check-in: 0ef3dd0c77 user: lexi tags: trunk | |
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 | |
Changes
Modified cortav.ct from [54b40e50db] to [fb6019b4ad].
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
* [`$[$macro] [$arg1]|[$arg2]|[$argn]…] invokes a block-level macro with the supplied arguments, and can be followed by a property override definition list the same way embed and resource lines can. note that while both [`$[$id]] and [`&[$id]] can be used to instantiate resources of type [`text/x.cortav], there is a critical difference: [`$[$id]] renders out the sub-document separately each time it is named, allowing for parameter expansion and for context variables to be overridden for each invocation. by contrast, [`&[$id]] can only insert copies of the same render; no parameters can be passed and context variables will be expanded to their value at the time the resource was defined. ** [`$mymacro arg 1|arg 2|arg 3] * [*horizontal rule] ([`\---]): inserts a horizontal rule or other context break; does not end the section. must be followed by newline. underlines can also be used in place of dashes ([`___], [`-_-], [`__-__-__] etc), as can horizontal unicode box drawing characters ([`─ ━ ┈] etc). * [*page break] ([`\^^]): for formats that support pagination, like EPUB or HTML (when printed), indicates that the rest of the current page should be blank. for formats that do not, extra margins will be inserted. does not create a new section * [*page rule] ([`\^-^]): inserts a page break for formats that support them, and a horizontal rule for formats that do not. does not create a new section. comprised of any number of horizontal rule characters surrounded by a pair of carets (e.g. [`^-^] [`^_^] [`^----^] [`^__--^] [`^┈┈┈┈┈^]) * [*table cells] ([`+ |]): see [>ex.tab table examples]. * [*equations] ([`=]): block-level equations can be inserted with the [`=] sequence * [*cross-references] ([`=>] [`⇒]): inserts a block-level link. uses the same syntax as span links ([`⇒[$ident] [$styled-text]]). can be followed by a caption to add a longer descriptive text. especially useful for gemtext output. ident can be omitted to cross-reference, for example, a physical book. * [*empty lines] (that is, lines consisting of nothing but whitespace) constitute a [!break], which terminates multiline objects that do not have a dedicated termination sequence, for example lists and asides. ##onspans styled text most blocks contain a sequence of spans. these spans are produced by interpreting a stream of [*styled-text] following the control sequence. styled-text is a sequence of codepoints potentially interspersed with escapes. an escape is formed by an open square bracket [`\[] followed by a [*span control sequence], and arguments for that sequence like more styled-text. escapes can be nested. * strong {obj *|styled-text}: causes its text to stand out from the narrative, generally rendered as bold or a brighter color. * emphatic {obj !|styled-text}: indicates that its text should be spoken with emphasis, generally rendered as italics |
| > > |
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
* [`$[$macro] [$arg1]|[$arg2]|[$argn]…] invokes a block-level macro with the supplied arguments, and can be followed by a property override definition list the same way embed and resource lines can. note that while both [`$[$id]] and [`&[$id]] can be used to instantiate resources of type [`text/x.cortav], there is a critical difference: [`$[$id]] renders out the sub-document separately each time it is named, allowing for parameter expansion and for context variables to be overridden for each invocation. by contrast, [`&[$id]] can only insert copies of the same render; no parameters can be passed and context variables will be expanded to their value at the time the resource was defined. ** [`$mymacro arg 1|arg 2|arg 3] * [*horizontal rule] ([`\---]): inserts a horizontal rule or other context break; does not end the section. must be followed by newline. underlines can also be used in place of dashes ([`___], [`-_-], [`__-__-__] etc), as can horizontal unicode box drawing characters ([`─ ━ ┈] etc). * [*page break] ([`\^^]): for formats that support pagination, like EPUB or HTML (when printed), indicates that the rest of the current page should be blank. for formats that do not, extra margins will be inserted. does not create a new section * [*page rule] ([`\^-^]): inserts a page break for formats that support them, and a horizontal rule for formats that do not. does not create a new section. comprised of any number of horizontal rule characters surrounded by a pair of carets (e.g. [`^-^] [`^_^] [`^----^] [`^__--^] [`^┈┈┈┈┈^]) * [*table cells] ([`+ |]): see [>ex.tab table examples]. * [*equations] ([`=]): block-level equations can be inserted with the [`=] sequence * [*cross-references] ([`=>] [`⇒]): inserts a block-level link. has two forms for the sake of gemtext compatibility. [$styled-text] is a descriptive text of the destination. especially useful for menus and gemtext output. ** the cortav syntax is [`=>[$ident] [$styled-text]], where [$ident] is an identifier; links to the same destination as [` \[>[$ident] [$styled-text]\]] would ** the compatibility syntax is [`=> [$uri] [$styled-text]] (note the space before [$uri]!). instead of taking an identifier for an object in the document, it directly accepts a URI. note that this is not formally equivalent to gemtext's link syntax, which also allows paths in place of URIs; [`cortav] does not. the gemtext line ["=> /somewhere] would need to be expressed as ["=> file:/somewhere], and ["=> /somewhere?key=val] as ["http:/somewhere?key=val] (or ["gemini:/somewhere?key=val], if the result is to be served over a gemini server). * [*empty lines] (that is, lines consisting of nothing but whitespace) constitute a [!break], which terminates multiline objects that do not have a dedicated termination sequence, for example lists and asides. ##onspans styled text most blocks contain a sequence of spans. these spans are produced by interpreting a stream of [*styled-text] following the control sequence. styled-text is a sequence of codepoints potentially interspersed with escapes. an escape is formed by an open square bracket [`\[] followed by a [*span control sequence], and arguments for that sequence like more styled-text. escapes can be nested. * strong {obj *|styled-text}: causes its text to stand out from the narrative, generally rendered as bold or a brighter color. * emphatic {obj !|styled-text}: indicates that its text should be spoken with emphasis, generally rendered as italics |
Modified cortav.lua from [08a1a9b804] to [c03c132cce].
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
....
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
|
flush() buf.header = c == '+' elseif c == ':' then local lst = l:sub(p.byte-#c,p.byte-#c) local nxt = l:sub(p.next.byte,p.next.byte) if lst == '|' or lst == '+' and l:sub(p.byte-2,p.byte-2) ~= '\\' then buf.align = 'left' elseif nxt == '|' or nxt == '|' then if buf.align == 'left' then buf.align = 'center' else buf.align = 'right' end else buf.str = buf.str .. c ................................................................................ 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}; |
|
|
|
>
|
>
|
|
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
....
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
|
flush() buf.header = c == '+' elseif c == ':' then local lst = l:sub(p.byte-#c,p.byte-#c) local nxt = l:sub(p.next.byte,p.next.byte) if lst == '|' or lst == '+' and l:sub(p.byte-2,p.byte-2) ~= '\\' then buf.align = 'left' elseif nxt == '|' or nxt == '+' then if buf.align == 'left' then buf.align = 'center' else buf.align = 'right' end else buf.str = buf.str .. c ................................................................................ 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+1) local k, uri, txt = r:match('^(%s*)([^%s]*)%s*(.*)$') return { kind = 'link'; uri = (k~='') and ss.uri(uri) or nil; ref = (k=='') and uri or nil; spans = ct.parse_span(txt, c); } end) end ct.ctlseqs = { {seq = '.', fn = insert_paragraph}; {seq = '¶', fn = insert_paragraph}; |
Modified render/html.lua from [78b49cf252] to [8ab06e9795].
401 402 403 404 405 406 407 408 409 410 411 412 413 414 ... 447 448 449 450 451 452 453 454 455 456 457 458 459 460 ... 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 ... 773 774 775 776 777 778 779 780 781 782 783 784 785 786 ... 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 |
h1 { page-break-before: always; } h1,h2,h3,h4,h5,h6 { page-break-after: avoid; } ]]; } local stylesNeeded = { flags = {}; order = {}; } local function addStyle(sty) ................................................................................ local renderJob = doc:job('render_html', nil, render_state_handle) doc.stage.job = renderJob; local runhook = function(h, ...) return renderJob:hook(h, render_state_handle, ...) end local tagproc do local html_open = function(t,attrs) if attrs then return t .. ss.reduce(function(a,b) return a..b end, '', ss.map(function(v,k) if v == true ................................................................................ end function span_renderers.raw(v,b,s) return htmlSpan(v.spans, b, s) end function span_renderers.link(sp,b,s) local dest_o, _, dest_s = b.origin:ref(sp.ref) local href if dest_o == nil then -- link is to the section itself href = '#' .. getSafeID(dest_s) else -- if sp.addr then href = sp.addr else if type(dest_o) == 'table' then href = '#' .. getSafeID(dest_o) else -- URI in reference local uri = ss.uri(dest_o) if uri.class[1] == 'file' or uri.class[1] == 'asset' then if uri.namespace == 'localhost' then -- emit an actual file url href = 'file://' .. uri:construct('path','frag') elseif uri.namespace == nil then -- this is gonna be tricky. first we establish the location -- of the CWD/asset base relative to the output file (if any; -- assume equivalent otherwise) then express the difference -- as a directory prefix. -- jk tho for now we just emit the path+frag sadlol TODO href = uri:construct('path','frag') else b.origin:fail('file: URI namespace must be empty or “localhost” for HTML links; others are not meaningful (offending URI: “%s”)', dest_o) end elseif uri:canfetch() == 'http' then local sc = 'http' if uri.class[1] == 'https' or uri.class[2] == 'tls' then sc = 'https' end if uri.namespace == nil and uri.auth == nil and uri.svc == nil then -- omit the scheme so we can use a relative path href = uri:construct('path','query','frag') else uri.class = {sc} href = tostring(uri) end else href = tostring(uri) end end end return tag('a',{href=href},next(sp.spans) and htmlSpan(sp.spans,b,s) or href) end span_renderers['line-break'] = function(sp,b,s) return elt('br') end function span_renderers.macro(m,b,s) ................................................................................ else -- handle other uses of labels here end end; ['list-item'] = function(b,s) return tag('li', nil, sr.htmlSpan(b.spans, b, s), b) end; table = function(b,s) local tb = {} for i, r in ipairs(b.rows) do local row = {} for i, c in ipairs(r) do table.insert(row, tag(c.header and 'th' or 'td', {align=c.align}, sr.htmlSpan(c.spans, b))) ................................................................................ function block_renderers.embed(b,s) local obj if b.rsrc then obj = b.rsrc else obj = b.origin:ref(b.ref) end local function htmlURI(u) local family = u:canfetch() if family == 'file' or (family == 'http' and u.namespace == nil) then -- TODO asset: return u.path else return tostring(u) end end local function uriForSource(s) if s.mode == 'link' or s.mode == 'auto' then return htmlURI(s.uri) elseif s.mode == 'embed' then local mime = s.mime:clone() mime.opts = {} return string.format('data:%s;base64,%s', mime, ss.str.b64e(s.raw)) |
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | > > > > > > > > > > > > < < < < < < < < < < |
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 ... 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 ... 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 ... 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 ... 925 926 927 928 929 930 931 932 933 934 935 936 937 938 |
h1 { page-break-before: always; } h1,h2,h3,h4,h5,h6 { page-break-after: avoid; } ]]; linkBlock = [[ a[href].link { position: relative; display: block; padding: .5em; padding-right: 1.5em; border: 1px solid @tone(0.2 30); background: @tone(0.05 30); font-size: 1.1em; margin: 0 1em; text-decoration: none; color: @tone(0.8 30); } a[href].link + a[href].link { margin-top: -1px; } a[href].link:hover { border-color: @tone(0.3 30); background: @tone(0.2 30); color: @tone(0.95 30); } a[href].link:hover + a[href].link { margin-top: 0; border-top: none; } a[href].link::after { display: block; position: absolute; right: .5em; content: "→"; top: 50%; margin-left: 1em; font-size: 1.8em; transform: translateY(-50%); color: @tone(0.3 30); } a[href].link:hover::after { color: @tone(0.7 30); } ]]; } local stylesNeeded = { flags = {}; order = {}; } local function addStyle(sty) ................................................................................ local renderJob = doc:job('render_html', nil, render_state_handle) doc.stage.job = renderJob; local runhook = function(h, ...) return renderJob:hook(h, render_state_handle, ...) end local function htmlURI(uri) local family = uri:canfetch() if family == 'file' then if uri.namespace == 'localhost' then -- emit an actual file url return 'file://' .. uri:construct('path','frag') elseif uri.namespace == nil then -- this is gonna be tricky. first we establish the location -- of the CWD/asset base relative to the output file (if any; -- assume equivalent otherwise) then express the difference -- as a directory prefix. -- jk tho for now we just emit the path+frag sadlol TODO if uri.path == nil and uri.frag then -- file:#sec links to #sec within the current document return uri:part 'frag' else return uri:construct('path','frag') end else b.origin:fail('file: URI namespace must be empty or “localhost” for HTML links; others are not meaningful (offending URI: “%s”)', uri.raw) end elseif family == 'http' then local sc = 'http' if uri.class[1] == 'https' or uri.class[2] == 'tls' then sc = 'https' end if uri.namespace == nil and uri.auth == nil and uri.svc == nil then -- omit the scheme so we can use a relative path return uri:construct('path','query','frag') else uri.class = {sc} return tostring(uri) end else return tostring(uri) end end local function idLink(id,b) local dest_o, _, dest_s = b.origin:ref(id) if dest_o == nil then -- link is to the section itself return '#' .. getSafeID(dest_s) else if type(dest_o) == 'table' then return '#' .. getSafeID(dest_o) else -- URI in reference return htmlURI(ss.uri(dest_o)) end end end local tagproc do local html_open = function(t,attrs) if attrs then return t .. ss.reduce(function(a,b) return a..b end, '', ss.map(function(v,k) if v == true ................................................................................ end function span_renderers.raw(v,b,s) return htmlSpan(v.spans, b, s) end function span_renderers.link(sp,b,s) local href = idLink(sp.ref,b) return tag('a',{href=href}, next(sp.spans) and htmlSpan(sp.spans,b,s) or href) end span_renderers['line-break'] = function(sp,b,s) return elt('br') end function span_renderers.macro(m,b,s) ................................................................................ else -- handle other uses of labels here end end; ['list-item'] = function(b,s) return tag('li', nil, sr.htmlSpan(b.spans, b, s), b) end; link = function(b,s) addStyle 'linkBlock' local href if b.uri then href = htmlURI(b.uri) elseif b.ref then href = idLink(b.ref, b) end local sp = sr.htmlSpan(b.spans, b, s) return tag('div', {}, catenate{tag('a',{class='link', href=href},sp)}) end; table = function(b,s) local tb = {} for i, r in ipairs(b.rows) do local row = {} for i, c in ipairs(r) do table.insert(row, tag(c.header and 'th' or 'td', {align=c.align}, sr.htmlSpan(c.spans, b))) ................................................................................ function block_renderers.embed(b,s) local obj if b.rsrc then obj = b.rsrc else obj = b.origin:ref(b.ref) end local function uriForSource(s) if s.mode == 'link' or s.mode == 'auto' then return htmlURI(s.uri) elseif s.mode == 'embed' then local mime = s.mime:clone() mime.opts = {} return string.format('data:%s;base64,%s', mime, ss.str.b64e(s.raw)) |