This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name BQN highlighting | |
// @namespace https://github.com/dzaima | |
// @version 0.2 | |
// @description Syntax highlighting for BQN code on GitHub | |
// @author dzaima | |
// @match https://github.com/* | |
// @grant none | |
// ==/UserScript== | |
let dark = true; | |
let prevLink = ""; | |
document.body.classList.add(dark? 'dt' : 'lt'); | |
function upd() { | |
'use strict'; | |
let newLink = window.location.href; | |
if (newLink.includes('#')) newLink = newLink.substring(0, newLink.indexOf('#')); | |
if (newLink==prevLink || !newLink.endsWith(".bqn")) return; | |
if (document.getElementsByClassName("highlight tab-size js-file-line-container").length==0) return; | |
prevLink = newLink; | |
let fnames = document.getElementsByClassName('final-path'); | |
if (fnames.length!=1 || !fnames[0].innerText.endsWith('.bqn')) return; | |
let els = [...document.getElementsByClassName("blob-code blob-code-inner js-file-line")]; | |
if (els.length) { | |
let table = document.querySelector("table.highlight.js-file-line-container"); | |
let tableP = table.parentElement; | |
table.remove(); | |
els.forEach(c=>{ | |
let txt = [...c.innerText]; | |
c.innerHTML = colorCode(txt, parseBQN(txt), 'B'); | |
}); | |
tableP.appendChild(table); | |
} | |
} | |
setTimeout(() => { | |
setInterval(upd, 1000); | |
upd(); | |
}, 1000); | |
// copy-pasted from paste: | |
let htmlMap = {}; | |
function html(str) { | |
let res = ""; | |
for (let chr of str) { | |
if (chr>='0'&chr<='9' | chr>='a'&chr<='z' | chr>='A'&chr<='Z' | chr==' ' | chr=='_') res+= chr; | |
else if (chr=='\n') res+= '<br>'; | |
else { | |
let m = htmlMap[chr]; | |
if (!m) m = htmlMap[chr] = new Option(chr).innerHTML; | |
res+= m; | |
} | |
} | |
return res; | |
} | |
function colorCode(str, cols, prefix) { | |
const wrap = (sub,col) => `<span class=${prefix+col}>${html(sub)}</span>`; | |
let code = ""; | |
let pcol = cols[0]; | |
let li = 0; | |
for (let i = 0; i < str.length; i++) { | |
let ncol = cols[i]; | |
if (ncol && pcol!=ncol) { | |
code+= wrap(str.slice(li,i), pcol); | |
li = i; | |
pcol = ncol; | |
} | |
} | |
if (pcol) code+= wrap(str.slice(li), pcol); | |
return code; | |
} | |
function parseBQN(str) { | |
str = str; | |
const regC = '0'; | |
const fnsC = '1'; let fns = "!+-×÷⋆*√⌊⌈∧∨¬|=≠≤<>≥≡≢⊣⊢⥊∾≍⋈↑↓↕⌽⍉/⍋⍒⊏⊑⊐⊒∊⍷⊔«»⍎⍕"; | |
const mopC = '2'; let mop = "`˜˘¨⁼⌜´˝˙"; | |
const dopC = '3'; const dop = "∘⊸⟜○⌾⎉⚇⍟⊘◶⎊"; | |
const namC = '4'; const nam = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_"; | |
const digC = '5'; const dig = "0123456789π∞"; const digS = dig+"¯."; const digM = "eEiI"; | |
const arrC = '6'; const arr = "·⍬‿⦃⦄⟨⟩[]@"; | |
const dfnC = '7'; const dfn = [..."𝕨𝕩𝔽𝔾𝕎𝕏𝕗𝕘𝕣ℝ𝕤𝕊{}:"]; // double-strucks are 2-byters | |
const strC = '8'; // '' "" | |
const dmdC = 'D'; const dmd = "←↩,⋄→⇐"; | |
const comC = 'C'; // # | |
if (!window.BQNStyle) { | |
const s = document.createElement("style"); | |
s.id = "BQNStyle"; | |
s.innerText=` | |
body.dt .B${regC} { color: #D2D2D2; } body.lt .B${regC} { color: #000000; } | |
body.dt .B${namC} { color: #D2D2D2; } body.lt .B${namC} { color: #000000; } | |
body.dt .B${comC} { color: #898989; } body.lt .B${comC} { color: #6A737D; } | |
body.dt .B${digC} { color: #ff6E6E; } body.lt .B${digC} { color: #005CC5; } | |
body.dt .B${arrC} { color: #DD99FF; } body.lt .B${arrC} { color: #005CC5; } | |
body.dt .B${dmdC} { color: #FFFF00; } body.lt .B${dmdC} { color: #0000FF; } | |
body.dt .B${strC} { color: #6A9FFB; } body.lt .B${strC} { color: #032F62; } | |
body.dt .B${fnsC} { color: #57d657; } body.lt .B${fnsC} { color: #D73A49; } | |
body.dt .B${mopC} { color: #EB60DB; } body.lt .B${mopC} { color: #ED5F00; } | |
body.dt .B${dopC} { color: #FFDD66; } body.lt .B${dopC} { color: #C82C00; } | |
body.dt .B${dfnC} { color: #AA77BB; } body.lt .B${dfnC} { color: #A906D4; } | |
`; | |
document.body.appendChild(s); | |
} | |
const res = new Array(str.length).fill(); | |
res[0] = regC; | |
for (let i = 0; i < str.length; ) { | |
const p = str[i-1]||'\0'; | |
const c = str[i ]; | |
const n = str[i+1]||'\0'; | |
if (digS.includes(c)) { | |
res[i] = digC; i++; | |
while(dig.includes(str[i]) || str[i]=='.' || digM.includes(str[i])&&digS.includes(str[i+1])) i++; | |
continue; | |
} | |
else if (fns.includes(c)) res[i] = fnsC; | |
else if (mop.includes(c)) res[i] = mopC; | |
else if (dop.includes(c)) res[i] = dopC; | |
else if (dfn.includes(c)) res[i] = dfnC; | |
else if (arr.includes(c)) res[i] = arrC; | |
else if (dmd.includes(c)) res[i] = dmdC; | |
else if (nam.includes(c) || c=='•') { | |
let fst = i; | |
if (str[i] == '•') i++; | |
let cs = str[i]; | |
while(nam.includes(str[i]) || dig.includes(str[i])) i++; | |
let ce = str[i-1]; | |
res[fst] = cs=='_'? (ce=='_'? dopC : mopC) : (cs>='A'&&cs<='Z'?fnsC : namC); | |
continue; | |
} | |
else if (c=="'" || c=='"') { | |
res[i] = strC; i++; | |
let q = c; | |
while(str[i] && str[i]!=q && str[i]!='\n') i++; | |
} | |
else if (c=='#') { | |
res[i] = comC; | |
while(str[i] && str[i]!='\n') i++; | |
} | |
else if (!' \n\t'.includes(c)) res[i] = regC; | |
i++; | |
} | |
return res; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello,
Just wanted You to be aware: Based on the above script (thank You for providing this, by the way!) and the already-existing APL support in the project, I have made a pull request to add BQN support to PrismJS (mostly because I want to post BQN code in my blog).