From c81d66998c319782014811e0bdcf8149cfff949a Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 26 Oct 2024 15:00:27 +0800 Subject: [PATCH 1/2] fix(typescript): certain features are only allowed to return results in contiguous mapped ranges --- .../lib/node/proxyLanguageService.ts | 62 +++++++++---------- packages/typescript/lib/node/transform.ts | 31 ++++++---- 2 files changed, 50 insertions(+), 43 deletions(-) diff --git a/packages/typescript/lib/node/proxyLanguageService.ts b/packages/typescript/lib/node/proxyLanguageService.ts index 892976cf..7b20cbd3 100644 --- a/packages/typescript/lib/node/proxyLanguageService.ts +++ b/packages/typescript/lib/node/proxyLanguageService.ts @@ -149,7 +149,7 @@ function getFormattingEditsForDocument(language: Language, getFormatting } const edits = getFormattingEditsForDocument(targetScript.id, options); return edits - .map(edit => transformTextChange(sourceScript, language, serviceScript, edit, isFormattingEnabled)?.[1]) + .map(edit => transformTextChange(sourceScript, language, serviceScript, edit, false, isFormattingEnabled)?.[1]) .filter(edit => !!edit); } else { @@ -170,7 +170,7 @@ function getFormattingEditsForRange(language: Language, getFormattingEdi if (generateStart !== undefined && generateEnd !== undefined) { const edits = getFormattingEditsForRange(targetScript.id, generateStart, generateEnd, options); return edits - .map(edit => transformTextChange(sourceScript, language, serviceScript, edit, isFormattingEnabled)?.[1]) + .map(edit => transformTextChange(sourceScript, language, serviceScript, edit, false, isFormattingEnabled)?.[1]) .filter(edit => !!edit); } return []; @@ -192,7 +192,7 @@ function getFormattingEditsAfterKeystroke(language: Language, getFormatt if (generatePosition !== undefined) { const edits = getFormattingEditsAfterKeystroke(targetScript.id, generatePosition, key, options); return edits - .map(edit => transformTextChange(sourceScript, language, serviceScript, edit, isFormattingEnabled)?.[1]) + .map(edit => transformTextChange(sourceScript, language, serviceScript, edit, false, isFormattingEnabled)?.[1]) .filter(edit => !!edit); } return []; @@ -205,7 +205,7 @@ function getFormattingEditsAfterKeystroke(language: Language, getFormatt function getEditsForFileRename(language: Language, getEditsForFileRename: ts.LanguageService['getEditsForFileRename']): ts.LanguageService['getEditsForFileRename'] { return (oldFilePath, newFilePath, formatOptions, preferences) => { const edits = getEditsForFileRename(oldFilePath, newFilePath, formatOptions, preferences); - return transformFileTextChanges(language, edits, isRenameEnabled); + return transformFileTextChanges(language, edits, false, isRenameEnabled); }; } function getLinkedEditingRangeAtPosition(language: Language, getLinkedEditingRangeAtPosition: ts.LanguageService['getLinkedEditingRangeAtPosition']): ts.LanguageService['getLinkedEditingRangeAtPosition'] { @@ -222,7 +222,7 @@ function getLinkedEditingRangeAtPosition(language: Language, getLinkedEd if (info) { return { ranges: info.ranges - .map(span => transformTextSpan(sourceScript, language, serviceScript, span, isLinkedEditingEnabled)?.[1]) + .map(span => transformTextSpan(sourceScript, language, serviceScript, span, false, isLinkedEditingEnabled)?.[1]) .filter(span => !!span), wordPattern: info.wordPattern, }; @@ -279,7 +279,7 @@ function provideCallHierarchyIncomingCalls(language: Language, provideCa .map(call => { const from = transformCallHierarchyItem(language, call.from, isCallHierarchyEnabled); const fromSpans = call.fromSpans - .map(span => transformSpan(language, call.from.file, span, isCallHierarchyEnabled)?.textSpan) + .map(span => transformSpan(language, call.from.file, span, false, isCallHierarchyEnabled)?.textSpan) .filter(span => !!span); return { from, @@ -310,7 +310,7 @@ function provideCallHierarchyOutgoingCalls(language: Language, provideCa const to = transformCallHierarchyItem(language, call.to, isCallHierarchyEnabled); const fromSpans = call.fromSpans .map(span => serviceScript - ? transformTextSpan(sourceScript, language, serviceScript, span, isCallHierarchyEnabled)?.[1] + ? transformTextSpan(sourceScript, language, serviceScript, span, false, isCallHierarchyEnabled)?.[1] : span ) .filter(span => !!span); @@ -324,7 +324,7 @@ function provideCallHierarchyOutgoingCalls(language: Language, provideCa function organizeImports(language: Language, organizeImports: ts.LanguageService['organizeImports']): ts.LanguageService['organizeImports'] { return (args, formatOptions, preferences) => { const unresolved = organizeImports(args, formatOptions, preferences); - return transformFileTextChanges(language, unresolved, isCodeActionsEnabled); + return transformFileTextChanges(language, unresolved, false, isCodeActionsEnabled); }; } function getQuickInfoAtPosition(language: Language, getQuickInfoAtPosition: ts.LanguageService['getQuickInfoAtPosition']): ts.LanguageService['getQuickInfoAtPosition'] { @@ -339,7 +339,7 @@ function getQuickInfoAtPosition(language: Language, getQuickInfoAtPositi for (const [generatePosition] of toGeneratedOffsets(language, serviceScript, sourceScript, position, isHoverEnabled)) { const info = getQuickInfoAtPosition(targetScript.id, generatePosition); if (info) { - const textSpan = transformTextSpan(sourceScript, language, serviceScript, info.textSpan, isHoverEnabled)?.[1]; + const textSpan = transformTextSpan(sourceScript, language, serviceScript, info.textSpan, true, isHoverEnabled)?.[1]; if (textSpan) { infos.push({ ...info, @@ -404,7 +404,7 @@ function getSignatureHelpItems(language: Language, getSignatureHelpItems if (generatePosition !== undefined) { const result = getSignatureHelpItems(targetScript.id, generatePosition, options); if (result) { - const applicableSpan = transformTextSpan(sourceScript, language, serviceScript, result.applicableSpan, isSignatureHelpEnabled)?.[1]; + const applicableSpan = transformTextSpan(sourceScript, language, serviceScript, result.applicableSpan, true, isSignatureHelpEnabled)?.[1]; if (applicableSpan) { return { ...result, @@ -443,11 +443,11 @@ function getDocumentHighlights(language: Language, getDocumentHighlights ...highlights, highlightSpans: highlights.highlightSpans .map(span => { - const { textSpan } = transformSpan(language, span.fileName ?? highlights.fileName, span.textSpan, isHighlightEnabled) ?? {}; + const { textSpan } = transformSpan(language, span.fileName ?? highlights.fileName, span.textSpan, false, isHighlightEnabled) ?? {}; if (textSpan) { return { ...span, - contextSpan: transformSpan(language, span.fileName ?? highlights.fileName, span.contextSpan, isHighlightEnabled)?.textSpan, + contextSpan: transformSpan(language, span.fileName ?? highlights.fileName, span.contextSpan, false, isHighlightEnabled)?.textSpan, textSpan, }; } @@ -509,7 +509,7 @@ function getEditsForRefactor(language: Language, getEditsForRefactor: ts edits = getEditsForRefactor(fileName, formatOptions, positionOrRange, refactorName, actionName, preferences); } if (edits) { - edits.edits = transformFileTextChanges(language, edits.edits, isCodeActionsEnabled); + edits.edits = transformFileTextChanges(language, edits.edits, false, isCodeActionsEnabled); return edits; } }; @@ -517,7 +517,7 @@ function getEditsForRefactor(language: Language, getEditsForRefactor: ts function getCombinedCodeFix(language: Language, getCombinedCodeFix: ts.LanguageService['getCombinedCodeFix']): ts.LanguageService['getCombinedCodeFix'] { return (...args) => { const codeActions = getCombinedCodeFix(...args); - codeActions.changes = transformFileTextChanges(language, codeActions.changes, isCodeActionsEnabled); + codeActions.changes = transformFileTextChanges(language, codeActions.changes, false, isCodeActionsEnabled); return codeActions; }; } @@ -536,7 +536,7 @@ function getRenameInfo(language: Language, getRenameInfo: ts.LanguageSer for (const [generateOffset] of toGeneratedOffsets(language, serviceScript, sourceScript, position, isRenameEnabled)) { const info = getRenameInfo(targetScript.id, generateOffset, options); if (info.canRename) { - const span = transformTextSpan(sourceScript, language, serviceScript, info.triggerSpan, isRenameEnabled)?.[1]; + const span = transformTextSpan(sourceScript, language, serviceScript, info.triggerSpan, false, isRenameEnabled)?.[1]; if (span) { info.triggerSpan = span; return info; @@ -585,7 +585,7 @@ function getCodeFixesAtPosition(language: Language, getCodeFixesAtPositi fixes = getCodeFixesAtPosition(fileName, start, end, errorCodes, formatOptions, preferences); } fixes = fixes.map(fix => { - fix.changes = transformFileTextChanges(language, fix.changes, isCodeActionsEnabled); + fix.changes = transformFileTextChanges(language, fix.changes, false, isCodeActionsEnabled); return fix; }); return fixes; @@ -622,7 +622,7 @@ function getEncodedSemanticClassifications(language: Language, getEncode const result = getEncodedSemanticClassifications(targetScript.id, { start, length: end - start }, format); const spans: number[] = []; for (let i = 0; i < result.spans.length; i += 3) { - for (const [_, sourceStart, sourceEnd] of toSourceRanges(sourceScript, language, serviceScript, result.spans[i], result.spans[i] + result.spans[i + 1], isSemanticTokensEnabled)) { + for (const [_, sourceStart, sourceEnd] of toSourceRanges(sourceScript, language, serviceScript, result.spans[i], result.spans[i] + result.spans[i + 1], false, isSemanticTokensEnabled)) { spans.push( sourceStart, sourceEnd - sourceStart, @@ -694,14 +694,14 @@ function getDefinitionAndBoundSpan(language: Language, getDefinitionAndB } ); const textSpan = unresolved - .map(s => transformSpan(language, fileName, s.textSpan, isDefinitionEnabled)?.textSpan) + .map(s => transformSpan(language, fileName, s.textSpan, true, isDefinitionEnabled)?.textSpan) .filter(s => !!s)[0]; if (!textSpan) { return; } const definitions = unresolved .map(s => s.definitions - ?.map(s => transformDocumentSpan(language, s, isDefinitionEnabled, s.fileName !== fileName)) + ?.map(s => transformDocumentSpan(language, s, true, isDefinitionEnabled, s.fileName !== fileName)) .filter(s => !!s) ?? [] ) @@ -732,11 +732,11 @@ function findReferences(language: Language, findReferences: ts.LanguageS const resolved: ts.ReferencedSymbol[] = unresolved .flat() .map(symbol => { - const definition = transformDocumentSpan(language, symbol.definition, isDefinitionEnabled, true)!; + const definition = transformDocumentSpan(language, symbol.definition, true, isDefinitionEnabled, true)!; return { definition, references: symbol.references - .map(r => transformDocumentSpan(language, r, isReferencesEnabled)) + .map(r => transformDocumentSpan(language, r, true, isReferencesEnabled)) .filter(r => !!r), }; }); @@ -760,7 +760,7 @@ function getDefinitionAtPosition(language: Language, getDefinitionAtPosi ); const resolved = unresolved .flat() - .map(s => transformDocumentSpan(language, s, isDefinitionEnabled, s.fileName !== fileName)) + .map(s => transformDocumentSpan(language, s, true, isDefinitionEnabled, s.fileName !== fileName)) .filter(s => !!s); return dedupeDocumentSpans(resolved); }; @@ -782,7 +782,7 @@ function getTypeDefinitionAtPosition(language: Language, getTypeDefiniti ); const resolved = unresolved .flat() - .map(s => transformDocumentSpan(language, s, isTypeDefinitionEnabled)) + .map(s => transformDocumentSpan(language, s, true, isTypeDefinitionEnabled)) .filter(s => !!s); return dedupeDocumentSpans(resolved); }; @@ -804,7 +804,7 @@ function getImplementationAtPosition(language: Language, getImplementati ); const resolved = unresolved .flat() - .map(s => transformDocumentSpan(language, s, isImplementationEnabled)) + .map(s => transformDocumentSpan(language, s, true, isImplementationEnabled)) .filter(s => !!s); return dedupeDocumentSpans(resolved); }; @@ -826,7 +826,7 @@ function findRenameLocations(language: Language, findRenameLocations: ts ); const resolved = unresolved .flat() - .map(s => transformDocumentSpan(language, s, isRenameEnabled)) + .map(s => transformDocumentSpan(language, s, false, isRenameEnabled)) .filter(s => !!s); return dedupeDocumentSpans(resolved); }; @@ -848,7 +848,7 @@ function getReferencesAtPosition(language: Language, getReferencesAtPosi ); const resolved = unresolved .flat() - .map(s => transformDocumentSpan(language, s, isReferencesEnabled)) + .map(s => transformDocumentSpan(language, s, true, isReferencesEnabled)) .filter(s => !!s); return dedupeDocumentSpans(resolved); }; @@ -877,10 +877,10 @@ function getCompletionsAtPosition(language: Language, getCompletionsAtPo result.entries = result.entries.filter(entry => !!entry.sourceDisplay); } for (const entry of result.entries) { - entry.replacementSpan = entry.replacementSpan && transformTextSpan(sourceScript, language, serviceScript, entry.replacementSpan, isCompletionEnabled)?.[1]; + entry.replacementSpan = entry.replacementSpan && transformTextSpan(sourceScript, language, serviceScript, entry.replacementSpan, false, isCompletionEnabled)?.[1]; } result.optionalReplacementSpan = result.optionalReplacementSpan - && transformTextSpan(sourceScript, language, serviceScript, result.optionalReplacementSpan, isCompletionEnabled)?.[1]; + && transformTextSpan(sourceScript, language, serviceScript, result.optionalReplacementSpan, false, isCompletionEnabled)?.[1]; if (isAdditional) { additionalResults.push(result); } @@ -928,7 +928,7 @@ function getCompletionEntryDetails(language: Language, getCompletionEntr if (details?.codeActions) { for (const codeAction of details.codeActions) { - codeAction.changes = transformFileTextChanges(language, codeAction.changes, isCompletionEnabled); + codeAction.changes = transformFileTextChanges(language, codeAction.changes, false, isCompletionEnabled); } } @@ -999,7 +999,7 @@ function getFileReferences(language: Language, getFileReferences: ts.Lan const fileName = filePath.replace(windowsPathReg, '/'); const unresolved = getFileReferences(fileName); const resolved = unresolved - .map(s => transformDocumentSpan(language, s, isReferencesEnabled)) + .map(s => transformDocumentSpan(language, s, true, isReferencesEnabled)) .filter(s => !!s); return dedupeDocumentSpans(resolved); }; @@ -1008,7 +1008,7 @@ function getNavigateToItems(language: Language, getNavigateToItems: ts.L return (...args) => { const unresolved = getNavigateToItems(...args); const resolved = unresolved - .map(s => transformDocumentSpan(language, s, isReferencesEnabled)) + .map(s => transformDocumentSpan(language, s, true, isReferencesEnabled)) .filter(s => !!s); return dedupeDocumentSpans(resolved); }; diff --git a/packages/typescript/lib/node/transform.ts b/packages/typescript/lib/node/transform.ts index 407a5d88..59110615 100644 --- a/packages/typescript/lib/node/transform.ts +++ b/packages/typescript/lib/node/transform.ts @@ -13,8 +13,8 @@ export function transformCallHierarchyItem( item: ts.CallHierarchyItem, filter: (data: CodeInformation) => boolean ): ts.CallHierarchyItem { - const span = transformSpan(language, item.file, item.span, filter); - const selectionSpan = transformSpan(language, item.file, item.selectionSpan, filter); + const span = transformSpan(language, item.file, item.span, false, filter); + const selectionSpan = transformSpan(language, item.file, item.selectionSpan, false, filter); return { ...item, file: span?.fileName ?? item.file, @@ -54,6 +54,7 @@ export function transformDiagnostic( start: diagnostic.start, length: diagnostic.length }, + true, data => shouldReportDiagnostics(data, String(diagnostic.source), String(diagnostic.code)) ) ?? []; const actualDiagnosticFile = sourceSpanFileName @@ -101,6 +102,7 @@ export function fillSourceFileText(language: Language, sourceFile: ts.So export function transformFileTextChanges( language: Language, changes: readonly ts.FileTextChanges[], + fallbackToAnyMatch: boolean, filter: (data: CodeInformation) => boolean ): ts.FileTextChanges[] { const changesPerFile: { [fileName: string]: TextChange[]; } = {}; @@ -109,7 +111,7 @@ export function transformFileTextChanges( const [_, source] = getServiceScript(language, fileChanges.fileName); if (source) { fileChanges.textChanges.forEach(c => { - const { fileName, textSpan } = transformSpan(language, fileChanges.fileName, c.span, filter) ?? {}; + const { fileName, textSpan } = transformSpan(language, fileChanges.fileName, c.span, fallbackToAnyMatch, filter) ?? {}; if (fileName && textSpan) { (changesPerFile[fileName] ?? (changesPerFile[fileName] = [])).push({ ...c, span: textSpan }); } @@ -139,10 +141,11 @@ export function transformFileTextChanges( export function transformDocumentSpan( language: Language, documentSpan: T, + fallbackToAnyMatch: boolean, filter: (data: CodeInformation) => boolean, shouldFallback?: boolean ): T | undefined { - let textSpan = transformSpan(language, documentSpan.fileName, documentSpan.textSpan, filter); + let textSpan = transformSpan(language, documentSpan.fileName, documentSpan.textSpan, fallbackToAnyMatch, filter); if (!textSpan && shouldFallback) { textSpan = { fileName: documentSpan.fileName, @@ -152,9 +155,9 @@ export function transformDocumentSpan( if (!textSpan) { return; } - const contextSpan = transformSpan(language, documentSpan.fileName, documentSpan.contextSpan, filter); - const originalTextSpan = transformSpan(language, documentSpan.originalFileName, documentSpan.originalTextSpan, filter); - const originalContextSpan = transformSpan(language, documentSpan.originalFileName, documentSpan.originalContextSpan, filter); + const contextSpan = transformSpan(language, documentSpan.fileName, documentSpan.contextSpan, fallbackToAnyMatch, filter); + const originalTextSpan = transformSpan(language, documentSpan.originalFileName, documentSpan.originalTextSpan, fallbackToAnyMatch, filter); + const originalContextSpan = transformSpan(language, documentSpan.originalFileName, documentSpan.originalContextSpan, fallbackToAnyMatch, filter); return { ...documentSpan, fileName: textSpan.fileName, @@ -170,6 +173,7 @@ export function transformSpan( language: Language, fileName: string | undefined, textSpan: ts.TextSpan | undefined, + fallbackToAnyMatch: boolean, filter: (data: CodeInformation) => boolean ): { fileName: string; @@ -180,7 +184,7 @@ export function transformSpan( } const [serviceScript] = getServiceScript(language, fileName); if (serviceScript) { - const [sourceSpanFileName, sourceSpan] = transformTextSpan(undefined, language, serviceScript, textSpan, filter) ?? []; + const [sourceSpanFileName, sourceSpan] = transformTextSpan(undefined, language, serviceScript, textSpan, fallbackToAnyMatch, filter) ?? []; if (sourceSpan && sourceSpanFileName) { return { fileName: sourceSpanFileName, @@ -201,9 +205,10 @@ export function transformTextChange( language: Language, serviceScript: TypeScriptServiceScript, textChange: ts.TextChange, + fallbackToAnyMatch: boolean, filter: (data: CodeInformation) => boolean ): [string, ts.TextChange] | undefined { - const [sourceSpanFileName, sourceSpan] = transformTextSpan(sourceScript, language, serviceScript, textChange.span, filter) ?? []; + const [sourceSpanFileName, sourceSpan] = transformTextSpan(sourceScript, language, serviceScript, textChange.span, fallbackToAnyMatch, filter) ?? []; if (sourceSpan && sourceSpanFileName) { return [sourceSpanFileName, { newText: textChange.newText, @@ -218,11 +223,12 @@ export function transformTextSpan( language: Language, serviceScript: TypeScriptServiceScript, textSpan: ts.TextSpan, + fallbackToAnyMatch: boolean, filter: (data: CodeInformation) => boolean ): [string, ts.TextSpan] | undefined { const start = textSpan.start; const end = textSpan.start + textSpan.length; - for (const [fileName, sourceStart, sourceEnd] of toSourceRanges(sourceScript, language, serviceScript, start, end, filter)) { + for (const [fileName, sourceStart, sourceEnd] of toSourceRanges(sourceScript, language, serviceScript, start, end, fallbackToAnyMatch, filter)) { return [fileName, { start: sourceStart, length: sourceEnd - sourceStart, @@ -248,6 +254,7 @@ export function* toSourceRanges( serviceScript: TypeScriptServiceScript, start: number, end: number, + fallbackToAnyMatch: boolean, filter: (data: CodeInformation) => boolean ): Generator<[fileName: string, start: number, end: number]> { if (sourceScript) { @@ -255,7 +262,7 @@ export function* toSourceRanges( for (const [sourceStart, sourceEnd] of map.toSourceRange( start - getMappingOffset(language, serviceScript), end - getMappingOffset(language, serviceScript), - true, + fallbackToAnyMatch, filter )) { yield [sourceScript.id, sourceStart, sourceEnd]; @@ -266,7 +273,7 @@ export function* toSourceRanges( for (const [sourceStart, sourceEnd] of map.toSourceRange( start - getMappingOffset(language, serviceScript), end - getMappingOffset(language, serviceScript), - true, + fallbackToAnyMatch, filter )) { yield [sourceScript.id, sourceStart, sourceEnd]; From 4e7fca8cd9a25f8f0bc776ae092feb257eeba7cf Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 26 Oct 2024 15:14:14 +0800 Subject: [PATCH 2/2] update --- packages/typescript/lib/node/proxyLanguageService.ts | 8 ++++---- packages/typescript/lib/node/transform.ts | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/typescript/lib/node/proxyLanguageService.ts b/packages/typescript/lib/node/proxyLanguageService.ts index 7b20cbd3..8f006f72 100644 --- a/packages/typescript/lib/node/proxyLanguageService.ts +++ b/packages/typescript/lib/node/proxyLanguageService.ts @@ -246,10 +246,10 @@ function prepareCallHierarchy(language: Language, prepareCallHierarchy: if (generatePosition !== undefined) { const item = prepareCallHierarchy(targetScript.id, generatePosition); if (Array.isArray(item)) { - return item.map(item => transformCallHierarchyItem(language, item, isCallHierarchyEnabled)); + return item.map(item => transformCallHierarchyItem(language, item, false, isCallHierarchyEnabled)); } else if (item) { - return transformCallHierarchyItem(language, item, isCallHierarchyEnabled); + return transformCallHierarchyItem(language, item, false, isCallHierarchyEnabled); } } } @@ -277,7 +277,7 @@ function provideCallHierarchyIncomingCalls(language: Language, provideCa } return calls .map(call => { - const from = transformCallHierarchyItem(language, call.from, isCallHierarchyEnabled); + const from = transformCallHierarchyItem(language, call.from, false, isCallHierarchyEnabled); const fromSpans = call.fromSpans .map(span => transformSpan(language, call.from.file, span, false, isCallHierarchyEnabled)?.textSpan) .filter(span => !!span); @@ -307,7 +307,7 @@ function provideCallHierarchyOutgoingCalls(language: Language, provideCa } return calls .map(call => { - const to = transformCallHierarchyItem(language, call.to, isCallHierarchyEnabled); + const to = transformCallHierarchyItem(language, call.to, false, isCallHierarchyEnabled); const fromSpans = call.fromSpans .map(span => serviceScript ? transformTextSpan(sourceScript, language, serviceScript, span, false, isCallHierarchyEnabled)?.[1] diff --git a/packages/typescript/lib/node/transform.ts b/packages/typescript/lib/node/transform.ts index 59110615..6cfda2b4 100644 --- a/packages/typescript/lib/node/transform.ts +++ b/packages/typescript/lib/node/transform.ts @@ -11,10 +11,11 @@ const transformedSourceFile = new WeakSet(); export function transformCallHierarchyItem( language: Language, item: ts.CallHierarchyItem, + fallbackToAnyMatch: boolean, filter: (data: CodeInformation) => boolean ): ts.CallHierarchyItem { - const span = transformSpan(language, item.file, item.span, false, filter); - const selectionSpan = transformSpan(language, item.file, item.selectionSpan, false, filter); + const span = transformSpan(language, item.file, item.span, fallbackToAnyMatch, filter); + const selectionSpan = transformSpan(language, item.file, item.selectionSpan, fallbackToAnyMatch, filter); return { ...item, file: span?.fileName ?? item.file,