diff --git a/web/src/js/editor/format.js b/web/src/js/editor/format.js index 4fe0530..8a83840 100644 --- a/web/src/js/editor/format.js +++ b/web/src/js/editor/format.js @@ -4,9 +4,9 @@ const format = document.getElementById('format'); format.addEventListener( 'click', - (e) => { + async (e) => { e.preventDefault(); - Command.format(); + await Command.format(); }, false, ); diff --git a/web/src/js/editor/index.js b/web/src/js/editor/index.js index 7f86c91..24be9d9 100644 --- a/web/src/js/editor/index.js +++ b/web/src/js/editor/index.js @@ -10,9 +10,8 @@ import './codemirror-kcl'; import { load, invokeKCLRun, invokeKCLFmt } from "@kcl-lang/wasm-lib"; const response = await fetch("kcl.wasm"); -const inst = await load({ - data: await response.arrayBuffer(), -}); +const wasm = await response.arrayBuffer(); +const inst = await load({ data: wasm }); const source = document.getElementById('source'); const run = document.getElementById('run'); const outputContainer = document.getElementById('output-container'); @@ -29,41 +28,89 @@ const editor = CodeMirror.fromTextArea(source, { smartIndent: true, }); -const playgroundOptions = { - 'compileURL': '/-/play/compile', - 'fmtURL': '/-/play/fmt', -}; +function generateRandomString(length = 10) { + const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + let result = ''; + for (let i = 0; i < length; i++) { + result += characters.charAt(Math.floor(Math.random() * characters.length)); + } + return result; +} -function getBackendUrl() { - var backendUrl = window.plutoEnv.BACKEND_URL; - if (/http:\/\/0.0.0.0/g.test(backendUrl)) { - var parts = backendUrl.split(":"); - var port = parts[parts.length - 1]; +class Mutex { + constructor() { + this.queue = []; + this.locked = false; + } - var protocol = window.location.protocol; - var host = window.location.hostname; - backendUrl = `${protocol}//${host}:${port}`; + async lock() { + return new Promise((resolve) => { + if (!this.locked) { + this.locked = true; + resolve(); + } else { + this.queue.push(resolve); + } + }); } - return backendUrl; -} + unlock() { + if (this.queue.length > 0) { + const next = this.queue.shift(); + next(); + } else { + this.locked = false; + } + } +} +const mutex = new Mutex(); export const Command = { - set: (value) => { - editor.setValue(value); + async run() { + await mutex.lock(); + try { + const code = editor.getValue(); + const result = invokeKCLRun(inst, { + filename: generateRandomString() + ".k", + source: code, + }); + if (result.startsWith("ERROR:")) { + Command.clear(); + Command.print(result.replace(/^ERROR:\s*/, '')); + } else { + Command.clear(); + Command.print(result); + } + } finally { + mutex.unlock(); + } }, - run: () => { - const code = Command.getValue() - const result = invokeKCLRun(inst, { - filename: "test.k", - source: code, - }); - if (result.startsWith("ERROR:")) { - Command.clear(); - Command.print(result.replace(/^ERROR:\s*/, '')); - } else { - Command.clear(); - Command.print(result); + async format() { + await mutex.lock(); + try { + const inst = await load({ data: wasm }); + const code = editor.getValue(); + const result = invokeKCLFmt(inst, { + source: code, + }); + if (result.startsWith("ERROR:")) { + Command.clear(); + Command.print(result.replace(/^ERROR:\s*/, '')); + } else { + Command.clear(); + Command.print(result); + } + } finally { + mutex.unlock(); + } + }, + + async set(value) { + await mutex.lock(); + try { + editor.setValue(value); + } finally { + mutex.unlock(); } }, @@ -88,22 +135,9 @@ export const Command = { output.innerHTML = ''; outputContainer.scrollTop = 0; }, - - format: () => { - const code = Command.getValue() - const result = invokeKCLFmt(inst, { - source: code, - }); - if (result.startsWith("ERROR:")) { - Command.clear(); - Command.print(result.replace(/^ERROR:\s*/, '')); - } else { - Command.clear(); - Command.print(result); - } - }, }; +editor.on("change", Command.run) const query = new window.URLSearchParams(window.location.search); if (query.has(SHARE_QUERY_KEY)) { @@ -145,9 +179,9 @@ document.addEventListener( run.addEventListener( 'click', - (e) => { + async (e) => { e.preventDefault(); - Command.run(); + await Command.run(); }, false, ); diff --git a/web/webpack.config.js b/web/webpack.config.js index c560728..e518f09 100644 --- a/web/webpack.config.js +++ b/web/webpack.config.js @@ -1,5 +1,5 @@ const path = require('path'); -const webpack = require("webpack"); +const webpack = require('webpack'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const CopyWebpackPlugin = require('copy-webpack-plugin'); @@ -12,7 +12,7 @@ module.exports = { mode: IS_PRODUCTION ? 'production' : 'development', entry: path.resolve(__dirname, 'src/js/index.js'), - target: "web", + target: 'web', output: { path: DIST_PATH, @@ -31,13 +31,13 @@ module.exports = { }, { resourceQuery: /raw/, - type: "asset/source", + type: 'asset/source', }, { resourceQuery: /wasm/, - type: "asset/resource", + type: 'asset/resource', generator: { - filename: "wasm/[name][ext]", + filename: 'wasm/[name][ext]', }, }, ], @@ -65,18 +65,18 @@ module.exports = { }), // needed by @wasmer/wasi new webpack.ProvidePlugin({ - Buffer: ["buffer", "Buffer"], + Buffer: ['buffer', 'Buffer'], }), ], externals: { // needed by @wasmer/wasi - "wasmer_wasi_js_bg.wasm": true, + 'wasmer_wasi_js_bg.wasm': true, }, resolve: { fallback: { // needed by @wasmer/wasi - buffer: require.resolve("buffer/"), - path: require.resolve("path-browserify"), + buffer: require.resolve('buffer/'), + path: require.resolve('path-browserify'), }, }, };