Skip to content

Commit

Permalink
fix refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
Kingwl committed Feb 26, 2018
1 parent 5133b68 commit e39a889
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 31 deletions.
5 changes: 2 additions & 3 deletions src/services/codefixes/fixStrictClassInitialization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,8 @@ namespace ts.codefix {
}

function addUndefinedType(changeTracker: textChanges.ChangeTracker, propertyDeclarationSourceFile: SourceFile, propertyDeclaration: PropertyDeclaration): void {
const undefinedTypeNode = createKeywordTypeNode(SyntaxKind.UndefinedKeyword);
const types = isUnionTypeNode(propertyDeclaration.type) ? propertyDeclaration.type.types.concat(undefinedTypeNode) : [propertyDeclaration.type, undefinedTypeNode];
changeTracker.replaceNode(propertyDeclarationSourceFile, propertyDeclaration.type, createUnionTypeNode(types));
const type = mergeTypeNodeToUnion(propertyDeclaration.type, createKeywordTypeNode(SyntaxKind.UndefinedKeyword));
changeTracker.replaceNode(propertyDeclarationSourceFile, propertyDeclaration.type, type);
}

function getActionForAddMissingInitializer (context: CodeFixContext, propertyDeclaration: PropertyDeclaration, newLineCharacter: string): CodeFixAction | undefined {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
/* @internal */
namespace ts.refactor.GenerateGetterAndSetter {
namespace ts.refactor.generateGetAccessorAndSetAccessor {
const actionName = "Generate 'get' and 'set' accessors";
const actionDescription = Diagnostics.Generate_get_and_set_accessors.message;

registerRefactor(actionName, { getEditsForAction, getAvailableActions });

interface Info {
originalName: string;
fieldName: string;
accessorName: string;
accessorType: TypeNode;
propertyDeclaration: PropertyDeclaration;
needUpdateName: boolean;
hasModifiers: boolean;
needUpdateModifiers: boolean;
}

function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined {
const { file, startPosition } = context;

Expand Down Expand Up @@ -34,15 +44,17 @@ namespace ts.refactor.GenerateGetterAndSetter {
const changeTracker = textChanges.ChangeTracker.fromContext(context);
const newLineCharacter = getNewLineOrDefaultFromHost(context.host, context.formatContext.options);

const { fieldName, accessorName, propertyDeclaration, needUpdateName, hasModifiers, needUpdateModifiers } = fieldInfo;
const accessorModifiers = hasModifiers ? createNodeArray([createToken(SyntaxKind.PublicKeyword)]) : undefined;
const { fieldName, accessorName, accessorType, propertyDeclaration, needUpdateName, hasModifiers, needUpdateModifiers } = fieldInfo;
const accessorModifiers = hasModifiers ? (
(getModifierFlags(propertyDeclaration) & ModifierFlags.Private || !propertyDeclaration.modifiers) ? createNodeArray([createToken(SyntaxKind.PublicKeyword)]) : propertyDeclaration.modifiers
) : undefined;

const getAccessor = generateGetAccessor(propertyDeclaration, fieldName, accessorName, accessorModifiers);
const setAccessor = generateSetAccessor(propertyDeclaration, fieldName, accessorName, accessorModifiers);
const getAccessor = generateGetAccessor(fieldName, accessorName, accessorType, accessorModifiers);
const setAccessor = generateSetAccessor(fieldName, accessorName, accessorType, accessorModifiers);

const modifiers = needUpdateModifiers ? createNodeArray([createToken(SyntaxKind.PrivateKeyword)]) : propertyDeclaration.modifiers;
const modifiers = hasModifiers ? createNodeArray([createToken(SyntaxKind.PrivateKeyword)]) : undefined;
if (needUpdateName || needUpdateModifiers) {
changeTracker.replaceNode(file, propertyDeclaration, updateOriginPropertyDeclaration(propertyDeclaration, fieldName, modifiers), {
changeTracker.replaceNode(file, propertyDeclaration, updateoriginalPropertyDeclaration(propertyDeclaration, fieldName, modifiers), {
suffix: newLineCharacter
});
}
Expand All @@ -57,13 +69,13 @@ namespace ts.refactor.GenerateGetterAndSetter {
};
}

interface Info { originName: string; fieldName: string; accessorName: string; propertyDeclaration: PropertyDeclaration; needUpdateName: boolean; hasModifiers: boolean; needUpdateModifiers: boolean; }
function getConvertibleFieldAtPosition(file: SourceFile, startPosition: number): Info | undefined {
const node = getTokenAtPosition(file, startPosition, /*includeJsDocComment*/ false);
const propertyDeclaration = findAncestor(node.parent, isPropertyDeclaration);

if (!(propertyDeclaration && propertyDeclaration.name.kind === SyntaxKind.Identifier &&
(getModifierFlags(propertyDeclaration) | ModifierFlags.AccessibilityModifier) === ModifierFlags.AccessibilityModifier)) return undefined;
if (!propertyDeclaration || propertyDeclaration.name.kind !== SyntaxKind.Identifier) return undefined;
// make sure propertyDeclaration have only AccessibilityModifier
if ((getModifierFlags(propertyDeclaration) | ModifierFlags.AccessibilityModifier) !== ModifierFlags.AccessibilityModifier) return undefined;

const containerClass = getContainingClass(propertyDeclaration);
if (!containerClass) return undefined;
Expand All @@ -79,26 +91,28 @@ namespace ts.refactor.GenerateGetterAndSetter {
if (find(members, member => needUpdateName ? member.name.getText() === fieldName : member.name.getText() === accessorName)) return undefined;

const hasModifiers = !!find(members, member => !!member.modifiers);
const needUpdateModifiers = hasModifiers && (!propertyDeclaration.modifiers || hasModifier(propertyDeclaration, ModifierFlags.Public));
const needUpdateModifiers = hasModifiers && (!propertyDeclaration.modifiers || !hasModifier(propertyDeclaration, ModifierFlags.Private));
const accessorType = propertyDeclaration.questionToken ? mergeTypeNodeToUnion(propertyDeclaration.type, createKeywordTypeNode(SyntaxKind.UndefinedKeyword)) : propertyDeclaration.type;

return {
originName: propertyDeclaration.name.text,
originalName: propertyDeclaration.name.text,
fieldName,
accessorName,
accessorType,
propertyDeclaration,
needUpdateName,
hasModifiers,
needUpdateModifiers
};
}

function generateGetAccessor (propertyDeclaration: PropertyDeclaration, fieldName: string, name: string, modifiers: ModifiersArray) {
function generateGetAccessor (fieldName: string, name: string, type: TypeNode, modifiers: ModifiersArray) {
return createGetAccessor(
/*decorators*/ undefined,
modifiers,
name,
/*parameters*/ undefined,
propertyDeclaration.type,
type,
createBlock([
createReturn(
createPropertyAccess(
Expand All @@ -110,7 +124,7 @@ namespace ts.refactor.GenerateGetterAndSetter {
);
}

function generateSetAccessor (propertyDeclaration: PropertyDeclaration, fieldName: string, name: string, modifiers: ModifiersArray) {
function generateSetAccessor (fieldName: string, name: string, type: TypeNode, modifiers: ModifiersArray) {
return createSetAccessor(
/*decorators*/ undefined,
modifiers,
Expand All @@ -121,7 +135,7 @@ namespace ts.refactor.GenerateGetterAndSetter {
/*dotDotDotToken*/ undefined,
createIdentifier("value"),
/*questionToken*/ undefined,
propertyDeclaration.type
type
)],
createBlock([
createStatement(
Expand All @@ -137,15 +151,15 @@ namespace ts.refactor.GenerateGetterAndSetter {
);
}

function updateOriginPropertyDeclaration (propertyDeclaration: PropertyDeclaration, fieldName: string, modifiers: ModifiersArray) {
function updateoriginalPropertyDeclaration (propertyDeclaration: PropertyDeclaration, fieldName: string, modifiers: ModifiersArray) {
return updateProperty(
propertyDeclaration,
/*decorators*/ undefined,
propertyDeclaration.decorators,
modifiers,
fieldName,
/*questionOrExclamationToken*/ undefined,
propertyDeclaration.questionToken || propertyDeclaration.exclamationToken,
propertyDeclaration.type,
propertyDeclaration.initializer,
propertyDeclaration.initializer
);
}
}
2 changes: 1 addition & 1 deletion src/services/refactors/refactors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@
/// <reference path="extractSymbol.ts" />
/// <reference path="installTypesForPackage.ts" />
/// <reference path="useDefaultImport.ts" />
/// <reference path="GenerateGetterAndSetter.ts" />
/// <reference path="generateGetAccessorAndSetAccessor.ts" />
4 changes: 4 additions & 0 deletions src/services/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,10 @@ namespace ts {
getExternalModuleImportEqualsDeclarationExpression(node.parent.parent) === node;
}

export function mergeTypeNodeToUnion(target: TypeNode, ...types: TypeNode[]) {
return createUnionTypeNode(isUnionTypeNode(target) ? target.types.concat(types) : [target].concat(types));
}

export function getContainerNode(node: Node): Declaration {
if (node.kind === SyntaxKind.JSDocTypedefTag) {
// This doesn't just apply to the node immediately under the comment, but to everything in its parent's scope.
Expand Down
23 changes: 23 additions & 0 deletions tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess12.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/// <reference path='fourslash.ts' />

//// class A {
//// @foo
//// /*a*/public a: string = "foo";/*b*/
//// }

goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Generate 'get' and 'set' accessors",
actionName: "Generate 'get' and 'set' accessors",
actionDescription: "Generate 'get' and 'set' accessors",
newContent: `class A {
@foo
private _a: string = "foo";
public get a(): string {
return this._a;
}
public set a(value: string) {
this._a = value;
}
}`,
});
21 changes: 21 additions & 0 deletions tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess13.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/// <reference path='fourslash.ts' />

//// class A {
//// /*a*/public a?: string = "foo";/*b*/
//// }

goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Generate 'get' and 'set' accessors",
actionName: "Generate 'get' and 'set' accessors",
actionDescription: "Generate 'get' and 'set' accessors",
newContent: `class A {
private _a?: string = "foo";
public get a(): string | undefined {
return this._a;
}
public set a(value: string | undefined) {
this._a = value;
}
}`,
});
21 changes: 21 additions & 0 deletions tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess14.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/// <reference path='fourslash.ts' />

//// class A {
//// /*a*/public a!: string = "foo";/*b*/
//// }

goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Generate 'get' and 'set' accessors",
actionName: "Generate 'get' and 'set' accessors",
actionDescription: "Generate 'get' and 'set' accessors",
newContent: `class A {
private _a!: string = "foo";
public get a(): string {
return this._a;
}
public set a(value: string) {
this._a = value;
}
}`,
});
16 changes: 16 additions & 0 deletions tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess15.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/// <reference path='fourslash.ts' />

//// class A {
//// /*a*/public "a": string = "foo";/*b*/
//// /*c*/public get b/*d*/ () { return 1; }
//// /*e*/public set b/*f*/ (v) { }
//// }

goTo.select("a", "b");
verify.not.refactorAvailable("Generate 'get' and 'set' accessors");

goTo.select("c", "d");
verify.not.refactorAvailable("Generate 'get' and 'set' accessors");

goTo.select("e", "f");
verify.not.refactorAvailable("Generate 'get' and 'set' accessors");
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/// <reference path='fourslash.ts' />

//// class A {
//// /*a*/public readonly a: string = "foo";/*b*/
//// /*c*/public static a: string = "foo";/*d*/
//// }

goTo.select("a", "b");
verify.not.refactorAvailable("Generate 'get' and 'set' accessors");
11 changes: 11 additions & 0 deletions tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess17.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/// <reference path='fourslash.ts' />

//// class A {
//// public _a: number = 1;
//// /*a*/public a: string = "foo";/*b*/
//// public b: number = 2;
//// /*c*/public _b: string = "foo";/*d*/
//// }

goTo.select("a", "b");
verify.not.refactorAvailable("Generate 'get' and 'set' accessors");
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ edit.applyRefactor({
actionName: "Generate 'get' and 'set' accessors",
actionDescription: "Generate 'get' and 'set' accessors",
newContent: `class A {
protected _a: string;
public get a(): string {
private _a: string;
protected get a(): string {
return this._a;
}
public set a(value: string) {
protected set a(value: string) {
this._a = value;
}
}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ edit.applyRefactor({
actionName: "Generate 'get' and 'set' accessors",
actionDescription: "Generate 'get' and 'set' accessors",
newContent: `class A {
protected _a: string;
public get a(): string {
private _a: string;
protected get a(): string {
return this._a;
}
public set a(value: string) {
protected set a(value: string) {
this._a = value;
}
}`,
Expand Down

0 comments on commit e39a889

Please sign in to comment.