Overview
Comment: | add weak modes that can be overridden by pragma |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA3-256: |
c15ffd5fef56f39abd50a1e181a3bc84 |
User & Date: | lexi on 2021-12-20 14:10:16 |
Other Links: | manifest | tags |
Context
2021-12-21
| ||
01:22 | tidy up repo, add freedesktop file format stuff and viewer handler script check-in: 3b2fea2df1 user: lexi tags: trunk | |
2021-12-20
| ||
14:10 | add weak modes that can be overridden by pragma check-in: c15ffd5fef user: lexi tags: trunk | |
00:40 | tweak headers check-in: bdd8d569a9 user: lexi tags: trunk | |
Changes
Modified cli.lua from [23a0968fc6] to [2240e68512].
22 22 for k, v in pairs(list) do 23 23 if fn(k,v) then new[k] = v end 24 24 end 25 25 return new 26 26 end 27 27 28 28 local function 29 -main(input, output, log, mode, vars) 29 +main(input, output, log, mode, suggestions, vars) 30 30 local doc = ct.parse(input.stream, input.src, mode) 31 31 input.stream:close() 32 32 if mode['parse:show-tree'] then 33 33 log:write(dump(doc)) 34 34 end 35 + 36 + -- the document has now had a chance to give its say; if it hasn't specified 37 + -- any modes of its own, we now merge in the 'weak modes' (suggestions) 38 + for k,v in pairs(suggestions) do 39 + if not mode[k] then mode[k] = v end 40 + end 35 41 36 42 if not mode['render:format'] then 37 43 error 'what output format should i translate the input to?' 38 44 end 39 45 if mode['render:format'] == 'none' then return 0 end 40 46 if not ct.render[mode['render:format']] then 41 47 ct.exns.unimpl('output format “%s” unsupported', mode['render:format']):throw() ................................................................................ 51 57 52 58 -- this is kind of gross but the context object belongs to the parser, 53 59 -- not the renderer, so that's not a suitable place for this information 54 60 doc.stage = { 55 61 kind = 'render'; 56 62 format = mode['render:format']; 57 63 mode = mode; 64 + suggestions = suggestions; 58 65 } 59 66 60 67 output:write(ct.render[mode['render:format']](doc, render_opts)) 61 68 return 0 62 69 end 63 70 64 71 local inp,outp,log = io.stdin, io.stdout, io.stderr 65 72 66 73 local function entry_cli() 67 - local mode, vars, input = default_mode, {}, { 74 + local suggestions, vars, input = default_mode, {}, { 68 75 stream = inp; 69 76 src = { 70 77 file = '(stdin)'; 71 78 } 72 79 } 80 + 81 + local mode = {} 73 82 74 83 local optnparams = function(o) 75 84 local param_opts = { 76 85 out = 1; 77 86 log = 1; 78 87 define = 2; -- key value 88 + 79 89 ['mode-set'] = 1; 80 90 ['mode-clear'] = 1; 81 91 mode = 2; 92 + 93 + ['mode-set-weak'] = 1; 94 + ['mode-clear-weak'] = 1; 95 + ['mode-weak'] = 2; 82 96 } 83 97 return param_opts[o] or 0 84 98 end 85 99 86 100 local optmap = { 87 101 o = 'out'; 88 102 l = 'log'; 89 103 d = 'define'; 90 104 V = 'version'; 91 105 h = 'help'; 92 - y = 'mode-set', n = 'mode-clear'; 93 - m = 'mode'; 106 + y = 'mode-set', Y = 'mode-set-weak'; 107 + n = 'mode-clear', N = 'mode-clear-weak'; 108 + m = 'mode', M = 'mode-weak'; 94 109 } 95 110 96 111 local checkmodekey = function(key) 97 112 if not key:match '[^:]+:.+' then 98 113 ct.exns.cli('invalid mode key %s', key):throw() 99 114 end 100 115 return key ................................................................................ 117 132 ct.exns.cli 'cannot define variable in restricted namespace':throw() 118 133 end 119 134 vars[key] = value 120 135 end; 121 136 mode = function(key,value) mode[checkmodekey(key)] = value end; 122 137 ['mode-set'] = function(key) mode[checkmodekey(key)] = true end; 123 138 ['mode-clear'] = function(key) mode[checkmodekey(key)] = false end; 139 + 140 + ['mode-weak'] = function(key,value) suggestions[checkmodekey(key)] = value end; 141 + ['mode-set-weak'] = function(key) suggestions[checkmodekey(key)] = true end; 142 + ['mode-clear-weak'] = function(key) suggestions[checkmodekey(key)] = false end; 124 143 } 125 144 126 145 local args = {} 127 146 local keepParsing = true 128 147 do local i = 1 while i <= #arg do local v = arg[i] 129 148 local execLongOpt = function(longopt) 130 149 if not onswitch[longopt] then ................................................................................ 172 191 if args[1] and args[1] ~= '' then 173 192 local file = io.open(arg[1], "rb") 174 193 if not file then error('unable to load file ' .. args[1]) end 175 194 input.stream = file 176 195 input.src.file = args[1] 177 196 end 178 197 179 - return main(input, outp, log, mode, vars) 198 + return main(input, outp, log, mode, suggestions, vars) 180 199 end 181 200 182 201 local ok, e = pcall(entry_cli) 183 202 -- local ok, e = true, entry_cli() 184 203 if not ok then 185 204 local str = 'translation failure' 186 205 if ss.exn.is(e) then
Modified cortav.ct from [96194d0b88] to [b7a7fbb952].
89 89 90 90 on systems with environment variables, these may be accessed as context variables by prefixing their name with [$env.]. 91 91 92 92 different renderers may provide context in different ways, such as from command line options or a context file. any predefined variables should carry an appropriate prefix to prevent conflation. 93 93 94 94 ## directives 95 95 d: [$%[*[##1]]] 96 -* {d format} gives a hint on how the document should be formatted. the first hint that is understood will be applied; all others will be discarded. standard hints include: 97 -** essay 98 -** narrative 99 -** screenplay: uses asides to denote actions, quotes for dialogue 100 -** stageplay: uses asides to denote actions, quotes for dialogue 101 -** manual 102 -** glossary 103 -** news 104 96 * {d author} encodes document authorship 105 97 * {d cols} specifies the number of columns the next object should be rendered with 106 98 * {d include} transcludes another file 107 99 * {d quote} transcludes another file, without expanding the text except for paragraphs 108 100 * {d embed}, where possible, embeds another file as an object within the current one. in HTML this could be accomplished with e.g. an iframe. 109 101 * {d expand} causes the next object (usually a code block) to be fully expanded when it would otherwise not be 102 +* {d pragma} supplies semantic data about author intent, the kind of information document contains and hints about how it should be displayed to the user. think of them like offhand remarks to the renderer -- there's no guarantee that it'll pay any attention, but if it does, your document will look better. pragmas have no scope; they affect the entire document. the pragma function exists primarily as a means to allow parameters that would normally need to be specified on e.g. the command line to be encoded in the document instead in a way that multiple implementations can understand. a few standard pragmas are defined 103 +** {d pragma layout} gives a hint on how the document should be layed out. the first hint that is understood will be applied; all others will be discarded. standard hints include: 104 +*** essay 105 +*** narrative 106 +*** screenplay: uses asides to denote actions, quotes for dialogue 107 +*** stageplay: uses asides to denote actions, quotes for dialogue 108 +*** manual 109 +*** glossary 110 +*** news 111 +** {d pragma accent} specifies an accent hue (in degrees around the color wheel) for renderers which support colorized output 112 +** {d pragma accent-spread} is a factor that controls the "spread" of hues used in the document. if 0, only the accent color will be used; if larger, other hues will be used in addition to the primary accent color. 113 +** {d pragma dark-on-light on|off} controls whether the color scheme used should be light-on-dark or dark-on-light 114 +** {d pragma page-width} indicates how wide the pages should be 110 115 111 116 ##ex examples 112 117 113 118 ~~~ blockquotes #bq [cortav] ~~~ 114 119 the following excerpts of text were recovered from a partially erased hard drive found in the Hawthorne manor in the weeks after the Incident. context is unknown. 115 120 116 121 #> ................................................................................ 247 252 248 253 ~~~ 249 254 $ luac -s -o stdin2html.lc $cortav_repo/{sirsem,cortav,ext/toc}.lua stdin2html.lua 250 255 ~~~ 251 256 252 257 and can then be operated with the command [$lua stdin2html.lc], with no further need for the cortav repository files. note that the order of the [$luac] command is important! [$sirsem.lua] must come first, followed by [$cortav.lua], followed by any extensions. your driver script (i.e. the script with the entry point into the application) should always come last. 253 258 259 +### building custom tools 260 +generally, most existing file-format conversion tools (cmark, pandoc, and so on) have a crucial limitation: they hardcode specific assumptions like document structure. this means that the files they output are generally not suitable as-is for the users' purposes, and require further munging, usually by hateful shell or perl scripts. some tools do provide libraries end users to use as a basis for designing their own tools, but these are often limited, and in any case the user ends up having to write their own (non-standard) driver. it's no surprise that very few people end up doing this. 261 + 262 +[$cortav.lua]'s design lends itself to a more elegant solution. one can of course write their own driver using [$cortav] as a library, but most of the time when you're compiling document sources, you just want a binary you can run from the command line or a makefile. with [$cortav.lua], you can extend its capabilities easily while keeping the same driver. 263 + 264 +in the [$cortav] spec, extensions are mostly intended to give different implementations the ability to offer extra capabilities, but the reference implementation uses an extension architecture that makes it easy to write and add your own. for each type of new behavior you want to implement, just create a new extension and list it on the make command line: 265 + 266 +~~~ 267 +$ nvim ~/dev/my-cortav-exts/imperial-edict.lua 268 +$ make cortav extens+=$HOME/dev/my-cortav-exts/*.lua 269 +~~~ 270 + 271 +the cortav binary this produces will have all the extra capabilities you personally need, without any need to fork [$cortav.lua] itself or even touch the repository. 272 + 273 +there's no reason [$cortav.lua] shouldn't be able to load extensions at runtime as well; i just haven't implemented this behavior yet. it probably would only take a few extra lines of code tho. 274 + 275 +i will eventually document the extension API, but for now, look at [$ext/toc.lua] for a simple example of how to register an extension. 276 + 254 277 ## command line driver 255 278 the [$cortav.lua] command line driver can be run from the repository directory with the command [$lua ./cli.lua], or by first compiling it into a bytecode form that links in all its dependencies. this is the preferred method for installation, as it produces a self-contained executable which loads more quickly, but running the driver in script form may be desirable for development or debugging. 256 279 257 280 the repository contains a GNU makefile to automate compilation of the reference implementation on unix-like OSes. simply run [$$ make cortav] or [$$ gmake cortav] from the repository root to produce a self-contained bytecode executable that can be installed anywhere on your filesystem, with no dependencies other than the lua interpreter. 258 281 259 282 ! note that the makefile strips debugging symbols to save space, so running [$cli.lua] directly as a script may be helpful if you encounter errors and need stacktraces or other debugging information. 260 283 ................................................................................ 270 293 $ cortav readme.ct 271 294 # reads from readme.ct, writes to standard output 272 295 ~~~ 273 296 274 297 ### switches 275 298 [$cortav.lua] offers various switches to control its behavior. 276 299 + long + short + function + 277 -| [$--out [!file]] :|:[$-o]:| sets the output file (default stdout) | 278 -| [$--log [!file]] :|:[$-l]:| sets the log file (default stderr) | 279 -| [$--define [!var] [!val]]:|:[$-d]:| sets the context variable [$var] to [$val] | 280 -| [$--mode-set [!mode]] :|:[$-y]:| activates the [>refimpl-mode mode] with ID [!mode] 281 -| [$--mode-clear [!mode]] :|:[$-n]:| disables the mode with ID [!mode] | 282 -| [$--mode [!id] [!val]] :|:[$-m]:| configures mode [!id] with the value [!val] | 283 -| [$--help] :|:[$-h]:| display online help | 284 -| [$--version] :|:[$-V]:| display the interpreter version | 300 +| [$--out [!file]] :|:[$-o]:| sets the output file (default stdout) | 301 +| [$--log [!file]] :|:[$-l]:| sets the log file (default stderr) | 302 +| [$--define [!var] [!val]] :|:[$-d]:| sets the context variable [$var] to [$val] | 303 +| [$--mode-set [!mode]] :|:[$-y]:| activates the [>refimpl-mode mode] with ID [!mode] 304 +| [$--mode-clear [!mode]] :|:[$-n]:| disables the mode with ID [!mode] | 305 +| [$--mode [!id] [!val]] :|:[$-m]:| configures mode [!id] with the value [!val] | 306 +| [$--mode-set-weak [!mode]] :|:[$-Y]:| activates the [>refimpl-mode mode] with ID [!mode] if the source file does not specify otherwise 307 +| [$--mode-clear-weak [!mode]] :|:[$-N]:| disables the mode with ID [!mode] if the source file does not specify otherwise 308 +| [$--mode-weak [!id] [!val]] :|:[$-M]:| configures mode [!id] with the value [!val] if the source file does not specify otherwise 309 +| [$--help] :|:[$-h]:| display online help | 310 +| [$--version] :|:[$-V]:| display the interpreter version | 285 311 286 312 ###refimpl-mode modes 287 313 most of [$cortav.lua]'s implementation-specific behavior is controlled by use of [!modes]. these are namespaced options which may have a boolean, string, or numeric value. boolean modes are set with the [$-y] [$-n] flags; other modes use the [$-m] flags. 288 314 289 315 most modes are defined by the renderer backend. the following modes affect the behavior of the frontend: 290 316 291 317 + ID + type + effect ................................................................................ 315 341 -m render:format html \ 316 342 -m html:width 40em \ 317 343 -m html:accent 80 \ 318 344 -m html:hue-spread 35 \ 319 345 -y html:dark-on-light # could also be written as: 320 346 $ cortav readme.ct -ommmmy readme.html render:format html html:width 40em html:accent 80 html:hue-spread 35 html:dark-on-light 321 347 ~~~ 348 + 349 +## further directions 350 + 351 +### LCH support 352 +right now, the use of color in the HTML renderer is very unsatisfactory. the accent mechanism operates on the basis of the CSS HSL function, which is not perceptually uniform; different hues will present different mixes of brightness and some (yellows?) may be ugly or unreadable. 353 + 354 +the ideal solution would be to simply switch to using LCH based colors. unfortunately, only Safari actually supports the LCH color function right now, and it's unlikely (unless Lea Verou and her husband manage to work a miracle) that Colors Level 4 is going to be implemented very widely any time soon. 355 + 356 +this leaves us in an awkward position. we can of course do the math ourselves, working in LCH to implement the internal [$@tone] macro, and then "converting" these colors to HSL. unfortunately, you can't actually convert from LCH to HSL; it's like converting from pounds to kilograms. LCH can represent any color the human visual system can perceive; sRGB can't, and CSS HSL is implemented in sRGB. however, we could at least approximate something that would allow for perceptually uniform brightness, which would be an improvement, and this is probably the direction to go in, unless a miracle occurs and [$lch()] or [$color()] pop up in Blink. 357 + 358 +it may be possible to do a more reasonable job of handling colors in the postscript and TeX outputs. unsure about SVG but i assume it suffers the same problems HTML/CSS do. does groff even support color??
Modified cortav.lua from [19e0c83304] to [cf364bf145].
209 209 fortran = { color = 0xff779a }; 210 210 python = { color = 0xffd277 }; 211 211 python = { color = 0xcdd6ff }; 212 212 } 213 213 214 214 local stylesets = { 215 215 header = [[ 216 - h1 { font-size: 200%; border-bottom-style: double !important; border-bottom-width: 3px !important; } 217 - h2 { font-size: 130%; } 218 - h3 { font-size: 110%; } 219 - h4 { font-size: 100%; font-weight: normal; } 216 + h1,h2,h3,h4,h5,h6 { border-bottom: 1px solid @tone(0.7); } 217 + h1 { font-size: 200%; border-bottom-style: double !important; border-bottom-width: 3px !important; margin: 0em -1em; } 218 + h2 { font-size: 130%; margin: 0em -0.7em; } 219 + h3 { font-size: 110%; margin: 0em -0.5em; } 220 + h4 { font-size: 100%; font-weight: normal; margin: 0em -0.2em; } 220 221 h5 { font-size: 90%; font-weight: normal; } 221 222 h6 { font-size: 80%; font-weight: normal; } 222 223 h3, h4, h5, h6 { border-bottom-style: dotted !important; } 224 + h1,h2,h3,h4,h5,h6 { 225 + margin-top: 0; 226 + margin-bottom: 0; 227 + } 228 + :is(h1,h2,h3,h4,h5,h6) + p { 229 + margin-top: 0.4em; 230 + } 231 + 232 + ]]; 233 + headingAnchors = [[ 234 + :is(h1,h2,h3,h4,h5,h6) > a[href].anchor { 235 + text-decoration: none; 236 + font-size: 1.2em; 237 + padding: 0.3em; 238 + opacity: 0%; 239 + transition: 0.3s; 240 + font-weight: 100; 241 + } 242 + :is(h1,h2,h3,h4,h5,h6):hover > a[href].anchor { 243 + opacity: 50%; 244 + } 245 + :is(h1,h2,h3,h4,h5,h6) > a[href].anchor:hover { 246 + opacity: 100%; 247 + } 248 + 249 + ]] .. -- this is necessary to avoid the sections jumping around 250 + -- when focus changes from one to another 251 + [[ section { 252 + border: 1px solid transparent; 253 + } 254 + 255 + section:target { 256 + margin-left: -2em; 257 + margin-right: -2em; 258 + padding: 0 2em; 259 + background: @tone(0.04); 260 + border: 1px dotted @tone(0.3); 261 + } 262 + 263 + section:target > :is(h1,h2,h3,h4,h5,h6) { 264 + 265 + } 266 + ]]; 267 + paragraph = [[ 268 + p { 269 + margin: 0.7em 0; 270 + } 271 + section { 272 + margin: 1.2em 0; 273 + } 274 + section:first-child { margin-top: 0; } 223 275 ]]; 224 276 accent = [[ 225 277 body { background: @bg; color: @fg } 226 278 a[href] { 227 279 color: @tone(0.7 30); 228 280 text-decoration-color: @tone/0.4(0.7 30); 229 281 } 230 282 a[href]:hover { 231 283 color: @tone(0.9 30); 232 284 text-decoration-color: @tone/0.7(0.7 30); 233 285 } 234 - h1,h2,h3,h4,h5,h6 { border-bottom: 1px solid @tone(0.7); } 235 286 h1 { color: @tone(2); } 236 287 h2 { color: @tone(1.5); } 237 288 h3 { color: @tone(1.2); } 238 289 h4 { color: @tone(1); } 239 290 h5,h6 { color: @tone(0.8); } 240 291 ]]; 241 292 code = [[ ................................................................................ 442 493 end 443 494 end 444 495 return lst 445 496 end 446 497 447 498 local block_renderers = { 448 499 paragraph = function(b,s) 500 + stylesNeeded.paragraph = true; 449 501 return tag('p', nil, sr.htmlSpan(b.spans, b, s), b) 450 502 end; 451 503 directive = function(b,s) 452 504 -- deal with renderer directives 453 505 local _, cmd, args = b.words(2) 454 506 if cmd == 'page-title' then 455 507 if not opts.title then doctitle = args end ................................................................................ 544 596 local irs 545 597 if sec.kind == 'ordinary' then 546 598 if #(sec.blocks) > 0 then 547 599 irs = {tag='section',attrs={id = getSafeID(sec)},nodes={}} 548 600 549 601 for i, block in ipairs(sec.blocks) do 550 602 local rd = irBlockRdrs[block.kind](block,sec) 551 - if rd then table.insert(irs.nodes, rd) end 603 + if rd then 604 + if opts['heading-anchors'] and block == sec.heading_node then 605 + stylesNeeded.headingAnchors = true 606 + table.insert(rd.nodes, ' ') 607 + table.insert(rd.nodes, { 608 + tag = 'a'; 609 + attrs = {href = '#' .. irs.attrs.id, class='anchor'}; 610 + nodes = {type(opts['heading-anchors'])=='string' and opts['heading-anchors'] or '§'}; 611 + }) 612 + end 613 + table.insert(irs.nodes, rd) 614 + end 552 615 end 553 616 end 554 617 elseif sec.kind == 'blockquote' then 555 618 elseif sec.kind == 'listing' then 556 619 elseif sec.kind == 'embed' then 557 620 end 558 621 if irs then table.insert(ir, irs) end
Modified makefile from [5cfd42ea5f] to [5a68812ac4].
1 1 lua != which lua 2 2 luac != which luac 3 3 4 -extens ?= $(patsubst ext/%.lua,%,$(wildcard ext/*.lua)) 5 -extens_srcs = $(patsubst %,ext/%.lua,$(extens)) 4 +extens = $(wildcard ext/*.lua) 5 +extens_names ?= $(basename $(notdir $(extens))) 6 6 7 -cortav: sirsem.lua cortav.lua $(extens_srcs) cli.lua 7 +cortav: sirsem.lua cortav.lua $(extens) cli.lua 8 + @echo ' » building with extensions $(extens_names)' 8 9 echo '#!$(lua)' > $@ 9 10 luac -o - $^ >> $@ 10 11 chmod +x $@ 11 12 12 13 cortav.html: cortav.ct cortav 13 14 ./cortav $< -o $@ -m render:format html -y html:fossil-uv 14 15 15 16 .PHONY: syncdoc 16 17 syncdoc: cortav.html 17 18 fossil uv add $< 18 19 fossil uv sync