From e96fd1ce574586767d55b17bee36b0957db18bd7 Mon Sep 17 00:00:00 2001 From: Jonathan Kelley Date: Sat, 8 Jul 2023 19:27:25 -0700 Subject: [PATCH] Add a little bit of docs and change activation event --- packages/autofmt/Cargo.toml | 1 + packages/autofmt/src/lib.rs | 2 + packages/extension/package.json | 6 +- packages/extension/src/lib.rs | 35 +++- packages/extension/src/main.ts | 280 ++++++--------------------- packages/extension/webpack.config.js | 10 - 6 files changed, 93 insertions(+), 241 deletions(-) diff --git a/packages/autofmt/Cargo.toml b/packages/autofmt/Cargo.toml index 1c947dc21..5c67a814a 100644 --- a/packages/autofmt/Cargo.toml +++ b/packages/autofmt/Cargo.toml @@ -20,6 +20,7 @@ serde = { version = "1.0.136", features = ["derive"] } prettyplease = { package = "prettier-please", version = "0.1.16", features = [ "verbatim", ] } +wasm-bindgen.workspace = true [dev-dependencies] pretty_assertions = "1.2.1" diff --git a/packages/autofmt/src/lib.rs b/packages/autofmt/src/lib.rs index 0d2426a0b..c81a50459 100644 --- a/packages/autofmt/src/lib.rs +++ b/packages/autofmt/src/lib.rs @@ -14,6 +14,8 @@ mod element; mod expr; mod writer; +use wasm_bindgen::prelude::*; + /// A modification to the original file to be applied by an IDE /// /// Right now this re-writes entire rsx! blocks at a time, instead of precise line-by-line changes. diff --git a/packages/extension/package.json b/packages/extension/package.json index ccf2b0893..5cb7e60f1 100644 --- a/packages/extension/package.json +++ b/packages/extension/package.json @@ -18,11 +18,7 @@ "Programming Languages" ], "activationEvents": [ - "*", - "onCommand:extension.htmlToDioxusRsx", - "onCommand:extension.htmlToDioxusComponent", - "onCommand:extension.formatRsx", - "onCommand:extension.formatRsxDocument" + "onLanguage:rust" ], "main": "./out/main.js", "extensionKind": [ diff --git a/packages/extension/src/lib.rs b/packages/extension/src/lib.rs index a5a55a3ae..7061f6217 100644 --- a/packages/extension/src/lib.rs +++ b/packages/extension/src/lib.rs @@ -1,12 +1,8 @@ //! This file exports functions into the vscode extension +use dioxus_autofmt::FormattedBlock; use wasm_bindgen::prelude::*; -#[wasm_bindgen] -pub fn greeet() { - // -} - #[wasm_bindgen] pub fn format_rsx(raw: String) -> String { let block = dioxus_autofmt::fmt_block(&raw, 0); @@ -14,7 +10,34 @@ pub fn format_rsx(raw: String) -> String { } #[wasm_bindgen] -pub fn translate_rsx(contents: String, component: bool) -> String { +pub fn format_selection(raw: String) -> String { + let block = dioxus_autofmt::fmt_block(&raw, 0); + block.unwrap() +} + +#[wasm_bindgen] +pub struct FormatBlockInstance { + new: String, + _edits: Vec, +} + +#[wasm_bindgen] +impl FormatBlockInstance { + #[wasm_bindgen] + pub fn formatted(&self) -> String { + self.new.clone() + } +} + +#[wasm_bindgen] +pub fn format_file(contents: String) -> FormatBlockInstance { + let _edits = dioxus_autofmt::fmt_file(&contents); + let out = dioxus_autofmt::apply_formats(&contents, _edits.clone()); + FormatBlockInstance { new: out, _edits } +} + +#[wasm_bindgen] +pub fn translate_rsx(contents: String, _component: bool) -> String { // Ensure we're loading valid HTML let dom = html_parser::Dom::parse(&contents).unwrap(); diff --git a/packages/extension/src/main.ts b/packages/extension/src/main.ts index 2005612fd..1937c067a 100644 --- a/packages/extension/src/main.ts +++ b/packages/extension/src/main.ts @@ -1,16 +1,26 @@ import * as vscode from 'vscode'; -import init, { translate_rsx } from 'dioxus-ext'; +import init, { translate_rsx, format_file } from 'dioxus-ext'; export async function activate(context: vscode.ExtensionContext) { + // Load the wasm from the file system const wasmSourceCode = await vscode.workspace.fs.readFile(vscode.Uri.joinPath(context.extensionUri, "./pkg/dioxus_ext_bg.wasm")); - const wasmPromise = await init(wasmSourceCode); + // Wait for the initialization to finish + // This is using the byte buffer directly which won't go through the "fetch" machinery + // + // For whatever reason, wasm-bindgen generates `fetch` when we don't want it to + // VSCode doesn't have a `fetch` implementation, but we don't really care about polyfilling it + await init(wasmSourceCode); + + // Todo: + // I want a paste-handler that translates HTML to RSX whenever HTML is pasted into an Rsx block + // Or, a little tooltip that pops up and asks if you want to translate the HTML to RSX context.subscriptions.push( vscode.commands.registerCommand('extension.htmlToDioxusRsx', () => translate(false)), vscode.commands.registerCommand('extension.htmlToDioxusComponent', () => translate(true)), - // vscode.commands.registerCommand('extension.formatRsx', fmtSelection), - // vscode.commands.registerCommand('extension.formatRsxDocument', formatRsxDocument), - // vscode.workspace.onWillSaveTextDocument(fmtDocumentOnSave) + vscode.commands.registerCommand('extension.formatRsx', fmtSelection), + vscode.commands.registerCommand('extension.formatRsxDocument', formatRsxDocument), + vscode.workspace.onWillSaveTextDocument(fmtDocumentOnSave) ); } @@ -36,226 +46,56 @@ function translate(component: boolean) { } +function formatRsxDocument() { + const editor = vscode.window.activeTextEditor; + if (!editor) return; -// function formatRsxDocument() { -// const editor = vscode.window.activeTextEditor; -// if (!editor) return; -// fmtDocument(editor.document); -// } + fmtDocument(editor.document); +} -// function fmtSelection() { -// const editor = vscode.window.activeTextEditor; -// if (!editor) return; +function fmtSelection() { + const editor = vscode.window.activeTextEditor; + if (!editor) return; -// const unformatted = editor.document.getText(editor.selection); + const unformatted = editor.document.getText(editor.selection); -// if (unformatted.length == 0) { -// vscode.window.showWarningMessage("Please select rsx invoking this command!"); -// return; -// } - -// const fileDir = editor.document.fileName.slice(0, editor.document.fileName.lastIndexOf('\\')); - -// const child_proc = spawn(serverPath, ["fmt", "--raw", unformatted.toString()], { -// cwd: fileDir ? fileDir : undefined, -// }); -// let result = ''; - -// child_proc.stdout?.on('data', data => result += data); - -// child_proc.on('close', () => { -// if (result.length > 0) editor.edit(editBuilder => editBuilder.replace(editor.selection, result)); -// }); - -// child_proc.on('error', (err) => { -// vscode.window.showWarningMessage(`Errors occurred while translating. Make sure you have the most recent Dioxus-CLI installed! \n${err}`); -// }); -// } - -// function fmtDocumentOnSave(e: vscode.TextDocumentWillSaveEvent) { -// // check the settings to make sure format on save is configured -// const dioxusConfig = vscode.workspace.getConfiguration('dioxus', e.document).get('formatOnSave'); -// const globalConfig = vscode.workspace.getConfiguration('editor', e.document).get('formatOnSave'); -// if ( -// (dioxusConfig === 'enabled') || -// (dioxusConfig !== 'disabled' && globalConfig) -// ) { -// fmtDocument(e.document); -// } -// } - -// function fmtDocument(document: vscode.TextDocument) { -// try { -// if (document.languageId !== "rust" || document.uri.scheme !== "file") { -// return; -// } - -// const [editor,] = vscode.window.visibleTextEditors.filter(editor => editor.document.fileName === document.fileName); -// if (!editor) return; // Need an editor to apply text edits. - -// const fileDir = document.fileName.slice(0, document.fileName.lastIndexOf('\\')); -// const child_proc = spawn(serverPath, ["fmt", "--file", document.fileName], { -// cwd: fileDir ? fileDir : undefined, -// }); - -// let result = ''; -// child_proc.stdout?.on('data', data => result += data); - -// /*type RsxEdit = { -// formatted: string, -// start: number, -// end: number -// }*/ - -// child_proc.on('close', () => { -// if (child_proc.exitCode !== 0) { -// vscode.window.showWarningMessage(`Errors occurred while formatting. Make sure you have the most recent Dioxus-CLI installed!\nDioxus-CLI exited with exit code ${child_proc.exitCode}\n\nData from Dioxus-CLI:\n${result}`); -// return; -// } - -// }); - -// child_proc.on('error', (err) => { -// vscode.window.showWarningMessage(`Errors occurred while formatting. Make sure you have the most recent Dioxus-CLI installed! \n${err}`); -// }); -// } catch (error) { -// vscode.window.showWarningMessage(`Errors occurred while formatting. Make sure you have the most recent Dioxus-CLI installed! \n${error}`); -// } -// } - - - -// // I'm using the approach defined in rust-analyzer here -// // -// // We ship the server as part of the extension, but we need to handle external paths and such -// // -// // https://github.com/rust-lang/rust-analyzer/blob/fee5555cfabed4b8abbd40983fc4442df4007e49/editors/code/src/main.ts#L270 -// async function bootstrap(context: vscode.ExtensionContext): Promise { - -// const ext = process.platform === "win32" ? ".exe" : ""; -// const bundled = vscode.Uri.joinPath(context.extensionUri, "server", `dioxus${ext}`); -// const bundledExists = await vscode.workspace.fs.stat(bundled).then( -// () => true, -// () => false -// ); - -// // if bunddled doesn't exist, try using a locally-installed version -// if (!bundledExists) { -// return "dioxus"; -// } - -// return bundled.fsPath; -// } - - -// function onPasteHandler() { -// // check settings to see if we should convert HTML to Rsx -// if (vscode.workspace.getConfiguration('dioxus').get('convertOnPaste')) { -// convertHtmlToRsxOnPaste(); -// } -// } - -// function convertHtmlToRsxOnPaste() { -// const editor = vscode.window.activeTextEditor; -// if (!editor) return; - -// // get the cursor location -// const cursor = editor.selection.active; - -// // try to parse the HTML at the cursor location -// const html = editor.document.getText(new vscode.Range(cursor, cursor)); -// } - -/*if (result.length === 0) return; - -// Used for error message: -const originalResult = result; -try { - // Only parse the last non empty line, to skip log warning messages: - const lines = result.replaceAll('\r\n', '\n').split('\n'); - const nonEmptyLines = lines.filter(line => line.trim().length !== 0); - result = nonEmptyLines[nonEmptyLines.length - 1] ?? ''; - - if (result.length === 0) return; - - const decoded: RsxEdit[] = JSON.parse(result); - if (decoded.length === 0) return; - - // Preform edits at the end of the file - // first (to not change previous text file - // offsets): - decoded.sort((a, b) => b.start - a.start); - - - // Convert from utf8 offsets to utf16 offsets used by VS Code: - - const utf8Text = new TextEncoder().encode(text); - const utf8ToUtf16Pos = (posUtf8: number) => { - // Find the line of the position as well as the utf8 and - // utf16 indexes for the start of that line: - let startOfLineUtf8 = 0; - let lineIndex = 0; - const newLineUtf8 = '\n'.charCodeAt(0); - // eslint-disable-next-line no-constant-condition - while (true) { - const nextLineAt = utf8Text.indexOf(newLineUtf8, startOfLineUtf8); - if (nextLineAt < 0 || posUtf8 <= nextLineAt) break; - startOfLineUtf8 = nextLineAt + 1; - lineIndex++; - } - const lineUtf16 = document.lineAt(lineIndex); - - // Move forward from a synced position in the text until the - // target pos is found: - let currentUtf8 = startOfLineUtf8; - let currentUtf16 = document.offsetAt(lineUtf16.range.start); - - const decodeBuffer = new Uint8Array(10); - const utf8Encoder = new TextEncoder(); - while (currentUtf8 < posUtf8) { - const { written } = utf8Encoder.encodeInto(text.charAt(currentUtf16), decodeBuffer); - currentUtf8 += written; - currentUtf16++; - } - return currentUtf16; - }; - - - type FixedEdit = { - range: vscode.Range, - formatted: string, - }; - - const edits: FixedEdit[] = []; - for (const edit of decoded) { - // Convert from utf8 to utf16: - const range = new vscode.Range( - document.positionAt(utf8ToUtf16Pos(edit.start)), - document.positionAt(utf8ToUtf16Pos(edit.end)) - ); - - if (editor.document.getText(range) !== document.getText(range)) { - // The text that was formatted has changed while we were working. - vscode.window.showWarningMessage(`Dioxus formatting was ignored since the source file changed before the change could be applied.`); - continue; - } - - edits.push({ - range, - formatted: edit.formatted, - }); + if (unformatted.length == 0) { + vscode.window.showWarningMessage("Please select rsx invoking this command!"); + return; } + const fileDir = editor.document.fileName.slice(0, editor.document.fileName.lastIndexOf('\\')); - // Apply edits: - editor.edit(editBuilder => { - edits.forEach((edit) => editBuilder.replace(edit.range, edit.formatted)); - }, { - undoStopAfter: false, - undoStopBefore: false - }); +} -} catch (err) { - vscode.window.showWarningMessage(`Errors occurred while formatting. Make sure you have the most recent Dioxus-CLI installed!\n${err}\n\nData from Dioxus-CLI:\n${originalResult}`); -}*/ -// import { TextEncoder } from 'util'; +function fmtDocumentOnSave(e: vscode.TextDocumentWillSaveEvent) { + // check the settings to make sure format on save is configured + const dioxusConfig = vscode.workspace.getConfiguration('dioxus', e.document).get('formatOnSave'); + const globalConfig = vscode.workspace.getConfiguration('editor', e.document).get('formatOnSave'); + if ( + (dioxusConfig === 'enabled') || + (dioxusConfig !== 'disabled' && globalConfig) + ) { + fmtDocument(e.document); + } +} + +function fmtDocument(document: vscode.TextDocument) { + try { + if (document.languageId !== "rust" || document.uri.scheme !== "file") { + return; + } + + const [editor,] = vscode.window.visibleTextEditors.filter(editor => editor.document.fileName === document.fileName); + if (!editor) return; // Need an editor to apply text edits. + + const contents = editor.document.getText(); + const formatted = format_file(contents); + + if (formatted.length() > 0) { + editor.edit(editBuilder => editBuilder.replace(new vscode.Range(0, 0, document.lineCount, 0), formatted.formatted())); + } + } catch (error) { + vscode.window.showWarningMessage(`Errors occurred while formatting. Make sure you have the most recent Dioxus-CLI installed! \n${error}`); + } +} diff --git a/packages/extension/webpack.config.js b/packages/extension/webpack.config.js index f61d87f14..021d5af4e 100644 --- a/packages/extension/webpack.config.js +++ b/packages/extension/webpack.config.js @@ -1,5 +1,4 @@ //@ts-check - 'use strict'; const path = require('path'); @@ -9,13 +8,8 @@ const webpack = require('webpack'); const config = { target: 'webworker', // vscode extensions run in webworker context for VS Code web 📖 -> https://webpack.js.org/configuration/target/#target experiments: { - asyncWebAssembly: true, layers: true, - - - // asyncWebAssembly: true, - // syncWebAssembly: true, }, entry: './src/main.ts', // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ output: { @@ -55,10 +49,6 @@ const config = { } ] } - // { - // test: /\.wasm$/, - // type: "asset/inline", - // } ] },