mirror of
https://github.com/alecthomas/chroma.git
synced 2025-11-25 22:32:32 +02:00
chore: add JS formatting with biome
This commit is contained in:
4
Makefile
4
Makefile
@@ -12,6 +12,10 @@ README.md: lexers/*/*.go
|
|||||||
tokentype_string.go: types.go
|
tokentype_string.go: types.go
|
||||||
go generate
|
go generate
|
||||||
|
|
||||||
|
.PHONY: format-js
|
||||||
|
format-js:
|
||||||
|
biome format --write cmd/chromad/static/{index.js,chroma.js}
|
||||||
|
|
||||||
.PHONY: chromad
|
.PHONY: chromad
|
||||||
chromad: build/chromad
|
chromad: build/chromad
|
||||||
|
|
||||||
|
|||||||
1
bin/.biome-1.9.4.pkg
Symbolic link
1
bin/.biome-1.9.4.pkg
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
hermit
|
||||||
6
biome.json
Normal file
6
biome.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://biomejs.dev/schemas/2.0.5/schema.json",
|
||||||
|
"formatter": {
|
||||||
|
"indentStyle": "space"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,72 +1,79 @@
|
|||||||
// chroma.js - TinyGo WASM runtime initialization for Chroma syntax highlighter
|
// chroma.js - TinyGo WASM runtime initialization for Chroma syntax highlighter
|
||||||
|
|
||||||
// Import wasm_exec.js so that it initialises the Go WASM runtime.
|
// Import wasm_exec.js so that it initialises the Go WASM runtime.
|
||||||
import './wasm_exec.js';
|
import "./wasm_exec.js";
|
||||||
|
|
||||||
class ChromaWASM {
|
class ChromaWASM {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.go = null;
|
this.go = null;
|
||||||
this.wasm = null;
|
this.wasm = null;
|
||||||
this.ready = false;
|
this.ready = false;
|
||||||
this.readyPromise = this.init();
|
this.readyPromise = this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
try {
|
||||||
|
// Create a new Go instance
|
||||||
|
this.go = new Go();
|
||||||
|
|
||||||
|
// Load the WASM module
|
||||||
|
const wasmResponse = await fetch("./static/chroma.wasm");
|
||||||
|
if (!wasmResponse.ok) {
|
||||||
|
throw new Error(`Failed to fetch chroma.wasm: ${wasmResponse.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const wasmBytes = await wasmResponse.arrayBuffer();
|
||||||
|
const wasmModule = await WebAssembly.instantiate(
|
||||||
|
wasmBytes,
|
||||||
|
this.go.importObject,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.wasm = wasmModule.instance;
|
||||||
|
|
||||||
|
// Run the Go program
|
||||||
|
this.go.run(this.wasm);
|
||||||
|
|
||||||
|
this.ready = true;
|
||||||
|
console.log("Chroma WASM module initialized successfully");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to initialize Chroma WASM module:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async waitForReady() {
|
||||||
|
await this.readyPromise;
|
||||||
|
if (!this.ready) {
|
||||||
|
throw new Error("Chroma WASM module failed to initialize");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async highlight(source, lexer, formatter, withClasses) {
|
||||||
|
await this.waitForReady();
|
||||||
|
|
||||||
|
if (typeof window.highlight !== "function") {
|
||||||
|
throw new Error("highlight function not available from WASM module");
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
try {
|
||||||
try {
|
return window.highlight(source, lexer, formatter, withClasses);
|
||||||
// Create a new Go instance
|
} catch (error) {
|
||||||
this.go = new Go();
|
console.error("Error calling highlight function:", error);
|
||||||
|
throw error;
|
||||||
// Load the WASM module
|
|
||||||
const wasmResponse = await fetch('./static/chroma.wasm');
|
|
||||||
if (!wasmResponse.ok) {
|
|
||||||
throw new Error(`Failed to fetch chroma.wasm: ${wasmResponse.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const wasmBytes = await wasmResponse.arrayBuffer();
|
|
||||||
const wasmModule = await WebAssembly.instantiate(wasmBytes, this.go.importObject);
|
|
||||||
|
|
||||||
this.wasm = wasmModule.instance;
|
|
||||||
|
|
||||||
// Run the Go program
|
|
||||||
this.go.run(this.wasm);
|
|
||||||
|
|
||||||
this.ready = true;
|
|
||||||
console.log('Chroma WASM module initialized successfully');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to initialize Chroma WASM module:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async waitForReady() {
|
|
||||||
await this.readyPromise;
|
|
||||||
if (!this.ready) {
|
|
||||||
throw new Error('Chroma WASM module failed to initialize');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async highlight(source, lexer, formatter, withClasses) {
|
|
||||||
await this.waitForReady();
|
|
||||||
|
|
||||||
if (typeof window.highlight !== 'function') {
|
|
||||||
throw new Error('highlight function not available from WASM module');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return window.highlight(source, lexer, formatter, withClasses);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error calling highlight function:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function isWasmSupported() {
|
export function isWasmSupported() {
|
||||||
try {
|
try {
|
||||||
if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") {
|
if (
|
||||||
|
typeof WebAssembly === "object" &&
|
||||||
|
typeof WebAssembly.instantiate === "function"
|
||||||
|
) {
|
||||||
// The smallest possible WebAssembly module (magic number + version)
|
// The smallest possible WebAssembly module (magic number + version)
|
||||||
const module = new WebAssembly.Module(Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00));
|
const module = new WebAssembly.Module(
|
||||||
|
Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00),
|
||||||
|
);
|
||||||
if (module instanceof WebAssembly.Module) {
|
if (module instanceof WebAssembly.Module) {
|
||||||
// Try to instantiate the module to ensure it's truly runnable
|
// Try to instantiate the module to ensure it's truly runnable
|
||||||
return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
|
return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
|
||||||
@@ -78,6 +85,5 @@ export function isWasmSupported() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Create global instance, null if WASM is not supported.
|
// Create global instance, null if WASM is not supported.
|
||||||
export const chroma = isWasmSupported() ? new ChromaWASM() : null;
|
export const chroma = isWasmSupported() ? new ChromaWASM() : null;
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ import * as Base64 from "./base64.js";
|
|||||||
import { chroma } from "./chroma.js";
|
import { chroma } from "./chroma.js";
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
var darkMode = (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
var darkMode =
|
||||||
var style = document.createElement('style');
|
window.matchMedia &&
|
||||||
var ref = document.querySelector('script');
|
window.matchMedia("(prefers-color-scheme: dark)").matches;
|
||||||
|
var style = document.createElement("style");
|
||||||
|
var ref = document.querySelector("script");
|
||||||
ref.parentNode.insertBefore(style, ref);
|
ref.parentNode.insertBefore(style, ref);
|
||||||
|
|
||||||
var form = document.getElementById('chroma');
|
var form = document.getElementById("chroma");
|
||||||
var textArea = form.elements["text"];
|
var textArea = form.elements["text"];
|
||||||
var styleSelect = form.elements["style"];
|
var styleSelect = form.elements["style"];
|
||||||
var languageSelect = form.elements["language"];
|
var languageSelect = form.elements["language"];
|
||||||
@@ -16,49 +18,62 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
var output = document.getElementById("output");
|
var output = document.getElementById("output");
|
||||||
var htmlCheckbox = document.getElementById("html");
|
var htmlCheckbox = document.getElementById("html");
|
||||||
|
|
||||||
(document.querySelectorAll('.notification .delete') || []).forEach((el) => {
|
(document.querySelectorAll(".notification .delete") || []).forEach((el) => {
|
||||||
const notification = el.parentNode;
|
const notification = el.parentNode;
|
||||||
el.addEventListener('click', () => {
|
el.addEventListener("click", () => {
|
||||||
notification.parentNode.removeChild(notification);
|
notification.parentNode.removeChild(notification);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
async function renderServer(formData) {
|
async function renderServer(formData) {
|
||||||
return (await fetch("api/render", {
|
return (
|
||||||
method: 'POST',
|
await fetch("api/render", {
|
||||||
mode: 'cors',
|
method: "POST",
|
||||||
cache: 'no-cache',
|
mode: "cors",
|
||||||
credentials: 'same-origin',
|
cache: "no-cache",
|
||||||
headers: {
|
credentials: "same-origin",
|
||||||
'X-CSRF-Token': csrfToken,
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
"X-CSRF-Token": csrfToken,
|
||||||
},
|
"Content-Type": "application/json",
|
||||||
redirect: 'follow',
|
},
|
||||||
referrer: 'no-referrer',
|
redirect: "follow",
|
||||||
body: JSON.stringify(formData),
|
referrer: "no-referrer",
|
||||||
})).json();
|
body: JSON.stringify(formData),
|
||||||
|
})
|
||||||
|
).json();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function renderWasm(formData) {
|
async function renderWasm(formData) {
|
||||||
return await chroma.highlight(
|
return await chroma.highlight(
|
||||||
formData.text,
|
formData.text,
|
||||||
formData.language,
|
formData.language,
|
||||||
formData.style,
|
formData.style,
|
||||||
formData.classes,
|
formData.classes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function render(formData) {
|
async function render(formData) {
|
||||||
return chroma !== null
|
return chroma !== null ? renderWasm(formData) : renderServer(formData);
|
||||||
? renderWasm(formData)
|
|
||||||
: renderServer(formData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// https://stackoverflow.com/a/37697925/7980
|
// https://stackoverflow.com/a/37697925/7980
|
||||||
function handleTab(e) {
|
function handleTab(e) {
|
||||||
var after, before, end, lastNewLine, changeLength, re, replace, selection, start, val;
|
var after,
|
||||||
if ((e.charCode === 9 || e.keyCode === 9) && !e.altKey && !e.ctrlKey && !e.metaKey) {
|
before,
|
||||||
|
end,
|
||||||
|
lastNewLine,
|
||||||
|
changeLength,
|
||||||
|
re,
|
||||||
|
replace,
|
||||||
|
selection,
|
||||||
|
start,
|
||||||
|
val;
|
||||||
|
if (
|
||||||
|
(e.charCode === 9 || e.keyCode === 9) &&
|
||||||
|
!e.altKey &&
|
||||||
|
!e.ctrlKey &&
|
||||||
|
!e.metaKey
|
||||||
|
) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
start = this.selectionStart;
|
start = this.selectionStart;
|
||||||
end = this.selectionEnd;
|
end = this.selectionEnd;
|
||||||
@@ -68,14 +83,14 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
replace = true;
|
replace = true;
|
||||||
if (start !== end) {
|
if (start !== end) {
|
||||||
selection = val.substring(start, end);
|
selection = val.substring(start, end);
|
||||||
if (~selection.indexOf('\n')) {
|
if (~selection.indexOf("\n")) {
|
||||||
replace = false;
|
replace = false;
|
||||||
changeLength = 0;
|
changeLength = 0;
|
||||||
lastNewLine = before.lastIndexOf('\n');
|
lastNewLine = before.lastIndexOf("\n");
|
||||||
if (!~lastNewLine) {
|
if (!~lastNewLine) {
|
||||||
selection = before + selection;
|
selection = before + selection;
|
||||||
changeLength = before.length;
|
changeLength = before.length;
|
||||||
before = '';
|
before = "";
|
||||||
} else {
|
} else {
|
||||||
selection = before.substring(lastNewLine) + selection;
|
selection = before.substring(lastNewLine) + selection;
|
||||||
changeLength = before.length - lastNewLine;
|
changeLength = before.length - lastNewLine;
|
||||||
@@ -87,9 +102,9 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
start--;
|
start--;
|
||||||
changeLength--;
|
changeLength--;
|
||||||
}
|
}
|
||||||
selection = selection.replace(re, '$1');
|
selection = selection.replace(re, "$1");
|
||||||
} else {
|
} else {
|
||||||
selection = selection.replace(/(\n|^)/g, '$1\t');
|
selection = selection.replace(/(\n|^)/g, "$1\t");
|
||||||
start++;
|
start++;
|
||||||
changeLength++;
|
changeLength++;
|
||||||
}
|
}
|
||||||
@@ -99,7 +114,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (replace && !e.shiftKey) {
|
if (replace && !e.shiftKey) {
|
||||||
this.value = before + '\t' + after;
|
this.value = before + "\t" + after;
|
||||||
this.selectionStart = this.selectionEnd = start + 1;
|
this.selectionStart = this.selectionEnd = start + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,7 +124,8 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
function debounce(func, wait, immediate) {
|
function debounce(func, wait, immediate) {
|
||||||
var timeout;
|
var timeout;
|
||||||
return function () {
|
return function () {
|
||||||
var context = this, args = arguments;
|
var context = this;
|
||||||
|
var args = arguments;
|
||||||
var later = function () {
|
var later = function () {
|
||||||
timeout = null;
|
timeout = null;
|
||||||
if (!immediate) func.apply(context, args);
|
if (!immediate) func.apply(context, args);
|
||||||
@@ -123,11 +139,11 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
|
|
||||||
function getFormJSON() {
|
function getFormJSON() {
|
||||||
return {
|
return {
|
||||||
"language": languageSelect.value,
|
language: languageSelect.value,
|
||||||
"style": styleSelect.value,
|
style: styleSelect.value,
|
||||||
"text": textArea.value,
|
text: textArea.value,
|
||||||
"classes": htmlCheckbox.checked,
|
classes: htmlCheckbox.checked,
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function update(event) {
|
async function update(event) {
|
||||||
@@ -145,12 +161,12 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
output.innerHTML = value.html;
|
output.innerHTML = value.html;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error highlighting code:', error);
|
console.error("Error highlighting code:", error);
|
||||||
// Fallback: display plain text
|
// Fallback: display plain text
|
||||||
if (htmlCheckbox.checked) {
|
if (htmlCheckbox.checked) {
|
||||||
output.innerText = textArea.value;
|
output.innerText = textArea.value;
|
||||||
} else {
|
} else {
|
||||||
output.innerHTML = '<pre>' + textArea.value + '</pre>';
|
output.innerHTML = "<pre>" + textArea.value + "</pre>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,7 +176,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function share(event) {
|
function share(event) {
|
||||||
let data = JSON.stringify(getFormJSON())
|
let data = JSON.stringify(getFormJSON());
|
||||||
data = Base64.encodeURI(data);
|
data = Base64.encodeURI(data);
|
||||||
location.hash = "#" + data;
|
location.hash = "#" + data;
|
||||||
try {
|
try {
|
||||||
@@ -172,26 +188,29 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (location.hash) {
|
if (location.hash) {
|
||||||
let json = Base64.decode(location.hash.substring(1))
|
let json = Base64.decode(location.hash.substring(1));
|
||||||
json = JSON.parse(json);
|
json = JSON.parse(json);
|
||||||
textArea.value = json.text;
|
textArea.value = json.text;
|
||||||
languageSelect.value = json.language;
|
languageSelect.value = json.language;
|
||||||
styleSelect.value = json.style;
|
styleSelect.value = json.style;
|
||||||
htmlCheckbox.checked = json.classes;
|
htmlCheckbox.checked = json.classes;
|
||||||
update(new Event('change'));
|
update(new Event("change"));
|
||||||
} else if (darkMode) {
|
} else if (darkMode) {
|
||||||
styleSelect.value = "monokai";
|
styleSelect.value = "monokai";
|
||||||
update(new Event("change"));
|
update(new Event("change"));
|
||||||
}
|
}
|
||||||
|
|
||||||
var eventHandler = (event) => update(event);
|
var eventHandler = (event) => update(event);
|
||||||
var debouncedEventHandler = debounce(eventHandler, chroma === null ? 250 : 100);
|
var debouncedEventHandler = debounce(
|
||||||
|
eventHandler,
|
||||||
|
chroma === null ? 250 : 100,
|
||||||
|
);
|
||||||
|
|
||||||
languageSelect.addEventListener('change', eventHandler);
|
languageSelect.addEventListener("change", eventHandler);
|
||||||
styleSelect.addEventListener('change', eventHandler);
|
styleSelect.addEventListener("change", eventHandler);
|
||||||
htmlCheckbox.addEventListener('change', eventHandler);
|
htmlCheckbox.addEventListener("change", eventHandler);
|
||||||
copyButton.addEventListener('click', share);
|
copyButton.addEventListener("click", share);
|
||||||
|
|
||||||
textArea.addEventListener('keydown', handleTab);
|
textArea.addEventListener("keydown", handleTab);
|
||||||
textArea.addEventListener('change', debouncedEventHandler);
|
textArea.addEventListener("change", debouncedEventHandler);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user