Fixes export destructured variables reference (#32007)

* Add basic test for current behavior

* Fixes getting destructured variables references(#31922)

* Add test case that renames destructured property

* Fixes missing nested object destrucuturing variable references
This commit is contained in:
sisisin 2019-08-06 04:00:06 +09:00 committed by Ron Buckton
parent c1e0db7953
commit 33a6509751
5 changed files with 206 additions and 24 deletions

View File

@ -577,10 +577,10 @@ namespace ts.FindAllReferences {
// If a reference is a class expression, the exported node would be its parent.
// If a reference is a variable declaration, the exported node would be the variable statement.
function getExportNode(parent: Node, node: Node): Node | undefined {
if (parent.kind === SyntaxKind.VariableDeclaration) {
const p = parent as VariableDeclaration;
return p.name !== node ? undefined :
p.parent.kind === SyntaxKind.CatchClause ? undefined : p.parent.parent.kind === SyntaxKind.VariableStatement ? p.parent.parent : undefined;
const declaration = isVariableDeclaration(parent) ? parent : isBindingElement(parent) ? walkUpBindingElementsAndPatterns(parent) : undefined;
if (declaration) {
return (parent as VariableDeclaration | BindingElement).name !== node ? undefined :
isCatchClause(declaration.parent) ? undefined : isVariableStatement(declaration.parent.parent) ? declaration.parent.parent : undefined;
}
else {
return parent;

View File

@ -131,6 +131,7 @@
"unittests/tsserver/formatSettings.ts",
"unittests/tsserver/getApplicableRefactors.ts",
"unittests/tsserver/getEditsForFileRename.ts",
"unittests/tsserver/getExportReferences.ts",
"unittests/tsserver/importHelpers.ts",
"unittests/tsserver/inferredProjects.ts",
"unittests/tsserver/languageService.ts",

View File

@ -1,11 +1,4 @@
namespace ts.projectSystem {
interface DocumentSpanFromSubstring {
file: File;
text: string;
options?: SpanFromSubstringOptions;
contextText?: string;
contextOptions?: SpanFromSubstringOptions;
}
function documentSpanFromSubstring({ file, text, contextText, options, contextOptions }: DocumentSpanFromSubstring): DocumentSpan {
const contextSpan = contextText !== undefined ? documentSpanFromSubstring({ file, text: contextText, options: contextOptions }) : undefined;
return {
@ -19,19 +12,6 @@ namespace ts.projectSystem {
return documentSpanFromSubstring(input);
}
interface MakeReferenceItem extends DocumentSpanFromSubstring {
isDefinition: boolean;
lineText: string;
}
function makeReferenceItem({ isDefinition, lineText, ...rest }: MakeReferenceItem): protocol.ReferencesResponseItem {
return {
...protocolFileSpanWithContextFromSubstring(rest),
isDefinition,
isWriteAccess: isDefinition,
lineText,
};
}
interface MakeReferenceEntry extends DocumentSpanFromSubstring {
isDefinition: boolean;
}

View File

@ -0,0 +1,185 @@
namespace ts.projectSystem {
describe("unittests:: tsserver:: getExportReferences", () => {
const exportVariable = "export const value = 0;";
const exportArrayDestructured = "export const [valueA, valueB] = [0, 1];";
const exportObjectDestructured = "export const { valueC, valueD: renamedD } = { valueC: 0, valueD: 1 };";
const exportNestedObject = "export const { nest: [valueE, { valueF }] } = { nest: [0, { valueF: 1 }] };";
const mainTs: File = {
path: "/main.ts",
content: 'import { value, valueA, valueB, valueC, renamedD, valueE, valueF } from "./mod";',
};
const modTs: File = {
path: "/mod.ts",
content: `${exportVariable}
${exportArrayDestructured}
${exportObjectDestructured}
${exportNestedObject}
`,
};
const tsconfig: File = {
path: "/tsconfig.json",
content: "{}",
};
function makeSampleSession() {
const host = createServerHost([mainTs, modTs, tsconfig]);
const session = createSession(host);
openFilesForSession([mainTs, modTs], session);
return session;
}
const referenceMainTs = (mainTs: File, text: string): protocol.ReferencesResponseItem =>
makeReferenceItem({
file: mainTs,
isDefinition: true,
lineText: mainTs.content,
contextText: mainTs.content,
text,
});
const referenceModTs = (
texts: { text: string; lineText: string; contextText?: string },
override: Partial<MakeReferenceItem> = {},
): protocol.ReferencesResponseItem =>
makeReferenceItem({
file: modTs,
isDefinition: true,
...texts,
...override,
});
it("should get const variable declaration references", () => {
const session = makeSampleSession();
const response = executeSessionRequest<protocol.ReferencesRequest, protocol.ReferencesResponse>(
session,
protocol.CommandTypes.References,
protocolFileLocationFromSubstring(modTs, "value"),
);
const expectResponse = {
refs: [
referenceModTs({ text: "value", lineText: exportVariable, contextText: exportVariable }),
referenceMainTs(mainTs, "value"),
],
symbolDisplayString: "const value: 0",
symbolName: "value",
symbolStartOffset: protocolLocationFromSubstring(modTs.content, "value").offset,
};
assert.deepEqual(response, expectResponse);
});
it("should get array destructuring declaration references", () => {
const session = makeSampleSession();
const response = executeSessionRequest<protocol.ReferencesRequest, protocol.ReferencesResponse>(
session,
protocol.CommandTypes.References,
protocolFileLocationFromSubstring(modTs, "valueA"),
);
const expectResponse = {
refs: [
referenceModTs({
text: "valueA",
lineText: exportArrayDestructured,
contextText: exportArrayDestructured,
}),
referenceMainTs(mainTs, "valueA"),
],
symbolDisplayString: "const valueA: number",
symbolName: "valueA",
symbolStartOffset: protocolLocationFromSubstring(modTs.content, "valueA").offset,
};
assert.deepEqual(response, expectResponse);
});
it("should get object destructuring declaration references", () => {
const session = makeSampleSession();
const response = executeSessionRequest<protocol.ReferencesRequest, protocol.ReferencesResponse>(
session,
protocol.CommandTypes.References,
protocolFileLocationFromSubstring(modTs, "valueC"),
);
const expectResponse = {
refs: [
referenceModTs({
text: "valueC",
lineText: exportObjectDestructured,
contextText: exportObjectDestructured,
}),
referenceMainTs(mainTs, "valueC"),
referenceModTs(
{ text: "valueC", lineText: exportObjectDestructured, contextText: "valueC: 0" },
{ options: { index: 1 } },
),
],
symbolDisplayString: "const valueC: number",
symbolName: "valueC",
symbolStartOffset: protocolLocationFromSubstring(modTs.content, "valueC").offset,
};
assert.deepEqual(response, expectResponse);
});
it("should get object declaration references that renames destructured property", () => {
const session = makeSampleSession();
const response = executeSessionRequest<protocol.ReferencesRequest, protocol.ReferencesResponse>(
session,
protocol.CommandTypes.References,
protocolFileLocationFromSubstring(modTs, "renamedD"),
);
const expectResponse = {
refs: [
referenceModTs({
text: "renamedD",
lineText: exportObjectDestructured,
contextText: exportObjectDestructured,
}),
referenceMainTs(mainTs, "renamedD"),
],
symbolDisplayString: "const renamedD: number",
symbolName: "renamedD",
symbolStartOffset: protocolLocationFromSubstring(modTs.content, "renamedD").offset,
};
assert.deepEqual(response, expectResponse);
});
it("should get nested object declaration references", () => {
const session = makeSampleSession();
const response = executeSessionRequest<protocol.ReferencesRequest, protocol.ReferencesResponse>(
session,
protocol.CommandTypes.References,
protocolFileLocationFromSubstring(modTs, "valueF"),
);
const expectResponse = {
refs: [
referenceModTs({
text: "valueF",
lineText: exportNestedObject,
contextText: exportNestedObject,
}),
referenceMainTs(mainTs, "valueF"),
referenceModTs(
{
text: "valueF",
lineText: exportNestedObject,
contextText: "valueF: 1",
},
{ options: { index: 1 } },
),
],
symbolDisplayString: "const valueF: number",
symbolName: "valueF",
symbolStartOffset: protocolLocationFromSubstring(modTs.content, "valueF").offset,
};
assert.deepEqual(response, expectResponse);
});
});
}

View File

@ -519,6 +519,8 @@ namespace ts.projectSystem {
file: File;
text: string;
options?: SpanFromSubstringOptions;
contextText?: string;
contextOptions?: SpanFromSubstringOptions;
}
export function protocolFileSpanFromSubstring({ file, text, options }: DocumentSpanFromSubstring): protocol.FileSpan {
return { file: file.path, ...protocolTextSpanFromSubstring(file.content, text, options) };
@ -727,4 +729,18 @@ namespace ts.projectSystem {
assert.strictEqual(outputs.length, index + 1, JSON.stringify(outputs));
}
}
export interface MakeReferenceItem extends DocumentSpanFromSubstring {
isDefinition: boolean;
lineText: string;
}
export function makeReferenceItem({ isDefinition, lineText, ...rest }: MakeReferenceItem): protocol.ReferencesResponseItem {
return {
...protocolFileSpanWithContextFromSubstring(rest),
isDefinition,
isWriteAccess: isDefinition,
lineText,
};
}
}