Clean up FAR aggregation (#48619)

* Clean up FAR and RenameLocations

This change had two goals:

1. Make the code easier to understand, primarily by simplifying the callback structure and minimizing side-effects
2. Improve performance by reducing repeated work, both FAR searches of individual projects and default tsconfig searches

This implementation attempts to preserve the merging order found in the original code (someone less relevant in the present state of using syntactic isDefinition).

* Stop enforcing search and aggregation order

...in preparation for implementing isDefinition explicitly.

Also restore convention of referring to `DocumentPosition`s as "locations".

* Introduce LanguageService.updateIsDefinitionOfReferencedSymbols

...to allow use of the checker when computing isDefinition across projects.

* Update baselines

* Tidy diff

* De-dup simplified results

* Baseline cross-project isDefinition results

* Move de-duping upstream to fix Full output

* Add server baseline test to confirm searches are not repeated

* Manually merge #48758

* Update baseline for newer fix to #48963
This commit is contained in:
Andrew Casey
2022-05-18 17:26:17 -07:00
committed by GitHub
parent e56a067801
commit 12ed01203c
36 changed files with 4233 additions and 228 deletions
@@ -400,7 +400,7 @@ namespace ts.projectSystem {
const response = executeSessionRequest<protocol.ReferencesRequest, protocol.ReferencesResponse>(session, protocol.CommandTypes.References, protocolFileLocationFromSubstring(userTs, "fnA()"));
assert.deepEqual<protocol.ReferencesResponseBody | undefined>(response, {
refs: [...referencesUserTs(userTs, /*isDefinition*/ undefined), referenceATs(aTs, /*isDefinition*/ true)], // Presently inconsistent across projects
refs: [...referencesUserTs(userTs, /*isDefinition*/ undefined), referenceATs(aTs, /*isDefinition*/ undefined)],
symbolName: "fnA",
symbolStartOffset: protocolLocationFromSubstring(userTs.content, "fnA()").offset,
symbolDisplayString: "function fnA(): void",
@@ -455,7 +455,7 @@ namespace ts.projectSystem {
},
references: [
makeReferencedSymbolEntry({ file: userTs, text: "fnA" }),
makeReferencedSymbolEntry({ file: aTs, text: "fnA", isDefinition: true, isWriteAccess: true, contextText: "export function fnA() {}" }),
makeReferencedSymbolEntry({ file: aTs, text: "fnA", isWriteAccess: true, contextText: "export function fnA() {}" }),
],
},
]);
@@ -581,6 +581,126 @@ testCompositeFunction('why hello there', 42);`
baselineTsserverLogs("projectReferences", `finding local reference doesnt load ancestor/sibling projects`, session);
});
it("when finding references in overlapping projects", () => {
const solutionLocation = "/user/username/projects/solution";
const solutionConfig: File = {
path: `${solutionLocation}/tsconfig.json`,
content: JSON.stringify({
files: [],
include: [],
references: [
{ path: "./a" },
{ path: "./b" },
{ path: "./c" },
{ path: "./d" },
]
})
};
const aConfig: File = {
path: `${solutionLocation}/a/tsconfig.json`,
content: JSON.stringify({
compilerOptions: {
composite: true,
module: "none"
},
files: ["./index.ts"]
})
};
const aFile: File = {
path: `${solutionLocation}/a/index.ts`,
content: `
export interface I {
M(): void;
}`
};
const bConfig: File = {
path: `${solutionLocation}/b/tsconfig.json`,
content: JSON.stringify({
compilerOptions: {
composite: true
},
files: ["./index.ts"],
references: [
{ path: "../a" }
]
})
};
const bFile: File = {
path: `${solutionLocation}/b/index.ts`,
content: `
import { I } from "../a";
export class B implements I {
M() {}
}`
};
const cConfig: File = {
path: `${solutionLocation}/c/tsconfig.json`,
content: JSON.stringify({
compilerOptions: {
composite: true
},
files: ["./index.ts"],
references: [
{ path: "../b" }
]
})
};
const cFile: File = {
path: `${solutionLocation}/c/index.ts`,
content: `
import { I } from "../a";
import { B } from "../b";
export const C: I = new B();
`
};
const dConfig: File = {
path: `${solutionLocation}/d/tsconfig.json`,
content: JSON.stringify({
compilerOptions: {
composite: true
},
files: ["./index.ts"],
references: [
{ path: "../c" }
]
})
};
const dFile: File = {
path: `${solutionLocation}/d/index.ts`,
content: `
import { I } from "../a";
import { C } from "../c";
export const D: I = C;
`
};
const files = [libFile, solutionConfig, aConfig, aFile, bConfig, bFile, cConfig, cFile, dConfig, dFile, libFile];
const host = createServerHost(files);
const session = createSession(host, { logger: createLoggerWithInMemoryLogs() });
openFilesForSession([bFile], session);
// The first search will trigger project loads
session.executeCommandSeq<protocol.ReferencesRequest>({
command: protocol.CommandTypes.References,
arguments: protocolFileLocationFromSubstring(bFile, "I", { index: 1 })
});
// The second search starts with the projects already loaded
// Formerly, this would search some projects multiple times
session.executeCommandSeq<protocol.ReferencesRequest>({
command: protocol.CommandTypes.References,
arguments: protocolFileLocationFromSubstring(bFile, "I", { index: 1 })
});
baselineTsserverLogs("projectReferences", `finding references in overlapping projects`, session);
});
describe("special handling of localness of the definitions for findAllRefs", () => {
function verify(scenario: string, definition: string, usage: string, referenceTerm: string) {
it(scenario, () => {