From b41eb1bc611746786204fead1386f34b2bc070b5 Mon Sep 17 00:00:00 2001 From: Alexander T Date: Fri, 13 Mar 2020 19:33:56 +0200 Subject: [PATCH] feat(36249): add quick-fix action to declare a property as private which starts from underscore (#36632) --- src/compiler/diagnosticMessages.json | 4 +++ src/services/codefixes/fixAddMissingMember.ts | 28 +++++++++++++------ .../generateGetAccessorAndSetAccessor.ts | 4 --- src/services/utilities.ts | 4 +++ .../fourslash/codeFixAddMissingMember17.ts | 25 +++++++++++++++++ 5 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 tests/cases/fourslash/codeFixAddMissingMember17.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index eb360f68649..42ea8dce745 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5113,6 +5113,10 @@ "category": "Message", "code": 90034 }, + "Declare private property '{0}'": { + "category": "Message", + "code": 90035 + }, "Declare a private field named '{0}'.": { "category": "Message", "code": 90053 diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 4bbbac4312d..7a5098d6065 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -74,7 +74,7 @@ namespace ts.codefix { } else { const typeNode = getTypeNode(program.getTypeChecker(), parentDeclaration, token); - addPropertyDeclaration(changes, declSourceFile, parentDeclaration, token.text, typeNode, makeStatic); + addPropertyDeclaration(changes, declSourceFile, parentDeclaration, token.text, typeNode, makeStatic ? ModifierFlags.Static : 0); } } } @@ -211,8 +211,17 @@ namespace ts.codefix { function getActionsForAddMissingMemberInTypeScriptFile(context: CodeFixContext, declSourceFile: SourceFile, classDeclaration: ClassOrInterface, token: Identifier | PrivateIdentifier, makeStatic: boolean): CodeFixAction[] | undefined { const typeNode = getTypeNode(context.program.getTypeChecker(), classDeclaration, token); - const addProp = createAddPropertyDeclarationAction(context, declSourceFile, classDeclaration, makeStatic, token.text, typeNode); - return makeStatic || isPrivateIdentifier(token) ? [addProp] : [addProp, createAddIndexSignatureAction(context, declSourceFile, classDeclaration, token.text, typeNode)]; + const actions: CodeFixAction[] = [createAddPropertyDeclarationAction(context, declSourceFile, classDeclaration, token.text, typeNode, makeStatic ? ModifierFlags.Static : 0)]; + if (makeStatic || isPrivateIdentifier(token)) { + return actions; + } + + if (startsWithUnderscore(token.text)) { + actions.unshift(createAddPropertyDeclarationAction(context, declSourceFile, classDeclaration, token.text, typeNode, ModifierFlags.Private)); + } + + actions.push(createAddIndexSignatureAction(context, declSourceFile, classDeclaration, token.text, typeNode)); + return actions; } function getTypeNode(checker: TypeChecker, classDeclaration: ClassOrInterface, token: Node) { @@ -230,15 +239,18 @@ namespace ts.codefix { return typeNode || createKeywordTypeNode(SyntaxKind.AnyKeyword); } - function createAddPropertyDeclarationAction(context: CodeFixContext, declSourceFile: SourceFile, classDeclaration: ClassOrInterface, makeStatic: boolean, tokenName: string, typeNode: TypeNode): CodeFixAction { - const changes = textChanges.ChangeTracker.with(context, t => addPropertyDeclaration(t, declSourceFile, classDeclaration, tokenName, typeNode, makeStatic)); - return createCodeFixAction(fixName, changes, [makeStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0, tokenName], fixId, Diagnostics.Add_all_missing_members); + function createAddPropertyDeclarationAction(context: CodeFixContext, declSourceFile: SourceFile, classDeclaration: ClassOrInterface, tokenName: string, typeNode: TypeNode, modifierFlags: ModifierFlags): CodeFixAction { + const changes = textChanges.ChangeTracker.with(context, t => addPropertyDeclaration(t, declSourceFile, classDeclaration, tokenName, typeNode, modifierFlags)); + if (modifierFlags & ModifierFlags.Private) { + return createCodeFixActionWithoutFixAll(fixName, changes, [Diagnostics.Declare_private_property_0, tokenName]); + } + return createCodeFixAction(fixName, changes, [modifierFlags & ModifierFlags.Static ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0, tokenName], fixId, Diagnostics.Add_all_missing_members); } - function addPropertyDeclaration(changeTracker: textChanges.ChangeTracker, declSourceFile: SourceFile, classDeclaration: ClassOrInterface, tokenName: string, typeNode: TypeNode, makeStatic: boolean): void { + function addPropertyDeclaration(changeTracker: textChanges.ChangeTracker, declSourceFile: SourceFile, classDeclaration: ClassOrInterface, tokenName: string, typeNode: TypeNode, modifierFlags: ModifierFlags): void { const property = createProperty( /*decorators*/ undefined, - /*modifiers*/ makeStatic ? [createToken(SyntaxKind.StaticKeyword)] : undefined, + /*modifiers*/ modifierFlags ? createNodeArray(createModifiersFromModifierFlags(modifierFlags)) : undefined, tokenName, /*questionToken*/ undefined, typeNode, diff --git a/src/services/refactors/generateGetAccessorAndSetAccessor.ts b/src/services/refactors/generateGetAccessorAndSetAccessor.ts index 3a00f528bef..d0d2d11df7e 100644 --- a/src/services/refactors/generateGetAccessorAndSetAccessor.ts +++ b/src/services/refactors/generateGetAccessorAndSetAccessor.ts @@ -112,10 +112,6 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { return modifiers && createNodeArray(modifiers); } - function startsWithUnderscore(name: string): boolean { - return name.charCodeAt(0) === CharacterCodes._; - } - function getConvertibleFieldAtPosition(context: RefactorContext): Info | undefined { const { file, startPosition, endPosition } = context; diff --git a/src/services/utilities.ts b/src/services/utilities.ts index c66e65197a4..7f675b2209f 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2760,5 +2760,9 @@ namespace ts { return symbol.name; } + export function startsWithUnderscore(name: string): boolean { + return name.charCodeAt(0) === CharacterCodes._; + } + // #endregion } diff --git a/tests/cases/fourslash/codeFixAddMissingMember17.ts b/tests/cases/fourslash/codeFixAddMissingMember17.ts new file mode 100644 index 00000000000..a5a81fac2f9 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingMember17.ts @@ -0,0 +1,25 @@ +/// + +////class A { +//// constructor() { +//// this._x = 10; +//// } +////} + +verify.codeFixAvailable([ + { description: "Declare private property '_x'" }, + { description: "Declare property '_x'" }, + { description: "Add index signature for property '_x'" } +]) + +verify.codeFix({ + description: [ts.Diagnostics.Declare_private_property_0.message, "_x"], + index: 0, + newFileContent: +`class A { + private _x: number; + constructor() { + this._x = 10; + } +}` +});