Skip to content

Commit 5d8f990

Browse files
authored
feat: migrate catalog-related providers to language-service (#91)
* feat: migrate catalog-related providers to language-service * code simplifier * fix: only check is package manifest file
1 parent 35273ab commit 5d8f990

8 files changed

Lines changed: 138 additions & 97 deletions

File tree

extensions/vscode/src/index.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { openInBrowser } from './commands/open-in-browser'
99
import { useCodeActions } from './providers/code-actions'
1010
import { useCompletionItem } from './providers/completion-item'
1111
import { useDecorators } from './providers/decorators'
12-
import { useDefinition } from './providers/definition'
1312
import { useDiagnostics } from './providers/diagnostics'
1413
import { useDocumentLink } from './providers/document-link'
1514
import { logger } from './state'
@@ -28,7 +27,6 @@ export const { activate, deactivate } = defineExtension((ctx) => {
2827
useDecorators()
2928
useCodeActions()
3029
useDocumentLink()
31-
useDefinition()
3230

3331
useCommands({
3432
[commands.openInBrowser]: openInBrowser,

extensions/vscode/src/providers/completion-item/catalog.ts

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { config } from '#state'
2-
import { PACKAGE_JSON_PATTERN, SUPPORTED_DOCUMENT_PATTERN } from '#utils/constants'
2+
import { SUPPORTED_DOCUMENT_PATTERN } from '#utils/constants'
33
import { watchEffect } from 'reactive-vscode'
44
import { languages } from 'vscode'
5-
import { CatalogCompletionItemProvider } from './catalog'
65
import { VersionCompletionItemProvider } from './version'
76

87
export function useCompletionItem() {
@@ -18,14 +17,4 @@ export function useCompletionItem() {
1817

1918
onCleanup(() => disposable.dispose())
2019
})
21-
22-
watchEffect((onCleanup) => {
23-
const disposable = languages.registerCompletionItemProvider(
24-
{ pattern: PACKAGE_JSON_PATTERN },
25-
new CatalogCompletionItemProvider(),
26-
...CatalogCompletionItemProvider.triggers,
27-
)
28-
29-
onCleanup(() => disposable.dispose())
30-
})
3120
}

extensions/vscode/src/providers/definition/catalog.ts

Lines changed: 0 additions & 39 deletions
This file was deleted.

extensions/vscode/src/providers/definition/index.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { PACKAGE_JSON_BASENAME, PNPM_WORKSPACE_BASENAME, YARN_WORKSPACE_BASENAME } from 'npmx-language-core/constants'
22

3-
export const PACKAGE_JSON_PATTERN = `**/${PACKAGE_JSON_BASENAME}`
43
export const SUPPORTED_DOCUMENT_PATTERN = `**/{${PACKAGE_JSON_BASENAME},${PNPM_WORKSPACE_BASENAME},${YARN_WORKSPACE_BASENAME}}`
54

65
export const PRERELEASE_PATTERN = /-.+/
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import type { LanguageServicePlugin } from '@volar/language-service'
22
import type { IWorkspaceState } from './types'
3+
import { create as createNpmxCatalogService } from './plugins/catalog'
34
import { create as createNpmxHoverService } from './plugins/hover'
45

56
export function createNpmxLanguageServicePlugins(workspace: IWorkspaceState): LanguageServicePlugin[] {
67
return [
8+
createNpmxCatalogService(workspace),
79
createNpmxHoverService(workspace),
810
]
911
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import type { CompletionItemKind, CompletionList, LanguageServicePlugin, LanguageServicePluginInstance, LocationLink } from '@volar/language-service'
2+
import type { DependencyInfo } from 'npmx-language-core/workspace'
3+
import type { IWorkspaceState } from '../types'
4+
import { isPackageManifest, normalizeCatalogName } from 'npmx-language-core/utils'
5+
import { URI } from 'vscode-uri'
6+
import { getResolvedDependencyAtOffset } from '../utils/range'
7+
8+
export function create(workspaceState: IWorkspaceState): LanguageServicePlugin {
9+
function getDependencyFileUri(documentUri: string): URI | undefined {
10+
const uri = URI.parse(documentUri)
11+
if (uri.scheme !== 'file' || !isPackageManifest(uri.path))
12+
return
13+
14+
return uri
15+
}
16+
17+
async function getCatalogDependency(documentUri: string, offset: number): Promise<DependencyInfo | undefined> {
18+
const dependencies = await workspaceState.getResolvedDependencies(documentUri)
19+
if (!dependencies)
20+
return
21+
22+
const dependency = getResolvedDependencyAtOffset(dependencies, offset)
23+
if (!dependency?.rawSpec.startsWith('catalog:'))
24+
return
25+
26+
return dependency
27+
}
28+
29+
function matchesCatalogDependency(candidate: DependencyInfo, dependency: DependencyInfo): boolean {
30+
return candidate.rawName === dependency.resolvedName
31+
&& candidate.categoryName != null
32+
&& dependency.categoryName != null
33+
&& normalizeCatalogName(candidate.categoryName) === normalizeCatalogName(dependency.categoryName)
34+
}
35+
36+
return {
37+
name: 'npmx-catalog',
38+
capabilities: {
39+
completionProvider: {
40+
triggerCharacters: [':'],
41+
},
42+
definitionProvider: true,
43+
},
44+
create(context): LanguageServicePluginInstance {
45+
return {
46+
async provideCompletionItems(document, position): Promise<CompletionList | undefined> {
47+
const dependencyFileUri = getDependencyFileUri(document.uri)
48+
if (!dependencyFileUri)
49+
return
50+
51+
const offset = document.offsetAt(position)
52+
const dependency = await getCatalogDependency(document.uri, offset)
53+
if (!dependency)
54+
return
55+
56+
const workspaceContext = await workspaceState.getWorkspaceContext(document.uri)
57+
if (!workspaceContext)
58+
return
59+
60+
const catalogs = await workspaceContext.getCatalogs()
61+
if (!catalogs)
62+
return
63+
64+
const items: CompletionList['items'] = []
65+
66+
for (const [name, catalog] of Object.entries(catalogs)) {
67+
const version = catalog[dependency.resolvedName]
68+
if (!version)
69+
continue
70+
71+
items.push({
72+
label: name,
73+
kind: 12 satisfies typeof CompletionItemKind.Value,
74+
detail: version,
75+
})
76+
}
77+
78+
return { isIncomplete: false, items }
79+
},
80+
81+
async provideDefinition(document, position): Promise<LocationLink[] | undefined> {
82+
const dependencyFileUri = getDependencyFileUri(document.uri)
83+
if (!dependencyFileUri)
84+
return
85+
86+
const offset = document.offsetAt(position)
87+
const dependency = await getCatalogDependency(document.uri, offset)
88+
if (!dependency)
89+
return
90+
91+
const workspaceContext = await workspaceState.getWorkspaceContext(document.uri)
92+
if (!workspaceContext?.workspaceFilePath)
93+
return
94+
95+
const workspaceFileInfo = await workspaceContext.loadWorkspaceFileInfo(workspaceContext.workspaceFilePath)
96+
if (!workspaceFileInfo)
97+
return
98+
99+
const targetDependency = workspaceFileInfo.dependencies.find((candidate) =>
100+
matchesCatalogDependency(candidate, dependency),
101+
)
102+
if (!targetDependency)
103+
return
104+
105+
const workspaceFileUri = dependencyFileUri.with({ path: workspaceContext.workspaceFilePath })
106+
const sourceScript = context.language.scripts.get(workspaceFileUri)
107+
if (!sourceScript)
108+
return
109+
110+
const workspaceDocument = context.documents.get(sourceScript.id, sourceScript.languageId, sourceScript.snapshot)
111+
112+
const [targetStart, targetEnd] = targetDependency.specRange
113+
const originStart = document.positionAt(dependency.specRange[0])
114+
const originEnd = document.positionAt(dependency.specRange[1])
115+
116+
return [{
117+
targetUri: workspaceFileUri.toString(),
118+
targetRange: {
119+
start: workspaceDocument.positionAt(targetStart),
120+
end: workspaceDocument.positionAt(targetEnd),
121+
},
122+
targetSelectionRange: {
123+
start: workspaceDocument.positionAt(targetStart),
124+
end: workspaceDocument.positionAt(targetEnd),
125+
},
126+
originSelectionRange: {
127+
start: originStart,
128+
end: originEnd,
129+
},
130+
}]
131+
},
132+
}
133+
},
134+
}
135+
}

0 commit comments

Comments
 (0)