Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions apps/vscode/src/providers/format.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,13 @@ class FormatCellCommand implements Command {
const edits = await formatActiveCell(editor, this.engine_);
if (edits) {
editor.edit((editBuilder) => {
edits.forEach((edit) => {
editBuilder.replace(edit.range, edit.newText);
});
// Sort edits by descending start position to avoid range shifting issues
edits
.slice()
.sort((a, b) => b.range.start.compareTo(a.range.start))
.forEach((edit) => {
editBuilder.replace(edit.range, edit.newText);
});
});
} else {
window.showInformationMessage(
Expand Down Expand Up @@ -204,15 +208,20 @@ async function formatActiveCell(editor: TextEditor, engine: MarkdownEngine) {
}

async function formatBlock(doc: TextDocument, block: TokenMath | TokenCodeBlock, language: EmbeddedLanguage) {
const blockLines = lines(codeForExecutableLanguageBlock(block));
blockLines.push("");
// Create virtual document containing the block
const blockLines = lines(codeForExecutableLanguageBlock(block, false));
const vdoc = virtualDocForCode(blockLines, language);

const edits = await executeFormatDocumentProvider(
vdoc,
doc,
formattingOptions(doc.uri, vdoc.language)
);

if (edits) {
// Because we format with the block code copied in an empty virtual
// document, we need to adjust the ranges to match the edits to the block
// cell in the original file.
const blockRange = new Range(
new Position(block.range.start.line, block.range.start.character),
new Position(block.range.end.line, block.range.end.character)
Expand All @@ -224,8 +233,8 @@ async function formatBlock(doc: TextDocument, block: TokenMath | TokenCodeBlock,
new Position(edit.range.end.line + block.range.start.line + 1, edit.range.end.character)
);
return new TextEdit(range, edit.newText);
})
.filter(edit => blockRange.contains(edit.range));
}).filter(edit => blockRange.contains(edit.range));

return adjustedEdits;
}
}
Expand Down
11 changes: 11 additions & 0 deletions apps/vscode/src/test/examples/cell-format.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
See https://github.com/quarto-dev/quarto/issues/745 "Reformating R cell in VSCODE with air does not correctly close chunk."

```{r}
list(foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar)
```

```{r}

list(foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar, foobar)

```
82 changes: 72 additions & 10 deletions apps/vscode/src/test/quartoDoc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,87 @@ suite("Quarto basics", function () {
// Note: this test runs after the previous test, so `hello.qmd` can already be touched by the previous
// test. That's okay for this test, but could cause issues if you expect a qmd to look how it
// does in `/examples`.
test("Roundtrip doesn't change hello.qmd", async function () {
const { doc } = await openAndShowTextDocument("hello.qmd");
// test("Roundtrip doesn't change hello.qmd", async function () {
// const { doc } = await openAndShowTextDocument("hello.qmd");

const { before, after } = await roundtrip(doc);
// const { before, after } = await roundtrip(doc);

assert.equal(before, after);
});
// assert.equal(before, after);
// });

// roundtripSnapshotTest('valid-basics.qmd');

// roundtripSnapshotTest('valid-basics-2.qmd');

// roundtripSnapshotTest('valid-nesting.qmd');

// roundtripSnapshotTest('invalid.qmd');

// roundtripSnapshotTest('capsule-leak.qmd');

test("cell formamtmtamt", async function () {

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: 'r' },
createFormatterFromStringFunc(format)
)

roundtripSnapshotTest('valid-basics.qmd');
setCursorPosition(line, character);
await wait(300);
await vscode.commands.executeCommand("quarto.formatCell");
await wait(300)

roundtripSnapshotTest('valid-basics-2.qmd');
const result = doc.getText()

roundtripSnapshotTest('valid-nesting.qmd');
formattingEditProvider.dispose()
await vscode.commands.executeCommand("workbench.action.closeActiveEditor");

roundtripSnapshotTest('invalid.qmd');
return result
}

roundtripSnapshotTest('capsule-leak.qmd');
const newlineFormatterResult = await testFormatter(
"cell-format.qmd",
[3, 1],
(sourceText) => sourceText + '\n'
)
const noopFormatterResult = await testFormatter(
"cell-format.qmd",
[3, 1],
(sourceText) => sourceText
)

assert.equal(newlineFormatterResult, noopFormatterResult)
});

suiteTeardown(() => {
vscode.window.showInformationMessage('All tests done!');
});
});

function createFormatterFromStringFunc(format: (sourceText: string) => string) {
return {
provideDocumentFormattingEdits(document: vscode.TextDocument): vscode.ProviderResult<vscode.TextEdit[]> {
const fileStart = new vscode.Position(0, 0);
const fileEnd = document.lineAt(document.lineCount - 1).range.end;

return [new vscode.TextEdit(new vscode.Range(fileStart, fileEnd), format(document.getText()))];
}
}
}

function setCursorPosition(line: number, character: number) {
const editor = vscode.window.activeTextEditor;
if (editor) {
const position = new vscode.Position(line, character);
const newSelection = new vscode.Selection(position, position);
editor.selection = newSelection;
editor.revealRange(newSelection, vscode.TextEditorRevealType.InCenter); // Optional: scroll to the new position
}
}

/**
*
* When the test is run on the dev's machine for the first time, saves the roundtripped file as a snapshot.
Expand Down
7 changes: 5 additions & 2 deletions packages/quarto-core/src/markdown/language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,14 @@ export function isExecutableLanguageBlock(token: Token) : token is TokenMath | T
}
}

export function codeForExecutableLanguageBlock(token: TokenMath | TokenCodeBlock) {
export function codeForExecutableLanguageBlock(
token: TokenMath | TokenCodeBlock,
appendNewline = true,
) {
if (isMath(token)) {
return token.data.text;
} else if (isCodeBlock(token)) {
return token.data + "\n";
return token.data + (appendNewline ? "\n" : "");
} else {
return "";
}
Expand Down