From 12f5b56a24aa1ba81bfdeada80d7fc9173528377 Mon Sep 17 00:00:00 2001 From: Alexander T Date: Wed, 5 Feb 2020 16:28:29 +0200 Subject: [PATCH] feat(36249): add quick-fix action to declare a property as private which starts from underscore --- src/compiler/diagnosticMessages.json | 4 +++ src/services/codefixes/fixAddMissingMember.ts | 28 +++++++++++++------ .../generateGetAccessorAndSetAccessor.ts | 4 --- src/services/utilities.ts | 4 +++ .../fourslash/codeFixAddMissingMember14.ts | 27 ++++++++++++++++++ 5 files changed, 55 insertions(+), 12 deletions(-) create mode 100644 tests/cases/fourslash/codeFixAddMissingMember14.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 2dc894e71fb50..c8183c1cb4ec5 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5073,6 +5073,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 a75e58135e03b..82a01c3987bc4 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); } } } @@ -208,8 +208,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) { @@ -227,15 +236,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 3a00f528bef6a..d0d2d11df7e37 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 655d6d890bad5..521e6c1490540 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -2748,5 +2748,9 @@ namespace ts { return isArray(valueOrArray) ? first(valueOrArray) : valueOrArray; } + export function startsWithUnderscore(name: string): boolean { + return name.charCodeAt(0) === CharacterCodes._; + } + // #endregion } diff --git a/tests/cases/fourslash/codeFixAddMissingMember14.ts b/tests/cases/fourslash/codeFixAddMissingMember14.ts new file mode 100644 index 0000000000000..9064ddc1489b4 --- /dev/null +++ b/tests/cases/fourslash/codeFixAddMissingMember14.ts @@ -0,0 +1,27 @@ +/// + +////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; + } +}` +}); + +