From 709bad8474330c278e535f31079d84b0a8e71f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sat, 8 Feb 2025 23:05:14 +0100 Subject: [PATCH 01/17] feat: protect code cell options from being formatted --- apps/vscode/src/providers/format.ts | 14 +++++++++++++- apps/vscode/src/providers/option.ts | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/vscode/src/providers/format.ts b/apps/vscode/src/providers/format.ts index acb0c691..3c1db554 100644 --- a/apps/vscode/src/providers/format.ts +++ b/apps/vscode/src/providers/format.ts @@ -47,6 +47,7 @@ import { virtualDocUri, withVirtualDocUri, } from "../vdoc/vdoc"; +import { languageOptionComment } from "./option"; export function activateCodeFormatting(engine: MarkdownEngine) { @@ -205,7 +206,18 @@ async function formatActiveCell(editor: TextEditor, engine: MarkdownEngine) { } async function formatBlock(doc: TextDocument, block: TokenMath | TokenCodeBlock, language: EmbeddedLanguage) { + const optionComment = languageOptionComment(language.ids[0]); const blockLines = lines(codeForExecutableLanguageBlock(block)); + let codeStartLine = block.range.start.line; + if (optionComment) { + for (const line of blockLines) { + if (line.startsWith(optionComment)) { + codeStartLine++; + } else { + break; + } + } + } blockLines.push(""); const vdoc = virtualDocForCode(blockLines, language); const edits = await executeFormatDocumentProvider( @@ -226,7 +238,7 @@ async function formatBlock(doc: TextDocument, block: TokenMath | TokenCodeBlock, ); return new TextEdit(range, edit.newText); }) - .filter(edit => blockRange.contains(edit.range)); + .filter(edit => blockRange.contains(edit.range) && edit.range.start.line >= codeStartLine); return adjustedEdits; } } diff --git a/apps/vscode/src/providers/option.ts b/apps/vscode/src/providers/option.ts index f49ee145..7e3e873a 100644 --- a/apps/vscode/src/providers/option.ts +++ b/apps/vscode/src/providers/option.ts @@ -89,7 +89,7 @@ function handleOptionEnter(editor: TextEditor, comment: string) { } } -function languageOptionComment(langauge: string) { +export function languageOptionComment(langauge: string) { // some mappings if (langauge === "ojs") { langauge = "js"; From 5fc93c265d6a777bd7168b8d2f6d65e1e6d890e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sat, 8 Feb 2025 23:34:12 +0100 Subject: [PATCH 02/17] refactor: prepend hashpipe and rename counter variable --- apps/vscode/src/providers/format.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/vscode/src/providers/format.ts b/apps/vscode/src/providers/format.ts index 3c1db554..f6ab48d5 100644 --- a/apps/vscode/src/providers/format.ts +++ b/apps/vscode/src/providers/format.ts @@ -206,13 +206,13 @@ async function formatActiveCell(editor: TextEditor, engine: MarkdownEngine) { } async function formatBlock(doc: TextDocument, block: TokenMath | TokenCodeBlock, language: EmbeddedLanguage) { - const optionComment = languageOptionComment(language.ids[0]); + const optionComment = languageOptionComment(language.ids[0]) + "| "; const blockLines = lines(codeForExecutableLanguageBlock(block)); - let codeStartLine = block.range.start.line; + let optionLines = 0; if (optionComment) { for (const line of blockLines) { if (line.startsWith(optionComment)) { - codeStartLine++; + optionLines++; } else { break; } @@ -238,7 +238,7 @@ async function formatBlock(doc: TextDocument, block: TokenMath | TokenCodeBlock, ); return new TextEdit(range, edit.newText); }) - .filter(edit => blockRange.contains(edit.range) && edit.range.start.line >= codeStartLine); + .filter(edit => blockRange.contains(edit.range) && edit.range.start.line > block.range.start.line + optionLines); return adjustedEdits; } } From cc2a8b0f6500374527ab8d067507d93caca2106e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sun, 9 Feb 2025 16:37:03 +0100 Subject: [PATCH 03/17] chore: add changelog entry --- apps/vscode/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/vscode/CHANGELOG.md b/apps/vscode/CHANGELOG.md index caa9c8cb..21e77eec 100644 --- a/apps/vscode/CHANGELOG.md +++ b/apps/vscode/CHANGELOG.md @@ -2,6 +2,8 @@ ## 1.119.0 (unreleased) +- Protect code cell options from formatting (). + ## 1.118.0 (Release on 2024-11-26) - Provide F1 help at cursor in Positron () From c96b81931953779c56d1ddd5dde3fd49f26b8d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sat, 8 Feb 2025 23:05:14 +0100 Subject: [PATCH 04/17] feat: protect code cell options from being formatted --- apps/vscode/src/providers/format.ts | 14 +++++++++++++- apps/vscode/src/providers/option.ts | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/vscode/src/providers/format.ts b/apps/vscode/src/providers/format.ts index acb0c691..3c1db554 100644 --- a/apps/vscode/src/providers/format.ts +++ b/apps/vscode/src/providers/format.ts @@ -47,6 +47,7 @@ import { virtualDocUri, withVirtualDocUri, } from "../vdoc/vdoc"; +import { languageOptionComment } from "./option"; export function activateCodeFormatting(engine: MarkdownEngine) { @@ -205,7 +206,18 @@ async function formatActiveCell(editor: TextEditor, engine: MarkdownEngine) { } async function formatBlock(doc: TextDocument, block: TokenMath | TokenCodeBlock, language: EmbeddedLanguage) { + const optionComment = languageOptionComment(language.ids[0]); const blockLines = lines(codeForExecutableLanguageBlock(block)); + let codeStartLine = block.range.start.line; + if (optionComment) { + for (const line of blockLines) { + if (line.startsWith(optionComment)) { + codeStartLine++; + } else { + break; + } + } + } blockLines.push(""); const vdoc = virtualDocForCode(blockLines, language); const edits = await executeFormatDocumentProvider( @@ -226,7 +238,7 @@ async function formatBlock(doc: TextDocument, block: TokenMath | TokenCodeBlock, ); return new TextEdit(range, edit.newText); }) - .filter(edit => blockRange.contains(edit.range)); + .filter(edit => blockRange.contains(edit.range) && edit.range.start.line >= codeStartLine); return adjustedEdits; } } diff --git a/apps/vscode/src/providers/option.ts b/apps/vscode/src/providers/option.ts index eff92603..885322ec 100644 --- a/apps/vscode/src/providers/option.ts +++ b/apps/vscode/src/providers/option.ts @@ -89,7 +89,7 @@ function handleOptionEnter(editor: TextEditor, comment: string) { } } -function languageOptionComment(language: string) { +export function languageOptionComment(language: string) { // some mappings if (language === "ojs") { language = "js"; From 7573a4e286d6961366b9a4b0cc36352939ef46fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sat, 8 Feb 2025 23:34:12 +0100 Subject: [PATCH 05/17] refactor: prepend hashpipe and rename counter variable --- apps/vscode/src/providers/format.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/vscode/src/providers/format.ts b/apps/vscode/src/providers/format.ts index 3c1db554..f6ab48d5 100644 --- a/apps/vscode/src/providers/format.ts +++ b/apps/vscode/src/providers/format.ts @@ -206,13 +206,13 @@ async function formatActiveCell(editor: TextEditor, engine: MarkdownEngine) { } async function formatBlock(doc: TextDocument, block: TokenMath | TokenCodeBlock, language: EmbeddedLanguage) { - const optionComment = languageOptionComment(language.ids[0]); + const optionComment = languageOptionComment(language.ids[0]) + "| "; const blockLines = lines(codeForExecutableLanguageBlock(block)); - let codeStartLine = block.range.start.line; + let optionLines = 0; if (optionComment) { for (const line of blockLines) { if (line.startsWith(optionComment)) { - codeStartLine++; + optionLines++; } else { break; } @@ -238,7 +238,7 @@ async function formatBlock(doc: TextDocument, block: TokenMath | TokenCodeBlock, ); return new TextEdit(range, edit.newText); }) - .filter(edit => blockRange.contains(edit.range) && edit.range.start.line >= codeStartLine); + .filter(edit => blockRange.contains(edit.range) && edit.range.start.line > block.range.start.line + optionLines); return adjustedEdits; } } From 7bbb8c7b086f4cf888f00129e9d0807104be81f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sun, 9 Feb 2025 16:37:03 +0100 Subject: [PATCH 06/17] chore: add changelog entry --- apps/vscode/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/vscode/CHANGELOG.md b/apps/vscode/CHANGELOG.md index 5a6a0909..e9345bdf 100644 --- a/apps/vscode/CHANGELOG.md +++ b/apps/vscode/CHANGELOG.md @@ -20,6 +20,8 @@ - Update cell background configuration to add the ability to use the appropriate theme color. The `quarto.cells.background` settings have changed names so you may need to update your configuration (). - Use new command to switch between source and visual editors in Positron (). +- Protect code cell options from formatting (). + ## 1.118.0 (Release on 2024-11-26) - Provide F1 help at cursor in Positron () From f39071a92f81749b9d22968c9c81297aaa6d86c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sun, 5 Oct 2025 21:41:07 +0200 Subject: [PATCH 07/17] test: draft for testign document formatting with Python code blocks --- .../test/examples/format-python-expected.qmd | 9 ++++++ .../src/test/examples/format-python.qmd | 9 ++++++ apps/vscode/src/test/formatting.test.ts | 30 +++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 apps/vscode/src/test/examples/format-python-expected.qmd create mode 100644 apps/vscode/src/test/examples/format-python.qmd create mode 100644 apps/vscode/src/test/formatting.test.ts diff --git a/apps/vscode/src/test/examples/format-python-expected.qmd b/apps/vscode/src/test/examples/format-python-expected.qmd new file mode 100644 index 00000000..28d7fc52 --- /dev/null +++ b/apps/vscode/src/test/examples/format-python-expected.qmd @@ -0,0 +1,9 @@ +--- +title: Formatting Python Code Cells +format: html +--- + +```{python} +#| label: my-code +1 + 1 +``` diff --git a/apps/vscode/src/test/examples/format-python.qmd b/apps/vscode/src/test/examples/format-python.qmd new file mode 100644 index 00000000..90c382b4 --- /dev/null +++ b/apps/vscode/src/test/examples/format-python.qmd @@ -0,0 +1,9 @@ +--- +title: Formatting Python Code Cells +format: html +--- + +```{python} +#| label: my-code +1+1 +``` diff --git a/apps/vscode/src/test/formatting.test.ts b/apps/vscode/src/test/formatting.test.ts new file mode 100644 index 00000000..a89533de --- /dev/null +++ b/apps/vscode/src/test/formatting.test.ts @@ -0,0 +1,30 @@ +import * as vscode from "vscode"; +import * as assert from "assert"; +import * as path from "path"; +import { openAndShowTextDocument, wait, WORKSPACE_PATH } from "./test-utils"; + +suite("Code Block Formatting", function () { + test("Format Python code block", async function () { + // Ensure Black formatter extension is installed + await vscode.commands.executeCommand("workbench.extensions.installExtension", "ms-python.black-formatter"); + await wait(1000); + + const { doc, editor } = await openAndShowTextDocument("format-python.qmd"); + + // const position = new vscode.Position(7, 2); // Line with "1+1" + // editor.selection = new vscode.Selection(position, position); + // await vscode.commands.executeCommand("quarto.formatCell"); + await vscode.commands.executeCommand("vscode.executeFormatDocumentProvider", doc.uri); + + await wait(500); + + const formattedText = doc.getText(); + + const expectedDoc = await vscode.workspace.openTextDocument( + vscode.Uri.file(path.join(WORKSPACE_PATH, "format-python-expected.qmd")) + ); + const expected = expectedDoc.getText(); + + assert.strictEqual(formattedText, expected, "Python code cell options should not be altered after formatting"); + }); +}); From 4903dbe42cb1781db508589ae4c16ee2841737d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sun, 5 Oct 2025 21:43:50 +0200 Subject: [PATCH 08/17] test: try to format cell only --- apps/vscode/src/test/formatting.test.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/vscode/src/test/formatting.test.ts b/apps/vscode/src/test/formatting.test.ts index a89533de..82dd2d49 100644 --- a/apps/vscode/src/test/formatting.test.ts +++ b/apps/vscode/src/test/formatting.test.ts @@ -11,10 +11,10 @@ suite("Code Block Formatting", function () { const { doc, editor } = await openAndShowTextDocument("format-python.qmd"); - // const position = new vscode.Position(7, 2); // Line with "1+1" - // editor.selection = new vscode.Selection(position, position); - // await vscode.commands.executeCommand("quarto.formatCell"); - await vscode.commands.executeCommand("vscode.executeFormatDocumentProvider", doc.uri); + const position = new vscode.Position(7, 2); // Line with "1+1" + editor.selection = new vscode.Selection(position, position); + await vscode.commands.executeCommand("quarto.formatCell"); + // await vscode.commands.executeCommand("vscode.executeFormatDocumentProvider", doc.uri); await wait(500); From a3694d6f28a178d72b77ac43fcd854713017880b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sun, 5 Oct 2025 21:49:59 +0200 Subject: [PATCH 09/17] test: skip extension installation for now --- apps/vscode/src/test/formatting.test.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/vscode/src/test/formatting.test.ts b/apps/vscode/src/test/formatting.test.ts index 82dd2d49..ff026484 100644 --- a/apps/vscode/src/test/formatting.test.ts +++ b/apps/vscode/src/test/formatting.test.ts @@ -5,9 +5,11 @@ import { openAndShowTextDocument, wait, WORKSPACE_PATH } from "./test-utils"; suite("Code Block Formatting", function () { test("Format Python code block", async function () { + this.timeout(30000); // Increase timeout to 30 seconds for extension installation and formatting + // Ensure Black formatter extension is installed - await vscode.commands.executeCommand("workbench.extensions.installExtension", "ms-python.black-formatter"); - await wait(1000); + // await vscode.commands.executeCommand("workbench.extensions.installExtension", "ms-python.black-formatter"); + // await wait(1000); const { doc, editor } = await openAndShowTextDocument("format-python.qmd"); From ba0918062ab3be355c47474ed46c56eaea0d6457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sun, 5 Oct 2025 22:05:06 +0200 Subject: [PATCH 10/17] test: remove timeout increase --- apps/vscode/src/test/formatting.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/vscode/src/test/formatting.test.ts b/apps/vscode/src/test/formatting.test.ts index ff026484..7e439828 100644 --- a/apps/vscode/src/test/formatting.test.ts +++ b/apps/vscode/src/test/formatting.test.ts @@ -5,8 +5,6 @@ import { openAndShowTextDocument, wait, WORKSPACE_PATH } from "./test-utils"; suite("Code Block Formatting", function () { test("Format Python code block", async function () { - this.timeout(30000); // Increase timeout to 30 seconds for extension installation and formatting - // Ensure Black formatter extension is installed // await vscode.commands.executeCommand("workbench.extensions.installExtension", "ms-python.black-formatter"); // await wait(1000); From 31d541519cf1d5847e712407e09a2c0e3c325c2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sun, 5 Oct 2025 22:28:16 +0200 Subject: [PATCH 11/17] test: use command line to install extension --- apps/vscode/src/test/formatting.test.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/vscode/src/test/formatting.test.ts b/apps/vscode/src/test/formatting.test.ts index 7e439828..d9cba061 100644 --- a/apps/vscode/src/test/formatting.test.ts +++ b/apps/vscode/src/test/formatting.test.ts @@ -5,10 +5,6 @@ import { openAndShowTextDocument, wait, WORKSPACE_PATH } from "./test-utils"; suite("Code Block Formatting", function () { test("Format Python code block", async function () { - // Ensure Black formatter extension is installed - // await vscode.commands.executeCommand("workbench.extensions.installExtension", "ms-python.black-formatter"); - // await wait(1000); - const { doc, editor } = await openAndShowTextDocument("format-python.qmd"); const position = new vscode.Position(7, 2); // Line with "1+1" From d2f81c8064744d42770110a8d85fea1f12f2abb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sun, 5 Oct 2025 23:47:34 +0200 Subject: [PATCH 12/17] test: install Black formatter extension --- apps/vscode/src/test/formatting.test.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/vscode/src/test/formatting.test.ts b/apps/vscode/src/test/formatting.test.ts index d9cba061..4d3ece2d 100644 --- a/apps/vscode/src/test/formatting.test.ts +++ b/apps/vscode/src/test/formatting.test.ts @@ -5,10 +5,17 @@ import { openAndShowTextDocument, wait, WORKSPACE_PATH } from "./test-utils"; suite("Code Block Formatting", function () { test("Format Python code block", async function () { + // Ensure Black formatter extension is installed + await vscode.commands.executeCommand("workbench.extensions.installExtension", "ms-python.black-formatter"); + await wait(1000); + const blackFormatterExtension = vscode.extensions.getExtension("ms-python.black-formatter"); + assert.notStrictEqual(blackFormatterExtension, undefined, "ms-python.black-formatter extension must be installed"); + const { doc, editor } = await openAndShowTextDocument("format-python.qmd"); - const position = new vscode.Position(7, 2); // Line with "1+1" + const position = new vscode.Position(7, 0); // Line with "1+1" editor.selection = new vscode.Selection(position, position); + await wait(1000); await vscode.commands.executeCommand("quarto.formatCell"); // await vscode.commands.executeCommand("vscode.executeFormatDocumentProvider", doc.uri); From 43078114a17c666cf6fa623977757825b9142aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Mon, 6 Oct 2025 00:12:08 +0200 Subject: [PATCH 13/17] test: add debugging code --- apps/vscode/src/test/formatting.test.ts | 28 +++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/apps/vscode/src/test/formatting.test.ts b/apps/vscode/src/test/formatting.test.ts index 4d3ece2d..e54cd5fc 100644 --- a/apps/vscode/src/test/formatting.test.ts +++ b/apps/vscode/src/test/formatting.test.ts @@ -2,6 +2,9 @@ import * as vscode from "vscode"; import * as assert from "assert"; import * as path from "path"; import { openAndShowTextDocument, wait, WORKSPACE_PATH } from "./test-utils"; +import { MarkdownEngine } from "../markdown/engine"; // DEBUGGING +import { embeddedDocumentFormattingProvider } from "../providers/format"; // DEBUGGING +import { isQuartoDoc } from "../core/doc"; // DEBUGGING suite("Code Block Formatting", function () { test("Format Python code block", async function () { @@ -16,8 +19,29 @@ suite("Code Block Formatting", function () { const position = new vscode.Position(7, 0); // Line with "1+1" editor.selection = new vscode.Selection(position, position); await wait(1000); - await vscode.commands.executeCommand("quarto.formatCell"); - // await vscode.commands.executeCommand("vscode.executeFormatDocumentProvider", doc.uri); + + // DEBUGGING + // Use embeddedDocumentFormattingProvider directly + assert.strictEqual(isQuartoDoc(editor?.document), true); + const engine = new MarkdownEngine(); + const provider = embeddedDocumentFormattingProvider(engine); + const next = async () => null; + const edits = await provider( + doc, + { tabSize: 4, insertSpaces: true }, + new vscode.CancellationTokenSource().token, + next + ); + assert.notStrictEqual(edits, null, "Provider should return edits"); + assert.notStrictEqual(edits, undefined, "Provider should return edits"); + assert.ok(Array.isArray(edits), "Edits should be an array"); + assert.ok(edits.length > 0, "Provider should return at least one edit"); + const editApplied = await editor.edit((editBuilder) => { + edits.forEach((edit) => { + editBuilder.replace(edit.range, edit.newText); + }); + }); + assert.strictEqual(editApplied, true, "Edits should be successfully applied"); await wait(500); From fb7c17868dcb7527b55c8e9e8558122ce1596aab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sun, 19 Oct 2025 18:34:23 +0200 Subject: [PATCH 14/17] test: change testing approach --- .../test/examples/format-python-expected.qmd | 4 +- .../src/test/examples/format-python.qmd | 2 +- apps/vscode/src/test/formatting.test.ts | 111 +++++++++++------- 3 files changed, 74 insertions(+), 43 deletions(-) diff --git a/apps/vscode/src/test/examples/format-python-expected.qmd b/apps/vscode/src/test/examples/format-python-expected.qmd index 28d7fc52..aba42e42 100644 --- a/apps/vscode/src/test/examples/format-python-expected.qmd +++ b/apps/vscode/src/test/examples/format-python-expected.qmd @@ -5,5 +5,7 @@ format: html ```{python} #| label: my-code -1 + 1 +x = 1 +y = 2 +z = x+y ``` diff --git a/apps/vscode/src/test/examples/format-python.qmd b/apps/vscode/src/test/examples/format-python.qmd index 90c382b4..80d46c75 100644 --- a/apps/vscode/src/test/examples/format-python.qmd +++ b/apps/vscode/src/test/examples/format-python.qmd @@ -5,5 +5,5 @@ format: html ```{python} #| label: my-code -1+1 +x=1;y=2;z=x+y ``` diff --git a/apps/vscode/src/test/formatting.test.ts b/apps/vscode/src/test/formatting.test.ts index e54cd5fc..03326b9f 100644 --- a/apps/vscode/src/test/formatting.test.ts +++ b/apps/vscode/src/test/formatting.test.ts @@ -1,57 +1,86 @@ import * as vscode from "vscode"; import * as assert from "assert"; import * as path from "path"; -import { openAndShowTextDocument, wait, WORKSPACE_PATH } from "./test-utils"; -import { MarkdownEngine } from "../markdown/engine"; // DEBUGGING -import { embeddedDocumentFormattingProvider } from "../providers/format"; // DEBUGGING -import { isQuartoDoc } from "../core/doc"; // DEBUGGING +import { openAndShowTextDocument, wait } from "./test-utils"; + +function createFormatterFromStringFunc( + format: (sourceText: string) => string +): vscode.DocumentFormattingEditProvider { + return { + provideDocumentFormattingEdits(document: vscode.TextDocument): vscode.TextEdit[] { + const text = document.getText(); + const formatted = format(text); + return [ + new vscode.TextEdit( + new vscode.Range( + document.positionAt(0), + document.positionAt(text.length) + ), + formatted + ), + ]; + }, + }; +} + +function setCursorPosition(line: number, character: number) { + const editor = vscode.window.activeTextEditor; + if (editor) { + const position = new vscode.Position(line, character); + editor.selection = new vscode.Selection(position, position); + } +} suite("Code Block Formatting", function () { - test("Format Python code block", async function () { + test("Format Python code block protects options from formatting", async function () { // Ensure Black formatter extension is installed await vscode.commands.executeCommand("workbench.extensions.installExtension", "ms-python.black-formatter"); await wait(1000); const blackFormatterExtension = vscode.extensions.getExtension("ms-python.black-formatter"); - assert.notStrictEqual(blackFormatterExtension, undefined, "ms-python.black-formatter extension must be installed"); + assert.notStrictEqual( + blackFormatterExtension, + undefined, + "ms-python.black-formatter extension must be installed" + ); - const { doc, editor } = await openAndShowTextDocument("format-python.qmd"); + async function testFormatter( + filename: string, + [line, character]: [number, number], + format: (sourceText: string) => string + ) { + const { doc, editor } = await openAndShowTextDocument(filename); - const position = new vscode.Position(7, 0); // Line with "1+1" - editor.selection = new vscode.Selection(position, position); - await wait(1000); + const formattingEditProvider = vscode.languages.registerDocumentFormattingEditProvider( + { scheme: "file", language: "python" }, + createFormatterFromStringFunc(format) + ); - // DEBUGGING - // Use embeddedDocumentFormattingProvider directly - assert.strictEqual(isQuartoDoc(editor?.document), true); - const engine = new MarkdownEngine(); - const provider = embeddedDocumentFormattingProvider(engine); - const next = async () => null; - const edits = await provider( - doc, - { tabSize: 4, insertSpaces: true }, - new vscode.CancellationTokenSource().token, - next - ); - assert.notStrictEqual(edits, null, "Provider should return edits"); - assert.notStrictEqual(edits, undefined, "Provider should return edits"); - assert.ok(Array.isArray(edits), "Edits should be an array"); - assert.ok(edits.length > 0, "Provider should return at least one edit"); - const editApplied = await editor.edit((editBuilder) => { - edits.forEach((edit) => { - editBuilder.replace(edit.range, edit.newText); - }); - }); - assert.strictEqual(editApplied, true, "Edits should be successfully applied"); - - await wait(500); - - const formattedText = doc.getText(); - - const expectedDoc = await vscode.workspace.openTextDocument( - vscode.Uri.file(path.join(WORKSPACE_PATH, "format-python-expected.qmd")) + setCursorPosition(line, character); + await wait(450); + await vscode.commands.executeCommand("quarto.formatCell"); + await wait(450); + + const result = doc.getText(); + formattingEditProvider.dispose(); + await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); + + return result; + } + + const formattedResult = await testFormatter( + "format-python.qmd", + [7, 0], + (sourceText) => sourceText.trim() + "\n" ); - const expected = expectedDoc.getText(); - assert.strictEqual(formattedText, expected, "Python code cell options should not be altered after formatting"); + // assert.ok( + // formattedResult.includes("x = 1\ny = 2\nz = x+y"), + // "Python code cell should be formatted" + // ); + + const { doc, editor } = await openAndShowTextDocument("format-python-expected.qmd"); + const expected = doc.getText(); + + assert.strictEqual(formattedResult, expected, "Python code cell options should not be altered after formatting"); }); }); From da31716a0f8c1c280e02f4541d72bafd25f1d43f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sun, 19 Oct 2025 18:42:00 +0200 Subject: [PATCH 15/17] test: tweak formatting test timing and documents --- apps/vscode/src/test/formatting.test.ts | 40 +++++++++++++------------ 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/apps/vscode/src/test/formatting.test.ts b/apps/vscode/src/test/formatting.test.ts index 03326b9f..04b61c0e 100644 --- a/apps/vscode/src/test/formatting.test.ts +++ b/apps/vscode/src/test/formatting.test.ts @@ -3,6 +3,11 @@ import * as assert from "assert"; import * as path from "path"; import { openAndShowTextDocument, wait } from "./test-utils"; +/** + * Creates a document formatting provider from a formatting function. + * @param format - Function that transforms source text + * @returns Document formatting edit provider + */ function createFormatterFromStringFunc( format: (sourceText: string) => string ): vscode.DocumentFormattingEditProvider { @@ -23,7 +28,12 @@ function createFormatterFromStringFunc( }; } -function setCursorPosition(line: number, character: number) { +/** + * Sets the cursor position in the active editor. + * @param line - Line number + * @param character - Character position + */ +function setCursorPosition(line: number, character: number): void { const editor = vscode.window.activeTextEditor; if (editor) { const position = new vscode.Position(line, character); @@ -33,21 +43,18 @@ function setCursorPosition(line: number, character: number) { suite("Code Block Formatting", function () { test("Format Python code block protects options from formatting", async function () { - // Ensure Black formatter extension is installed - await vscode.commands.executeCommand("workbench.extensions.installExtension", "ms-python.black-formatter"); - await wait(1000); - const blackFormatterExtension = vscode.extensions.getExtension("ms-python.black-formatter"); - assert.notStrictEqual( - blackFormatterExtension, - undefined, - "ms-python.black-formatter extension must be installed" - ); - + /** + * Tests formatter on a file at a given cursor position. + * @param filename - Name of test file + * @param position - Tuple of line and character position + * @param format - Formatting function + * @returns Formatted document text + */ async function testFormatter( filename: string, [line, character]: [number, number], format: (sourceText: string) => string - ) { + ): Promise { const { doc, editor } = await openAndShowTextDocument(filename); const formattingEditProvider = vscode.languages.registerDocumentFormattingEditProvider( @@ -70,15 +77,10 @@ suite("Code Block Formatting", function () { const formattedResult = await testFormatter( "format-python.qmd", [7, 0], - (sourceText) => sourceText.trim() + "\n" + (sourceText: string): string => sourceText.trim() + "\n" ); - // assert.ok( - // formattedResult.includes("x = 1\ny = 2\nz = x+y"), - // "Python code cell should be formatted" - // ); - - const { doc, editor } = await openAndShowTextDocument("format-python-expected.qmd"); + const { doc } = await openAndShowTextDocument("format-python-expected.qmd"); const expected = doc.getText(); assert.strictEqual(formattedResult, expected, "Python code cell options should not be altered after formatting"); From 2d087609de20e5924e96e88cbe81b583026ef758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Sun, 19 Oct 2025 19:04:40 +0200 Subject: [PATCH 16/17] refactor: use hardcoded expected values --- .../test/examples/format-python-expected.qmd | 11 --- apps/vscode/src/test/formatting.test.ts | 70 ++++++++++--------- 2 files changed, 37 insertions(+), 44 deletions(-) delete mode 100644 apps/vscode/src/test/examples/format-python-expected.qmd diff --git a/apps/vscode/src/test/examples/format-python-expected.qmd b/apps/vscode/src/test/examples/format-python-expected.qmd deleted file mode 100644 index aba42e42..00000000 --- a/apps/vscode/src/test/examples/format-python-expected.qmd +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Formatting Python Code Cells -format: html ---- - -```{python} -#| label: my-code -x = 1 -y = 2 -z = x+y -``` diff --git a/apps/vscode/src/test/formatting.test.ts b/apps/vscode/src/test/formatting.test.ts index 04b61c0e..68083fc0 100644 --- a/apps/vscode/src/test/formatting.test.ts +++ b/apps/vscode/src/test/formatting.test.ts @@ -12,7 +12,9 @@ function createFormatterFromStringFunc( format: (sourceText: string) => string ): vscode.DocumentFormattingEditProvider { return { - provideDocumentFormattingEdits(document: vscode.TextDocument): vscode.TextEdit[] { + provideDocumentFormattingEdits( + document: vscode.TextDocument + ): vscode.TextEdit[] { const text = document.getText(); const formatted = format(text); return [ @@ -41,48 +43,50 @@ function setCursorPosition(line: number, character: number): void { } } -suite("Code Block Formatting", function () { - test("Format Python code block protects options from formatting", async function () { - /** - * Tests formatter on a file at a given cursor position. - * @param filename - Name of test file - * @param position - Tuple of line and character position - * @param format - Formatting function - * @returns Formatted document text - */ - async function testFormatter( - filename: string, - [line, character]: [number, number], - format: (sourceText: string) => string - ): Promise { - const { doc, editor } = await openAndShowTextDocument(filename); +/** + * Tests formatter on a file at a given cursor position. + * @param filename - Name of test file + * @param position - Tuple of line and character position + * @param format - Formatting function + * @returns Formatted document text + */ +async function testFormatter( + filename: string, + [line, character]: [number, number], + format: (sourceText: string) => string +) { + const { doc } = await openAndShowTextDocument(filename); - const formattingEditProvider = vscode.languages.registerDocumentFormattingEditProvider( - { scheme: "file", language: "python" }, - createFormatterFromStringFunc(format) - ); + const formattingEditProvider = + vscode.languages.registerDocumentFormattingEditProvider( + { scheme: "file", language: "python" }, + createFormatterFromStringFunc(format) + ); - setCursorPosition(line, character); - await wait(450); - await vscode.commands.executeCommand("quarto.formatCell"); - await wait(450); + setCursorPosition(line, character); + await wait(450); + await vscode.commands.executeCommand("quarto.formatCell"); + await wait(450); - const result = doc.getText(); - formattingEditProvider.dispose(); - await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); + const result = doc.getText(); + formattingEditProvider.dispose(); + await vscode.commands.executeCommand("workbench.action.closeActiveEditor"); - return result; - } + return result; +} +suite("Code Block Formatting", function () { + test("Format Python code block protects options from formatting", async function () { const formattedResult = await testFormatter( "format-python.qmd", [7, 0], (sourceText: string): string => sourceText.trim() + "\n" ); - const { doc } = await openAndShowTextDocument("format-python-expected.qmd"); - const expected = doc.getText(); - - assert.strictEqual(formattedResult, expected, "Python code cell options should not be altered after formatting"); + assert.ok(formattedResult.includes("x = 1"), "Code should be formatted"); + assert.ok( + formattedResult.includes("#| label: my-code"), + "Code Cell option should be preserved" + ); }); }); From 03872b31e32f3331721d2c12321bb2dda9ab2a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Canouil?= <8896044+mcanouil@users.noreply.github.com> Date: Mon, 20 Oct 2025 12:19:03 +0200 Subject: [PATCH 17/17] chore: Add subtitle to format-python.qmd Added subtitle with link to pull request for context. --- apps/vscode/src/test/examples/format-python.qmd | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/vscode/src/test/examples/format-python.qmd b/apps/vscode/src/test/examples/format-python.qmd index 80d46c75..5c929cb0 100644 --- a/apps/vscode/src/test/examples/format-python.qmd +++ b/apps/vscode/src/test/examples/format-python.qmd @@ -1,5 +1,6 @@ --- title: Formatting Python Code Cells +subtitle: https://github.com/quarto-dev/quarto/pull/655 format: html ---