diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index f0bb5e5ed7d5d..34b531c0d79ab 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -4289,5 +4289,13 @@ "Convert '{0}' to mapped object type": { "category": "Message", "code": 95055 + }, + "Convert namespace import to named imports": { + "category": "Message", + "code": 95056 + }, + "Convert named imports to namespace import": { + "category": "Message", + "code": 95057 } } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index a3da44c5b1fea..82b4aef0dc757 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -223,11 +223,11 @@ namespace ts { } /** - * Returns a value indicating whether a name is unique globally or within the current file + * Returns a value indicating whether a name is unique globally or within the current file. + * Note: This does not consider whether a name appears as a free identifier or not, so at the expression `x.y` this includes both `x` and `y`. */ - export function isFileLevelUniqueName(currentSourceFile: SourceFile, name: string, hasGlobalName?: PrintHandlers["hasGlobalName"]): boolean { - return !(hasGlobalName && hasGlobalName(name)) - && !currentSourceFile.identifiers.has(name); + export function isFileLevelUniqueName(sourceFile: SourceFile, name: string, hasGlobalName?: PrintHandlers["hasGlobalName"]): boolean { + return !(hasGlobalName && hasGlobalName(name)) && !sourceFile.identifiers.has(name); } // Returns true if this node is missing from the actual source code. A 'missing' node is different diff --git a/src/harness/tsconfig.json b/src/harness/tsconfig.json index 102bddf91c7ca..83c571c560169 100644 --- a/src/harness/tsconfig.json +++ b/src/harness/tsconfig.json @@ -120,6 +120,7 @@ "../services/codefixes/useDefaultImport.ts", "../services/codefixes/fixAddModuleReferTypeMissingTypeof.ts", "../services/codefixes/convertToMappedObjectType.ts", + "../services/refactors/convertImport.ts", "../services/refactors/extractSymbol.ts", "../services/refactors/generateGetAccessorAndSetAccessor.ts", "../services/refactors/moveToNewFile.ts", diff --git a/src/server/tsconfig.json b/src/server/tsconfig.json index 79cd76fc2c053..a92a19f9c018a 100644 --- a/src/server/tsconfig.json +++ b/src/server/tsconfig.json @@ -116,6 +116,7 @@ "../services/codefixes/useDefaultImport.ts", "../services/codefixes/fixAddModuleReferTypeMissingTypeof.ts", "../services/codefixes/convertToMappedObjectType.ts", + "../services/refactors/convertImport.ts", "../services/refactors/extractSymbol.ts", "../services/refactors/generateGetAccessorAndSetAccessor.ts", "../services/refactors/moveToNewFile.ts", diff --git a/src/server/tsconfig.library.json b/src/server/tsconfig.library.json index 57f6862cd6f19..1675002c653ad 100644 --- a/src/server/tsconfig.library.json +++ b/src/server/tsconfig.library.json @@ -122,6 +122,7 @@ "../services/codefixes/useDefaultImport.ts", "../services/codefixes/fixAddModuleReferTypeMissingTypeof.ts", "../services/codefixes/convertToMappedObjectType.ts", + "../services/refactors/convertImport.ts", "../services/refactors/extractSymbol.ts", "../services/refactors/generateGetAccessorAndSetAccessor.ts", "../services/refactors/moveToNewFile.ts", diff --git a/src/services/codefixes/convertToEs6Module.ts b/src/services/codefixes/convertToEs6Module.ts index 8eadbc3a306ca..c763d4da0c88e 100644 --- a/src/services/codefixes/convertToEs6Module.ts +++ b/src/services/codefixes/convertToEs6Module.ts @@ -437,15 +437,19 @@ namespace ts.codefix { type FreeIdentifiers = ReadonlyMap>; function collectFreeIdentifiers(file: SourceFile): FreeIdentifiers { const map = createMultiMap(); - file.forEachChild(function recur(node) { - if (isIdentifier(node) && isFreeIdentifier(node)) { - map.add(node.text, node); - } - node.forEachChild(recur); - }); + forEachFreeIdentifier(file, id => map.add(id.text, id)); return map; } + /** + * A free identifier is an identifier that can be accessed through name lookup as a local variable. + * In the expression `x.y`, `x` is a free identifier, but `y` is not. + */ + function forEachFreeIdentifier(node: Node, cb: (id: Identifier) => void): void { + if (isIdentifier(node) && isFreeIdentifier(node)) cb(node); + node.forEachChild(child => forEachFreeIdentifier(child, cb)); + } + function isFreeIdentifier(node: Identifier): boolean { const { parent } = node; switch (parent.kind) { @@ -453,6 +457,8 @@ namespace ts.codefix { return (parent as PropertyAccessExpression).name !== node; case SyntaxKind.BindingElement: return (parent as BindingElement).propertyName !== node; + case SyntaxKind.ImportSpecifier: + return (parent as ImportSpecifier).propertyName !== node; default: return true; } diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index cc13cfd28c4db..6a2b90f78d097 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -712,16 +712,23 @@ namespace ts.FindAllReferences.Core { } /** Used as a quick check for whether a symbol is used at all in a file (besides its definition). */ - export function isSymbolReferencedInFile(definition: Identifier, checker: TypeChecker, sourceFile: SourceFile) { + export function isSymbolReferencedInFile(definition: Identifier, checker: TypeChecker, sourceFile: SourceFile): boolean { + return eachSymbolReferenceInFile(definition, checker, sourceFile, () => true) || false; + } + + export function eachSymbolReferenceInFile(definition: Identifier, checker: TypeChecker, sourceFile: SourceFile, cb: (token: Identifier) => T): T | undefined { const symbol = checker.getSymbolAtLocation(definition); - if (!symbol) return true; // Be lenient with invalid code. - return getPossibleSymbolReferenceNodes(sourceFile, symbol.name).some(token => { - if (!isIdentifier(token) || token === definition || token.escapedText !== definition.escapedText) return false; - const referenceSymbol = checker.getSymbolAtLocation(token)!; - return referenceSymbol === symbol + if (!symbol) return undefined; + for (const token of getPossibleSymbolReferenceNodes(sourceFile, symbol.name)) { + if (!isIdentifier(token) || token === definition || token.escapedText !== definition.escapedText) continue; + const referenceSymbol: Symbol = checker.getSymbolAtLocation(token)!; // See GH#19955 for why the type annotation is necessary + if (referenceSymbol === symbol || checker.getShorthandAssignmentValueSymbol(token.parent) === symbol - || isExportSpecifier(token.parent) && getLocalSymbolForExportSpecifier(token, referenceSymbol, token.parent, checker) === symbol; - }); + || isExportSpecifier(token.parent) && getLocalSymbolForExportSpecifier(token, referenceSymbol, token.parent, checker) === symbol) { + const res = cb(token); + if (res) return res; + } + } } function getPossibleSymbolReferenceNodes(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile): ReadonlyArray { diff --git a/src/services/refactors/convertImport.ts b/src/services/refactors/convertImport.ts new file mode 100644 index 0000000000000..bdd3e7f4a9684 --- /dev/null +++ b/src/services/refactors/convertImport.ts @@ -0,0 +1,130 @@ +/* @internal */ +namespace ts.refactor.generateGetAccessorAndSetAccessor { + const refactorName = "Convert import"; + const actionNameNamespaceToNamed = "Convert namespace import to named imports"; + const actionNameNamedToNamespace = "Convert named imports to namespace import"; + registerRefactor(refactorName, { + getAvailableActions(context): ApplicableRefactorInfo[] | undefined { + const i = getImportToConvert(context); + if (!i) return undefined; + const description = i.kind === SyntaxKind.NamespaceImport ? Diagnostics.Convert_namespace_import_to_named_imports.message : Diagnostics.Convert_named_imports_to_namespace_import.message; + const actionName = i.kind === SyntaxKind.NamespaceImport ? actionNameNamespaceToNamed : actionNameNamedToNamespace; + return [{ name: refactorName, description, actions: [{ name: actionName, description }] }]; + }, + getEditsForAction(context, actionName): RefactorEditInfo { + Debug.assert(actionName === actionNameNamespaceToNamed || actionName === actionNameNamedToNamespace); + const edits = textChanges.ChangeTracker.with(context, t => doChange(context.file, context.program, t, Debug.assertDefined(getImportToConvert(context)))); + return { edits, renameFilename: undefined, renameLocation: undefined }; + } + }); + + // Can convert imports of the form `import * as m from "m";` or `import d, { x, y } from "m";`. + function getImportToConvert(context: RefactorContext): NamedImportBindings | undefined { + const { file } = context; + const span = getRefactorContextSpan(context); + const token = getTokenAtPosition(file, span.start, /*includeJsDocComment*/ false); + const importDecl = getParentNodeInSpan(token, file, span); + if (!importDecl || !isImportDeclaration(importDecl)) return undefined; + const { importClause } = importDecl; + return importClause && importClause.namedBindings; + } + + function doChange(sourceFile: SourceFile, program: Program, changes: textChanges.ChangeTracker, toConvert: NamedImportBindings): void { + const checker = program.getTypeChecker(); + if (toConvert.kind === SyntaxKind.NamespaceImport) { + doChangeNamespaceToNamed(sourceFile, checker, changes, toConvert, getAllowSyntheticDefaultImports(program.getCompilerOptions())); + } + else { + doChangeNamedToNamespace(sourceFile, checker, changes, toConvert); + } + } + + function doChangeNamespaceToNamed(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, toConvert: NamespaceImport, allowSyntheticDefaultImports: boolean): void { + let usedAsNamespaceOrDefault = false; + + const nodesToReplace: PropertyAccessExpression[] = []; + const conflictingNames = createMap(); + + FindAllReferences.Core.eachSymbolReferenceInFile(toConvert.name, checker, sourceFile, id => { + if (!isPropertyAccessExpression(id.parent)) { + usedAsNamespaceOrDefault = true; + } + else { + const parent = cast(id.parent, isPropertyAccessExpression); + const exportName = parent.name.text; + if (checker.resolveName(exportName, id, SymbolFlags.All, /*excludeGlobals*/ true)) { + conflictingNames.set(exportName, true); + } + Debug.assert(parent.expression === id); + nodesToReplace.push(parent); + } + }); + + // We may need to change `mod.x` to `_x` to avoid a name conflict. + const exportNameToImportName = createMap(); + + for (const propertyAccess of nodesToReplace) { + const exportName = propertyAccess.name.text; + let importName = exportNameToImportName.get(exportName); + if (importName === undefined) { + exportNameToImportName.set(exportName, importName = conflictingNames.has(exportName) ? getUniqueName(exportName, sourceFile) : exportName); + } + changes.replaceNode(sourceFile, propertyAccess, createIdentifier(importName)); + } + + const importSpecifiers: ImportSpecifier[] = []; + exportNameToImportName.forEach((name, propertyName) => { + importSpecifiers.push(createImportSpecifier(name === propertyName ? undefined : createIdentifier(propertyName), createIdentifier(name))); + }); + + const importDecl = toConvert.parent.parent; + if (usedAsNamespaceOrDefault && !allowSyntheticDefaultImports) { + // Need to leave the namespace import alone + changes.insertNodeAfter(sourceFile, importDecl, updateImport(importDecl, /*defaultImportName*/ undefined, importSpecifiers)); + } + else { + changes.replaceNode(sourceFile, importDecl, updateImport(importDecl, usedAsNamespaceOrDefault ? createIdentifier(toConvert.name.text) : undefined, importSpecifiers)); + } + } + + function doChangeNamedToNamespace(sourceFile: SourceFile, checker: TypeChecker, changes: textChanges.ChangeTracker, toConvert: NamedImports): void { + const importDecl = toConvert.parent.parent; + const { moduleSpecifier } = importDecl; + + const preferredName = moduleSpecifier && isStringLiteral(moduleSpecifier) ? codefix.moduleSpecifierToValidIdentifier(moduleSpecifier.text, ScriptTarget.ESNext) : "module"; + const namespaceNameConflicts = toConvert.elements.some(element => + FindAllReferences.Core.eachSymbolReferenceInFile(element.name, checker, sourceFile, id => + !!checker.resolveName(preferredName, id, SymbolFlags.All, /*excludeGlobals*/ true)) || false); + const namespaceImportName = namespaceNameConflicts ? getUniqueName(preferredName, sourceFile) : preferredName; + + const neededNamedImports: ImportSpecifier[] = []; + + for (const element of toConvert.elements) { + const propertyName = (element.propertyName || element.name).text; + FindAllReferences.Core.eachSymbolReferenceInFile(element.name, checker, sourceFile, id => { + const access = createPropertyAccess(createIdentifier(namespaceImportName), propertyName); + if (isShorthandPropertyAssignment(id.parent)) { + changes.replaceNode(sourceFile, id.parent, createPropertyAssignment(id.text, access)); + } + else if (isExportSpecifier(id.parent) && !id.parent.propertyName) { + if (!neededNamedImports.some(n => n.name === element.name)) { + neededNamedImports.push(createImportSpecifier(element.propertyName && createIdentifier(element.propertyName.text), createIdentifier(element.name.text))); + } + } + else { + changes.replaceNode(sourceFile, id, access); + } + }); + } + + changes.replaceNode(sourceFile, toConvert, createNamespaceImport(createIdentifier(namespaceImportName))); + if (neededNamedImports.length) { + changes.insertNodeAfter(sourceFile, toConvert.parent.parent, updateImport(importDecl, /*defaultImportName*/ undefined, neededNamedImports)); + } + } + + function updateImport(old: ImportDeclaration, defaultImportName: Identifier | undefined, elements: ReadonlyArray | undefined): ImportDeclaration { + return createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, + createImportClause(defaultImportName, elements && elements.length ? createNamedImports(elements) : undefined), old.moduleSpecifier); + } +} diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 073f3badcd265..3f2794e439daf 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -718,7 +718,7 @@ namespace ts.refactor.extractSymbol { // Make a unique name for the extracted function const file = scope.getSourceFile(); - const functionNameText = getUniqueName(isClassLike(scope) ? "newMethod" : "newFunction", file.text); + const functionNameText = getUniqueName(isClassLike(scope) ? "newMethod" : "newFunction", file); const isJS = isInJavaScriptFile(scope); const functionName = createIdentifier(functionNameText); @@ -1005,7 +1005,7 @@ namespace ts.refactor.extractSymbol { // Make a unique name for the extracted variable const file = scope.getSourceFile(); - const localNameText = getUniqueName(isClassLike(scope) ? "newProperty" : "newLocal", file.text); + const localNameText = getUniqueName(isClassLike(scope) ? "newProperty" : "newLocal", file); const isJS = isInJavaScriptFile(scope); const variableType = isJS || !checker.isContextSensitive(node) @@ -1744,23 +1744,6 @@ namespace ts.refactor.extractSymbol { } } - function getParentNodeInSpan(node: Node | undefined, file: SourceFile, span: TextSpan): Node | undefined { - if (!node) return undefined; - - while (node.parent) { - if (isSourceFile(node.parent) || !spanContainsNode(span, node.parent, file)) { - return node; - } - - node = node.parent; - } - } - - function spanContainsNode(span: TextSpan, node: Node, file: SourceFile): boolean { - return textSpanContainsPosition(span, node.getStart(file)) && - node.getEnd() <= textSpanEnd(span); - } - /** * Computes whether or not a node represents an expression in a position where it could * be extracted. diff --git a/src/services/refactors/generateGetAccessorAndSetAccessor.ts b/src/services/refactors/generateGetAccessorAndSetAccessor.ts index 07104f8e6204c..2e4f11c4b279f 100644 --- a/src/services/refactors/generateGetAccessorAndSetAccessor.ts +++ b/src/services/refactors/generateGetAccessorAndSetAccessor.ts @@ -129,8 +129,8 @@ namespace ts.refactor.generateGetAccessorAndSetAccessor { const name = declaration.name.text; const startWithUnderscore = startsWithUnderscore(name); - const fieldName = createPropertyName(startWithUnderscore ? name : getUniqueName(`_${name}`, file.text), declaration.name); - const accessorName = createPropertyName(startWithUnderscore ? getUniqueName(name.substring(1), file.text) : name, declaration.name); + const fieldName = createPropertyName(startWithUnderscore ? name : getUniqueName(`_${name}`, file), declaration.name); + const accessorName = createPropertyName(startWithUnderscore ? getUniqueName(name.substring(1), file) : name, declaration.name); return { isStatic: hasStaticModifier(declaration), isReadonly: hasReadonlyModifier(declaration), diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 2aabfdcd6a326..7d6687fb987e9 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -113,6 +113,7 @@ "codefixes/useDefaultImport.ts", "codefixes/fixAddModuleReferTypeMissingTypeof.ts", "codefixes/convertToMappedObjectType.ts", + "refactors/convertImport.ts", "refactors/extractSymbol.ts", "refactors/generateGetAccessorAndSetAccessor.ts", "refactors/moveToNewFile.ts", diff --git a/src/services/utilities.ts b/src/services/utilities.ts index de577ec5946cf..941f03455f5ec 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1309,6 +1309,23 @@ namespace ts { return forEachEntry(this.map, pred) || false; } } + + export function getParentNodeInSpan(node: Node | undefined, file: SourceFile, span: TextSpan): Node | undefined { + if (!node) return undefined; + + while (node.parent) { + if (isSourceFile(node.parent) || !spanContainsNode(span, node.parent, file)) { + return node; + } + + node = node.parent; + } + } + + function spanContainsNode(span: TextSpan, node: Node, file: SourceFile): boolean { + return textSpanContainsPosition(span, node.getStart(file)) && + node.getEnd() <= textSpanEnd(span); + } } // Display-part writer helpers @@ -1610,9 +1627,9 @@ namespace ts { } /* @internal */ - export function getUniqueName(baseName: string, fileText: string): string { + export function getUniqueName(baseName: string, sourceFile: SourceFile): string { let nameText = baseName; - for (let i = 1; stringContains(fileText, nameText); i++) { + for (let i = 1; !isFileLevelUniqueName(sourceFile, nameText); i++) { nameText = `${baseName}_${i}`; } return nameText; @@ -1631,7 +1648,7 @@ namespace ts { Debug.assert(fileName === renameFilename); for (const change of textChanges) { const { span, newText } = change; - const index = newText.indexOf(name); + const index = indexInTextChange(newText, name); if (index !== -1) { lastPos = span.start + delta + index; @@ -1649,4 +1666,13 @@ namespace ts { Debug.assert(lastPos >= 0); return lastPos; } + + function indexInTextChange(change: string, name: string): number { + if (startsWith(change, name)) return 0; + // Add a " " to avoid references inside words + let idx = change.indexOf(" " + name); + if (idx === -1) idx = change.indexOf("." + name); + if (idx === -1) idx = change.indexOf('"' + name); + return idx === -1 ? -1 : idx + 1; + } } diff --git a/tests/cases/fourslash/extract-method-uniqueName.ts b/tests/cases/fourslash/extract-method-uniqueName.ts index ae62f4ba7e7ac..218cad66f6867 100644 --- a/tests/cases/fourslash/extract-method-uniqueName.ts +++ b/tests/cases/fourslash/extract-method-uniqueName.ts @@ -1,6 +1,6 @@ /// -////// newFunction +////const newFunction = 0; /////*start*/1 + 1/*end*/; goTo.select('start', 'end') @@ -9,7 +9,7 @@ edit.applyRefactor({ actionName: "function_scope_0", actionDescription: "Extract to function in global scope", newContent: -`// newFunction +`const newFunction = 0; /*RENAME*/newFunction_1(); function newFunction_1() { diff --git a/tests/cases/fourslash/refactorConvertImport_namedToNamespace.ts b/tests/cases/fourslash/refactorConvertImport_namedToNamespace.ts new file mode 100644 index 0000000000000..f9215b8bc449b --- /dev/null +++ b/tests/cases/fourslash/refactorConvertImport_namedToNamespace.ts @@ -0,0 +1,24 @@ +/// + +/////*a*/import { x, y as z, T } from "m";/*b*/ +////const m = 0; +////const o = { x }; +////export { x }; // Need a named import for this +////z; +////const n: T = 0; + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert import", + actionName: "Convert named imports to namespace import", + actionDescription: "Convert named imports to namespace import", + newContent: +// TODO: GH#23781 (m_1.y shouldn't be indented) +`import * as m_1 from "m"; +import { x } from "m"; +const m = 0; +const o = { x: m_1.x }; +export { x }; // Need a named import for this + m_1.y; +const n: m_1.T = 0;`, +}); diff --git a/tests/cases/fourslash/refactorConvertImport_namespaceToNamed.ts b/tests/cases/fourslash/refactorConvertImport_namespaceToNamed.ts new file mode 100644 index 0000000000000..0a13239217e47 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertImport_namespaceToNamed.ts @@ -0,0 +1,18 @@ +/// + +/////*a*/import * as m from "m";/*b*/ +////const a = 0; +////m.a; +////m.b; + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert import", + actionName: "Convert namespace import to named imports", + actionDescription: "Convert namespace import to named imports", + newContent: +`import { a as a_1, b } from "m"; +const a = 0; +a_1; +b;`, +}); diff --git a/tests/cases/fourslash/refactorConvertImport_namespaceToNamed_namespaceUsed.ts b/tests/cases/fourslash/refactorConvertImport_namespaceToNamed_namespaceUsed.ts new file mode 100644 index 0000000000000..4f965d6d7820b --- /dev/null +++ b/tests/cases/fourslash/refactorConvertImport_namespaceToNamed_namespaceUsed.ts @@ -0,0 +1,17 @@ +/// + +/////*a*/import * as m from "m";/*b*/ +////m.a; +////m; + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert import", + actionName: "Convert namespace import to named imports", + actionDescription: "Convert namespace import to named imports", + newContent: +`import * as m from "m"; +import { a } from "m"; +a; +m;`, +}); diff --git a/tests/cases/fourslash/refactorConvertImport_notAtDefaultName.ts b/tests/cases/fourslash/refactorConvertImport_notAtDefaultName.ts new file mode 100644 index 0000000000000..5f62bbb439446 --- /dev/null +++ b/tests/cases/fourslash/refactorConvertImport_notAtDefaultName.ts @@ -0,0 +1,6 @@ +/// + +////import /*a*/d/*b*/, * as n from "m"; + +goTo.select("a", "b"); +verify.not.refactorAvailable("Convert import"); diff --git a/tests/cases/fourslash/refactorConvertImport_useDefault.ts b/tests/cases/fourslash/refactorConvertImport_useDefault.ts new file mode 100644 index 0000000000000..1f4c2dcb11e1e --- /dev/null +++ b/tests/cases/fourslash/refactorConvertImport_useDefault.ts @@ -0,0 +1,16 @@ +/// + +// @allowSyntheticDefaultImports: true + +/////*a*/import * as m from "m";/*b*/ +////m(); + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Convert import", + actionName: "Convert namespace import to named imports", + actionDescription: "Convert namespace import to named imports", + newContent: +`import m from "m"; +m();`, +}); diff --git a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess23.ts b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess23.ts index 32f4bfe009dd1..6c262044a99b3 100644 --- a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess23.ts +++ b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess23.ts @@ -29,10 +29,10 @@ edit.applyRefactor({ actionDescription: "Generate 'get' and 'set' accessors", newContent: `class A { private _a: number = 1; - public get /*RENAME*/a_2(): number { + public get /*RENAME*/a_1(): number { return this._a; } - public set a_2(value: number) { + public set a_1(value: number) { this._a = value; } private _a_1: string = "foo"; diff --git a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess4.ts b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess4.ts index c2d9a7eea7e02..ed01e35964feb 100644 --- a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess4.ts +++ b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess4.ts @@ -11,10 +11,10 @@ edit.applyRefactor({ actionDescription: "Generate 'get' and 'set' accessors", newContent: `class A { private _a: string; - public get /*RENAME*/a_1(): string { + public get /*RENAME*/a(): string { return this._a; } - public set a_1(value: string) { + public set a(value: string) { this._a = value; } }`, diff --git a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess5.ts b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess5.ts index 9d2526d8a7d79..f68726495fecf 100644 --- a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess5.ts +++ b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess5.ts @@ -11,10 +11,10 @@ edit.applyRefactor({ actionDescription: "Generate 'get' and 'set' accessors", newContent: `class A { private _a: string; - public get /*RENAME*/a_1(): string { + public get /*RENAME*/a(): string { return this._a; } - public set a_1(value: string) { + public set a(value: string) { this._a = value; } }`, diff --git a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess6.ts b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess6.ts index b3899c0647a5e..08ddf0e0fd6a7 100644 --- a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess6.ts +++ b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess6.ts @@ -1,8 +1,8 @@ /// -//// class A { -//// /*a*/public _a: string;/*b*/ -//// } +////class A { +//// /*a*/public _a: string;/*b*/ +////} goTo.select("a", "b"); edit.applyRefactor({ @@ -11,10 +11,10 @@ edit.applyRefactor({ actionDescription: "Generate 'get' and 'set' accessors", newContent: `class A { private _a: string; - public get /*RENAME*/a_1(): string { + public get /*RENAME*/a(): string { return this._a; } - public set a_1(value: string) { + public set a(value: string) { this._a = value; } }`, diff --git a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess7.ts b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess7.ts index 114cfd39d8757..ebac8142dc1e2 100644 --- a/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess7.ts +++ b/tests/cases/fourslash/refactorConvertToGetAccessAndSetAccess7.ts @@ -11,10 +11,10 @@ edit.applyRefactor({ actionDescription: "Generate 'get' and 'set' accessors", newContent: `class A { private _a: string; - protected get /*RENAME*/a_1(): string { + protected get /*RENAME*/a(): string { return this._a; } - protected set a_1(value: string) { + protected set a(value: string) { this._a = value; } }`,