diff --git a/extensions/vscode/src/index.ts b/extensions/vscode/src/index.ts index 96edea6..37f30a0 100644 --- a/extensions/vscode/src/index.ts +++ b/extensions/vscode/src/index.ts @@ -9,7 +9,6 @@ import { addToIgnore } from './commands/add-to-ignore' import { openFileInNpmx } from './commands/open-file-in-npmx' import { openInBrowser } from './commands/open-in-browser' import { useDecorators } from './providers/decorators' -import { useDocumentLink } from './providers/document-link' import { logger } from './state' export const { activate, deactivate } = defineExtension((ctx) => { @@ -22,7 +21,6 @@ export const { activate, deactivate } = defineExtension((ctx) => { useWorkspaceContext() useDecorators() - useDocumentLink() useCommand(ADD_TO_IGNORE_COMMAND, addToIgnore) diff --git a/extensions/vscode/src/providers/document-link/index.ts b/extensions/vscode/src/providers/document-link/index.ts deleted file mode 100644 index d5a9aa1..0000000 --- a/extensions/vscode/src/providers/document-link/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { config } from '#state' -import { SUPPORTED_DOCUMENT_PATTERN } from '#utils/constants' -import { watchEffect } from 'reactive-vscode' -import { languages } from 'vscode' -import { NpmxDocumentLinkProvider } from './npmx' - -export function useDocumentLink() { - watchEffect((onCleanup) => { - if (config.packageLinks === 'off') - return - - const disposable = languages.registerDocumentLinkProvider({ pattern: SUPPORTED_DOCUMENT_PATTERN }, new NpmxDocumentLinkProvider()) - - onCleanup(() => disposable.dispose()) - }) -} diff --git a/extensions/vscode/src/providers/document-link/npmx.ts b/extensions/vscode/src/providers/document-link/npmx.ts deleted file mode 100644 index 3904f84..0000000 --- a/extensions/vscode/src/providers/document-link/npmx.ts +++ /dev/null @@ -1,42 +0,0 @@ -import type { DocumentLink, DocumentLinkProvider, TextDocument } from 'vscode' -import { getResolvedDependencies } from '#core/workspace' -import { config, logger } from '#state' -import { offsetRangeToRange } from '#utils/ast' -import { npmxPackageUrl } from 'npmx-language-core/links' -import { Uri, DocumentLink as VscodeDocumentLink } from 'vscode' - -export class NpmxDocumentLinkProvider implements DocumentLinkProvider { - async provideDocumentLinks(document: TextDocument): Promise { - logger.info('[document-link] set document links') - const dependencies = await getResolvedDependencies(document.uri) - if (!dependencies) - return [] - - const links: DocumentLink[] = [] - const linkMode = config.packageLinks - - for (const dep of dependencies) { - if (dep.resolvedProtocol !== 'npm') - continue - - const { resolvedName, resolvedSpec, nameRange } = dep - - let targetVersion: string | undefined - - if (linkMode === 'declared') { - targetVersion = resolvedSpec - } else if (linkMode === 'resolved') { - targetVersion = await dep.resolvedVersion() ?? resolvedSpec - } - - const url = targetVersion - ? npmxPackageUrl(resolvedName, targetVersion) - : npmxPackageUrl(resolvedName) - const link = new VscodeDocumentLink(offsetRangeToRange(document, nameRange), Uri.parse(url)) - link.tooltip = `Open ${resolvedName}@${targetVersion ?? 'latest'} on npmx` - links.push(link) - } - - return links - } -} diff --git a/packages/language-service/src/index.ts b/packages/language-service/src/index.ts index c451fd4..a95dd9c 100644 --- a/packages/language-service/src/index.ts +++ b/packages/language-service/src/index.ts @@ -2,6 +2,7 @@ import type { LanguageServicePlugin } from '@volar/language-service' import type { IWorkspaceState } from './types' import { create as createNpmxCatalogService } from './plugins/catalog' import { create as createNpmxDiagnosticsService } from './plugins/diagnostics' +import { create as createNpmxDocumentLinkService } from './plugins/document-link' import { create as createNpmxHoverService } from './plugins/hover' import { create as createNpmxVersionCompletionService } from './plugins/version-completion' @@ -9,6 +10,7 @@ export function createNpmxLanguageServicePlugins(workspace: IWorkspaceState): La return [ createNpmxCatalogService(workspace), createNpmxDiagnosticsService(workspace), + createNpmxDocumentLinkService(workspace), createNpmxHoverService(workspace), createNpmxVersionCompletionService(workspace), ] diff --git a/packages/language-service/src/plugins/document-link.ts b/packages/language-service/src/plugins/document-link.ts new file mode 100644 index 0000000..ac26e43 --- /dev/null +++ b/packages/language-service/src/plugins/document-link.ts @@ -0,0 +1,65 @@ +import type { DocumentLink, LanguageServicePlugin, LanguageServicePluginInstance } from '@volar/language-service' +import type { IWorkspaceState } from '../types' +import { npmxPackageUrl } from 'npmx-language-core/links' +import { isDependencyFile } from 'npmx-language-core/utils' +import { URI } from 'vscode-uri' +import { getConfig } from '../config' + +export function create(workspaceState: IWorkspaceState): LanguageServicePlugin { + return { + name: 'npmx-document-link', + capabilities: { + documentLinkProvider: {}, + }, + create(context): LanguageServicePluginInstance { + return { + async provideDocumentLinks(document): Promise { + const linkMode = await getConfig(context, 'npmx.packageLinks') + if (linkMode === 'off') + return + + const uri = URI.parse(document.uri) + if (uri.scheme !== 'file' || !isDependencyFile(uri.path)) + return + + const dependencies = await workspaceState.getResolvedDependencies(document.uri) + if (!dependencies) + return + + const links: DocumentLink[] = [] + + for (const dep of dependencies) { + if (dep.resolvedProtocol !== 'npm') + continue + + const { resolvedName, resolvedSpec, nameRange } = dep + + let targetVersion: string | undefined + + if (linkMode === 'declared') { + targetVersion = resolvedSpec + } else if (linkMode === 'resolved') { + targetVersion = await dep.resolvedVersion() ?? resolvedSpec + } + + const url = targetVersion + ? npmxPackageUrl(resolvedName, targetVersion) + : npmxPackageUrl(resolvedName) + + const [start, end] = nameRange + links.push({ + range: { + start: document.positionAt(start), + end: document.positionAt(end), + }, + target: url, + tooltip: `Open ${resolvedName}@${targetVersion ?? 'latest'} on npmx`, + }) + } + + return links + }, + } + }, + } +}