From bc5ff18e28a0f36a9346266c5b9e1ba659a96184 Mon Sep 17 00:00:00 2001 From: Johnson Chu Date: Sat, 9 Mar 2024 07:48:12 +0800 Subject: [PATCH] wip --- packages/typescript/index.ts | 87 +++++++++-- .../typescript/lib/features/codeAction.ts | 4 +- .../lib/features/codeActionResolve.ts | 8 +- .../lib/features/completions/resolve.ts | 8 +- .../typescript/lib/features/fileRename.ts | 4 +- packages/typescript/lib/features/hover.ts | 43 ------ .../typescript/lib/features/prepareRename.ts | 25 --- packages/typescript/lib/features/rename.ts | 146 ------------------ .../typescript/lib/utils/lspConverters.ts | 132 +++++++++++++++- packages/typescript/lib/utils/previewer.ts | 57 +++---- 10 files changed, 241 insertions(+), 273 deletions(-) delete mode 100644 packages/typescript/lib/features/hover.ts delete mode 100644 packages/typescript/lib/features/prepareRename.ts delete mode 100644 packages/typescript/lib/features/rename.ts diff --git a/packages/typescript/index.ts b/packages/typescript/index.ts index cf18823a..4865aaea 100644 --- a/packages/typescript/index.ts +++ b/packages/typescript/index.ts @@ -8,6 +8,7 @@ import type { ServicePlugin, ServicePluginInstance, VirtualCode, + WorkspaceEdit } from '@volar/language-service'; import { getDocumentRegistry } from '@volar/typescript'; import * as semver from 'semver'; @@ -25,12 +26,9 @@ import * as diagnostics from './lib/features/diagnostics'; import * as documentHighlight from './lib/features/documentHighlight'; import * as fileReferences from './lib/features/fileReferences'; import * as fileRename from './lib/features/fileRename'; -import * as hover from './lib/features/hover'; import * as implementation from './lib/features/implementation'; import * as inlayHints from './lib/features/inlayHints'; -import * as prepareRename from './lib/features/prepareRename'; import * as references from './lib/features/references'; -import * as rename from './lib/features/rename'; import * as selectionRanges from './lib/features/selectionRanges'; import * as semanticTokens from './lib/features/semanticTokens'; import * as signatureHelp from './lib/features/signatureHelp'; @@ -43,10 +41,16 @@ import { convertCallHierarchyOutgoingCall, convertDefinitionInfoAndBoundSpan, convertDocumentSpantoLocationLink, + convertFileTextChanges, convertNavTree, convertOutliningSpan, + convertQuickInfo, + convertRenameLocations, convertTextChange, + convertTextSpan, } from './lib/utils/lspConverters'; +import * as path from 'path-browserify'; +import { getUserPreferences } from './lib/configs/getUserPreferences'; export * from '@volar/typescript'; @@ -222,8 +226,6 @@ export function create( const findReferences = references.register(ctx); const findFileReferences = fileReferences.register(ctx); const findImplementations = implementation.register(ctx); - const doPrepareRename = prepareRename.register(ctx); - const doRename = rename.register(ctx); const getEditsForFileRename = fileRename.register(ctx); const getCodeActions = codeActions.register(ctx); const doCodeActionResolve = codeActionResolve.register(ctx); @@ -234,11 +236,14 @@ export function create( const doCompletionResolve = completionResolve.register(ctx); const doDirectiveCommentComplete = directiveCommentCompletions.register(); const doJsDocComplete = jsDocCompletions.register(ctx); - const doHover = hover.register(ctx); const getSignatureHelp = signatureHelp.register(ctx); const getSelectionRanges = selectionRanges.register(ctx); const doValidation = diagnostics.register(ctx); const getDocumentSemanticTokens = semanticTokens.register(ctx); + + /* typescript-language-features is hardcode true */ + const renameInfoOptions = { allowRenameOfImportPath: true }; + return { provide: { @@ -311,7 +316,16 @@ export function create( return; return worker(token, () => { - return doPrepareRename(document, position); + const fileName = ctx.uriToFileName(document.uri); + const offset = document.offsetAt(position); + const renameInfo = safeCall(() => ctx.languageService.getRenameInfo(fileName, offset, renameInfoOptions)); + if (!renameInfo) { + return; + } + if (!renameInfo.canRename) { + return { message: renameInfo.localizedErrorMessage }; + } + return convertTextSpan(renameInfo.triggerSpan, document); }); }, @@ -320,8 +334,55 @@ export function create( if (!isSemanticDocument(document, true)) return; - return worker(token, () => { - return doRename(document, position, newName); + return worker(token, async () => { + const fileName = ctx.uriToFileName(document.uri); + const offset = document.offsetAt(position); + const renameInfo = safeCall(() => ctx.languageService.getRenameInfo(fileName, offset, renameInfoOptions)); + if (!renameInfo?.canRename) { + return; + } + if (renameInfo.fileToRename) { + const [formatOptions, preferences] = await Promise.all([ + getFormatCodeSettings(ctx, document), + getUserPreferences(ctx, document), + ]); + return renameFile(renameInfo.fileToRename, newName, formatOptions, preferences); + } + + const { providePrefixAndSuffixTextForRename } = await getUserPreferences(ctx, document); + const entries = ctx.languageService.findRenameLocations(fileName, offset, false, false, providePrefixAndSuffixTextForRename); + if (!entries) { + return; + } + return convertRenameLocations(newName, entries, ctx.fileNameToUri, ctx.getTextDocument); + + function renameFile( + fileToRename: string, + newName: string, + formatOptions: ts.FormatCodeSettings, + preferences: ts.UserPreferences, + ): WorkspaceEdit | undefined { + // Make sure we preserve file extension if none provided + if (!path.extname(newName)) { + newName += path.extname(fileToRename); + } + const dirname = path.dirname(fileToRename); + const newFilePath = path.join(dirname, newName); + const response = safeCall(() => ctx.languageService.getEditsForFileRename(fileToRename, newFilePath, formatOptions, preferences)); + if (!response) { + return; + } + const edits = convertFileTextChanges(response, ctx.fileNameToUri, ctx.getTextDocument); + if (!edits.documentChanges) { + edits.documentChanges = []; + } + edits.documentChanges.push({ + kind: 'rename', + oldUri: ctx.fileNameToUri(fileToRename), + newUri: ctx.fileNameToUri(newFilePath), + }); + return edits; + } }); }, @@ -460,7 +521,13 @@ export function create( return; return worker(token, () => { - return doHover(document, position); + const fileName = ctx.uriToFileName(document.uri); + const offset = document.offsetAt(position); + const info = safeCall(() => ctx.languageService.getQuickInfoAtPosition(fileName, offset)); + if (!info) { + return; + } + return convertQuickInfo(ts, info, document, ctx.fileNameToUri, ctx.getTextDocument); }); }, diff --git a/packages/typescript/lib/features/codeAction.ts b/packages/typescript/lib/features/codeAction.ts index 1889742e..4a025247 100644 --- a/packages/typescript/lib/features/codeAction.ts +++ b/packages/typescript/lib/features/codeAction.ts @@ -6,8 +6,8 @@ import { safeCall } from '../shared'; import type { SharedContext } from '../types'; import * as fixNames from '../utils/fixNames'; import { resolveFixAllCodeAction, resolveOrganizeImportsCodeAction, resolveRefactorCodeAction } from './codeActionResolve'; -import { fileTextChangesToWorkspaceEdit } from './rename'; import type { TextDocument } from 'vscode-languageserver-textdocument'; +import { convertFileTextChanges } from '../utils/lspConverters'; export interface FixAllData { type: 'fixAll', @@ -232,7 +232,7 @@ export function register(ctx: SharedContext) { } } function transformCodeFix(codeFix: ts.CodeFixAction, diagnostics: vscode.Diagnostic[], kind: vscode.CodeActionKind) { - const edit = fileTextChangesToWorkspaceEdit(codeFix.changes, ctx); + const edit = convertFileTextChanges(codeFix.changes, ctx.fileNameToUri, ctx.getTextDocument); const codeActions: vscode.CodeAction[] = []; const fix: vscode.CodeAction = { title: codeFix.description, diff --git a/packages/typescript/lib/features/codeActionResolve.ts b/packages/typescript/lib/features/codeActionResolve.ts index 43525038..ad81ec80 100644 --- a/packages/typescript/lib/features/codeActionResolve.ts +++ b/packages/typescript/lib/features/codeActionResolve.ts @@ -6,7 +6,7 @@ import { getUserPreferences } from '../configs/getUserPreferences'; import { safeCall } from '../shared'; import type { SharedContext } from '../types'; import type { Data, FixAllData, RefactorData } from './codeAction'; -import { fileTextChangesToWorkspaceEdit } from './rename'; +import { convertFileTextChanges } from '../utils/lspConverters'; export function register(ctx: SharedContext) { return async (codeAction: vscode.CodeAction) => { @@ -41,7 +41,7 @@ export function resolveFixAllCodeAction( ) { const fixes = data.fixIds.map(fixId => safeCall(() => ctx.languageService.getCombinedCodeFix({ type: 'file', fileName: data.fileName }, fixId, formatOptions, preferences))); const changes = fixes.map(fix => fix?.changes ?? []).flat(); - codeAction.edit = fileTextChangesToWorkspaceEdit(changes, ctx); + codeAction.edit = convertFileTextChanges(changes, ctx.fileNameToUri, ctx.getTextDocument); } export function resolveRefactorCodeAction( @@ -56,7 +56,7 @@ export function resolveRefactorCodeAction( if (!editInfo) { return; } - codeAction.edit = fileTextChangesToWorkspaceEdit(editInfo.edits, ctx); + codeAction.edit = convertFileTextChanges(editInfo.edits, ctx.fileNameToUri, ctx.getTextDocument); if (editInfo.renameLocation !== undefined && editInfo.renameFilename !== undefined) { codeAction.command = ctx.commands.rename.create( document.uri, @@ -73,5 +73,5 @@ export function resolveOrganizeImportsCodeAction( preferences: ts.UserPreferences, ) { const changes = safeCall(() => ctx.languageService.organizeImports({ type: 'file', fileName: data.fileName }, formatOptions, preferences)); - codeAction.edit = fileTextChangesToWorkspaceEdit(changes ?? [], ctx); + codeAction.edit = convertFileTextChanges(changes ?? [], ctx.fileNameToUri, ctx.getTextDocument); } diff --git a/packages/typescript/lib/features/completions/resolve.ts b/packages/typescript/lib/features/completions/resolve.ts index ab446f34..0125e34c 100644 --- a/packages/typescript/lib/features/completions/resolve.ts +++ b/packages/typescript/lib/features/completions/resolve.ts @@ -72,7 +72,7 @@ export function register(ctx: SharedContext) { } } if (details.displayParts) { - detailTexts.push(previewer.plainWithLinks(details.displayParts, { toResource }, ctx)); + detailTexts.push(previewer.plainWithLinks(details.displayParts, ctx.fileNameToUri, ctx.getTextDocument)); } if (detailTexts.length) { item.detail = detailTexts.join('\n'); @@ -80,7 +80,7 @@ export function register(ctx: SharedContext) { item.documentation = { kind: 'markdown', - value: previewer.markdownDocumentation(details.documentation, details.tags, { toResource }, ctx), + value: previewer.markdownDocumentation(details.documentation, details.tags, ctx.fileNameToUri, ctx.getTextDocument), }; if (details) { @@ -121,10 +121,6 @@ export function register(ctx: SharedContext) { } return item; - - function toResource(path: string) { - return ctx.fileNameToUri(path); - } }; } diff --git a/packages/typescript/lib/features/fileRename.ts b/packages/typescript/lib/features/fileRename.ts index 5eaebbd5..958d1741 100644 --- a/packages/typescript/lib/features/fileRename.ts +++ b/packages/typescript/lib/features/fileRename.ts @@ -3,7 +3,7 @@ import { getFormatCodeSettings } from '../configs/getFormatCodeSettings'; import { getUserPreferences } from '../configs/getUserPreferences'; import { safeCall } from '../shared'; import type { SharedContext } from '../types'; -import { fileTextChangesToWorkspaceEdit } from './rename'; +import { convertFileTextChanges } from '../utils/lspConverters'; export function register(ctx: SharedContext) { return async (oldUri: string, newUri: string): Promise => { @@ -19,7 +19,7 @@ export function register(ctx: SharedContext) { const response = safeCall(() => ctx.languageService.getEditsForFileRename(fileToRename, newFilePath, formatOptions, preferences)); if (!response?.length) return; - const edits = fileTextChangesToWorkspaceEdit(response, ctx); + const edits = convertFileTextChanges(response, ctx.fileNameToUri, ctx.getTextDocument); return edits; }; } diff --git a/packages/typescript/lib/features/hover.ts b/packages/typescript/lib/features/hover.ts deleted file mode 100644 index 2c9f8ae8..00000000 --- a/packages/typescript/lib/features/hover.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type * as vscode from '@volar/language-service'; -import { safeCall } from '../shared'; -import type { SharedContext } from '../types'; -import * as previewer from '../utils/previewer'; -import type { TextDocument } from 'vscode-languageserver-textdocument'; - -export function register(ctx: SharedContext) { - const { ts } = ctx; - return (document: TextDocument, position: vscode.Position, documentOnly = false): vscode.Hover | undefined => { - const fileName = ctx.uriToFileName(document.uri); - const offset = document.offsetAt(position); - const info = safeCall(() => ctx.languageService.getQuickInfoAtPosition(fileName, offset)); - if (!info) return; - - const parts: string[] = []; - const displayString = ts.displayPartsToString(info.displayParts); - const documentation = previewer.markdownDocumentation(info.documentation ?? [], info.tags, { toResource }, ctx); - - if (displayString && !documentOnly) { - parts.push(['```typescript', displayString, '```'].join('\n')); - } - if (documentation) { - parts.push(documentation); - } - - const markdown: vscode.MarkupContent = { - kind: 'markdown' satisfies typeof vscode.MarkupKind.Markdown, - value: parts.join('\n\n'), - }; - - return { - contents: markdown, - range: { - start: document.positionAt(info.textSpan.start), - end: document.positionAt(info.textSpan.start + info.textSpan.length), - }, - }; - - function toResource(path: string) { - return ctx.fileNameToUri(path); - } - }; -} diff --git a/packages/typescript/lib/features/prepareRename.ts b/packages/typescript/lib/features/prepareRename.ts deleted file mode 100644 index 25c3c97e..00000000 --- a/packages/typescript/lib/features/prepareRename.ts +++ /dev/null @@ -1,25 +0,0 @@ -import type * as vscode from '@volar/language-service'; -import { safeCall } from '../shared'; -import type { SharedContext } from '../types'; -import type { TextDocument } from 'vscode-languageserver-textdocument'; - -/* typescript-language-features is hardcode true */ -export const renameInfoOptions = { allowRenameOfImportPath: true }; - -export function register(ctx: SharedContext) { - return (document: TextDocument, position: vscode.Position): vscode.Range | { message: string; } | undefined => { - const fileName = ctx.uriToFileName(document.uri); - const offset = document.offsetAt(position); - const renameInfo = safeCall(() => ctx.languageService.getRenameInfo(fileName, offset, renameInfoOptions)); - if (!renameInfo) return; - - if (!renameInfo.canRename) { - return { message: renameInfo.localizedErrorMessage }; - } - - return { - start: document.positionAt(renameInfo.triggerSpan.start), - end: document.positionAt(renameInfo.triggerSpan.start + renameInfo.triggerSpan.length), - }; - }; -} diff --git a/packages/typescript/lib/features/rename.ts b/packages/typescript/lib/features/rename.ts deleted file mode 100644 index d0ced625..00000000 --- a/packages/typescript/lib/features/rename.ts +++ /dev/null @@ -1,146 +0,0 @@ -import type * as vscode from '@volar/language-service'; -import * as path from 'path-browserify'; -import type * as ts from 'typescript'; -import { getFormatCodeSettings } from '../configs/getFormatCodeSettings'; -import { getUserPreferences } from '../configs/getUserPreferences'; -import { safeCall } from '../shared'; -import type { SharedContext } from '../types'; -import { renameInfoOptions } from './prepareRename'; -import type { TextDocument } from 'vscode-languageserver-textdocument'; - -export function register(ctx: SharedContext) { - - return async (document: TextDocument, position: vscode.Position, newName: string): Promise => { - const fileName = ctx.uriToFileName(document.uri); - const offset = document.offsetAt(position); - const renameInfo = safeCall(() => ctx.languageService.getRenameInfo(fileName, offset, renameInfoOptions)); - if (!renameInfo?.canRename) return; - - if (renameInfo.fileToRename) { - const [formatOptions, preferences] = await Promise.all([ - getFormatCodeSettings(ctx, document), - getUserPreferences(ctx, document), - ]); - return renameFile(renameInfo.fileToRename, newName, formatOptions, preferences); - } - - const { providePrefixAndSuffixTextForRename } = await getUserPreferences(ctx, document); - const entries = ctx.languageService.findRenameLocations(fileName, offset, false, false, providePrefixAndSuffixTextForRename); - if (!entries) - return; - - const locations = locationsToWorkspaceEdit(newName, entries, ctx); - return locations; - }; - - function renameFile( - fileToRename: string, - newName: string, - formatOptions: ts.FormatCodeSettings, - preferences: ts.UserPreferences, - ): vscode.WorkspaceEdit | undefined { - // Make sure we preserve file extension if none provided - if (!path.extname(newName)) { - newName += path.extname(fileToRename); - } - - const dirname = path.dirname(fileToRename); - const newFilePath = path.join(dirname, newName); - - const response = ctx.languageService.getEditsForFileRename(fileToRename, newFilePath, formatOptions, preferences); - const edits = fileTextChangesToWorkspaceEdit(response, ctx); - if (!edits.documentChanges) { - edits.documentChanges = []; - } - - edits.documentChanges.push({ - kind: 'rename', - oldUri: ctx.fileNameToUri(fileToRename), - newUri: ctx.fileNameToUri(newFilePath), - }); - - return edits; - } -} - -export function fileTextChangesToWorkspaceEdit( - changes: readonly ts.FileTextChanges[], - ctx: SharedContext, -) { - const workspaceEdit: vscode.WorkspaceEdit = {}; - - for (const change of changes) { - - if (!workspaceEdit.documentChanges) { - workspaceEdit.documentChanges = []; - } - - const uri = ctx.fileNameToUri(change.fileName); - let doc = ctx.getTextDocument(uri); - - if (change.isNewFile) { - workspaceEdit.documentChanges.push({ kind: 'create', uri }); - } - - if (!change.isNewFile) - continue; - - const docEdit: vscode.TextDocumentEdit = { - textDocument: { - uri, - version: null, // fix https://github.com/johnsoncodehk/volar/issues/2025 - }, - edits: [], - }; - - for (const textChange of change.textChanges) { - docEdit.edits.push({ - newText: textChange.newText, - range: { - start: doc.positionAt(textChange.span.start), - end: doc.positionAt(textChange.span.start + textChange.span.length), - }, - }); - } - workspaceEdit.documentChanges.push(docEdit); - } - - return workspaceEdit; -} -function locationsToWorkspaceEdit( - newText: string, - locations: readonly ts.RenameLocation[], - ctx: SharedContext, -) { - const workspaceEdit: vscode.WorkspaceEdit = {}; - - for (const location of locations) { - - if (!workspaceEdit.changes) { - workspaceEdit.changes = {}; - } - - const uri = ctx.fileNameToUri(location.fileName); - const doc = ctx.getTextDocument(uri); - - if (!workspaceEdit.changes[uri]) { - workspaceEdit.changes[uri] = []; - } - - let _newText = newText; - if (location.prefixText) - _newText = location.prefixText + _newText; - if (location.suffixText) - _newText = _newText + location.suffixText; - - workspaceEdit.changes[uri].push({ - newText: _newText, - range: { - start: doc.positionAt(location.textSpan.start), - end: doc.positionAt(location.textSpan.start + location.textSpan.length), - }, - }); - } - - return workspaceEdit; -} diff --git a/packages/typescript/lib/utils/lspConverters.ts b/packages/typescript/lib/utils/lspConverters.ts index 01e1bfdd..228a674f 100644 --- a/packages/typescript/lib/utils/lspConverters.ts +++ b/packages/typescript/lib/utils/lspConverters.ts @@ -1,13 +1,139 @@ import type * as vscode from '@volar/language-service'; +import * as path from 'path-browserify'; import type * as ts from 'typescript'; import type { TextDocument } from 'vscode-languageserver-textdocument'; -import type { SharedContext } from '../types'; -import { notEmpty } from '../shared'; -import * as path from 'path-browserify'; import * as PConst from '../protocol.const'; +import { notEmpty } from '../shared'; +import type { SharedContext } from '../types'; import { parseKindModifier } from '../utils/modifiers'; +import * as previewer from '../utils/previewer'; import * as typeConverters from '../utils/typeConverters'; +// rename + +export function convertFileTextChanges( + changes: readonly ts.FileTextChanges[], + fileNameToUri: (fileName: string) => string, + getTextDocument: (uri: string) => TextDocument, +) { + const workspaceEdit: vscode.WorkspaceEdit = {}; + + for (const change of changes) { + + if (!workspaceEdit.documentChanges) { + workspaceEdit.documentChanges = []; + } + + const uri = fileNameToUri(change.fileName); + let doc = getTextDocument(uri); + + if (change.isNewFile) { + workspaceEdit.documentChanges.push({ kind: 'create', uri }); + } + + if (!change.isNewFile) + continue; + + const docEdit: vscode.TextDocumentEdit = { + textDocument: { + uri, + version: null, // fix https://github.com/johnsoncodehk/volar/issues/2025 + }, + edits: [], + }; + + for (const textChange of change.textChanges) { + docEdit.edits.push({ + newText: textChange.newText, + range: { + start: doc.positionAt(textChange.span.start), + end: doc.positionAt(textChange.span.start + textChange.span.length), + }, + }); + } + workspaceEdit.documentChanges.push(docEdit); + } + + return workspaceEdit; +} + +// rename file + +export function convertRenameLocations( + newText: string, + locations: readonly ts.RenameLocation[], + fileNameToUri: (fileName: string) => string, + getTextDocument: (uri: string) => TextDocument, +) { + const workspaceEdit: vscode.WorkspaceEdit = {}; + + for (const location of locations) { + + if (!workspaceEdit.changes) { + workspaceEdit.changes = {}; + } + + const uri = fileNameToUri(location.fileName); + const doc = getTextDocument(uri); + + if (!workspaceEdit.changes[uri]) { + workspaceEdit.changes[uri] = []; + } + + let _newText = newText; + if (location.prefixText) + _newText = location.prefixText + _newText; + if (location.suffixText) + _newText = _newText + location.suffixText; + + workspaceEdit.changes[uri].push({ + newText: _newText, + range: { + start: doc.positionAt(location.textSpan.start), + end: doc.positionAt(location.textSpan.start + location.textSpan.length), + }, + }); + } + + return workspaceEdit; +} + +// hover + +export function convertQuickInfo( + ts: typeof import('typescript'), + info: ts.QuickInfo, + document: TextDocument, + fileNameToUri: (fileName: string) => string, + getTextDocument: (uri: string) => TextDocument, +): vscode.Hover { + const parts: string[] = []; + const displayString = ts.displayPartsToString(info.displayParts); + const documentation = previewer.markdownDocumentation( + info.documentation ?? [], + info.tags, + fileNameToUri, + getTextDocument, + ); + if (displayString) { + parts.push(['```typescript', displayString, '```'].join('\n')); + } + if (documentation) { + parts.push(documentation); + } + const markdown: vscode.MarkupContent = { + kind: 'markdown' satisfies typeof vscode.MarkupKind.Markdown, + value: parts.join('\n\n'), + }; + return { + contents: markdown, + range: { + start: document.positionAt(info.textSpan.start), + end: document.positionAt(info.textSpan.start + info.textSpan.length), + }, + }; +} + // documentSymbol export function convertNavTree(item: ts.NavigationTree, document: TextDocument): vscode.DocumentSymbol[] { diff --git a/packages/typescript/lib/utils/previewer.ts b/packages/typescript/lib/utils/previewer.ts index 306d1c4a..043ef131 100644 --- a/packages/typescript/lib/utils/previewer.ts +++ b/packages/typescript/lib/utils/previewer.ts @@ -4,14 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import type * as ts from 'typescript'; -import type { SharedContext } from '../types'; - -export interface IFilePathToResourceConverter { - /** - * Convert a typescript filepath to a VS Code resource. - */ - toResource(filepath: string): string; -} +import type { TextDocument } from 'vscode-languageserver-textdocument'; function replaceLinks(text: string): string { return text @@ -33,8 +26,8 @@ function processInlineTags(text: string): string { function getTagBodyText( tag: ts.server.protocol.JSDocTagInfo, - filePathConverter: IFilePathToResourceConverter, - ctx: SharedContext, + fileNameToUri: (fileName: string) => string, + getTextDocument: (uri: string) => TextDocument, ): string | undefined { if (!tag.text) { return undefined; @@ -48,7 +41,7 @@ function getTagBodyText( return '```\n' + text + '\n```'; } - const text = convertLinkTags(tag.text, filePathConverter, ctx); + const text = convertLinkTags(tag.text, fileNameToUri, getTextDocument); switch (tag.name) { case 'example': // check for caption tags, fix for #79704 @@ -76,15 +69,15 @@ function getTagBodyText( function getTagDocumentation( tag: ts.server.protocol.JSDocTagInfo, - filePathConverter: IFilePathToResourceConverter, - ctx: SharedContext, + fileNameToUri: (fileName: string) => string, + getTextDocument: (uri: string) => TextDocument, ): string | undefined { switch (tag.name) { case 'augments': case 'extends': case 'param': case 'template': - const body = (convertLinkTags(tag.text, filePathConverter, ctx)).split(/^(\S+)\s*-?\s*/); + const body = (convertLinkTags(tag.text, fileNameToUri, getTextDocument)).split(/^(\S+)\s*-?\s*/); if (body?.length === 3) { const param = body[1]; const doc = body[2]; @@ -98,7 +91,7 @@ function getTagDocumentation( // Generic tag const label = `*@${tag.name}*`; - const text = getTagBodyText(tag, filePathConverter, ctx); + const text = getTagBodyText(tag, fileNameToUri, getTextDocument); if (!text) { return label; } @@ -107,10 +100,10 @@ function getTagDocumentation( export function plainWithLinks( parts: readonly ts.server.protocol.SymbolDisplayPart[] | string, - filePathConverter: IFilePathToResourceConverter, - ctx: SharedContext, + fileNameToUri: (fileName: string) => string, + getTextDocument: (uri: string) => TextDocument, ): string { - return processInlineTags(convertLinkTags(parts, filePathConverter, ctx)); + return processInlineTags(convertLinkTags(parts, fileNameToUri, getTextDocument)); } /** @@ -118,8 +111,8 @@ export function plainWithLinks( */ function convertLinkTags( parts: readonly ts.server.protocol.SymbolDisplayPart[] | string | undefined, - filePathConverter: IFilePathToResourceConverter, - ctx: SharedContext, + fileNameToUri: (fileName: string) => string, + getTextDocument: (uri: string) => TextDocument, ): string { if (!parts) { return ''; @@ -144,7 +137,7 @@ function convertLinkTags( fileName: string, textSpan: { start: number, length: number; }, }; - const fileDoc = ctx.getTextDocument(ctx.uriToFileName(_target.fileName)); + const fileDoc = getTextDocument(fileNameToUri(_target.fileName)); const start = fileDoc.positionAt(_target.textSpan.start); const end = fileDoc.positionAt(_target.textSpan.start + _target.textSpan.length); target = { @@ -161,7 +154,7 @@ function convertLinkTags( } if (target) { - const link = filePathConverter.toResource(target.file) + '#' + `L${target.start.line},${target.start.offset}`; + const link = fileNameToUri(target.file) + '#' + `L${target.start.line},${target.start.offset}`; out.push(`[${text}](${link})`); } else { @@ -198,34 +191,34 @@ function convertLinkTags( export function tagsMarkdownPreview( tags: readonly ts.JSDocTagInfo[], - filePathConverter: IFilePathToResourceConverter, - ctx: SharedContext, + fileNameToUri: (fileName: string) => string, + getTextDocument: (uri: string) => TextDocument, ): string { - return tags.map(tag => getTagDocumentation(tag, filePathConverter, ctx)).join(' \n\n'); + return tags.map(tag => getTagDocumentation(tag, fileNameToUri, getTextDocument)).join(' \n\n'); } export function markdownDocumentation( documentation: ts.server.protocol.SymbolDisplayPart[] | string | undefined, tags: ts.JSDocTagInfo[] | undefined, - filePathConverter: IFilePathToResourceConverter, - ctx: SharedContext, + fileNameToUri: (fileName: string) => string, + getTextDocument: (uri: string) => TextDocument, ): string { - return addMarkdownDocumentation('', documentation, tags, filePathConverter, ctx); + return addMarkdownDocumentation('', documentation, tags, fileNameToUri, getTextDocument); } export function addMarkdownDocumentation( out: string, documentation: ts.server.protocol.SymbolDisplayPart[] | string | undefined, tags: ts.JSDocTagInfo[] | undefined, - converter: IFilePathToResourceConverter, - ctx: SharedContext, + fileNameToUri: (fileName: string) => string, + getTextDocument: (uri: string) => TextDocument, ): string { if (documentation) { - out += plainWithLinks(documentation, converter, ctx); + out += plainWithLinks(documentation, fileNameToUri, getTextDocument); } if (tags) { - const tagsPreview = tagsMarkdownPreview(tags, converter, ctx); + const tagsPreview = tagsMarkdownPreview(tags, fileNameToUri, getTextDocument); if (tagsPreview) { out += '\n\n' + tagsPreview; }