diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index 05ab94c722c..ee12ee59d6d 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -1199,50 +1199,62 @@ class LinkProviderAdapter { private readonly _provider: vscode.DocumentLinkProvider ) { } - provideLinks(resource: URI, token: CancellationToken): Promise { + async provideLinks(resource: URI, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); - return asPromise(() => this._provider.provideDocumentLinks(doc, token)).then(links => { - if (!Array.isArray(links) || links.length === 0) { - // bad result - return undefined; - } + const links = await asPromise(() => this._provider.provideDocumentLinks(doc, token)); + if (!Array.isArray(links) || links.length === 0) { + // bad result + return undefined; + } + if (token.isCancellationRequested) { + // cancelled -> return without further ado, esp no caching + // of results as they will leak + return undefined; + } + if (typeof this._provider.resolveDocumentLink !== 'function') { + // no resolve -> no caching + return { links: links.filter(LinkProviderAdapter._validateLink).map(typeConvert.DocumentLink.from) }; - if (token.isCancellationRequested) { - // cancelled -> return without further ado, esp no caching - // of results as they will leak - return undefined; - } + } else { + // cache links for future resolving + const pid = this._cache.add(links); + const result: extHostProtocol.ILinksListDto = { links: [], id: pid }; + for (let i = 0; i < links.length; i++) { - if (typeof this._provider.resolveDocumentLink !== 'function') { - // no resolve -> no caching - return { links: links.map(typeConvert.DocumentLink.from) }; - - } else { - // cache links for future resolving - const pid = this._cache.add(links); - const result: extHostProtocol.ILinksListDto = { links: [], id: pid }; - for (let i = 0; i < links.length; i++) { - const dto: extHostProtocol.ILinkDto = typeConvert.DocumentLink.from(links[i]); - dto.cacheId = [pid, i]; - result.links.push(dto); + if (!LinkProviderAdapter._validateLink(links[i])) { + continue; } - return result; + + const dto: extHostProtocol.ILinkDto = typeConvert.DocumentLink.from(links[i]); + dto.cacheId = [pid, i]; + result.links.push(dto); } - }); + return result; + } } - resolveLink(id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise { + private static _validateLink(link: vscode.DocumentLink): boolean { + if (link.target && link.target.path.length > 50_000) { + console.warn('DROPPING link because it is too long'); + return false; + } + return true; + } + + async resolveLink(id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise { if (typeof this._provider.resolveDocumentLink !== 'function') { - return Promise.resolve(undefined); + return undefined; } const item = this._cache.get(...id); if (!item) { - return Promise.resolve(undefined); + return undefined; } - return asPromise(() => this._provider.resolveDocumentLink!(item, token)).then(value => { - return value && typeConvert.DocumentLink.from(value) || undefined; - }); + const link = await asPromise(() => this._provider.resolveDocumentLink!(item, token)); + if (!link || LinkProviderAdapter._validateLink(link)) { + return undefined; + } + return typeConvert.DocumentLink.from(link); } releaseLinks(id: number): any {