mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-06 20:14:01 -06:00
Allow to find all references of the 'this 'keyword
This commit is contained in:
parent
ba78d1e35c
commit
e7acef125d
@ -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; },
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
33
tests/cases/fourslash/findAllRefsThisKeyword.ts
Normal file
33
tests/cases/fourslash/findAllRefsThisKeyword.ts
Normal file
@ -0,0 +1,33 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
// @noLib: true
|
||||
|
||||
////[|this|];
|
||||
////function f([|this|]) {
|
||||
//// return [|this|];
|
||||
//// function g([|this|]) { return [|this|]; }
|
||||
////}
|
||||
////class C {
|
||||
//// static x() {
|
||||
//// [|this|];
|
||||
//// }
|
||||
//// static y() {
|
||||
//// () => [|this|];
|
||||
//// }
|
||||
//// constructor() {
|
||||
//// [|this|];
|
||||
//// }
|
||||
//// method() {
|
||||
//// () => [|this|];
|
||||
//// }
|
||||
////}
|
||||
////// These are *not* real uses of the 'this' keyword, they are identifiers.
|
||||
////const x = { [|this|]: 0 }
|
||||
////x.[|this|];
|
||||
|
||||
const [global, f0, f1, g0, g1, x, y, constructor, method, propDef, propUse] = test.ranges();
|
||||
verify.referencesOf(global, [global]);
|
||||
verify.rangesReferenceEachOther([f0, f1]);
|
||||
verify.rangesReferenceEachOther([g0, g1]);
|
||||
verify.rangesReferenceEachOther([x, y]);
|
||||
verify.rangesReferenceEachOther([constructor, method]);
|
||||
verify.rangesReferenceEachOther([propDef, propUse]);
|
||||
@ -1,18 +1,15 @@
|
||||
/// <reference path='fourslash.ts' />
|
||||
|
||||
// @Filename: file1.ts
|
||||
////this; this;
|
||||
////[|this|]; [|this|];
|
||||
|
||||
// @Filename: file2.ts
|
||||
////this;
|
||||
////this;
|
||||
////[|this|];
|
||||
////[|this|];
|
||||
|
||||
// @Filename: file3.ts
|
||||
//// ((x = this, y) => t/**/his)(this, this);
|
||||
//// ((x = [|this|], y) => [|this|])([|this|], [|this|]);
|
||||
//// // different 'this'
|
||||
//// function f(this) { return this; }
|
||||
|
||||
goTo.file("file1.ts");
|
||||
goTo.marker();
|
||||
|
||||
// TODO (drosen): The CURRENT behavior is that findAllRefs doesn't work on 'this' or 'super' keywords.
|
||||
// This should change down the line.
|
||||
verify.referencesCountIs(0);
|
||||
verify.rangesReferenceEachOther();
|
||||
|
||||
@ -216,7 +216,7 @@ declare namespace FourSlashInterface {
|
||||
}[]): void;
|
||||
renameInfoSucceeded(displayName?: string, fullDisplayName?: string, kind?: string, kindModifiers?: string): void;
|
||||
renameInfoFailed(message?: string): void;
|
||||
renameLocations(findInStrings: boolean, findInComments: boolean): void;
|
||||
renameLocations(findInStrings: boolean, findInComments: boolean, ranges?: Range[]): void;
|
||||
verifyQuickInfoDisplayParts(kind: string, kindModifiers: string, textSpan: {
|
||||
start: number;
|
||||
length: number;
|
||||
|
||||
22
tests/cases/fourslash/renameThis.ts
Normal file
22
tests/cases/fourslash/renameThis.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/// <reference path='fourslash.ts'/>
|
||||
|
||||
////function f([|this|]) {
|
||||
//// return [|this|];
|
||||
////}
|
||||
////this/**/;
|
||||
////const _ = { [|this|]: 0 }.[|this|];
|
||||
|
||||
let [r0, r1, r2, r3] = test.ranges()
|
||||
for (let range of [r0, r1]) {
|
||||
goTo.position(range.start);
|
||||
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false, [r0, r1]);
|
||||
}
|
||||
|
||||
// Trying to rename a legitimate 'this' should fail
|
||||
goTo.marker();
|
||||
verify.renameInfoFailed("You cannot rename this element.");
|
||||
|
||||
for (let range of [r2, r3]) {
|
||||
goTo.position(range.start);
|
||||
verify.renameLocations(/*findInStrings*/ false, /*findInComments*/ false, [r2, r3]);
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user