cortav  Check-in [a8358d587d]

Overview
Comment:better footnotes, update syntax def, delete broken old span
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: a8358d587d209c4a3690fcda3edda8bb2f6ed24aab1581cf04c097dfc9489e90
User & Date: lexi on 2022-09-10 13:31:23
Other Links: manifest | tags
Context
2022-09-10
17:28
add remote fetch via curl, shim native bindings check-in: ee086767f3 user: lexi tags: trunk
13:31
better footnotes, update syntax def, delete broken old span check-in: a8358d587d user: lexi tags: trunk
03:16
defuck xref structure check-in: 0ef3dd0c77 user: lexi tags: trunk
Changes

Modified cortav.ct from [fb6019b4ad] to [fe93ee725b].

   104    104   *[*definition] ([^def-ex tab]): a line [^def-tab-enc beginning with a tab] is a multipurpose metadata syntax. the tab may be followed by an identifier, a colon, and a value string, in which case it opens a new definition; alternatively, a second tab character turns the line into a [*definition continuation], adding the remaining characters as a new line to the definition value on the previous line.  when a new definition is opened on a line immediately following certain kinds of objects, such as resources, embeds, or multiline macro expansions, it attaches key-value metadata to that object. when a definition is not preceded by such an object, an independent [*reference] is created instad.
   105    105   ** a [*reference] is a general mechanism for out-of-line metadata, and references are used in many different ways -- e.g. to specify link destinations, footnote contents, abbreviations, or macro bodies. to ensure that a definition is interpreted as a reference, rather than as metadata for an object, precede it with a blank line.
   106    106   	def-tab-enc: in encodings without tab characters, a definition is opened by a line beginning with two blanks, and continued by a line beginning with four blanks.
   107    107   	def-ex: [*open a new reference]: [`[!\\t][$key]: [$value]]
   108    108   		[*continue a reference]: [`[!\\t\\t][$value]]
   109    109   * [*quotation] ([`<]): a line of the form [`<[$name]> [$quote]] denotes an utterance by [$name].
   110    110   * [*blockquote] ([`>]): alternate blockquote syntax. can be nested by repeating the [`>] character.
   111         -* [*subtitle/caption] ([`\--]): attaches a subtitle to the previous header, or caption to the previous object
          111  +* [*subtitle/caption] (["--]): attaches a subtitle to the previous header, or caption to the previous object
   112    112   * [*embed] ([`&]): embeds a referenced object. can be used to show images or repeat previously defined objects like lists or tables, optionally with a caption. an embed line can be followed immediately by a sequence of [*definitions] in the same way that resource definitions can, to override resource properties on a per-instance basis. note that only presentation-related properties like [$desc] can be meaningful overridden, as embed does not trigger a re-render of the parse tree; if you want to override e.g. context variables, use a multiline macro invocation instead.
   113    113   ** [`&[$image]] embeds an image or other block-level object. [!image] can be a reference with a url or file path, or it can be an embed section (e.g. for SVG files)
   114    114   ***[`&myimg All that remained of the unfortunate blood magic pageant contestants and audience (police photo)]
   115    115   ** [`&-[$ident] [$styled-text]] embeds a closed disclosure element containing the text of the named object (a nonprinting section or cortav resource should usually be used to store the content; it can also name an image or video, of course). in interactive outputs, this will display as a block which can be clicked on to view the full contents of the referenced object [$ident]; if [$styled-text] is present, it overrides the title of the section you are embedding (if any). in static outputs, the disclosure object will display as an enclosed box with [$styled-text] as the title text
   116    116   *** [`&-ex-a Prosecution Exhibit A (GRAPHIC CONTENT)]
   117    117   ** [`&+[$section] [$styled-text]] is like the above, but the disclosure element is open by default
   118    118   * [`$[$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.
   119    119   ** [`$mymacro arg 1|arg 2|arg 3]
   120         -* [*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).
   121         -* [*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
   122         -* [*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. [`^-^] [`^_^] [`^----^] [`^__--^] [`^┈┈┈┈┈^])
          120  +* [*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).
          121  +* [*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
          122  +* [*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. [`^-^] [`^_^] [`^----^] [`^__--^] [`^┈┈┈┈┈^])
   123    123   * [*table cells] ([`+ |]): see [>ex.tab table examples].
   124    124   * [*equations] ([`=]): block-level equations can be inserted with the [`=] sequence
   125    125   * [*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.
   126    126   ** the cortav syntax is [`=>[$ident] [$styled-text]], where [$ident] is an identifier; links to the same destination as [` \[>[$ident] [$styled-text]\]] would
   127    127   ** 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).
   128    128   * [*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.
   129    129   
................................................................................
   134    134   * emphatic {obj !|styled-text}: indicates that its text should be spoken with emphasis, generally rendered as italics
   135    135   * custom style {span .|id|[$styled-text]}: applies a specially defined font style. for example, if you have defined [`caution] to mean "demibold italic underline", cortav will try to apply the proper weight and styling within the constraints of the current font to the span [$styled-text]. see the [>fonts-sty fonts section] for more information about this mechanism.
   136    136   * literal {obj `|styled-text}: indicates that its text is a reference to a literal sequence of characters or other discrete token. generally rendered in monospace
   137    137   * variable {obj $|styled-text}: indicates to the reader that its text is a placeholder, rather than a literal representation. generally rendered in italic monospace, ideally of a different color
   138    138   * underline {obj _|styled-text}: underlines the text. use sparingly on text intended for webpages -- underlined text  [!is] distinct from links, but underlining non-links is still a violation of convention.
   139    139   * strikeout {obj ~|styled-text}: indicates that its text should be struck through or otherwise indicated for deletion
   140    140   * insertion {obj +|styled-text}: indicates that its text should be indicated as a new addition to the text body.
   141         -** consider using a macro definition [`\edit: [~[#1]][+[#2]]] to save typing if you are doing editing work
          141  +** consider using a macro definition ["edit: [~[#1]][+[#2]]] to save typing if you are doing editing work
   142    142   * link [` \[>[$ref] [$styled-text]\]]: produces a hyperlink or cross-reference denoted by [$ref], which may be either a URL specified with a reference or the name of an object like an image or section elsewhere in the document. the unicode characters [`→] and [`🔗] can also be used instead of [`>] to denote a link.
   143         -* footnote {span ^|ref|[$styled-text]}: annotates the text with a defined footnote. in interactive output media [`\[^citations.qtheo Quantum Theosophy: A Neophyte's Catechism\]] will insert a link with the text [`Quantum Theosophy: A Neophyte's Catechism] that, when clicked, causes a footnote to pop up on the screen. for static output media, the text will simply have a superscript integer after it denoting where the footnote is to be found.
          143  +* footnote {span ^|[$ref]|[$styled-text]}: annotates the text with a defined footnote. in interactive output media ["[^citations.qtheo Quantum Theosophy: A Neophyte's Catechism]] will insert a link with the text [`Quantum Theosophy: A Neophyte's Catechism] that, when clicked, causes a footnote to pop up on the screen. for static output media, the text will simply have a superscript integer after it denoting where the footnote is to be found. [$ref] can be the ID of a reference, in which case the reference value is parsed as [`cortav] markup to form the body of the footnote; it can also be the ID of a resource, which can be of any MIME type compatible with the current renderer, as as [`text/x.cortav], [`text/plain], or [`image/png].
   144    144   * superscript {obj '|[$styled-text]}
   145    145   * subscript {obj ,|[$styled-text]}
   146    146   * raw {obj \\ |[$raw-text]}: causes all characters within to be interpreted literally, without expansion. the only special characters are square brackets, which must have a matching closing bracket, and backslashes.
   147    147   * raw literal [` \["[$raw-text]\]]: shorthand for a raw inside a literal, that is ["[`[\\…]]]
   148    148   * macro [` \{[$name] [$arguments]}]: invokes a [>ex.mac macro] inline, specified with a reference. if the result of macro expansion contains newlines, they will be treated as line breaks, rather than paragraph breaks as they would be in a multiline context.
   149    149   * argument {obj #|var}: in macros only, inserts the [$var]-th argument. otherwise, inserts a context variable provided by the renderer.
   150    150   * raw argument {obj ##|var}: like above, but does not evaluate [$var].

Modified cortav.lua from [c03c132cce] to [648cf1722d].

   738    738   		}
   739    739   	end
   740    740   	ct.spanctls = {
   741    741   		{seq = '!', parse = formatter 'emph'};
   742    742   		{seq = '*', parse = formatter 'strong'};
   743    743   		{seq = '~', parse = formatter 'strike'};
   744    744   		{seq = '+', parse = formatter 'insert'};
   745         -		{seq = '"', parse = rawcode};
   746         -		-- deprecated
   747         -			{seq = '`\\', parse = rawcode};
   748         -			{seq = '\\\\', parse = rawcode};
   749    745   		{seq = '\\', parse = function(s, c) -- raw
   750    746   			return {
   751    747   				kind = 'raw';
   752    748   				spans = {s};
   753    749   				origin = c:clone();
   754    750   			}
   755    751   		end};
   756    752   		{seq = '`', parse = formatter 'literal'};
          753  +		{seq = '"', parse = rawcode};
   757    754   		{seq = '$', parse = formatter 'variable'};
   758         -		{seq = '^', parse = function(s,c) --footnotes
   759         -			local r, t = s:match '^([^%s]+)%s*(.-)$'
          755  +		{seq = '^', parse = function(s, c)
          756  +		-- TODO support for footnote sections
          757  +			local fn, t = s:match '^([^%s]+)%s*(.-)$'
   760    758   			return {
   761    759   				kind = 'footnote';
   762         -				ref = r;
   763         -				spans = ct.parse_span(t, c);
          760  +				spans = (t and t~='') and ct.parse_span(t, c) or {};
          761  +				ref = fn;
   764    762   				origin = c:clone();
   765    763   			}
   766         -		-- TODO support for footnote sections
   767    764   		end};
   768    765   		{seq = '=', parse = function(s,c) --math mode
   769    766   			local tx = {
   770    767   				['%*'] = '×';
   771    768   				['/'] = '÷';
   772    769   			}
   773    770   			for k,v in pairs(tx) do s = s:gsub(k,v) end
................................................................................
   789    786   		end};
   790    787   		{seq = '&', parse = function(s, c)
   791    788   			local r, t = s:match '^([^%s]+)%s*(.-)$'
   792    789   			return {
   793    790   				kind = 'deref';
   794    791   				spans = (t and t ~= "") and ct.parse_span(t, c) or {};
   795    792   				ref = r;
   796         -				origin = c:clone();
   797         -			}
   798         -		end};
   799         -		{seq = '^', parse = function(s, c)
   800         -			local fn, t = s:match '^([^%s]+)%s*(.-)$'
   801         -			return {
   802         -				kind = 'footnote';
   803         -				spans = (t and t~='') and ct.parse_span(t, c) or {};
   804         -				ref = fn;
   805    793   				origin = c:clone();
   806    794   			}
   807    795   		end};
   808    796   		{seq = '>', parse = insert_link};
   809    797   		{seq = '→', parse = insert_link};
   810    798   		{seq = '🔗', parse = insert_link};
   811    799   		{seq = '##', parse = insert_var_ref(true)};

Modified desk/cortav.xml from [dc63948a6a] to [a3601c692e].

     6      6     ? Kate/kwrite-compatible syntax definition for the cortav markup format
     7      7     > ln cortav.xml $HOME/.local/share/org.kde.syntax-highlighting/syntax/
     8      8     ! NOTE: the Kate syntax engine cannot capture all the syntactic properties
     9      9             of Cortav. we do the best we can, but note the following important
    10     10             discrepancies:
    11     11   
    12     12               1) the inline resource syntax allows a wide range of complex
    13         -               brackets pairs such as <!--:[ ]:--!>; the Kate syntax only
           13  +               brackets pairs such as <!-:[ ]:-!>; the Kate syntax only
    14     14                  accounts for the pair { }
    15     15   -->
    16         -<language name='Cortav' version='1' kateversion='2.4' section='Markup' extensions='*.ct'>
           16  +<language name='Cortav'
           17  +			 section='Markup'
           18  +			 extensions='*.ct'
           19  +			 version='1'
           20  +			 kateversion='2.4'>
    17     21   	<highlighting>
    18     22   		<list name='extension-directives'>
    19     23   			<item>uses</item>
    20     24   			<item>needs</item>
    21     25   			<item>inhibits</item>
    22     26   		</list>
    23     27   		<list name='meta-directives'>
................................................................................
   158    162   			</context>
   159    163   
   160    164   			<context name='span-del' attribute='Deleted Text' lineEndContext='#pop'>
   161    165   				<IncludeRules context='span'/>
   162    166   			</context>
   163    167   
   164    168   			<context name='span-cue' attribute='Span Cue' lineEndContext='#pop' fallthroughContext="error">
   165         -				<StringDetect attribute='Span Cue' String='`\' context='#pop!flat-span' />
   166    169   				<StringDetect attribute='Span Cue' String='"' context='#pop!flat-span' />
   167    170   
   168    171   				<DetectChar   attribute='Span Cue' char='!' context='#pop!span-emph' />
   169    172   				<DetectChar   attribute='Span Cue' char='*' context='#pop!span-strong' />
   170    173   				<DetectChar   attribute='Span Cue' char='~' context='#pop!span-del' />
   171    174   
   172    175   				<AnyChar      attribute='Span Cue' String='`$+🔒' context='#pop!span' />

Modified ext/toc.lua from [38c6bac1b2] to [823e0f174d].

   221    221   					local sec = secptr.ref
   222    222   					if sec.heading_node then -- does this section have a label?
   223    223   						local ent = tag('li',nil,
   224    224   							 catenate{tag('a', {href='#'..getSafeID(sec)},
   225    225   								sr.htmlSpan(sec.heading_node.spans, sec.heading_node, sec))})
   226    226   						if secptr.depth > #stack then
   227    227   							local n = {tag = 'ol', attrs={}, nodes={ent}}
          228  +							if top() then
   228    229   							table.insert(top().nodes[#top().nodes].nodes, n)
          230  +							end
   229    231   							table.insert(stack, n)
   230    232   						else
   231    233   							if secptr.depth < #stack then
   232    234   								for j=#stack,secptr.depth+1,-1 do stack[j] = nil end
   233    235   							end
          236  +							if top() then
   234    237   							table.insert(top().nodes, ent)
   235    238   						end
          239  +						end
   236    240   
   237    241   						-- now we need to assemble a list of items within the
   238    242   						-- section worthy of an entry on their own. currently
   239    243   						-- this is only anchors created with %toc mark|name
   240    244   						local innerlinks = {}
   241    245   						local noteworthy = { anchor = true }
   242    246   						for j, block in pairs(sec.blocks) do

Modified render/html.lua from [4a60de4c59] to [e235e554e3].

    55     55   			li {
    56     56   				padding: 0.1em 0;
    57     57   			}
    58     58   		]];
    59     59   		list_ordered = [[]];
    60     60   		list_unordered = [[]];
    61     61   		footnote = [[
           62  +			@media screen {
           63  +				a[href].fnref {
           64  +					text-decoration-style: dashed;
           65  +					color: @tone(0.7 45);
           66  +					text-decoration-color: @tone/0.4(0.7 45);
           67  +				}
           68  +				a[href]:hover.fnref {
           69  +					color: @tone(0.9 45);
           70  +					text-decoration-color: @tone/0.7(0.7 45);
           71  +				}
           72  +			}
    62     73   			aside.footnote {
    63     74   				font-family: 90%;
    64     75   				grid-template-columns: 1em 1fr min-content;
    65     76   				grid-template-rows: 1fr min-content;
    66     77   				position: fixed;
    67     78   				padding: 1em;
    68     79   				background: @tone(0.03);
................................................................................
    98    109   					backdrop-filter: blur(0px);
    99    110   				}
   100    111   				aside.footnote:target ~ #cover {
   101    112   					opacity: 100%;
   102    113   					pointer-events: all;
   103    114   					backdrop-filter: blur(5px);
   104    115   				}
          116  +			}
          117  +			@media screen and (max-width: calc(@width + 20em)) {
          118  +				aside.footnote {
          119  +					left: 1em;
          120  +					right: 1em;
          121  +				}
   105    122   			}
   106    123   			@media print {
   107    124   				aside.footnote {
   108    125   					display: grid;
   109    126   					position: relative;
   110    127   				}
   111    128   				aside.footnote:first-of-type {
................................................................................
   145    162   				}
   146    163   			}
   147    164   			aside.footnote > div.number {
   148    165   				text-align:right;
   149    166   				grid-row: 1/2;
   150    167   				grid-column: 1/2;
   151    168   			}
   152         -			aside.footnote > div.text {
          169  +			aside.footnote > .text {
   153    170   				grid-row: 1/2;
   154    171   				grid-column: 2/4;
   155    172   				padding-left: 1em;
   156    173   				overflow-y: auto;
          174  +				margin-top: 0;
   157    175   			}
   158         -			aside.footnote > div.text > p:first-child {
          176  +			aside.footnote > .text > :first-child {
   159    177   				margin-top: 0;
   160    178   			}
   161    179   		]];
   162    180   		header = [[
   163    181   			body { padding: 0 2.5em !important }
   164    182   			h1,h2,h3,h4,h5,h6 { border-bottom: 1px solid @tone(0.7); }
   165    183   			h1 { font-size: 200%; border-bottom-style: double !important; border-bottom-width: 3px !important; margin: 0em -1em; }
................................................................................
   761    779   		end
   762    780   		function span_renderers.footnote(f,b,s)
   763    781   			local linkattr = {}
   764    782   			if opts.epub then
   765    783   				linkattr['epub:type'] = 'noteref'
   766    784   			else
   767    785   				addStyle 'footnote'
          786  +				linkattr.class = 'fnref'
   768    787   			end
   769    788   			local source, sid, ssec = b.origin:ref(f.ref)
   770    789   			local cnc = getSafeID(ssec) .. ' ' .. sid
   771    790   			local fn
   772    791   			if footnotes[cnc] then
   773    792   				fn = footnotes[cnc]
   774    793   			else
................................................................................
  1189   1208   	do local fnsorted = {}
  1190   1209   		for _, fn in pairs(footnotes) do
  1191   1210   			fnsorted[fn.num] = fn
  1192   1211   		end
  1193   1212   
  1194   1213   		for _, fn in ipairs(fnsorted) do
  1195   1214   			local tag = tagproc.toIR.tag
  1196         -			local body = {nodes={}}
         1215  +			local body
         1216  +			if type(fn.source) == 'table' then
         1217  +				if fn.source.kind == 'resource' then
         1218  +					local fake_embed = {
         1219  +						kind = 'embed';
         1220  +						rsrc = fn.source;
         1221  +						origin = fn.origin;
         1222  +						mode = 'inline';
         1223  +					}
         1224  +					local rendered = astproc.toIR.block_renderers.embed(
         1225  +						fake_embed, fn.origin.sec
         1226  +					)
         1227  +					if not rendered then
         1228  +						fn.origin:fail('unacceptable resource mime type “%s” for footnote target “%s”', fn.source.mime, fn.source.id or '(anonymous)')
         1229  +					end
         1230  +					body = rendered
         1231  +				else
         1232  +					fn.origin:fail('footnote span links to block “%s” of unacceptable kind “%s”', fn.source.kind)
         1233  +				end
         1234  +			else
         1235  +				body = {tag='div',nodes={}}
  1197   1236   			local ftir = {}
  1198   1237   			for l in fn.source:gmatch('([^\n]*)') do
  1199   1238   				ct.parse_line(l, fn.origin, ftir)
  1200   1239   			end
  1201   1240   			renderBlocks(ftir,body)
         1241  +			end
  1202   1242   			local fattr = {id=fn.id}
  1203   1243   			if opts.epub then
  1204   1244   				---UUUUUUGHHH
  1205   1245   				local npfx = string.format('(%u) ', fn.num)
  1206   1246   				if next(body.nodes) then
  1207   1247   					local n = body.nodes[1]
  1208   1248   					repeat
................................................................................
  1215   1255   						else
  1216   1256   							n.nodes[1] = {tag='p',attrs={},nodes={npfx}}
  1217   1257   							break
  1218   1258   						end
  1219   1259   					until false
  1220   1260   
  1221   1261   				else
         1262  +					if body.tag == 'div' then
  1222   1263   					body.nodes[1] = {tag='p',attrs={},nodes={npfx}}
         1264  +					elseif body.tag == 'pre' then
         1265  +						body.nodes[1] = npfx .. body.nodes[1]
         1266  +					else
         1267  +						body = {tag='div', nodes = {npfx, body}}
         1268  +					end
  1223   1269   				end
  1224   1270   				fattr['epub:type'] = 'footnote'
  1225   1271   			else
  1226   1272   				fattr.class = 'footnote'
  1227   1273   			end
         1274  +			body.attrs = body.attrs or {}
         1275  +			body.attrs.class = 'text'
  1228   1276   			local note = tag('aside', fattr, opts.epub and body.nodes or {
  1229   1277   				tag('div',{class='number'}, tostring(fn.num)),
  1230         -				tag('div',{class='text'}, body.nodes),
         1278  +				body,
         1279  +-- 				tag('div',{class='text'}, body.nodes),
  1231   1280   				tag('a',{href='#0'},'⤫')
  1232   1281   			})
  1233   1282   			table.insert(ir, note)
  1234   1283   		end
  1235   1284   	end
  1236   1285   	if next(footnotes) and not opts.epub then
  1237   1286   		table.insert(ir, tagproc.toIR.tag('div',{id='cover'},''))