@@ -5,6 +5,9 @@ import { config } from './config';
55import * as doctor from './features/doctor' ;
66import * as nameCasing from './features/nameCasing' ;
77import * as splitEditors from './features/splitEditors' ;
8+ import * as semver from 'semver' ;
9+ import * as fs from 'fs' ;
10+ import * as path from 'path' ;
811
912let client : lsp . BaseLanguageClient ;
1013
@@ -17,8 +20,6 @@ type CreateLanguageClient = (
1720 outputChannel : vscode . OutputChannel ,
1821) => lsp . BaseLanguageClient ;
1922
20- const beginHybridMode = config . server . hybridMode ;
21-
2223export async function activate ( context : vscode . ExtensionContext , createLc : CreateLanguageClient ) {
2324
2425 const stopCheck = vscode . window . onDidChangeActiveTextEditor ( tryActivate ) ;
@@ -36,17 +37,137 @@ export async function activate(context: vscode.ExtensionContext, createLc: Creat
3637 }
3738}
3839
40+ export const currentHybridModeStatus = getCurrentHybridModeStatus ( ) ;
41+
42+ function getCurrentHybridModeStatus ( report = false ) {
43+ if ( config . server . hybridMode === 'auto' ) {
44+ const unknownExtensions : string [ ] = [ ] ;
45+ for ( const extension of vscode . extensions . all ) {
46+ const hasTsPlugin = ! ! extension . packageJSON ?. contributes ?. typescriptServerPlugins ;
47+ if ( hasTsPlugin ) {
48+ if (
49+ extension . id === 'Vue.volar'
50+ || extension . id === 'unifiedjs.vscode-mdx'
51+ || extension . id === 'astro-build.astro-vscode'
52+ || extension . id === 'ije.esm-vscode'
53+ || extension . id === 'johnsoncodehk.vscode-tsslint'
54+ || extension . id === 'VisualStudioExptTeam.vscodeintellicode'
55+ ) {
56+ continue ;
57+ }
58+ else {
59+ unknownExtensions . push ( extension . id ) ;
60+ }
61+ }
62+ }
63+ if ( unknownExtensions . length ) {
64+ if ( report ) {
65+ vscode . window . showInformationMessage (
66+ `Hybrid Mode is disabled automatically because there is a potentially incompatible ${ unknownExtensions . join ( ', ' ) } TypeScript plugin installed.` ,
67+ 'Open Settings' ,
68+ 'Report a false positive' ,
69+ ) . then ( value => {
70+ if ( value === 'Open Settings' ) {
71+ vscode . commands . executeCommand ( 'workbench.action.openSettings' , 'vue.server.hybridMode' ) ;
72+ }
73+ else if ( value == 'Report a false positive' ) {
74+ vscode . env . openExternal ( vscode . Uri . parse ( 'https://github.com/vuejs/language-tools/pull/4206' ) ) ;
75+ }
76+ } ) ;
77+ }
78+ return false ;
79+ }
80+ const vscodeTsdkVersion = getVScodeTsdkVersion ( ) ;
81+ const workspaceTsdkVersion = getWorkspaceTsdkVersion ( ) ;
82+ if (
83+ ( vscodeTsdkVersion && ! semver . gte ( vscodeTsdkVersion , '5.3.0' ) )
84+ || ( workspaceTsdkVersion && ! semver . gte ( workspaceTsdkVersion , '5.3.0' ) )
85+ ) {
86+ if ( report ) {
87+ let msg = `Hybrid Mode is disabled automatically because TSDK >= 5.3.0 is required (VSCode TSDK: ${ vscodeTsdkVersion } ` ;
88+ if ( workspaceTsdkVersion ) {
89+ msg += `, Workspace TSDK: ${ workspaceTsdkVersion } ` ;
90+ }
91+ msg += `).` ;
92+ vscode . window . showInformationMessage ( msg , 'Open Settings' ) . then ( value => {
93+ if ( value === 'Open Settings' ) {
94+ vscode . commands . executeCommand ( 'workbench.action.openSettings' , 'vue.server.hybridMode' ) ;
95+ }
96+ } ) ;
97+ }
98+ return false ;
99+ }
100+ return true ;
101+ }
102+ else {
103+ return config . server . hybridMode ;
104+ }
105+
106+ function getVScodeTsdkVersion ( ) {
107+ const nightly = vscode . extensions . getExtension ( 'ms-vscode.vscode-typescript-next' ) ;
108+ if ( nightly ) {
109+ const libPath = path . join (
110+ nightly . extensionPath . replace ( / \\ / g, '/' ) ,
111+ 'node_modules/typescript/lib' ,
112+ ) ;
113+ return getTsVersion ( libPath ) ;
114+ }
115+
116+ if ( vscode . env . appRoot ) {
117+ const libPath = path . join (
118+ vscode . env . appRoot . replace ( / \\ / g, '/' ) ,
119+ 'extensions/node_modules/typescript/lib' ,
120+ ) ;
121+ return getTsVersion ( libPath ) ;
122+ }
123+ }
124+
125+ function getWorkspaceTsdkVersion ( ) {
126+ const libPath = vscode . workspace . getConfiguration ( 'typescript' ) . get < string > ( 'tsdk' ) ?. replace ( / \\ / g, '/' ) ;
127+ if ( libPath ) {
128+ return getTsVersion ( libPath ) ;
129+ }
130+ }
131+
132+ function getTsVersion ( libPath : string ) : string | undefined {
133+
134+ const p = libPath . toString ( ) . split ( '/' ) ;
135+ const p2 = p . slice ( 0 , - 1 ) ;
136+ const modulePath = p2 . join ( '/' ) ;
137+ const filePath = modulePath + '/package.json' ;
138+ const contents = fs . readFileSync ( filePath , 'utf-8' ) ;
139+
140+ if ( contents === undefined ) {
141+ return ;
142+ }
143+
144+ let desc : any = null ;
145+ try {
146+ desc = JSON . parse ( contents ) ;
147+ } catch ( err ) {
148+ return ;
149+ }
150+ if ( ! desc || ! desc . version ) {
151+ return ;
152+ }
153+
154+ return desc . version ;
155+ }
156+ }
157+
39158async function doActivate ( context : vscode . ExtensionContext , createLc : CreateLanguageClient ) {
40159
41- vscode . commands . executeCommand ( 'setContext' , 'vue.activated' , true ) ;
160+ getCurrentHybridModeStatus ( true ) ;
42161
43162 const outputChannel = vscode . window . createOutputChannel ( 'Vue Language Server' ) ;
44163
164+ vscode . commands . executeCommand ( 'setContext' , 'vue.activated' , true ) ;
165+
45166 client = createLc (
46167 'vue' ,
47168 'Vue' ,
48169 getDocumentSelector ( ) ,
49- await getInitializationOptions ( context ) ,
170+ await getInitializationOptions ( context , currentHybridModeStatus ) ,
50171 6009 ,
51172 outputChannel
52173 ) ;
@@ -72,20 +193,20 @@ async function doActivate(context: vscode.ExtensionContext, createLc: CreateLang
72193 lsp . activateWriteVirtualFiles ( 'vue.action.writeVirtualFiles' , client ) ;
73194 lsp . activateServerSys ( client ) ;
74195
75- if ( ! config . server . hybridMode ) {
196+ if ( ! currentHybridModeStatus ) {
76197 lsp . activateTsConfigStatusItem ( selectors , 'vue.tsconfig' , client ) ;
77198 lsp . activateTsVersionStatusItem ( selectors , 'vue.tsversion' , context , client , text => 'TS ' + text ) ;
78199 }
79200
80201 const hybridModeStatus = vscode . languages . createLanguageStatusItem ( 'vue-hybrid-mode' , selectors ) ;
81202 hybridModeStatus . text = 'Hybrid Mode' ;
82- hybridModeStatus . detail = config . server . hybridMode ? 'Enabled ' : 'Disabled' ;
203+ hybridModeStatus . detail = ( currentHybridModeStatus ? 'Enabled' : 'Disabled' ) + ( config . server . hybridMode === 'auto' ? ' (Auto) ' : '' ) ;
83204 hybridModeStatus . command = {
84205 title : 'Open Setting' ,
85206 command : 'workbench.action.openSettings' ,
86207 arguments : [ 'vue.server.hybridMode' ] ,
87208 } ;
88- if ( ! config . server . hybridMode ) {
209+ if ( ! currentHybridModeStatus ) {
89210 hybridModeStatus . severity = vscode . LanguageStatusSeverity . Warning ;
90211 }
91212
@@ -122,12 +243,15 @@ async function doActivate(context: vscode.ExtensionContext, createLc: CreateLang
122243
123244 function activateConfigWatcher ( ) {
124245 context . subscriptions . push ( vscode . workspace . onDidChangeConfiguration ( e => {
125- if ( e . affectsConfiguration ( 'vue.server.hybridMode' ) && config . server . hybridMode !== beginHybridMode ) {
126- requestReloadVscode (
127- config . server . hybridMode
128- ? 'Please reload VSCode to enable Hybrid Mode.'
129- : 'Please reload VSCode to disable Hybrid Mode.'
130- ) ;
246+ if ( e . affectsConfiguration ( 'vue.server.hybridMode' ) ) {
247+ const newStatus = getCurrentHybridModeStatus ( ) ;
248+ if ( newStatus !== currentHybridModeStatus ) {
249+ requestReloadVscode (
250+ newStatus
251+ ? 'Please reload VSCode to enable Hybrid Mode.'
252+ : 'Please reload VSCode to disable Hybrid Mode.'
253+ ) ;
254+ }
131255 }
132256 else if ( e . affectsConfiguration ( 'vue' ) ) {
133257 vscode . commands . executeCommand ( 'vue.action.restartServer' , false ) ;
@@ -139,7 +263,7 @@ async function doActivate(context: vscode.ExtensionContext, createLc: CreateLang
139263 context . subscriptions . push ( vscode . commands . registerCommand ( 'vue.action.restartServer' , async ( restartTsServer : boolean = true ) => {
140264 await client . stop ( ) ;
141265 outputChannel . clear ( ) ;
142- client . clientOptions . initializationOptions = await getInitializationOptions ( context ) ;
266+ client . clientOptions . initializationOptions = await getInitializationOptions ( context , currentHybridModeStatus ) ;
143267 await client . start ( ) ;
144268 nameCasing . activate ( context , client , selectors ) ;
145269 if ( restartTsServer ) {
@@ -167,18 +291,19 @@ export function getDocumentSelector(): lsp.DocumentFilter[] {
167291
168292async function getInitializationOptions (
169293 context : vscode . ExtensionContext ,
294+ hybridMode : boolean ,
170295) : Promise < VueInitializationOptions > {
171296 return {
172297 // volar
173298 diagnosticModel : config . server . diagnosticModel === 'pull' ? DiagnosticModel . Pull : DiagnosticModel . Push ,
174299 typescript : { tsdk : ( await lsp . getTsdk ( context ) ) . tsdk } ,
175300 maxFileSize : config . server . maxFileSize ,
176301 semanticTokensLegend : {
177- tokenTypes : [ 'component' ] ,
302+ tokenTypes : [ ] ,
178303 tokenModifiers : [ ] ,
179304 } ,
180305 vue : {
181- hybridMode : beginHybridMode ,
306+ hybridMode,
182307 additionalExtensions : [
183308 ...config . server . additionalExtensions ,
184309 ...! config . server . petiteVue . supportHtmlFile ? [ ] : [ 'html' ] ,
0 commit comments