Skip to content

Commit 4d5978d

Browse files
authored
Apply disableReferencedProjectLoad to getOriginalLocationEnsuringConfiguredProject (microsoft#44836)
* Apply disableReferencedProjectLoad to getOriginalLocationEnsuringConfiguredProject * Reuse previously computed values and refine comments * Add baselines for test matrix
1 parent bb7de99 commit 4d5978d

File tree

18 files changed

+1841
-4
lines changed

18 files changed

+1841
-4
lines changed

src/server/editorServices.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3224,20 +3224,39 @@ namespace ts.server {
32243224

32253225
/*@internal*/
32263226
getOriginalLocationEnsuringConfiguredProject(project: Project, location: DocumentPosition): DocumentPosition | undefined {
3227-
const originalLocation = project.isSourceOfProjectReferenceRedirect(location.fileName) ?
3227+
const isSourceOfProjectReferenceRedirect = project.isSourceOfProjectReferenceRedirect(location.fileName);
3228+
const originalLocation = isSourceOfProjectReferenceRedirect ?
32283229
location :
32293230
project.getSourceMapper().tryGetSourcePosition(location);
32303231
if (!originalLocation) return undefined;
32313232

32323233
const { fileName } = originalLocation;
3233-
if (!this.getScriptInfo(fileName) && !this.host.fileExists(fileName)) return undefined;
3234+
const scriptInfo = this.getScriptInfo(fileName);
3235+
if (!scriptInfo && !this.host.fileExists(fileName)) return undefined;
32343236

32353237
const originalFileInfo: OriginalFileInfo = { fileName: toNormalizedPath(fileName), path: this.toPath(fileName) };
32363238
const configFileName = this.getConfigFileNameForFile(originalFileInfo);
32373239
if (!configFileName) return undefined;
32383240

3239-
let configuredProject: ConfiguredProject | undefined = this.findConfiguredProjectByProjectName(configFileName) ||
3240-
this.createAndLoadConfiguredProject(configFileName, `Creating project for original file: ${originalFileInfo.fileName}${location !== originalLocation ? " for location: " + location.fileName : ""}`);
3241+
let configuredProject: ConfiguredProject | undefined = this.findConfiguredProjectByProjectName(configFileName);
3242+
if (!configuredProject) {
3243+
if (project.getCompilerOptions().disableReferencedProjectLoad) {
3244+
// If location was a project reference redirect, then `location` and `originalLocation` are the same.
3245+
if (isSourceOfProjectReferenceRedirect) {
3246+
return location;
3247+
}
3248+
3249+
// Otherwise, if we found `originalLocation` via a source map instead, then we check whether it's in
3250+
// an open project. If it is, we should search the containing project(s), even though the "default"
3251+
// configured project isn't open. However, if it's not in an open project, we need to stick with
3252+
// `location` (i.e. the .d.ts file) because otherwise we'll miss the references in that file.
3253+
return scriptInfo?.containingProjects.length
3254+
? originalLocation
3255+
: location;
3256+
}
3257+
3258+
configuredProject = this.createAndLoadConfiguredProject(configFileName, `Creating project for original file: ${originalFileInfo.fileName}${location !== originalLocation ? " for location: " + location.fileName : ""}`);
3259+
}
32413260
updateProjectIfDirty(configuredProject);
32423261

32433262
const projectContainsOriginalInfo = (project: ConfiguredProject) => {

src/testRunner/unittests/tsserver/projectReferences.ts

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,5 +1353,120 @@ bar;`
13531353
});
13541354
baselineTsserverLogs("projectReferences", `when files from two projects are open and one project references`, session);
13551355
});
1356+
1357+
describe("find refs to symbol from other project", () => {
1358+
const indexA: File = {
1359+
path: `${tscWatch.projectRoot}/a/index.ts`,
1360+
content: `import { B } from "../b/lib";
1361+
1362+
const b: B = new B();`
1363+
};
1364+
1365+
const configB: File = {
1366+
path: `${tscWatch.projectRoot}/b/tsconfig.json`,
1367+
content: `{
1368+
"compilerOptions": {
1369+
"declarationMap": true,
1370+
"outDir": "lib",
1371+
"composite": true
1372+
}
1373+
}`
1374+
};
1375+
1376+
const indexB: File = {
1377+
path: `${tscWatch.projectRoot}/b/index.ts`,
1378+
content: `export class B {
1379+
M() {}
1380+
}`
1381+
};
1382+
1383+
const helperB: File = {
1384+
path: `${tscWatch.projectRoot}/b/helper.ts`,
1385+
content: `import { B } from ".";
1386+
1387+
const b: B = new B();`
1388+
};
1389+
1390+
const dtsB: File = {
1391+
path: `${tscWatch.projectRoot}/b/lib/index.d.ts`,
1392+
content: `export declare class B {
1393+
M(): void;
1394+
}
1395+
//# sourceMappingURL=index.d.ts.map`
1396+
};
1397+
1398+
const dtsMapB: File = {
1399+
path: `${tscWatch.projectRoot}/b/lib/index.d.ts.map`,
1400+
content: `{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,qBAAa,CAAC;IACV,CAAC;CACJ"}`
1401+
};
1402+
1403+
function baselineDisableReferencedProjectLoad(
1404+
projectAlreadyLoaded: boolean,
1405+
disableReferencedProjectLoad: boolean,
1406+
disableSourceOfProjectReferenceRedirect: boolean,
1407+
dtsMapPresent: boolean) {
1408+
1409+
// Mangled to stay under windows path length limit
1410+
const subScenario =
1411+
`when proj ${projectAlreadyLoaded ? "is" : "is not"} loaded ` +
1412+
` and refd proj loading is ${disableReferencedProjectLoad ? "disabled" : "enabled"}` +
1413+
` and proj ref redirects are ${disableSourceOfProjectReferenceRedirect ? "disabled" : "enabled"}` +
1414+
` and a decl map is ${dtsMapPresent ? "present" : "missing"}`;
1415+
const compilerOptions: CompilerOptions = {
1416+
disableReferencedProjectLoad,
1417+
disableSourceOfProjectReferenceRedirect,
1418+
composite: true
1419+
};
1420+
1421+
it(subScenario, () => {
1422+
const configA: File = {
1423+
path: `${tscWatch.projectRoot}/a/tsconfig.json`,
1424+
content: `{
1425+
"compilerOptions": ${JSON.stringify(compilerOptions)},
1426+
"references": [{ "path": "../b" }]
1427+
}`
1428+
};
1429+
1430+
const host = createServerHost([configA, indexA, configB, indexB, helperB, dtsB, ...(dtsMapPresent ? [dtsMapB] : [])]);
1431+
const session = createSession(host, { logger: createLoggerWithInMemoryLogs() });
1432+
openFilesForSession([indexA, ...(projectAlreadyLoaded ? [helperB] : [])], session);
1433+
1434+
session.executeCommandSeq<protocol.ReferencesRequest>({
1435+
command: protocol.CommandTypes.References,
1436+
arguments: protocolFileLocationFromSubstring(indexA, `B`, { index: 1 })
1437+
});
1438+
baselineTsserverLogs("projectReferences", `find all references to a symbol declared in another project ${subScenario}`, session);
1439+
});
1440+
}
1441+
1442+
/* eslint-disable boolean-trivia */
1443+
1444+
// Pre-loaded = A file from project B is already open when FAR is invoked
1445+
// dRPL = Project A has disableReferencedProjectLoad
1446+
// dSOPRR = Project A has disableSourceOfProjectReferenceRedirect
1447+
// Map = The declaration map file b/lib/index.d.ts.map exists
1448+
// B refs = files under directory b in which references are found (all scenarios find all references in a/index.ts)
1449+
1450+
// Pre-loaded | dRPL | dSOPRR | Map | B state | Notes | B refs | Notes
1451+
// -----------+--------+--------+----------+------------+--------------+---------------------+---------------------------------------------------
1452+
baselineDisableReferencedProjectLoad(true, true, true, true); // Pre-loaded | | index.ts, helper.ts | Via map and pre-loaded project
1453+
baselineDisableReferencedProjectLoad(true, true, true, false); // Pre-loaded | | lib/index.d.ts | Even though project is loaded
1454+
baselineDisableReferencedProjectLoad(true, true, false, true); // Pre-loaded | | index.ts, helper.ts |
1455+
baselineDisableReferencedProjectLoad(true, true, false, false); // Pre-loaded | | index.ts, helper.ts |
1456+
baselineDisableReferencedProjectLoad(true, false, true, true); // Pre-loaded | | index.ts, helper.ts | Via map and pre-loaded project
1457+
baselineDisableReferencedProjectLoad(true, false, true, false); // Pre-loaded | | lib/index.d.ts | Even though project is loaded
1458+
baselineDisableReferencedProjectLoad(true, false, false, true); // Pre-loaded | | index.ts, helper.ts |
1459+
baselineDisableReferencedProjectLoad(true, false, false, false); // Pre-loaded | | index.ts, helper.ts |
1460+
baselineDisableReferencedProjectLoad(false, true, true, true); // Not loaded | | lib/index.d.ts | Even though map is present
1461+
baselineDisableReferencedProjectLoad(false, true, true, false); // Not loaded | | lib/index.d.ts |
1462+
baselineDisableReferencedProjectLoad(false, true, false, true); // Not loaded | | index.ts | But not helper.ts, which is not referenced from a
1463+
baselineDisableReferencedProjectLoad(false, true, false, false); // Not loaded | | index.ts | But not helper.ts, which is not referenced from a
1464+
baselineDisableReferencedProjectLoad(false, false, true, true); // Loaded | Via map | index.ts, helper.ts | Via map and newly loaded project
1465+
baselineDisableReferencedProjectLoad(false, false, true, false); // Not loaded | | lib/index.d.ts |
1466+
baselineDisableReferencedProjectLoad(false, false, false, true); // Loaded | Via redirect | index.ts, helper.ts |
1467+
baselineDisableReferencedProjectLoad(false, false, false, false); // Loaded | Via redirect | index.ts, helper.ts |
1468+
1469+
/* eslint-enable boolean-trivia */
1470+
});
13561471
});
13571472
}

0 commit comments

Comments
 (0)