From c5e4c4c76d11c22ee512dc6694ac520bcc9e9b1e Mon Sep 17 00:00:00 2001 From: Oleh Omelchenko Date: Wed, 1 Jul 2026 16:00:49 +0300 Subject: [PATCH] Editor: cleaner transform-scaffold insertion Insert the empty transform array inline (transform: []) instead of a multi-line block, and pass adjustWhitespace:false to Monaco's snippet engine so it stops re-basing the explicit indentation. The multi-line form left the closing bracket under-indented and a stray blank line; the inline form lands cleanly indented as the view's first property, with the first step filling it in place. --- src/app/services/spec-transform-scaffold.ts | 29 ++++++++++----------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/app/services/spec-transform-scaffold.ts b/src/app/services/spec-transform-scaffold.ts index b03b4a2..541eae5 100644 --- a/src/app/services/spec-transform-scaffold.ts +++ b/src/app/services/spec-transform-scaffold.ts @@ -54,14 +54,16 @@ const COMMON_STEP_IDS = ['filter', 'aggregate', 'calculate', 'bin', 'timeUnit'] /** Monaco's snippet contribution — the public entry to insert a `${n:…}` template with tab stops. */ interface SnippetInserter extends monaco.editor.IEditorContribution { - insert(template: string): void; + insert(template: string, opts?: { adjustWhitespace?: boolean }): void; } /** * Insert a snippet template at `offset` through Monaco's snippet engine, so its * `${n:default}` tab stops become a live, Tab-through session. Places the cursor - * first (the controller inserts at the selection). No-op if the contribution is - * absent (it ships in `edcore.main`, so this is just defensive). + * first (the controller inserts at the selection). `adjustWhitespace: false` keeps + * the engine from re-basing our explicit indentation to the insertion line's — it + * otherwise leaves a multi-line block's closing bracket under-indented. No-op if the + * contribution is absent (it ships in `edcore.main`, so this is just defensive). */ function insertSnippetAt( editor: monaco.editor.IStandaloneCodeEditor, @@ -72,7 +74,9 @@ function insertSnippetAt( if (!model) return; editor.setPosition(model.getPositionAt(offset)); editor.focus(); - editor.getContribution('snippetController2')?.insert(template); + editor.getContribution('snippetController2')?.insert(template, { + adjustWhitespace: false, + }); } let registered = false; @@ -156,11 +160,11 @@ function runAddStep( /** * Insert an empty `transform: []` as the view's first property and drop the cursor - * inside it (a `$0` tab stop), so the per-step lenses appear next. Indented off the - * view line's own nesting, and separated from the next property with a comma only - * when one follows — a view with no other key (a bare `{}`) takes none, so the result - * stays valid JSON. The snippet engine precludes a reformat, so the indent is a best - * effort the next document format tidies. + * between its brackets (`$0`), so the per-step lenses appear next and the first step + * fills the array in place. The array is written **inline** — no interior newlines for + * the snippet engine to re-base — so it lands cleanly indented off the view line's own + * nesting. Separated from the following property with a comma only when one follows (a + * view with no other key takes none), so the result stays valid JSON. */ function runAddTransform(editor: monaco.editor.IStandaloneCodeEditor, viewOffset: number): void { const model = editor.getModel(); @@ -176,13 +180,8 @@ function runAddTransform(editor: monaco.editor.IStandaloneCodeEditor, viewOffset const braceLine = model.getPositionAt(brace).lineNumber; const baseIndent = /^\s*/.exec(model.getLineContent(braceLine))?.[0].length ?? 0; const indent = ' '.repeat(baseIndent + tab); - const inner = ' '.repeat(tab); const trail = neighbour(text, brace + 1, 1) === '}' ? '' : ','; - insertSnippetAt( - editor, - brace + 1, - `\n${indent}"transform": [\n${indent}${inner}$0\n${indent}]${trail}`, - ); + insertSnippetAt(editor, brace + 1, `\n${indent}"transform": [$0]${trail}`); } /**