* wip: failing test * edit harness a bit * wip, not fixed entirely * fix comment accumulation and whitespace * fix: autofmt syncrhonize edits with vscode waitUntil edit pipeline * fix optimization collapsing away whitespace * comment out test case for now
208 lines
6.8 KiB
TypeScript
208 lines
6.8 KiB
TypeScript
import * as vscode from 'vscode';
|
|
import init, * as dioxus 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"));
|
|
|
|
// 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);
|
|
|
|
// Register format-on-save handler using waitUntil() for proper synchronization
|
|
// This runs alongside (not instead of) other formatters like rust-analyzer,
|
|
// allowing both rustfmt and Dioxus RSX formatting to work together
|
|
context.subscriptions.push(
|
|
vscode.workspace.onWillSaveTextDocument(formatOnSave)
|
|
);
|
|
|
|
// 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)
|
|
);
|
|
|
|
context.subscriptions.push(vscode.window.registerUriHandler(new UriLaunchServer()));
|
|
}
|
|
|
|
// Format RSX on save using waitUntil() for proper synchronization with VSCode's save pipeline
|
|
function formatOnSave(e: vscode.TextDocumentWillSaveEvent) {
|
|
if (e.document.languageId !== 'rust') {
|
|
return;
|
|
}
|
|
|
|
// Check if Dioxus formatting is enabled
|
|
const dioxusConfig = vscode.workspace.getConfiguration('dioxus', e.document).get('formatOnSave');
|
|
if (dioxusConfig === 'disabled') {
|
|
return;
|
|
}
|
|
|
|
// Use waitUntil() to properly synchronize with VSCode's save pipeline
|
|
// This returns TextEdit[] which VSCode applies before completing the save
|
|
e.waitUntil(formatDocument(e.document));
|
|
}
|
|
|
|
// Returns a promise of TextEdit[] for use with waitUntil()
|
|
function formatDocument(document: vscode.TextDocument): Thenable<vscode.TextEdit[]> {
|
|
return new Promise((resolve) => {
|
|
try {
|
|
const contents = document.getText();
|
|
|
|
// Get editor options for this document
|
|
const editor = vscode.window.visibleTextEditors.find(
|
|
e => e.document.uri.toString() === document.uri.toString()
|
|
);
|
|
|
|
const tabSize = (typeof editor?.options.tabSize === 'number') ? editor.options.tabSize : 4;
|
|
const useTabs = editor ? !editor.options.insertSpaces : false;
|
|
|
|
const formatted = dioxus.format_file(contents, useTabs, tabSize);
|
|
|
|
if (formatted.length() > 0) {
|
|
const fullRange = new vscode.Range(
|
|
document.positionAt(0),
|
|
document.positionAt(contents.length)
|
|
);
|
|
resolve([vscode.TextEdit.replace(fullRange, formatted.formatted())]);
|
|
} else {
|
|
resolve([]);
|
|
}
|
|
} catch (error) {
|
|
vscode.window.showWarningMessage(`Dioxus formatting error: ${error}`);
|
|
resolve([]);
|
|
}
|
|
});
|
|
}
|
|
|
|
function translate(component: boolean) {
|
|
// Load the activate editor
|
|
const editor = vscode.window.activeTextEditor;
|
|
if (!editor) return;
|
|
|
|
// Get the selected text
|
|
const html = editor.document.getText(editor.selection);
|
|
if (html.length == 0) {
|
|
vscode.window.showWarningMessage("Please select HTML fragment before invoking this command!");
|
|
return;
|
|
}
|
|
|
|
// Translate the HTML to RSX
|
|
const out = dioxus.translate_rsx(html, component);
|
|
if (out.length > 0) {
|
|
editor.edit(editBuilder => editBuilder.replace(editor.selection, out));
|
|
} else {
|
|
vscode.window.showWarningMessage(`Errors occurred while translating, make sure this block of HTML is valid`);
|
|
}
|
|
}
|
|
|
|
|
|
async function formatRsxDocument() {
|
|
const editor = vscode.window.activeTextEditor;
|
|
if (!editor) return;
|
|
|
|
// Apply RSX formatting directly using a WorkspaceEdit
|
|
const edits = await formatDocument(editor.document);
|
|
if (edits.length > 0) {
|
|
const workspaceEdit = new vscode.WorkspaceEdit();
|
|
for (const edit of edits) {
|
|
workspaceEdit.replace(editor.document.uri, edit.range, edit.newText);
|
|
}
|
|
await vscode.workspace.applyEdit(workspaceEdit);
|
|
}
|
|
}
|
|
|
|
function fmtSelection() {
|
|
const editor = vscode.window.activeTextEditor;
|
|
if (!editor) return;
|
|
|
|
if (editor.document.languageId !== "rust") {
|
|
return;
|
|
}
|
|
|
|
let end_line = editor.selection.end.line;
|
|
|
|
// Select full lines of selection
|
|
let selection_range = new vscode.Range(
|
|
editor.selection.start.line,
|
|
0,
|
|
end_line,
|
|
editor.document.lineAt(end_line).range.end.character
|
|
);
|
|
|
|
let unformatted = editor.document.getText(selection_range);
|
|
|
|
if (unformatted.trim().length == 0) {
|
|
vscode.window.showWarningMessage("Please select rsx invoking this command!");
|
|
return;
|
|
}
|
|
|
|
// If number of closing braces is lower than opening braces, expand selection to end of initial block
|
|
while ((unformatted.match(/{/g) || []).length > (unformatted.match(/}/g) || []).length && end_line < editor.document.lineCount - 1) {
|
|
end_line += 1;
|
|
|
|
selection_range = new vscode.Range(
|
|
editor.selection.start.line,
|
|
0,
|
|
end_line,
|
|
editor.document.lineAt(end_line).range.end.character
|
|
);
|
|
|
|
unformatted = editor.document.getText(selection_range);
|
|
}
|
|
|
|
let tabSize: number;
|
|
if (typeof editor.options.tabSize === 'number') {
|
|
tabSize = editor.options.tabSize;
|
|
} else {
|
|
tabSize = 4;
|
|
}
|
|
|
|
let end_above = Math.max(editor.selection.start.line - 1, 0);
|
|
|
|
let lines_above = editor.document.getText(
|
|
new vscode.Range(
|
|
0,
|
|
0,
|
|
end_above,
|
|
editor.document.lineAt(end_above).range.end.character
|
|
)
|
|
);
|
|
|
|
// Calculate indent for current selection
|
|
let base_indentation = (lines_above.match(/{/g) || []).length - (lines_above.match(/}/g) || []).length - 1;
|
|
|
|
try {
|
|
let formatted = dioxus.format_selection(unformatted, !editor.options.insertSpaces, tabSize, base_indentation);
|
|
for (let i = 0; i <= base_indentation; i++) {
|
|
formatted = (editor.options.insertSpaces ? " ".repeat(tabSize) : "\t") + formatted;
|
|
}
|
|
if (formatted.length > 0) {
|
|
editor.edit(editBuilder => {
|
|
editBuilder.replace(selection_range, formatted);
|
|
});
|
|
}
|
|
} catch (error) {
|
|
vscode.window.showErrorMessage(`Errors occurred while formatting. Make sure you have the most recent Dioxus-CLI installed and you have selected valid rsx with your cursor! \n${error}`);
|
|
}
|
|
|
|
}
|
|
|
|
class UriLaunchServer implements vscode.UriHandler {
|
|
handleUri(uri: vscode.Uri): vscode.ProviderResult<void> {
|
|
if (uri.path === '/debugger') {
|
|
let query = decodeURIComponent(uri.query);
|
|
let params = new URLSearchParams(query);
|
|
let route = params.get('uri');
|
|
vscode.window.showInformationMessage(`Opening Chrome debugger: ${route}`);
|
|
vscode.commands.executeCommand('extension.js-debug.debugLink', route);
|
|
}
|
|
}
|
|
}
|