(apis: ReadonlyArray<JSDocApi>)
| 2250 | } |
| 2251 | |
| 2252 | function createJSDocApiSeeLinkResolver(apis: ReadonlyArray<JSDocApi>) { |
| 2253 | const byModuleAndName = new Map<string, JSDocApi>() |
| 2254 | const byLocalName = new Map<string, Array<JSDocApi>>() |
| 2255 | const bySourceRange = new Map<string, JSDocApi>() |
| 2256 | const bySourceFile = new Map<string, Array<JSDocApi>>() |
| 2257 | for (const api of apis) { |
| 2258 | byModuleAndName.set(`${api.moduleName}:${api.apiName}`, api) |
| 2259 | bySourceRange.set(sourceRangeKey(api.source.file, api.source.range), api) |
| 2260 | const sourceApis = bySourceFile.get(api.source.file) ?? [] |
| 2261 | sourceApis.push(api) |
| 2262 | bySourceFile.set(api.source.file, sourceApis) |
| 2263 | for (const name of new Set([api.apiName, api.localName])) { |
| 2264 | const existing = byLocalName.get(name) ?? [] |
| 2265 | existing.push(api) |
| 2266 | byLocalName.set(name, existing) |
| 2267 | } |
| 2268 | } |
| 2269 | const resolveSymbol = (link: ParsedInlineLink): JSDocApiSeeLinkResolution | undefined => { |
| 2270 | if (link.symbol === undefined) return undefined |
| 2271 | const symbol = link.symbol |
| 2272 | const exact = bySourceRange.get(sourceRangeKey(symbol.file, symbol.range)) |
| 2273 | if (exact !== undefined) return { _tag: "Resolved", apiId: exact.id, apiFqn: exact.apiFqn } |
| 2274 | const names = seeTargetNames(link) |
| 2275 | const candidates = (bySourceFile.get(symbol.file) ?? []).filter((api) => |
| 2276 | apiMatchesSeeTarget(api, names) && |
| 2277 | (rangeContains(symbol.range, api.source.range) || |
| 2278 | rangeContains(api.source.range, symbol.range) || |
| 2279 | rangeOverlaps(symbol.range, api.source.range)) |
| 2280 | ).sort((self, that) => rangeWidth(self.source.range) - rangeWidth(that.source.range)) |
| 2281 | const unique = Array.from(new Map(candidates.map((candidate) => [candidate.id, candidate])).values()) |
| 2282 | if (unique.length === 1) return { _tag: "Resolved", apiId: unique[0].id, apiFqn: unique[0].apiFqn } |
| 2283 | if (unique.length > 1) { |
| 2284 | return { _tag: "Unresolved", reason: "ambiguous", candidates: unique.map((candidate) => candidate.id) } |
| 2285 | } |
| 2286 | const sameFileByName = (bySourceFile.get(symbol.file) ?? []).filter((api) => apiMatchesSeeTarget(api, names)) |
| 2287 | const uniqueByName = Array.from(new Map(sameFileByName.map((candidate) => [candidate.id, candidate])).values()) |
| 2288 | if (uniqueByName.length === 1) { |
| 2289 | return { _tag: "Resolved", apiId: uniqueByName[0].id, apiFqn: uniqueByName[0].apiFqn } |
| 2290 | } |
| 2291 | if (uniqueByName.length > 1) { |
| 2292 | return { _tag: "Unresolved", reason: "ambiguous", candidates: uniqueByName.map((candidate) => candidate.id) } |
| 2293 | } |
| 2294 | return undefined |
| 2295 | } |
| 2296 | return ( |
| 2297 | context: { readonly moduleName: string; readonly namespacePath: ReadonlyArray<string> }, |
| 2298 | link: ParsedInlineLink |
| 2299 | ): JSDocApiSeeLinkResolution => { |
| 2300 | const symbolResolution = resolveSymbol(link) |
| 2301 | if (symbolResolution !== undefined) return symbolResolution |
| 2302 | const targets = new Set<string>() |
| 2303 | for (const target of normalizeSeeTarget(link.target)) targets.add(target) |
| 2304 | if (link.text !== null) { |
| 2305 | for (const target of normalizeSeeTarget(link.text)) targets.add(target) |
| 2306 | } |
| 2307 | for (const target of targets) { |
| 2308 | const sameModule = byModuleAndName.get(`${context.moduleName}:${target}`) |
| 2309 | if (sameModule !== undefined) return { _tag: "Resolved", apiId: sameModule.id, apiFqn: sameModule.apiFqn } |
no test coverage detected