diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index df515e7a8c8..f1458091df5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -65,7 +65,8 @@ module ts { symbolToString: symbolToString, getAugmentedPropertiesOfApparentType: getAugmentedPropertiesOfApparentType, getRootSymbol: getRootSymbol, - getContextualType: getContextualType + getContextualType: getContextualType, + getFullyQualifiedName: getFullyQualifiedName }; var undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined"); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index c7893f8d997..82fac6b8913 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -634,6 +634,7 @@ module ts { getApparentType(type: Type): ApparentType; typeToString(type: Type, enclosingDeclaration?: Node, flags?: TypeFormatFlags): string; symbolToString(symbol: Symbol, enclosingDeclaration?: Node, meaning?: SymbolFlags): string; + getFullyQualifiedName(symbol: Symbol): string; getAugmentedPropertiesOfApparentType(type: Type): Symbol[]; getRootSymbol(symbol: Symbol): Symbol; getContextualType(node: Node): Type; diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 360dc3df51e..ed6dc1a71f7 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -898,6 +898,44 @@ module FourSlash { } } + private validate(name: string, expected: string, actual: string) { + if (expected && expected !== actual) { + throw new Error("Expected " + name + " '" + expected + "'. Got '" + actual + "' instead."); + } + } + + public verifyRenameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string) { + var renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition); + if (!renameInfo.canRename) { + throw new Error("Rename did not succeed"); + } + + this.validate("displayName", displayName, renameInfo.displayName); + this.validate("fullDisplayName", fullDisplayName, renameInfo.fullDisplayName); + this.validate("kind", kind, renameInfo.kind); + this.validate("kindModifiers", kindModifiers, renameInfo.kindModifiers); + + if (this.getRanges().length !== 1) { + throw new Error("Expected a single range to be selected in the test file."); + } + + var expectedRange = this.getRanges()[0]; + if (renameInfo.triggerSpan.start() !== expectedRange.start || + renameInfo.triggerSpan.end() !== expectedRange.end) { + throw new Error("Expected triggerSpan [" + expectedRange.start + "," + expectedRange.end + "). Got [" + + renameInfo.triggerSpan.start() + "," + renameInfo.triggerSpan.end() + ") instead."); + } + } + + public verifyRenameInfoFailed(message?: string) { + var renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition); + if (renameInfo.canRename) { + throw new Error("Rename was expected to fail"); + } + + this.validate("error", message, renameInfo.localizedErrorMessage); + } + //private getFormalParameter() { // var help = this.languageService.getSignatureHelpItems(this.activeFile.fileName, this.currentCaretPosition); // return help.formal; diff --git a/src/services/services.ts b/src/services/services.ts index 6742e86dc38..94fae880d25 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3896,6 +3896,32 @@ module ts { return null; } + function getRenameInfo(fileName: string, position: number): RenameInfo { + synchronizeHostData(); + + fileName = TypeScript.switchToForwardSlashes(fileName); + var sourceFile = getSourceFile(fileName); + + var node = getNodeAtPosition(sourceFile, position); + + // Can only rename an identifier. + if (node && node.kind === SyntaxKind.Identifier) { + var symbol = typeInfoResolver.getSymbolInfo(node); + + // Only allow a symbol to be renamed if it actually has at least one declaration. + if (symbol && symbol.getDeclarations() && symbol.getDeclarations().length > 0) { + var kind = getSymbolKind(symbol); + if (kind) { + return RenameInfo.Create(symbol.name, typeInfoResolver.getFullyQualifiedName(symbol), kind, + getNodeModifiers(symbol.getDeclarations()[0]), + new TypeScript.TextSpan(node.getStart(), node.getWidth())); + } + } + } + + return RenameInfo.CreateError(getLocaleSpecificMessage(Diagnostics.You_cannot_rename_this_element.key)); + } + return { dispose: dispose, cleanupSemanticCache: cleanupSemanticCache, @@ -3916,7 +3942,7 @@ module ts { getNameOrDottedNameSpan: getNameOrDottedNameSpan, getBreakpointStatementAtPosition: getBreakpointStatementAtPosition, getNavigateToItems: getNavigateToItems, - getRenameInfo: (fileName, position): RenameInfo => RenameInfo.CreateError(getLocaleSpecificMessage(Diagnostics.You_cannot_rename_this_element.key)), + getRenameInfo: getRenameInfo, getNavigationBarItems: getNavigationBarItems, getOutliningSpans: getOutliningSpans, getTodoComments: getTodoComments, diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index ccbaa34b900..f9476ee4d92 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -404,6 +404,14 @@ module FourSlashInterface { public semanticClassificationsAre(...classifications: { classificationType: string; text: string }[]) { FourSlash.currentTestState.verifySemanticClassifications(classifications); } + + public renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string) { + FourSlash.currentTestState.verifyRenameInfoSucceeded(displayName, fullDisplayName, kind, kindModifiers) + } + + public renameInfoFailed(message?: string) { + FourSlash.currentTestState.verifyRenameInfoFailed(message) + } } export class edit { diff --git a/tests/cases/fourslash/getRenameInfoTests1.ts b/tests/cases/fourslash/getRenameInfoTests1.ts new file mode 100644 index 00000000000..6f4300282d6 --- /dev/null +++ b/tests/cases/fourslash/getRenameInfoTests1.ts @@ -0,0 +1,8 @@ +/// + +////class [|/**/C|] { +//// +////} + +goTo.marker(""); +verify.renameInfoSucceeded("C"); \ No newline at end of file diff --git a/tests/cases/fourslash/getRenameInfoTests2.ts b/tests/cases/fourslash/getRenameInfoTests2.ts new file mode 100644 index 00000000000..93e8e451b4a --- /dev/null +++ b/tests/cases/fourslash/getRenameInfoTests2.ts @@ -0,0 +1,8 @@ +/// + +/////**/class C { +//// +////} + +goTo.marker(""); +verify.renameInfoFailed(); \ No newline at end of file