diff --git a/packages/vscode-vue/src/common.ts b/packages/vscode-vue/src/common.ts index ef9a38720..ad0abb777 100644 --- a/packages/vscode-vue/src/common.ts +++ b/packages/vscode-vue/src/common.ts @@ -2,7 +2,6 @@ import { activateAutoInsertion, activateFindFileReferences, activateReloadProjects, - activateServerStats, activateServerSys, activateTsConfigStatusItem, activateTsVersionStatusItem, @@ -115,7 +114,6 @@ async function doActivate(context: vscode.ExtensionContext, createLc: CreateLang }, ); activateReloadProjects('volar.action.reloadProject', [semanticClient]); - activateServerStats('volar.action.serverStats', [semanticClient]); activateTsVersionStatusItem('volar.selectTypeScriptVersion', context, semanticClient, document => { return document.languageId === 'vue' diff --git a/packages/vue-component-meta/package.json b/packages/vue-component-meta/package.json index 64f2433d7..e7ab4f043 100644 --- a/packages/vue-component-meta/package.json +++ b/packages/vue-component-meta/package.json @@ -13,7 +13,7 @@ "directory": "packages/vue-component-meta" }, "dependencies": { - "@volar/language-core": "1.6.9", + "@volar/typescript": "1.6.9", "@vue/language-core": "1.7.8", "typesafe-path": "^0.2.2", "vue-component-type-helpers": "1.7.8" diff --git a/packages/vue-component-meta/src/index.ts b/packages/vue-component-meta/src/index.ts index 891c8ee79..a1572e8ad 100644 --- a/packages/vue-component-meta/src/index.ts +++ b/packages/vue-component-meta/src/index.ts @@ -1,8 +1,8 @@ import * as vue from '@vue/language-core'; -import { createLanguageContext } from '@volar/language-core'; import type * as ts from 'typescript/lib/tsserverlibrary'; import * as path from 'typesafe-path/posix'; import typeHelpersCode from 'vue-component-type-helpers'; +import { createLanguageService } from '@volar/typescript'; import type { MetaCheckerOptions, @@ -156,7 +156,6 @@ export function baseCreate( vueCompilerOptions, ts, ) : []; - const core = createLanguageContext({ typescript: ts }, host, vueLanguages); const proxyApis: Partial = checkerOptions.forceUseTs ? { getScriptKind: (fileName) => { if (fileName.endsWith('.vue.js')) { @@ -165,10 +164,10 @@ export function baseCreate( if (fileName.endsWith('.vue.jsx')) { return ts.ScriptKind.TSX; } - return core.typescript.languageServiceHost.getScriptKind!(fileName); + return host.getScriptKind!(fileName); }, } : {}; - const proxyHost = new Proxy(core.typescript.languageServiceHost, { + const proxyHost = new Proxy(host, { get(target, propKey: keyof ts.LanguageServiceHost) { if (propKey in proxyApis) { return proxyApis[propKey]; @@ -176,7 +175,8 @@ export function baseCreate( return target[propKey]; } }); - const tsLs = ts.createLanguageService(proxyHost); + const core = vue.createLanguageContext(proxyHost, vueLanguages); + const tsLs = createLanguageService(core, ts); let globalPropNames: string[] | undefined; return { @@ -432,7 +432,7 @@ function createSchemaResolvers( symbolNode: ts.Expression, { rawType, schema: options, noDeclarations }: MetaCheckerOptions, ts: typeof import('typescript/lib/tsserverlibrary'), - core: ReturnType, + core: vue.LanguageContext, ) { const visited = new Set();; diff --git a/packages/vue-language-server/src/languageServerPlugin.ts b/packages/vue-language-server/src/languageServerPlugin.ts index 6b344704f..11728f76c 100644 --- a/packages/vue-language-server/src/languageServerPlugin.ts +++ b/packages/vue-language-server/src/languageServerPlugin.ts @@ -100,22 +100,22 @@ export function createServerPlugin(connection: Connection) { connection.onRequest(DetectNameCasingRequest.type, async params => { const languageService = await getService(params.textDocument.uri); - if (languageService?.context.typescript) { + if (languageService) { return nameCasing.detect(ts, languageService.context, params.textDocument.uri); } }); connection.onRequest(GetConvertTagCasingEditsRequest.type, async params => { const languageService = await getService(params.textDocument.uri); - if (languageService?.context.typescript) { + if (languageService) { return nameCasing.convertTagName(ts, languageService.context, params.textDocument.uri, params.casing); } }); connection.onRequest(GetConvertAttrCasingEditsRequest.type, async params => { const languageService = await getService(params.textDocument.uri); - if (languageService?.context.typescript) { - const vueOptions = hostToVueOptions.get(languageService.context.host); + if (languageService) { + const vueOptions = hostToVueOptions.get(languageService.context.core.host); if (vueOptions) { return nameCasing.convertAttrName(ts, languageService.context, params.textDocument.uri, params.casing); } @@ -127,20 +127,19 @@ export function createServerPlugin(connection: Connection) { connection.onRequest(GetComponentMeta.type, async params => { const languageService = await getService(params.uri); - - if (!languageService?.context.typescript) + if (!languageService) return; - let checker = checkers.get(languageService.context.host); + let checker = checkers.get(languageService.context.core.host); if (!checker) { checker = componentMeta.baseCreate( - languageService.context.host, - hostToVueOptions.get(languageService.context.host)!, + languageService.context.core.host, + hostToVueOptions.get(languageService.context.core.host)!, {}, - languageService.context.host.getCurrentDirectory() + '/tsconfig.json.global.vue', + languageService.context.core.host.getCurrentDirectory() + '/tsconfig.json.global.vue', ts, ); - checkers.set(languageService.context.host, checker); + checkers.set(languageService.context.core.host, checker); } return checker.getComponentMeta(env.uriToFileName(params.uri)); }); diff --git a/packages/vue-language-service/package.json b/packages/vue-language-service/package.json index 6d9a18561..bbf46ffc6 100644 --- a/packages/vue-language-service/package.json +++ b/packages/vue-language-service/package.json @@ -19,6 +19,7 @@ "dependencies": { "@volar/language-core": "1.6.9", "@volar/language-service": "1.6.9", + "@volar/typescript": "1.6.9", "@vue/compiler-dom": "^3.3.0", "@vue/language-core": "1.7.8", "@vue/reactivity": "^3.3.0", diff --git a/packages/vue-language-service/src/ideFeatures/nameCasing.ts b/packages/vue-language-service/src/ideFeatures/nameCasing.ts index 0b3061540..614042492 100644 --- a/packages/vue-language-service/src/ideFeatures/nameCasing.ts +++ b/packages/vue-language-service/src/ideFeatures/nameCasing.ts @@ -4,17 +4,15 @@ import { checkComponentNames, getTemplateTagsAndAttrs, checkPropsOfTag, checkNat import * as vue from '@vue/language-core'; import type * as vscode from 'vscode-languageserver-protocol'; import { AttrNameCasing, TagNameCasing } from '../types'; +import type { Provide } from 'volar-service-typescript'; export async function convertTagName( ts: typeof import('typescript/lib/tsserverlibrary'), - context: ServiceContext, + context: ServiceContext, uri: string, casing: TagNameCasing, ) { - if (!context.typescript) - return; - const rootFile = context.documents.getSourceByUri(uri)?.root; if (!(rootFile instanceof vue.VueFile)) return; @@ -23,11 +21,13 @@ export async function convertTagName( if (!desc.template) return; + const languageService = context.inject('typescript/languageService'); + const languageServiceHost = context.inject('typescript/languageServiceHost'); const template = desc.template; const document = context.documents.getDocumentByFileName(rootFile.snapshot, rootFile.fileName); const edits: vscode.TextEdit[] = []; - const nativeTags = checkNativeTags(ts, context.typescript.languageService, context.typescript.languageServiceHost); - const components = checkComponentNames(ts, context.typescript.languageService, rootFile, nativeTags); + const nativeTags = checkNativeTags(ts, languageService, languageServiceHost); + const components = checkComponentNames(ts, languageService, rootFile, nativeTags); const tags = getTemplateTagsAndAttrs(rootFile); for (const [tagName, { offsets }] of tags) { @@ -57,9 +57,6 @@ export async function convertAttrName( casing: AttrNameCasing, ) { - if (!context.typescript) - return; - const rootFile = context.documents.getSourceByUri(uri)?.root; if (!(rootFile instanceof vue.VueFile)) return; @@ -68,17 +65,19 @@ export async function convertAttrName( if (!desc.template) return; + const languageService = context.inject('typescript/languageService'); + const languageServiceHost = context.inject('typescript/languageServiceHost'); const template = desc.template; const document = context.documents.getDocumentByFileName(rootFile.snapshot, rootFile.fileName); const edits: vscode.TextEdit[] = []; - const nativeTags = checkNativeTags(ts, context.typescript.languageService, context.typescript.languageServiceHost); - const components = checkComponentNames(ts, context.typescript.languageService, rootFile, nativeTags); + const nativeTags = checkNativeTags(ts, languageService, languageServiceHost); + const components = checkComponentNames(ts, languageService, rootFile, nativeTags); const tags = getTemplateTagsAndAttrs(rootFile); for (const [tagName, { attrs }] of tags) { const componentName = components.find(component => component === tagName || hyphenate(component) === tagName); if (componentName) { - const props = checkPropsOfTag(ts, context.typescript.languageService, rootFile, componentName, nativeTags); + const props = checkPropsOfTag(ts, languageService, rootFile, componentName, nativeTags); for (const [attrName, { offsets }] of attrs) { const propName = props.find(prop => prop === attrName || hyphenate(prop) === attrName); if (propName) { @@ -130,13 +129,6 @@ export function detect( attr: AttrNameCasing[], } { - if (!context.typescript) { - return { - tag: [], - attr: [], - }; - } - const rootFile = context.documents.getSourceByUri(uri)?.root; if (!(rootFile instanceof vue.VueFile)) { return { @@ -145,6 +137,9 @@ export function detect( }; } + const languageService = context.inject('typescript/languageService'); + const languageServiceHost = context.inject('typescript/languageServiceHost'); + return { tag: getTagNameCase(rootFile), attr: getAttrNameCase(rootFile), @@ -176,12 +171,8 @@ export function detect( } function getTagNameCase(file: VirtualFile): TagNameCasing[] { - if (!context.typescript) { - return []; - } - - const nativeTags = checkNativeTags(ts, context.typescript.languageService, context.typescript.languageServiceHost); - const components = checkComponentNames(ts, context.typescript.languageService, file, nativeTags); + const nativeTags = checkNativeTags(ts, languageService, languageServiceHost); + const components = checkComponentNames(ts, languageService, file, nativeTags); const tagNames = getTemplateTagsAndAttrs(file); const result: TagNameCasing[] = []; diff --git a/packages/vue-language-service/src/languageService.ts b/packages/vue-language-service/src/languageService.ts index 91f9dce99..137402033 100644 --- a/packages/vue-language-service/src/languageService.ts +++ b/packages/vue-language-service/src/languageService.ts @@ -125,7 +125,6 @@ function resolvePlugins( const suffix = capitalize(ext.substring('.'.length)); // .vue -> Vue if ( itemData?.uri - && _context.typescript && item.textEdit?.newText.endsWith(suffix) && item.additionalTextEdits?.length === 1 && item.additionalTextEdits[0].newText.indexOf('import ' + item.textEdit.newText + ' from ') >= 0 && (await _context.env.getConfiguration?.('vue.complete.normalizeComponentImportName') ?? true) diff --git a/packages/vue-language-service/src/plugins/vue-autoinsert-dotvalue.ts b/packages/vue-language-service/src/plugins/vue-autoinsert-dotvalue.ts index 493de9fef..b088dd0d3 100644 --- a/packages/vue-language-service/src/plugins/vue-autoinsert-dotvalue.ts +++ b/packages/vue-language-service/src/plugins/vue-autoinsert-dotvalue.ts @@ -1,19 +1,15 @@ -import { AutoInsertionContext, Service } from '@volar/language-service'; +import { AutoInsertionContext, Service, ServiceContext } from '@volar/language-service'; import { hyphenate } from '@vue/shared'; import type * as ts from 'typescript/lib/tsserverlibrary'; import type * as vscode from 'vscode-languageserver-protocol'; import type { TextDocument } from 'vscode-languageserver-textdocument'; -const plugin: Service = (context, modules) => { +const plugin: Service = (context: ServiceContext | undefined, modules) => { if (!modules?.typescript) return {}; - if (!context?.typescript) - return {}; - const ts = modules.typescript; - const _ts = context.typescript; return { @@ -25,15 +21,15 @@ const plugin: Service = (context, modules) => { if (!isCharacterTyping(document, insertContext)) return; - const enabled = await context.env.getConfiguration?.('vue.autoInsert.dotValue') ?? true; + const enabled = await context!.env.getConfiguration?.('vue.autoInsert.dotValue') ?? true; if (!enabled) return; - const program = _ts.languageService.getProgram(); + const program = context!.inject('typescript/languageService').getProgram(); if (!program) return; - const sourceFile = program.getSourceFile(context.env.uriToFileName(document.uri)); + const sourceFile = program.getSourceFile(context!.env.uriToFileName(document.uri)); if (!sourceFile) return; @@ -44,9 +40,9 @@ const plugin: Service = (context, modules) => { if (!node) return; - const token = _ts.languageServiceHost.getCancellationToken?.(); + const token = context!.inject('typescript/languageServiceHost').getCancellationToken?.(); if (token) { - _ts.languageService.getQuickInfoAtPosition(context.env.uriToFileName(document.uri), node.end); + context!.inject('typescript/languageService').getQuickInfoAtPosition(context!.env.uriToFileName(document.uri), node.end); if (token?.isCancellationRequested()) { return; // check cancel here because type checker do not use cancel token } diff --git a/packages/vue-language-service/src/plugins/vue-template.ts b/packages/vue-language-service/src/plugins/vue-template.ts index 3adbfa31c..6930caa03 100644 --- a/packages/vue-language-service/src/plugins/vue-template.ts +++ b/packages/vue-language-service/src/plugins/vue-template.ts @@ -1,4 +1,4 @@ -import { FileRangeCapabilities, Service, SourceMapWithDocuments } from '@volar/language-service'; +import { FileRangeCapabilities, Service, ServiceContext, SourceMapWithDocuments } from '@volar/language-service'; import * as vue from '@vue/language-core'; import { hyphenate, capitalize, camelize } from '@vue/shared'; import * as html from 'vscode-html-languageservice'; @@ -18,7 +18,7 @@ export default (options: { baseService: S, isSupportedDocument: (document: TextDocument) => boolean, vueCompilerOptions: VueCompilerOptions, -}): Service => (_context, modules): ReturnType => { +}): Service => (_context: ServiceContext | undefined, modules): ReturnType => { const htmlOrPugService = options.baseService(_context, modules) as ReturnType; const triggerCharacters = [ @@ -26,7 +26,7 @@ export default (options: { '@', // vue event shorthand ]; - if (!_context?.typescript || !modules?.typescript) + if (!_context || !modules?.typescript) return { triggerCharacters }; builtInData ??= loadTemplateData(_context.env.locale ?? 'en'); @@ -63,7 +63,6 @@ export default (options: { } const ts = modules.typescript; - const _ts = _context.typescript; return { @@ -106,6 +105,8 @@ export default (options: { if (!enabled) return; + const languageService = _context.inject('typescript/languageService'); + const languageServiceHost = _context.inject('typescript/languageServiceHost'); const result: vscode.InlayHint[] = []; for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { @@ -115,8 +116,8 @@ export default (options: { // visualize missing required props const casing = await getNameCasing(ts, _context, map.sourceFileDocument.uri); - const nativeTags = checkNativeTags(ts, _ts.languageService, _ts.languageServiceHost); - const components = checkComponentNames(ts, _ts.languageService, virtualFile, nativeTags); + const nativeTags = checkNativeTags(ts, languageService, languageServiceHost); + const components = checkComponentNames(ts, languageService, virtualFile, nativeTags); const componentProps: Record = {}; let token: html.TokenType; let current: { @@ -133,7 +134,7 @@ export default (options: { : components.find(component => component === tagName || hyphenate(component) === tagName); const checkTag = tagName.indexOf('.') >= 0 ? tagName : component; if (checkTag) { - componentProps[checkTag] ??= checkPropsOfTag(ts, _ts.languageService, virtualFile, checkTag, nativeTags, true); + componentProps[checkTag] ??= checkPropsOfTag(ts, languageService, virtualFile, checkTag, nativeTags, true); current = { unburnedRequiredProps: [...componentProps[checkTag]], labelOffset: scanner.getTokenOffset() + scanner.getTokenLength(), @@ -284,14 +285,17 @@ export default (options: { if (!scanner) return; + const languageService = _context.inject('typescript/languageService'); + const languageServiceHost = _context.inject('typescript/languageServiceHost'); + for (const [_, map] of _context.documents.getMapsByVirtualFileUri(document.uri)) { const virtualFile = _context.documents.getSourceByUri(map.sourceFileDocument.uri)?.root; if (!virtualFile || !(virtualFile instanceof vue.VueFile)) continue; - const nativeTags = checkNativeTags(ts, _ts.languageService, _ts.languageServiceHost); - const templateScriptData = checkComponentNames(ts, _ts.languageService, virtualFile, nativeTags); + const nativeTags = checkNativeTags(ts, languageService, languageServiceHost); + const templateScriptData = checkComponentNames(ts, languageService, virtualFile, nativeTags); const components = new Set([ ...templateScriptData, ...templateScriptData.map(hyphenate), @@ -339,6 +343,8 @@ export default (options: { async function provideHtmlData(map: SourceMapWithDocuments, vueSourceFile: vue.VueFile) { + const languageService = _context!.inject('typescript/languageService'); + const languageServiceHost = _context!.inject('typescript/languageServiceHost'); const casing = await getNameCasing(ts, _context!, map.sourceFileDocument.uri); if (builtInData.tags) { @@ -358,7 +364,7 @@ export default (options: { } } - const nativeTags = checkNativeTags(ts, _ts.languageService, _ts.languageServiceHost); + const nativeTags = checkNativeTags(ts, languageService, languageServiceHost); options.updateCustomData(htmlOrPugService, [ html.newHTMLDataProvider('vue-template-built-in', builtInData), { @@ -366,7 +372,7 @@ export default (options: { isApplicable: () => true, provideTags: () => { - const components = checkComponentNames(ts, _ts.languageService, vueSourceFile, nativeTags) + const components = checkComponentNames(ts, languageService, vueSourceFile, nativeTags) .filter(name => name !== 'Transition' && name !== 'TransitionGroup' @@ -408,9 +414,9 @@ export default (options: { }, provideAttributes: (tag) => { - const attrs = getElementAttrs(ts, _ts.languageService, _ts.languageServiceHost, tag); - const props = new Set(checkPropsOfTag(ts, _ts.languageService, vueSourceFile, tag, nativeTags)); - const events = checkEventsOfTag(ts, _ts.languageService, vueSourceFile, tag, nativeTags); + const attrs = getElementAttrs(ts, languageService, languageServiceHost, tag); + const props = new Set(checkPropsOfTag(ts, languageService, vueSourceFile, tag, nativeTags)); + const events = checkEventsOfTag(ts, languageService, vueSourceFile, tag, nativeTags); const attributes: html.IAttributeData[] = []; for (const prop of [...props, ...attrs]) { @@ -514,9 +520,11 @@ export default (options: { function afterHtmlCompletion(completionList: vscode.CompletionList, map: SourceMapWithDocuments, vueSourceFile: vue.VueFile) { + const languageService = _context!.inject('typescript/languageService'); + const languageServiceHost = _context!.inject('typescript/languageServiceHost'); const replacement = getReplacement(completionList, map.sourceFileDocument); - const nativeTags = checkNativeTags(ts, _ts.languageService, _ts.languageServiceHost); - const componentNames = new Set(checkComponentNames(ts, _ts.languageService, vueSourceFile, nativeTags).map(hyphenate)); + const nativeTags = checkNativeTags(ts, languageService, languageServiceHost); + const componentNames = new Set(checkComponentNames(ts, languageService, vueSourceFile, nativeTags).map(hyphenate)); if (replacement) { diff --git a/packages/vue-language-service/src/plugins/vue-twoslash-queries.ts b/packages/vue-language-service/src/plugins/vue-twoslash-queries.ts index 95b32b549..ff6973889 100644 --- a/packages/vue-language-service/src/plugins/vue-twoslash-queries.ts +++ b/packages/vue-language-service/src/plugins/vue-twoslash-queries.ts @@ -1,17 +1,13 @@ -import { FileKind, forEachEmbeddedFile, Service } from '@volar/language-service'; +import { FileKind, forEachEmbeddedFile, Service, ServiceContext } from '@volar/language-service'; import * as vue from '@vue/language-core'; import type * as vscode from 'vscode-languageserver-protocol'; -const plugin: Service = (context, modules) => { +const plugin: Service = (context: ServiceContext | undefined, modules) => { - if (!modules?.typescript) - return {}; - - if (!context?.typescript) + if (!context || !modules?.typescript) return {}; const ts = modules.typescript; - const _ts = context.typescript; return { @@ -20,6 +16,7 @@ const plugin: Service = (context, modules) => { const hoverOffsets: [vscode.Position, number][] = []; const inlayHints: vscode.InlayHint[] = []; + const languageService = context.inject('typescript/languageService'); for (const pointer of document.getText(range).matchAll(//g)) { const offset = pointer.index! + pointer[0].indexOf('^?') + document.offsetAt(range.start); @@ -36,7 +33,7 @@ const plugin: Service = (context, modules) => { for (const [pointerPosition, hoverOffset] of hoverOffsets) { for (const [tsOffset, mapping] of map.map.toGeneratedOffsets(hoverOffset)) { if (mapping.data.hover) { - const quickInfo = _ts.languageService.getQuickInfoAtPosition(embedded.fileName, tsOffset); + const quickInfo = languageService.getQuickInfoAtPosition(embedded.fileName, tsOffset); if (quickInfo) { inlayHints.push({ position: { line: pointerPosition.line, character: pointerPosition.character + 2 }, diff --git a/packages/vue-language-service/src/plugins/vue.ts b/packages/vue-language-service/src/plugins/vue.ts index 4b8fd2ff7..b3ee00d6c 100644 --- a/packages/vue-language-service/src/plugins/vue.ts +++ b/packages/vue-language-service/src/plugins/vue.ts @@ -1,4 +1,4 @@ -import type { Service } from '@volar/language-service'; +import type { Service, ServiceContext } from '@volar/language-service'; import * as html from 'vscode-html-languageservice'; import type * as vscode from 'vscode-languageserver-protocol'; import { TextDocument } from 'vscode-languageserver-textdocument'; @@ -12,19 +12,17 @@ export interface Provide { 'vue/vueFile': (document: TextDocument) => vue.VueFile | undefined; } -export default (): Service => (context, modules): ReturnType> => { +export default (): Service => (context: ServiceContext | undefined, modules): ReturnType> => { const htmlPlugin = createHtmlPlugin({ validLang: 'vue', disableCustomData: true })(context, modules); - if (!context?.typescript) + if (!context) return htmlPlugin as any; sfcDataProvider ??= html.newHTMLDataProvider('vue', loadLanguageBlocks(context.env.locale ?? 'en')); htmlPlugin.provide['html/languageService']().setDataProviders(false, [sfcDataProvider]); - const _ts = context.typescript; - return { ...htmlPlugin, @@ -42,7 +40,7 @@ export default (): Service => (context, modules): ReturnType {{ foo }} diff --git a/packages/vue-test-workspace/rename/regular-component/output/entry.vue b/packages/vue-test-workspace/rename/regular-component/output/entry.vue index 8542ea25a..9fb7b7f58 100644 --- a/packages/vue-test-workspace/rename/regular-component/output/entry.vue +++ b/packages/vue-test-workspace/rename/regular-component/output/entry.vue @@ -1,4 +1,3 @@ -import { defineComponent } from 'vue';