Skip to content

Commit 98c73bf

Browse files
Format on Document Range (#1275)
Enables the feature to format highlighted Q# code, rather than formatting the whole file.
1 parent 8168235 commit 98c73bf

File tree

4 files changed

+116
-15
lines changed

4 files changed

+116
-15
lines changed

vscode/src/extension.ts

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import { initCodegen } from "./qirGeneration.js";
3939
import { createReferenceProvider } from "./references.js";
4040
import { createRenameProvider } from "./rename.js";
4141
import { createSignatureHelpProvider } from "./signature.js";
42-
import { createFormatProvider } from "./format.js";
42+
import { createFormattingProvider } from "./format.js";
4343
import { activateTargetProfileStatusBarItem } from "./statusbar.js";
4444
import {
4545
EventType,
@@ -185,14 +185,20 @@ async function activateLanguageService(extensionUri: vscode.Uri) {
185185
// format document
186186
const isFormattingEnabled = getEnableFormating();
187187
const formatterHandle = {
188-
handle: undefined as vscode.Disposable | undefined,
188+
DocumentFormattingHandle: undefined as vscode.Disposable | undefined,
189+
DocumentRangeFormattingHandle: undefined as vscode.Disposable | undefined,
189190
};
190191
log.debug("Enable formatting set to: " + isFormattingEnabled);
191192
if (isFormattingEnabled) {
192-
formatterHandle.handle =
193+
formatterHandle.DocumentFormattingHandle =
193194
vscode.languages.registerDocumentFormattingEditProvider(
194195
qsharpLanguageId,
195-
createFormatProvider(languageService),
196+
createFormattingProvider(languageService),
197+
);
198+
formatterHandle.DocumentRangeFormattingHandle =
199+
vscode.languages.registerDocumentRangeFormattingEditProvider(
200+
qsharpLanguageId,
201+
createFormattingProvider(languageService),
196202
);
197203
}
198204

@@ -290,13 +296,21 @@ async function updateLanguageServiceEnableFormatting(
290296
const isFormattingEnabled = getEnableFormating();
291297
log.debug("Enable formatting set to: " + isFormattingEnabled);
292298
if (isFormattingEnabled) {
293-
formatterHandle.handle =
299+
formatterHandle.DocumentFormattingHandle =
294300
vscode.languages.registerDocumentFormattingEditProvider(
295301
qsharpLanguageId,
296-
createFormatProvider(languageService),
302+
createFormattingProvider(languageService),
303+
);
304+
formatterHandle.DocumentRangeFormattingHandle =
305+
vscode.languages.registerDocumentRangeFormattingEditProvider(
306+
qsharpLanguageId,
307+
createFormattingProvider(languageService),
297308
);
298309
} else {
299-
formatterHandle.handle?.dispose();
310+
formatterHandle.DocumentFormattingHandle?.dispose();
311+
formatterHandle.DocumentFormattingHandle = undefined;
312+
formatterHandle.DocumentRangeFormattingHandle?.dispose();
313+
formatterHandle.DocumentRangeFormattingHandle = undefined;
300314
}
301315
}
302316

vscode/src/format.ts

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,32 @@
44
import { ILanguageService } from "qsharp-lang";
55
import * as vscode from "vscode";
66
import { toVscodeRange } from "./common";
7-
import { EventType, sendTelemetryEvent } from "./telemetry";
7+
import { EventType, FormatEvent, sendTelemetryEvent } from "./telemetry";
88
import { getRandomGuid } from "./utils";
99

10-
export function createFormatProvider(languageService: ILanguageService) {
11-
return new QSharpFormatProvider(languageService);
10+
export function createFormattingProvider(languageService: ILanguageService) {
11+
return new QSharpFormattingProvider(languageService);
1212
}
1313

14-
class QSharpFormatProvider implements vscode.DocumentFormattingEditProvider {
14+
class QSharpFormattingProvider
15+
implements
16+
vscode.DocumentFormattingEditProvider,
17+
vscode.DocumentRangeFormattingEditProvider
18+
{
1519
constructor(public languageService: ILanguageService) {}
1620

17-
async provideDocumentFormattingEdits(document: vscode.TextDocument) {
21+
private async getFormatChanges(
22+
document: vscode.TextDocument,
23+
eventKind: FormatEvent,
24+
range?: vscode.Range,
25+
) {
1826
// telemetry start format
1927
const associationId = getRandomGuid();
20-
sendTelemetryEvent(EventType.FormatStart, { associationId }, {});
28+
sendTelemetryEvent(
29+
EventType.FormatStart,
30+
{ associationId, event: eventKind },
31+
{},
32+
);
2133
const start = performance.now();
2234

2335
const lsEdits = await this.languageService.getFormatChanges(
@@ -37,10 +49,17 @@ class QSharpFormatProvider implements vscode.DocumentFormattingEditProvider {
3749
return [];
3850
}
3951

40-
const edits = lsEdits.map(
52+
let edits = lsEdits.map(
4153
(edit) => new vscode.TextEdit(toVscodeRange(edit.range), edit.newText),
4254
);
4355

56+
if (range) {
57+
edits = edits.filter(
58+
(e) =>
59+
range.start.isBefore(e.range.end) && range.end.isAfter(e.range.start),
60+
);
61+
}
62+
4463
// telemetry end format
4564
sendTelemetryEvent(
4665
EventType.FormatEnd,
@@ -53,4 +72,15 @@ class QSharpFormatProvider implements vscode.DocumentFormattingEditProvider {
5372

5473
return edits;
5574
}
75+
76+
async provideDocumentFormattingEdits(document: vscode.TextDocument) {
77+
return await this.getFormatChanges(document, FormatEvent.OnDocument);
78+
}
79+
80+
async provideDocumentRangeFormattingEdits(
81+
document: vscode.TextDocument,
82+
range: vscode.Range,
83+
) {
84+
return await this.getFormatChanges(document, FormatEvent.OnRange, range);
85+
}
5686
}

vscode/src/telemetry.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ type EventTypes = {
209209
measurements: { timeToCompleteMs: number };
210210
};
211211
[EventType.FormatStart]: {
212-
properties: { associationId: string };
212+
properties: { associationId: string; event: FormatEvent };
213213
measurements: Empty;
214214
};
215215
[EventType.FormatEnd]: {
@@ -237,6 +237,12 @@ export enum DebugEvent {
237237
Continue = "Continue",
238238
}
239239

240+
export enum FormatEvent {
241+
OnDocument = "OnDocument",
242+
OnRange = "OnRange",
243+
OnType = "OnType",
244+
}
245+
240246
let reporter: TelemetryReporter | undefined;
241247
let userAgentString: string | undefined;
242248

vscode/test/suites/language-service/language-service.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,57 @@ suite("Q# Language Service Tests", function suite() {
108108
);
109109
});
110110

111+
test("Format Document", async () => {
112+
await vscode.workspace.openTextDocument(docUri);
113+
114+
const actualFormatEdits = (await vscode.commands.executeCommand(
115+
"vscode.executeFormatDocumentProvider",
116+
docUri,
117+
)) as vscode.TextEdit[];
118+
119+
assert.lengthOf(actualFormatEdits, 1);
120+
assert.equal(actualFormatEdits[0].range.start.line, 7);
121+
assert.equal(actualFormatEdits[0].range.start.character, 27);
122+
assert.equal(actualFormatEdits[0].range.end.line, 8);
123+
assert.equal(actualFormatEdits[0].range.end.character, 4);
124+
assert.equal(actualFormatEdits[0].newText, "");
125+
});
126+
127+
test("Format Document Range", async () => {
128+
await vscode.workspace.openTextDocument(docUri);
129+
130+
const noEditRange = new vscode.Range(
131+
new vscode.Position(7, 24),
132+
new vscode.Position(7, 27),
133+
);
134+
const editRange = new vscode.Range(
135+
new vscode.Position(7, 27),
136+
new vscode.Position(8, 4),
137+
);
138+
139+
let actualFormatEdits = (await vscode.commands.executeCommand(
140+
"vscode.executeFormatRangeProvider",
141+
docUri,
142+
noEditRange,
143+
)) as vscode.TextEdit[];
144+
145+
// assert that edits outside the range aren't returned
146+
assert.isUndefined(actualFormatEdits);
147+
148+
actualFormatEdits = (await vscode.commands.executeCommand(
149+
"vscode.executeFormatRangeProvider",
150+
docUri,
151+
editRange,
152+
)) as vscode.TextEdit[];
153+
154+
assert.lengthOf(actualFormatEdits, 1);
155+
assert.equal(actualFormatEdits[0].range.start.line, 7);
156+
assert.equal(actualFormatEdits[0].range.start.character, 27);
157+
assert.equal(actualFormatEdits[0].range.end.line, 8);
158+
assert.equal(actualFormatEdits[0].range.end.character, 4);
159+
assert.equal(actualFormatEdits[0].newText, "");
160+
});
161+
111162
test("Code Lens", async () => {
112163
const doc = await vscode.workspace.openTextDocument(docUri);
113164

0 commit comments

Comments
 (0)