Replies: 2 comments
-
Ditch WASM but leave the code for the futureAfter 2 days trying to make it work and facing countless issues I decided to move on and ditch wasm for now. Maybe later we will back to this. ProblemLooks like this stack (Go+wasm+vscode) isn't used a lot (if I used something like Rust it would be easier). The last problems that I faced was that extension calls Codewasm_exec_vscode.ts // This is modified version of the original wasm_exec_node.js from Go authors.
import * as fs from "fs";
import * as path from "path";
import { TextEncoder, TextDecoder } from "util";
import * as os from "os";
import { performance } from "perf_hooks";
import * as crypto from "crypto";
(globalThis as any).fs = fs;
(globalThis as any).path = path;
(globalThis as any).TextEncoder = TextEncoder;
(globalThis as any).TextDecoder = TextDecoder;
(globalThis as any).performance ??= performance;
(globalThis as any).crypto ??= crypto;
require("./wasm_exec.js"); // globalThis must be patched before this line
declare var Go: any; // it injects Go constructor into global scope
const go = new Go();
go.env = Object.assign({ TMPDIR: os.tmpdir() }, process.env);
go.exit = process.exit;
export const patchGlobal = async (): Promise<
WebAssembly.WebAssemblyInstantiatedSource | undefined
> => {
try {
const source = await WebAssembly.instantiate(
fs.readFileSync(path.resolve(__dirname, "../../wasm/main.wasm")),
go.importObject
);
process.on("exit", (code) => {
if (code === 0 && !go.exited) {
go._pendingEvent = { id: 0 };
go._resume();
}
});
go.run(source.instance); // async, injects functions into global
return source;
} catch (err) {
console.error(err);
}
}; extension.ts import { Disposable, ExtensionContext, window } from "vscode";
import { ParseFunc, NevaEditor } from "./editor";
import { patchGlobal } from "./wasm/wasm_exec_vscode";
declare global {
var parseNevaFile: ParseFunc;
}
const viewType = "neva.editNeva";
export async function activate(context: ExtensionContext) {
console.log("vscode neva activated");
await patchGlobal(); // injects parseNevaFile into global
const editor = new NevaEditor(context, global.parseNevaFile);
const disposable: Disposable = window.registerCustomEditorProvider(
viewType,
editor,
{ supportsMultipleEditorsPerDocument: true }
);
context.subscriptions.push(disposable);
} editor.ts import {
Disposable,
Webview,
WebviewPanel,
window,
Uri,
ColorThemeKind,
ColorTheme,
CustomTextEditorProvider,
CancellationToken,
TextDocument,
ExtensionContext,
WorkspaceEdit,
Range,
workspace,
} from "vscode";
export type ParseFunc = (text: string) => string;
export class NevaEditor implements CustomTextEditorProvider {
private readonly context: ExtensionContext;
private readonly parse: ParseFunc;
constructor(context: ExtensionContext, parseFunc: ParseFunc) {
this.context = context;
this.parse = parseFunc;
}
resolveCustomTextEditor(
document: TextDocument,
webviewPanel: WebviewPanel,
token: CancellationToken
): void | Thenable<void> {
let protoString: string;
try {
protoString = this.parse(document.getText());
} catch (e) {
console.error(e);
return;
}
console.log(protoString);
// const file = File.fromJSON(protoString);
const extensionUri = this.context.extensionUri;
webviewPanel.webview.options = {
enableScripts: true,
localResourceRoots: [
(Uri as any).joinPath(extensionUri, "out"),
(Uri as any).joinPath(extensionUri, "webview/dist"),
],
};
webviewPanel.webview.html = getWebviewContent(
webviewPanel.webview,
extensionUri
);
const disposables: Disposable[] = [];
let isUpdating = {
creationTime: Date.now(),
current: false,
editTime: undefined,
};
workspace.onDidChangeTextDocument(
(event) => {
if (event.document.uri.toString() === document.uri.toString()) {
if (!isUpdating.current) {
console.log("update window", isUpdating);
updateWindow(webviewPanel, event.document);
} else {
isUpdating.current = false;
}
}
},
undefined,
disposables
);
window.onDidChangeActiveColorTheme(
(event: ColorTheme) => {
let isDarkTheme = event.kind === ColorThemeKind.Dark;
webviewPanel.webview.postMessage({
type: "theme",
isDarkTheme: isDarkTheme,
});
},
undefined,
disposables
);
webviewPanel.webview.onDidReceiveMessage(
(message: any) => {
const command = message.command;
const text = message.text;
switch (command) {
case "update":
const edit = new WorkspaceEdit();
isUpdating.current = true;
isUpdating.editTime = Date.now() as any;
console.log("update", message.uri, text);
edit.replace(
message.uri,
new Range(0, 0, document.lineCount, 0),
text
);
workspace.applyEdit(edit);
}
},
undefined,
disposables
);
webviewPanel.onDidDispose(() => {
while (disposables.length) {
const disposable = disposables.pop();
if (disposable) {
disposable.dispose();
}
}
});
updateWindow(webviewPanel, document);
}
}
function getWebviewContent(webview: Webview, extensionUri: Uri) {
const stylesUri = getUri(webview, extensionUri, [
"webview",
"dist",
"assets",
"index.css",
]);
const scriptUri = getUri(webview, extensionUri, [
"webview",
"dist",
"assets",
"index.js",
]);
return /*html*/ `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="${stylesUri}">
<title>Neva Editor</title>
</head>
<body>
<div id="root"></div>
<script type="module" nonce="${getNonce()}" src="${scriptUri}"></script>
</body>
</html>
`;
}
function updateWindow(panel: WebviewPanel, document: TextDocument) {
const currentTheme = window.activeColorTheme;
const isDarkTheme = currentTheme.kind === ColorThemeKind.Dark;
panel.webview.postMessage({
type: "revive",
value: document.getText(),
uri: document.uri,
isDarkTheme: isDarkTheme,
});
}
function getNonce() {
let text = "";
const possible =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
function getUri(webview: Webview, extensionUri: Uri, pathList: string[]) {
return webview.asWebviewUri((Uri as any).joinPath(extensionUri, ...pathList));
} I don't have Go code but it was main.go with package main with import syscall/js and setting global value, injecting js wrapper func as "parseNevaFile", that wrapper can return numbers and strings (at least) and receive js.Value as (this, args) parameters. IdeaI also was thinking about getting rid of protobuf and mapping src.* values right to js.Value(s) |
Beta Was this translation helpful? Give feedback.
-
Missed go code for wasmpackage main
import (
"context"
"syscall/js"
"google.golang.org/protobuf/proto"
"github.com/nevalang/neva/pkg/protoparser"
)
func main() {
js.Global().Set("parseNevaFile", js.FuncOf(ParseNevaFile))
<-make(chan struct{})
}
func ParseNevaFile(this js.Value, args []js.Value) (str any) {
if len(args) != 1 {
return "error: args count must be 1"
}
v, err := protoparser.SrcBytesToProto(context.Background(), []byte(args[0].String()))
if err != nil {
return err.Error()
}
result, err := proto.Marshal(v)
if err != nil {
return err.Error()
}
return string(result)
} |
Beta Was this translation helpful? Give feedback.
-
tinygo + wasm is not an option because
Beta Was this translation helpful? Give feedback.
All reactions