mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Add compact JSON formatter with format-on-paste (§03A)
This commit is contained in:
@@ -23,6 +23,7 @@ import * as monaco from 'monaco-editor/esm/vs/editor/edcore.main';
|
||||
import 'monaco-editor/esm/vs/language/json/monaco.contribution';
|
||||
import '../infrastructure/monaco-env'; // side-effect: wire workers before create
|
||||
import { configureVegaLiteJson } from '../infrastructure/monaco-schema';
|
||||
import { configureJsonFormatter, installFormatOnPaste } from '../infrastructure/monaco-format';
|
||||
import { openModal } from '../modals/ModalCoordinator';
|
||||
import { useAppStore } from '../stores/AppStore';
|
||||
import { confirm } from '../stores/ConfirmStore';
|
||||
@@ -134,6 +135,8 @@ function EditorSettings() {
|
||||
// Register the bundled Vega-Lite schema once: resolves `$schema` locally (no
|
||||
// network warning) and powers validation, autocomplete, and hover docs.
|
||||
configureVegaLiteJson();
|
||||
// Register the compact JSON formatter once (Format Document + format-on-paste, §03A).
|
||||
configureJsonFormatter();
|
||||
|
||||
function EditorToolbar() {
|
||||
const activeId = useSnippetStore((s) => s.activeSnippetId);
|
||||
@@ -267,6 +270,11 @@ export function SpecEditor() {
|
||||
}
|
||||
});
|
||||
|
||||
// Pasting reindents the whole spec to keep it consistently formatted (§03A).
|
||||
// The reformat fires onDidChangeModelContent above, so the draft buffer picks
|
||||
// up the formatted text. No-op on the read-only published view / invalid JSON.
|
||||
const pasteSub = installFormatOnPaste(editor);
|
||||
|
||||
// Cmd/Ctrl+S is owned globally by the EventRouter (docs/architecture/04 →
|
||||
// "bind listeners in exactly one place"), which publishes before the
|
||||
// interactive-context gate so it works while the editor has focus. Monaco
|
||||
@@ -274,6 +282,7 @@ export function SpecEditor() {
|
||||
|
||||
return () => {
|
||||
sub.dispose();
|
||||
pasteSub.dispose();
|
||||
editor.dispose();
|
||||
editorRef.current = null;
|
||||
};
|
||||
|
||||
@@ -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 }]);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user