Skip to content

Commit 6b5d3ee

Browse files
authored
Fix 'Extract to function' formatting/selection bugs. (#11621)
* Fix 'Extract to function' formatting/selection bugs.
1 parent 5b3c4c7 commit 6b5d3ee

File tree

1 file changed

+35
-17
lines changed

1 file changed

+35
-17
lines changed

Extension/src/LanguageServer/client.ts

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3497,7 +3497,11 @@ export class DefaultClient implements Client {
34973497
let workspaceEdits: vscode.WorkspaceEdit = new vscode.WorkspaceEdit();
34983498
let replaceEditRange: vscode.Range | undefined;
34993499
let hasProcessedReplace: boolean = false;
3500-
const formatUriAndRanges: VsCodeUriAndRange[] = [];
3500+
// NOTE: References to source/header are in reference to the more common case when it's
3501+
// invoked on the source file (alternatively named the first file). When invoked on the header file,
3502+
// the header file operates as if it were the source file (isSourceFile stays true).
3503+
const sourceFormatUriAndRanges: VsCodeUriAndRange[] = [];
3504+
const headerFormatUriAndRanges: VsCodeUriAndRange[] = [];
35013505
let lineOffset: number = 0;
35023506
let headerFileLineOffset: number = 0;
35033507
let isSourceFile: boolean = true;
@@ -3561,11 +3565,16 @@ export class DefaultClient implements Client {
35613565
rangeStartCharacter = 1;
35623566
rangeStartLine += 1;
35633567
}
3564-
formatUriAndRanges.push({uri, range: new vscode.Range(
3568+
const newRange: vscode.Range = new vscode.Range(
35653569
new vscode.Position(rangeStartLine + (nextLineOffset < 0 ? nextLineOffset : 0), range.start.character),
35663570
new vscode.Position(rangeStartLine + (nextLineOffset < 0 ? 0 : nextLineOffset),
35673571
isReplace ? range.end.character :
3568-
range.end.character + edit.newText.length - rangeStartCharacter))});
3572+
range.end.character + edit.newText.length - rangeStartCharacter));
3573+
if (isSourceFile) {
3574+
sourceFormatUriAndRanges.push({uri, range: newRange});
3575+
} else {
3576+
headerFormatUriAndRanges.push({uri, range: newRange});
3577+
}
35693578
if (isReplace) {
35703579
// Handle additional declaration lines added before the new function call.
35713580
let currentText: string = edit.newText.substring(rangeStartCharacter);
@@ -3595,34 +3604,45 @@ export class DefaultClient implements Client {
35953604
}
35963605
}
35973606

3598-
if (replaceEditRange === undefined || formatUriAndRanges.length === 0) {
3607+
if (replaceEditRange === undefined || sourceFormatUriAndRanges.length === 0) {
35993608
return;
36003609
}
36013610

36023611
// Apply the extract to function text edits.
36033612
await vscode.workspace.applyEdit(workspaceEdits, { isRefactoring: true });
36043613

3605-
const firstUri: vscode.Uri = formatUriAndRanges[0].uri;
3614+
// Select the replaced code.
3615+
await vscode.window.showTextDocument(sourceFormatUriAndRanges[0].uri, { selection: replaceEditRange, preserveFocus: false });
36063616

36073617
// Format the new text edits.
36083618
const formatEdits: vscode.WorkspaceEdit = new vscode.WorkspaceEdit();
3609-
for (const formatUriAndRange of formatUriAndRanges) {
3619+
const formatRanges = async (formatUriAndRanges: VsCodeUriAndRange[]) => {
3620+
if (formatUriAndRanges.length === 0) {
3621+
return;
3622+
}
3623+
const formatUriAndRange: VsCodeUriAndRange = formatUriAndRanges[0];
3624+
const isMultipleFormatRanges: boolean = formatUriAndRanges.length > 1;
36103625
const settings: OtherSettings = new OtherSettings(vscode.workspace.getWorkspaceFolder(formatUriAndRange.uri)?.uri);
36113626
const formatOptions: vscode.FormattingOptions = {
36123627
insertSpaces: settings.editorInsertSpaces ?? true,
36133628
tabSize: settings.editorTabSize ?? 4,
3614-
onChanges: true,
3629+
onChanges: isMultipleFormatRanges,
36153630
preserveFocus: true
36163631
};
36173632

3618-
const doFormat = async () => {
3633+
const tryFormat = async () => {
36193634
const versionBeforeFormatting: number | undefined = openFileVersions.get(formatUriAndRange.uri.toString());
36203635
if (versionBeforeFormatting === undefined) {
36213636
return true;
36223637
}
36233638

3624-
const formatTextEdits: vscode.TextEdit[] | undefined = await vscode.commands.executeCommand<vscode.TextEdit[] | undefined>(
3625-
"vscode.executeFormatDocumentProvider", formatUriAndRange.uri, formatOptions);
3639+
// Only use document (onChange) formatting when there are multiple ranges.
3640+
const formatTextEdits: vscode.TextEdit[] | undefined = isMultipleFormatRanges ?
3641+
await vscode.commands.executeCommand<vscode.TextEdit[] | undefined>(
3642+
"vscode.executeFormatDocumentProvider", formatUriAndRange.uri, formatOptions) :
3643+
await vscode.commands.executeCommand<vscode.TextEdit[] | undefined>(
3644+
"vscode.executeFormatRangeProvider", formatUriAndRange.uri, formatUriAndRange.range, formatOptions);
3645+
36263646
if (!formatTextEdits || formatTextEdits.length === 0 || versionBeforeFormatting === undefined) {
36273647
return true;
36283648
}
@@ -3635,19 +3655,17 @@ export class DefaultClient implements Client {
36353655
formatEdits.set(formatUriAndRange.uri, formatTextEdits);
36363656
return true;
36373657
};
3638-
if (!await doFormat())
3658+
if (!await tryFormat())
36393659
{
3640-
await doFormat(); // Try again;
3660+
await tryFormat(); // Try again;
36413661
}
3642-
}
3662+
};
36433663

3664+
await formatRanges(headerFormatUriAndRanges);
3665+
await formatRanges(sourceFormatUriAndRanges);
36443666
if (formatEdits.size > 0) {
36453667
await vscode.workspace.applyEdit(formatEdits, { isRefactoring: true });
36463668
}
3647-
3648-
// This is required to be done after the formatting is done, because that can trigger the
3649-
// active document to switch to the wrong file (the header).
3650-
await vscode.window.showTextDocument(firstUri, { selection: replaceEditRange, preserveFocus: false });
36513669
}
36523670

36533671
public onInterval(): void {

0 commit comments

Comments
 (0)