Add compact JSON formatter with format-on-paste (§03A)

This commit is contained in:
2026-06-07 18:45:17 +03:00
parent a62e2c6128
commit 9f7bf27b7a
6 changed files with 148 additions and 0 deletions
+51
View File
@@ -0,0 +1,51 @@
/**
* Monaco JSON formatting (spec §03A; docs/architecture/08 → compact formatter).
*
* Two pieces, both backed by the portable core pretty-printer (`core/json-format`):
* - `configureJsonFormatter()` registers a document-formatting provider so
* "Format Document" (Shift+Alt+F) produces Vega's compact style.
* - `installFormatOnPaste(editor)` reindents the whole document after a paste,
* satisfying the spec's "pasting … triggers automatic reformatting".
*
* Both no-op when the document is not valid JSON, so a partial/in-progress paste
* is never mangled (mirrors auto-save's "skip when unparseable" rule). Paste
* handling is wired directly via `onDidPaste` rather than Monaco's `formatOnPaste`
* option so it reformats the entire spec (not just the pasted range) and does not
* depend on which registered formatter Monaco happens to pick.
*/
import * as monaco from 'monaco-editor/esm/vs/editor/edcore.main';
import { formatJson } from '@core/json-format';
let registered = false;
/** Register the compact JSON document formatter once. Idempotent. */
export function configureJsonFormatter(): void {
if (registered) return;
registered = true;
monaco.languages.registerDocumentFormattingEditProvider('json', {
provideDocumentFormattingEdits: (model, options) => {
const current = model.getValue();
const formatted = formatJson(current, { indent: options.tabSize });
if (formatted === null || formatted === current) return [];
return [{ range: model.getFullModelRange(), text: formatted }];
},
});
}
/**
* Reformat the whole document after each paste. Returns a disposable; callers
* dispose it when the editor unmounts.
*/
export function installFormatOnPaste(
editor: monaco.editor.IStandaloneCodeEditor,
): monaco.IDisposable {
return editor.onDidPaste(() => {
const model = editor.getModel();
if (!model) return;
const current = model.getValue();
const formatted = formatJson(current, { indent: model.getOptions().tabSize });
if (formatted === null || formatted === current) return;
editor.executeEdits('format-on-paste', [{ range: model.getFullModelRange(), text: formatted }]);
});
}