Skip to content

Commit 4ec16b2

Browse files
author
Minh Quy
authored
Fix#48281 - Indentation not respected when executing various refactorings (TypeScript/JavaScript) (#48340)
* fix(48281) - Preserve indentation when adding missing properties * fix(48281) - Fix object literal indentation like block * fix(48281) - Indentation for object literal started with curly brace similar to block
1 parent ba3645e commit 4ec16b2

File tree

4 files changed

+78
-9
lines changed

4 files changed

+78
-9
lines changed

src/services/codefixes/fixAddMissingMember.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,15 +171,16 @@ namespace ts.codefix {
171171

172172
const properties = arrayFrom(checker.getUnmatchedProperties(checker.getTypeAtLocation(parent), checker.getTypeAtLocation(param), /* requireOptionalProperties */ false, /* matchDiscriminantProperties */ false));
173173
if (!length(properties)) return undefined;
174-
return { kind: InfoKind.ObjectLiteral, token: param.name, properties, indentation: 0, parentDeclaration: parent };
174+
return { kind: InfoKind.ObjectLiteral, token: param.name, properties, parentDeclaration: parent };
175175
}
176176

177177
if (!isMemberName(token)) return undefined;
178178

179179
if (isIdentifier(token) && hasInitializer(parent) && parent.initializer && isObjectLiteralExpression(parent.initializer)) {
180180
const properties = arrayFrom(checker.getUnmatchedProperties(checker.getTypeAtLocation(parent.initializer), checker.getTypeAtLocation(token), /* requireOptionalProperties */ false, /* matchDiscriminantProperties */ false));
181181
if (!length(properties)) return undefined;
182-
return { kind: InfoKind.ObjectLiteral, token, properties, indentation: undefined, parentDeclaration: parent.initializer };
182+
183+
return { kind: InfoKind.ObjectLiteral, token, properties, parentDeclaration: parent.initializer };
183184
}
184185

185186
if (isIdentifier(token) && isJsxOpeningLikeElement(token.parent)) {
@@ -235,6 +236,7 @@ namespace ts.codefix {
235236
if (enumDeclaration && !isPrivateIdentifier(token) && !isSourceFileFromLibrary(program, enumDeclaration.getSourceFile())) {
236237
return { kind: InfoKind.Enum, token, parentDeclaration: enumDeclaration };
237238
}
239+
238240
return undefined;
239241
}
240242

src/services/formatting/smartIndenter.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,28 @@ namespace ts.formatting {
5555
// indentation is first non-whitespace character in a previous line
5656
// for block indentation, we should look for a line which contains something that's not
5757
// whitespace.
58-
if (options.indentStyle === IndentStyle.Block) {
58+
const currentToken = getTokenAtPosition(sourceFile, position);
59+
// for object literal, we want to the indentation work like block
60+
// if { starts in any position (can be in the middle of line)
61+
// the following indentation should treat { as starting of that line (including leading whitespace)
62+
// ```
63+
// const a: { x: undefined, y: undefined } = {} // leading 4 whitespaces and { starts in the middle of line
64+
// ->
65+
// const a: { x: undefined, y: undefined } = {
66+
// x: undefined,
67+
// y: undefined,
68+
// }
69+
// ---------------------
70+
// const a: {x : undefined, y: undefined } =
71+
// {}
72+
// ->
73+
// const a: { x: undefined, y: undefined } =
74+
// { // leading 5 whitespaces and { starts at 6 column
75+
// x: undefined,
76+
// y: undefined,
77+
// }
78+
// ```
79+
if (options.indentStyle === IndentStyle.Block || currentToken.kind === SyntaxKind.OpenBraceToken) {
5980
return getBlockIndent(sourceFile, position, options);
6081
}
6182

src/services/textChanges.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,6 @@ namespace ts.textChanges {
116116
* Text of inserted node will be formatted with this delta, otherwise delta will be inferred from the new node kind
117117
*/
118118
delta?: number;
119-
/**
120-
* Do not trim leading white spaces in the edit range
121-
*/
122-
preserveLeadingWhitespace?: boolean;
123119
}
124120

125121
export interface ReplaceWithMultipleNodesOptions extends InsertNodeOptions {
@@ -492,7 +488,7 @@ namespace ts.textChanges {
492488
}
493489
const startPosition = getPrecedingNonSpaceCharacterPosition(sourceFile.text, fnStart - 1);
494490
const indent = sourceFile.text.slice(startPosition, fnStart);
495-
this.insertNodeAt(sourceFile, fnStart, tag, { preserveLeadingWhitespace: false, suffix: this.newLineCharacter + indent });
491+
this.insertNodeAt(sourceFile, fnStart, tag, { suffix: this.newLineCharacter + indent });
496492
}
497493

498494
private createJSDocText(sourceFile: SourceFile, node: HasJSDoc) {
@@ -1068,7 +1064,7 @@ namespace ts.textChanges {
10681064
? change.nodes.map(n => removeSuffix(format(n), newLineCharacter)).join(change.options?.joiner || newLineCharacter)
10691065
: format(change.node);
10701066
// strip initial indentation (spaces or tabs) if text will be inserted in the middle of the line
1071-
const noIndent = (options.preserveLeadingWhitespace || options.indentation !== undefined || getLineStartPositionForPosition(pos, sourceFile) === pos) ? text : text.replace(/^\s+/, "");
1067+
const noIndent = (options.indentation !== undefined || getLineStartPositionForPosition(pos, sourceFile) === pos) ? text : text.replace(/^\s+/, "");
10721068
return (options.prefix || "") + noIndent
10731069
+ ((!options.suffix || endsWith(noIndent, options.suffix))
10741070
? "" : options.suffix);
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////interface Test {
4+
//// foo: string;
5+
//// bar(a: string): void;
6+
////}
7+
////function f (_spec: any) {}
8+
////function g (_spec: Test) {}
9+
////[|f(() => {
10+
//// g({});
11+
//// g(
12+
//// {});
13+
//// g(
14+
//// {}
15+
//// );
16+
////});|]
17+
18+
verify.codeFixAll({
19+
fixId: "fixMissingProperties",
20+
fixAllDescription: ts.Diagnostics.Add_all_missing_properties.message,
21+
newFileContent: `interface Test {
22+
foo: string;
23+
bar(a: string): void;
24+
}
25+
function f (_spec: any) {}
26+
function g (_spec: Test) {}
27+
f(() => {
28+
g({
29+
foo: "",
30+
bar: function(a: string): void {
31+
throw new Error("Function not implemented.");
32+
}
33+
});
34+
g(
35+
{
36+
foo: "",
37+
bar: function(a: string): void {
38+
throw new Error("Function not implemented.");
39+
}
40+
});
41+
g(
42+
{
43+
foo: "",
44+
bar: function(a: string): void {
45+
throw new Error("Function not implemented.");
46+
}
47+
}
48+
);
49+
});`,
50+
});

0 commit comments

Comments
 (0)