Skip to content

Commit db1a5d2

Browse files
committed
Add Python formatter button
1 parent de41b23 commit db1a5d2

4 files changed

Lines changed: 89 additions & 0 deletions

File tree

index.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
<div id="editor-bar">
6565
<button class="purple-button btn-new">New<i class="fa-solid fa-plus"></i></button>
6666
<button class="purple-button btn-open">Open<i class="fa-solid fa-folder-open"></i></button>
67+
<button class="purple-button btn-format">Format<i class="fa-solid fa-wand-magic-sparkles"></i></button>
6768
<button class="purple-button btn-save">Save<i class="fa-solid fa-floppy-disk"></i></button>
6869
<button class="purple-button btn-save-as">Save As<i class="fa-solid fa-download"></i></button>
6970
<div class="file-path"></div>
@@ -85,6 +86,7 @@
8586
<ul>
8687
<li><a class="btn-new">New<i class="fa-solid fa-plus"></i></a></li>
8788
<li><a class="btn-open">Open<i class="fa-solid fa-folder-open"></i></a></li>
89+
<li><a class="btn-format">Format<i class="fa-solid fa-wand-magic-sparkles"></i></a></li>
8890
<li><a class="btn-save">Save<i class="fa-solid fa-floppy-disk"></i></a></li>
8991
<li><a class="btn-save-as">Save As<i class="fa-solid fa-download"></i></a></li>
9092
</ul>

js/script.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { xml } from "@codemirror/lang-xml";
1111
import { markdown } from "@codemirror/lang-markdown";
1212
import { syntaxHighlighting, indentUnit } from "@codemirror/language";
1313
import { classHighlighter } from "@lezer/highlight";
14+
import initRuffFormatter, { PositionEncoding, Workspace } from "@astral-sh/ruff-wasm-web";
1415
import { circuitpythonHighlight } from "./common/circuitpython_highlight.js";
1516
import { getFileIcon } from "./common/file_dialog.js";
1617

@@ -40,6 +41,9 @@ let workflow = null;
4041
let unchanged = 0;
4142
let connectionPromise = null;
4243
let debugMessageAnsi = null;
44+
let formatterInitPromise = null;
45+
let formatterWorkspace = null;
46+
let currentEditorPath = null;
4347

4448
const btnRestart = document.querySelector('.btn-restart');
4549
const btnHalt = document.querySelector('.btn-halt');
@@ -48,6 +52,7 @@ const btnClear = document.querySelector('.btn-clear');
4852
const btnConnect = document.querySelectorAll('.btn-connect');
4953
const btnNew = document.querySelectorAll('.btn-new');
5054
const btnOpen = document.querySelectorAll('.btn-open');
55+
const btnFormat = document.querySelectorAll('.btn-format');
5156
const btnSave = document.querySelectorAll('.btn-save');
5257
const btnSaveAs = document.querySelectorAll('.btn-save-as');
5358
const btnSaveRun = document.querySelectorAll('.btn-save-run');
@@ -112,6 +117,21 @@ function getFileExtensionFromPath(path) {
112117
return base.split(".").pop().toLowerCase();
113118
}
114119

120+
function isPythonFilePath(path) {
121+
if (path === null || path === undefined) {
122+
return true;
123+
}
124+
return getFileExtensionFromPath(path) === "py";
125+
}
126+
127+
function updateFormatterButtonState(path) {
128+
const enabled = isPythonFilePath(path);
129+
btnFormat.forEach((element) => {
130+
element.disabled = !enabled;
131+
element.setAttribute("aria-disabled", enabled ? "false" : "true");
132+
});
133+
}
134+
115135
// Pick the CodeMirror language extensions to use for a given file path.
116136
// Returns an array so callers can spread it directly into the editor's
117137
// extension list. New (untitled) docs default to Python so the editor
@@ -211,6 +231,15 @@ btnOpen.forEach((element) => {
211231
});
212232
});
213233

234+
// Format Link/Button (Mobile and Desktop Layout)
235+
btnFormat.forEach((element) => {
236+
element.addEventListener('click', async function(e) {
237+
e.preventDefault();
238+
e.stopPropagation();
239+
await formatCurrentFile();
240+
});
241+
});
242+
214243
// Save Link/Button (Mobile and Desktop Layout)
215244
btnSave.forEach((element) => {
216245
element.addEventListener('click', async function(e) {
@@ -301,6 +330,53 @@ async function openFile() {
301330
}
302331
}
303332

333+
async function initFormatter() {
334+
if (!formatterInitPromise) {
335+
formatterInitPromise = initRuffFormatter().then(() => {
336+
formatterWorkspace = new Workspace(
337+
{
338+
"line-length": 88,
339+
"indent-width": 4,
340+
format: {
341+
"indent-style": "space",
342+
"quote-style": "double",
343+
},
344+
},
345+
PositionEncoding.Utf16,
346+
);
347+
});
348+
}
349+
return formatterInitPromise;
350+
}
351+
352+
async function formatCurrentFile() {
353+
if (!isPythonFilePath(currentEditorPath)) {
354+
await showMessage("Format is only available for Python files.");
355+
return false;
356+
}
357+
358+
try {
359+
await initFormatter();
360+
const contents = editor.state.doc.sliceString(0);
361+
const formatted = formatterWorkspace.format(contents);
362+
363+
if (formatted !== contents) {
364+
editor.dispatch({
365+
changes: {
366+
from: 0,
367+
to: editor.state.doc.length,
368+
insert: formatted,
369+
},
370+
});
371+
}
372+
return true;
373+
} catch (e) {
374+
console.error("Could not format Python file", e);
375+
await showMessage(`Could not format Python file. ${e.message || e}`);
376+
return false;
377+
}
378+
}
379+
304380
async function saveFile() {
305381
if (await checkConnected()) {
306382
await workflow.saveFile();
@@ -436,6 +512,9 @@ async function checkReadOnly() {
436512

437513
/* Update the filename and update the UI */
438514
function setFilename(path) {
515+
currentEditorPath = path;
516+
updateFormatterButtonState(path);
517+
439518
// Refresh the CodeMirror language plugin whenever the active file
440519
// changes — this is the single chokepoint that all filename
441520
// changes route through (Open File, New File, Save As, backend

package-lock.json

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"dependencies": {
1717
"@adafruit/ble-file-transfer-js": "adafruit/ble-file-transfer-js#1.0.5",
1818
"@adafruit/circuitpython-repl-js": "adafruit/circuitpython-repl-js#3.3.0",
19+
"@astral-sh/ruff-wasm-web": "^0.15.21",
1920
"@codemirror/lang-css": "^6.3.1",
2021
"@codemirror/lang-html": "^6.4.11",
2122
"@codemirror/lang-javascript": "^6.2.5",

0 commit comments

Comments
 (0)