Allow to find all references of the 'this 'keyword

This commit is contained in:
Andy Hanson
2016-06-20 07:13:53 -07:00
parent ba78d1e35c
commit e7acef125d
6 changed files with 116 additions and 29 deletions
+5 -5
View File
@@ -760,7 +760,7 @@ namespace FourSlash {
// Find the unaccounted-for reference.
for (const actual of actualReferences) {
if (!ts.forEach(expectedReferences, r => r.start === actual.textSpan.start)) {
this.raiseError(`A reference ${actual} is unaccounted for.`);
this.raiseError(`A reference ${stringify(actual)} is unaccounted for.`);
}
}
// Probably will never reach here.
@@ -907,13 +907,13 @@ namespace FourSlash {
assert.equal(getDisplayPartsJson(actualQuickInfo.documentation), getDisplayPartsJson(documentation), this.messageAtLastKnownMarker("QuickInfo documentation"));
}
public verifyRenameLocations(findInStrings: boolean, findInComments: boolean) {
public verifyRenameLocations(findInStrings: boolean, findInComments: boolean, ranges?: Range[]) {
const renameInfo = this.languageService.getRenameInfo(this.activeFile.fileName, this.currentCaretPosition);
if (renameInfo.canRename) {
let references = this.languageService.findRenameLocations(
this.activeFile.fileName, this.currentCaretPosition, findInStrings, findInComments);
let ranges = this.getRanges();
ranges = ranges || this.getRanges();
if (!references) {
if (ranges.length !== 0) {
@@ -3128,8 +3128,8 @@ namespace FourSlashInterface {
this.state.verifyRenameInfoFailed(message);
}
public renameLocations(findInStrings: boolean, findInComments: boolean) {
this.state.verifyRenameLocations(findInStrings, findInComments);
public renameLocations(findInStrings: boolean, findInComments: boolean, ranges?: FourSlash.Range[]) {
this.state.verifyRenameLocations(findInStrings, findInComments, ranges);
}
public verifyQuickInfoDisplayParts(kind: string, kindModifiers: string, textSpan: { start: number; length: number; },
+48 -13
View File
@@ -5811,17 +5811,32 @@ namespace ts {
return undefined;
}
if (node.kind !== SyntaxKind.Identifier &&
// TODO (drosen): This should be enabled in a later release - currently breaks rename.
// node.kind !== SyntaxKind.ThisKeyword &&
// node.kind !== SyntaxKind.SuperKeyword &&
node.kind !== SyntaxKind.StringLiteral &&
!isLiteralNameOfPropertyDeclarationOrIndexAccess(node)) {
return undefined;
switch (node.kind) {
case SyntaxKind.NumericLiteral:
if (!isLiteralNameOfPropertyDeclarationOrIndexAccess(node)) {
break;
}
// Fallthrough
case SyntaxKind.Identifier:
case SyntaxKind.ThisKeyword:
// case SyntaxKind.SuperKeyword: TODO:GH#9268
case SyntaxKind.StringLiteral:
return getReferencedSymbolsForNode(node, program.getSourceFiles(), findInStrings, findInComments);
}
return undefined;
}
Debug.assert(node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.NumericLiteral || node.kind === SyntaxKind.StringLiteral);
return getReferencedSymbolsForNode(node, program.getSourceFiles(), findInStrings, findInComments);
function isThis(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ThisKeyword:
// case SyntaxKind.ThisType: TODO: GH#9267
return true;
case SyntaxKind.Identifier:
// 'this' as a parameter
return (node as Identifier).originalKeywordKind === SyntaxKind.ThisKeyword && node.parent.kind === SyntaxKind.Parameter;
default:
return false;
}
}
function getReferencedSymbolsForNode(node: Node, sourceFiles: SourceFile[], findInStrings: boolean, findInComments: boolean): ReferencedSymbol[] {
@@ -5841,7 +5856,7 @@ namespace ts {
}
}
if (node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.ThisType) {
if (isThis(node)) {
return getReferencesForThisKeyword(node, sourceFiles);
}
@@ -6376,7 +6391,7 @@ namespace ts {
cancellationToken.throwIfCancellationRequested();
const node = getTouchingWord(sourceFile, position);
if (!node || (node.kind !== SyntaxKind.ThisKeyword && node.kind !== SyntaxKind.ThisType)) {
if (!node || !isThis(node)) {
return;
}
@@ -8003,11 +8018,11 @@ namespace ts {
const node = getTouchingWord(sourceFile, position, /*includeJsDocComment*/ true);
// Can only rename an identifier.
if (node) {
if (node.kind === SyntaxKind.Identifier ||
node.kind === SyntaxKind.StringLiteral ||
isLiteralNameOfPropertyDeclarationOrIndexAccess(node)) {
isLiteralNameOfPropertyDeclarationOrIndexAccess(node) ||
isThis(node)) {
const symbol = typeChecker.getSymbolAtLocation(node);
// Only allow a symbol to be renamed if it actually has at least one declaration.
@@ -8054,6 +8069,26 @@ namespace ts {
}
}
}
else if (node.kind === SyntaxKind.ThisKeyword) {
const container = ts.getThisContainer(node, /*includeArrowFunctions*/ false);
// Only allow rename to change a function with a 'this' type to one with a regular parameter,
// e.g. `function(this: number) { return this; }` to `function(x: number) { return x; }`
if (isFunctionLike(container)) {
const sig = typeChecker.getSignatureFromDeclaration(container);
if (sig.thisType) {
return {
canRename: true,
kind: ScriptElementKind.parameterElement,
displayName: "this",
localizedErrorMessage: undefined,
fullDisplayName: "this",
kindModifiers: "",
triggerSpan: createTriggerSpanForNode(node, sourceFile)
};
}
}
// fallthrough to error
}
}
}