code wiki / _hdl_build / nx_md_lib.nx

nx_md_lib.nx source

↩ module page · 146 lines · 10217 B

1// nx_md_lib.nx -- the Markdown -> HTML renderer as a LIBRARY (rule 15 DRY): shared by the nx_md CLI organ 2// (Linux/dispatch path) and the NishiOS app layer (nx_nishios_docview), where there is no process fork yet so 3// renderers LINK instead of exec. Pure functions, no main. license_tier: ORIGINAL 4import "nx_syscalls.nx" 5 6func md_cat(dst: *u8, off: i64, s: *u8) -> i64 { var i: i64=0; while s[i]!=(0 as u8) { dst[off+i]=s[i]; i=i+1 } return off+i } 7 8// emit one char HTML-escaped (& < >) into dst; return new offset 9func md_esc1(dst: *u8, o: i64, c: i64) -> i64 { 10 if c==38 { return md_cat(dst,o,"&amp;" as *u8) } 11 if c==60 { return md_cat(dst,o,"&lt;" as *u8) } 12 if c==62 { return md_cat(dst,o,"&gt;" as *u8) } 13 dst[o]=c as u8; return o+1 14} 15 16// inline span: **bold** *italic* `code` [text](url); escape the rest. src[s,e) -> dst. 17func md_inline(dst: *u8, off: i64, src: *u8, s: i64, e: i64) -> i64 { 18 var o: i64=off; var i: i64=s 19 var bold: i64=0; var ital: i64=0; var code: i64=0 20 while i<e { 21 let c: i64=src[i] as i64 22 var handled: i64=0 23 // inline code toggles first (inside code, no other markup) 24 if handled==0 { if c==96 { if code==0 { o=md_cat(dst,o,"<code>" as *u8); code=1 } else { o=md_cat(dst,o,"</code>" as *u8); code=0 } i=i+1; handled=1 } } 25 if handled==0 { if code==1 { o=md_esc1(dst,o,c); i=i+1; handled=1 } } 26 // **bold** 27 if handled==0 { if c==42 { if i+1<e { if src[i+1]==(42 as u8) { 28 if bold==0 { o=md_cat(dst,o,"<b>" as *u8); bold=1 } else { o=md_cat(dst,o,"</b>" as *u8); bold=0 } 29 i=i+2; handled=1 30 } } } } 31 // *italic* 32 if handled==0 { if c==42 { if ital==0 { o=md_cat(dst,o,"<i>" as *u8); ital=1 } else { o=md_cat(dst,o,"</i>" as *u8); ital=0 } i=i+1; handled=1 } } 33 // [text](url) 34 if handled==0 { if c==91 { 35 var t: i64=i+1; var te: i64=0-1 36 while t<e { if src[t]==(93 as u8) { te=t; t=e } else { t=t+1 } } 37 var ok: i64=0; var ue: i64=0-1; var us: i64=0 38 if te>=0 { if te+1<e { if src[te+1]==(40 as u8) { 39 us=te+2; var u: i64=us 40 while u<e { if src[u]==(41 as u8) { ue=u; u=e } else { u=u+1 } } 41 if ue>=0 { ok=1 } 42 } } } 43 if ok==1 { 44 o=md_cat(dst,o,"<a href=\"" as *u8) 45 var k: i64=us; while k<ue { o=md_esc1(dst,o,src[k] as i64); k=k+1 } 46 o=md_cat(dst,o,"\">" as *u8) 47 k=i+1; while k<te { o=md_esc1(dst,o,src[k] as i64); k=k+1 } 48 o=md_cat(dst,o,"</a>" as *u8) 49 i=ue+1; handled=1 50 } 51 } } 52 if handled==0 { o=md_esc1(dst,o,c); i=i+1 } 53 } 54 if code==1 { o=md_cat(dst,o,"</code>" as *u8) } 55 if ital==1 { o=md_cat(dst,o,"</i>" as *u8) } 56 if bold==1 { o=md_cat(dst,o,"</b>" as *u8) } 57 return o 58} 59 60// is src[s,e) all '-' / '*' / '_' (a horizontal rule), min 3 61func md_all_dash(src: *u8, s: i64, e: i64) -> i64 { 62 if e-s<3 { return 0 } 63 var i: i64=s; var ok: i64=1 64 while i<e { let c: i64=src[i] as i64; if c!=45 { if c!=42 { if c!=95 { ok=0 } } } i=i+1 } 65 return ok 66} 67 68// small int emit into a buffer (for <hN>) 69func md_num_to(dst: *u8, off: i64, v: i64) -> i64 { dst[off]=(48+v) as u8; return off+1 } 70 71func md_to_html(src: *u8, slen: i64, out: *u8, dlurl: *u8) -> i64 { 72 var o: i64=0 73 o=md_cat(out,o,"<!DOCTYPE html>\n<html lang=en><head><meta charset=utf-8><meta name=viewport content=\"width=device-width,initial-scale=1\"><title>Document Preview - Nishi Office</title><style>\nbody{font:16px/1.65 system-ui,-apple-system,Segoe UI,Roboto,sans-serif;margin:0;background:#eef1f5;color:#1a1d21}\nheader{background:#0f172a;color:#fff;padding:16px 24px}\nheader h1{margin:0;font-size:17px;font-weight:600}\n.doc{max-width:820px;margin:22px auto;background:#fff;border:1px solid #e2e8f0;border-radius:12px;padding:34px 46px;box-shadow:0 1px 4px rgba(15,23,42,.08)}\n.doc h1{font-size:26px;margin:18px 0 10px}.doc h2{font-size:21px;margin:20px 0 8px;border-bottom:1px solid #eef2f7;padding-bottom:5px}.doc h3{font-size:18px;margin:16px 0 6px}\n.doc p{margin:10px 0}.doc ul,.doc ol{margin:10px 0 10px 8px;padding-left:22px}.doc li{margin:4px 0}\n.doc code{background:#f1f5f9;border-radius:4px;padding:1px 5px;font-family:ui-monospace,Consolas,monospace;font-size:14px}\n.doc pre{background:#0f172a;color:#e2e8f0;border-radius:8px;padding:14px 16px;overflow-x:auto}.doc pre code{background:none;color:inherit;padding:0}\n.doc blockquote{border-left:4px solid #cbd5e1;margin:12px 0;padding:4px 16px;color:#475569}\n.doc hr{border:none;border-top:1px solid #e2e8f0;margin:20px 0}\n.doc a{color:#2563eb}.doc table{border-collapse:collapse;margin:14px 0}.doc td,.doc th{border:1px solid #cbd5e1;padding:6px 11px}\n.bar{max-width:820px;margin:0 auto 6px;text-align:right}.dl{display:inline-block;padding:9px 18px;background:#2563eb;color:#fff;border-radius:8px;text-decoration:none;font-size:14px;font-weight:500}\nfooter{max-width:820px;margin:14px auto 46px;color:#64748b;font-size:13px;text-align:center}\n</style></head>\n<body>\n<header><h1>&#128196; Document Preview &mdash; Nishi Office</h1></header>\n<div class=doc>\n" as *u8) 74 var i: i64=0 75 var infence: i64=0 76 var inlist: i64=0 // 0 none, 1 ul, 2 ol 77 while i<slen { 78 var e: i64=i 79 while e<slen { if src[e]==(10 as u8) { break } e=e+1 } 80 var end: i64=e 81 if end>i { if src[end-1]==(13 as u8) { end=end-1 } } 82 // fenced code 83 var isfence: i64=0 84 if end>=i+3 { if src[i]==(96 as u8) { if src[i+1]==(96 as u8) { if src[i+2]==(96 as u8) { isfence=1 } } } } 85 if isfence==1 { 86 if infence==0 { if inlist!=0 { if inlist==1 { o=md_cat(out,o,"</ul>\n" as *u8) } else { o=md_cat(out,o,"</ol>\n" as *u8) } inlist=0 } o=md_cat(out,o,"<pre><code>" as *u8); infence=1 } else { o=md_cat(out,o,"</code></pre>\n" as *u8); infence=0 } 87 i=e+1 88 } else { 89 if infence==1 { 90 var k: i64=i; while k<end { o=md_esc1(out,o,src[k] as i64); k=k+1 } o=md_cat(out,o,"\n" as *u8) 91 i=e+1 92 } else { 93 // count leading '#' 94 var hn: i64=0; var hh: i64=0 95 while hh<end-i { if src[i+hh]==(35 as u8) { hn=hn+1; hh=hh+1 } else { hh=end-i } } 96 var isheading: i64=0 97 if hn>=1 { if hn<=6 { if i+hn<end { if src[i+hn]==(32 as u8) { isheading=1 } } } } 98 // list markers 99 var isul: i64=0 100 if end>=i+2 { if src[i+1]==(32 as u8) { if src[i]==(45 as u8) { isul=1 } else { if src[i]==(42 as u8) { isul=1 } else { if src[i]==(43 as u8) { isul=1 } } } } } 101 var isol: i64=0 102 if end>=i+3 { var d: i64=i; var nd: i64=0; while d<end { let dc: i64=src[d] as i64; if dc>=48 { if dc<=57 { nd=nd+1; d=d+1 } else { d=end } } else { d=end } } 103 if nd>=1 { if i+nd<end { if src[i+nd]==(46 as u8) { if i+nd+1<end { if src[i+nd+1]==(32 as u8) { isol=1 } } } } } } 104 var isquote: i64=0 105 if end>i { if src[i]==(62 as u8) { isquote=1 } } 106 var ishr: i64=md_all_dash(src, i, end) 107 if end<=i { 108 // blank line: close any open list 109 if inlist!=0 { if inlist==1 { o=md_cat(out,o,"</ul>\n" as *u8) } else { o=md_cat(out,o,"</ol>\n" as *u8) } inlist=0 } 110 } else { 111 if isheading==1 { 112 if inlist!=0 { if inlist==1 { o=md_cat(out,o,"</ul>\n" as *u8) } else { o=md_cat(out,o,"</ol>\n" as *u8) } inlist=0 } 113 o=md_cat(out,o,"<h" as *u8); o=md_num_to(out,o,hn); o=md_cat(out,o,">" as *u8) 114 o=md_inline(out,o,src,i+hn+1,end) 115 o=md_cat(out,o,"</h" as *u8); o=md_num_to(out,o,hn); o=md_cat(out,o,">\n" as *u8) 116 } else { if ishr==1 { 117 if inlist!=0 { if inlist==1 { o=md_cat(out,o,"</ul>\n" as *u8) } else { o=md_cat(out,o,"</ol>\n" as *u8) } inlist=0 } 118 o=md_cat(out,o,"<hr>\n" as *u8) 119 } else { if isul==1 { 120 if inlist!=1 { if inlist==2 { o=md_cat(out,o,"</ol>\n" as *u8) } o=md_cat(out,o,"<ul>\n" as *u8); inlist=1 } 121 o=md_cat(out,o,"<li>" as *u8); o=md_inline(out,o,src,i+2,end); o=md_cat(out,o,"</li>\n" as *u8) 122 } else { if isol==1 { 123 if inlist!=2 { if inlist==1 { o=md_cat(out,o,"</ul>\n" as *u8) } o=md_cat(out,o,"<ol>\n" as *u8); inlist=2 } 124 var ms: i64=i; while ms<end { if src[ms]==(32 as u8) { ms=ms+1; break } ms=ms+1 } 125 o=md_cat(out,o,"<li>" as *u8); o=md_inline(out,o,src,ms,end); o=md_cat(out,o,"</li>\n" as *u8) 126 } else { if isquote==1 { 127 if inlist!=0 { if inlist==1 { o=md_cat(out,o,"</ul>\n" as *u8) } else { o=md_cat(out,o,"</ol>\n" as *u8) } inlist=0 } 128 var qs: i64=i+1; if qs<end { if src[qs]==(32 as u8) { qs=qs+1 } } 129 o=md_cat(out,o,"<blockquote>" as *u8); o=md_inline(out,o,src,qs,end); o=md_cat(out,o,"</blockquote>\n" as *u8) 130 } else { 131 if inlist!=0 { if inlist==1 { o=md_cat(out,o,"</ul>\n" as *u8) } else { o=md_cat(out,o,"</ol>\n" as *u8) } inlist=0 } 132 o=md_cat(out,o,"<p>" as *u8); o=md_inline(out,o,src,i,end); o=md_cat(out,o,"</p>\n" as *u8) 133 } } } } } 134 } 135 i=e+1 136 } 137 } 138 } 139 if infence==1 { o=md_cat(out,o,"</code></pre>\n" as *u8) } 140 if inlist==1 { o=md_cat(out,o,"</ul>\n" as *u8) } 141 if inlist==2 { o=md_cat(out,o,"</ol>\n" as *u8) } 142 o=md_cat(out,o,"</div>\n" as *u8) 143 if (dlurl as i64)!=0 { o=md_cat(out,o,"<div class=bar><a class=dl href=\"" as *u8); o=md_cat(out,o,dlurl); o=md_cat(out,o,"\">&#11015; Download source</a></div>\n" as *u8) } 144 o=md_cat(out,o,"<footer>Rendered natively from Markdown by <b>nx_md</b> &middot; viewable inline in the Nishi browser</footer>\n</body></html>\n" as *u8) 145 return o 146}