1
+ import type { LanguageServer } from '@volar/language-server' ;
2
+ import { createLanguageServiceEnvironment } from '@volar/language-server/lib/project/simpleProject' ;
1
3
import { createConnection , createServer , loadTsdkByPath } from '@volar/language-server/node' ;
2
- import { createVueLanguagePlugin , getDefaultCompilerOptions } from '@vue/language-core' ;
3
- import { getHybridModeLanguageServicePlugins } from '@vue/language-service' ;
4
- import { createHybridModeProject } from './lib/hybridModeProject' ;
4
+ import { createLanguage , createParsedCommandLine , createVueLanguagePlugin , getDefaultCompilerOptions } from '@vue/language-core' ;
5
+ import { createLanguageService , createUriMap , getHybridModeLanguageServicePlugins , LanguageService } from '@vue/language-service' ;
6
+ import type * as ts from 'typescript' ;
7
+ import { URI } from 'vscode-uri' ;
5
8
import type { VueInitializationOptions } from './lib/types' ;
6
9
7
10
const connection = createConnection ( ) ;
@@ -20,62 +23,138 @@ connection.onInitialize(params => {
20
23
}
21
24
22
25
const { typescript : ts } = loadTsdkByPath ( options . typescript . tsdk , params . locale ) ;
26
+ const tsconfigProjects = createUriMap < LanguageService > ( ) ;
27
+
28
+ server . fileWatcher . onDidChangeWatchedFiles ( ( { changes } ) => {
29
+ for ( const change of changes ) {
30
+ const changeUri = URI . parse ( change . uri ) ;
31
+ if ( tsconfigProjects . has ( changeUri ) ) {
32
+ tsconfigProjects . get ( changeUri ) ! . dispose ( ) ;
33
+ tsconfigProjects . delete ( changeUri ) ;
34
+ }
35
+ }
36
+ } ) ;
37
+
38
+ let simpleLs : LanguageService | undefined ;
39
+
23
40
return server . initialize (
24
41
params ,
25
- createHybridModeProject (
26
- ( ) => {
27
- const commandLine = {
28
- vueOptions : getDefaultCompilerOptions ( ) ,
29
- options : ts . getDefaultCompilerOptions ( ) ,
30
- } ;
31
- return {
32
- languagePlugins : [
33
- createVueLanguagePlugin (
34
- ts ,
35
- commandLine . options ,
36
- commandLine . vueOptions ,
37
- uri => uri . fsPath . replace ( / \\ / g, '/' )
38
- ) ,
39
- ] ,
40
- setup ( { project } ) {
41
- project . vue = { compilerOptions : commandLine . vueOptions } ;
42
- } ,
43
- } ;
44
- }
45
- ) ,
42
+ {
43
+ setup ( ) { } ,
44
+ async getLanguageService ( uri ) {
45
+ if ( uri . scheme === 'file' && options . typescript . requestForwardingCommand ) {
46
+ const fileName = uri . fsPath . replace ( / \\ / g, '/' ) ;
47
+ const projectInfo = await sendTsRequest < ts . server . protocol . ProjectInfo > (
48
+ ts . server . protocol . CommandTypes . ProjectInfo ,
49
+ {
50
+ file : fileName ,
51
+ needFileNameList : false ,
52
+ } satisfies ts . server . protocol . ProjectInfoRequestArgs
53
+ ) ;
54
+ if ( projectInfo ) {
55
+ const { configFileName } = projectInfo ;
56
+ let ls = tsconfigProjects . get ( URI . file ( configFileName ) ) ;
57
+ if ( ! ls ) {
58
+ ls = createLs ( server , configFileName ) ;
59
+ tsconfigProjects . set ( URI . file ( configFileName ) , ls ) ;
60
+ }
61
+ return ls ;
62
+ }
63
+ }
64
+ return simpleLs ??= createLs ( server , undefined ) ;
65
+ } ,
66
+ getExistingLanguageServices ( ) {
67
+ return Promise . all ( [
68
+ ...tsconfigProjects . values ( ) ,
69
+ simpleLs ,
70
+ ] . filter ( promise => ! ! promise ) ) ;
71
+ } ,
72
+ reload ( ) {
73
+ for ( const ls of [
74
+ ...tsconfigProjects . values ( ) ,
75
+ simpleLs ,
76
+ ] ) {
77
+ ls ?. dispose ( ) ;
78
+ }
79
+ tsconfigProjects . clear ( ) ;
80
+ simpleLs = undefined ;
81
+ } ,
82
+ } ,
46
83
getHybridModeLanguageServicePlugins ( ts , options . typescript . requestForwardingCommand ? {
47
84
collectExtractProps ( ...args ) {
48
- return connection . sendRequest ( options . typescript . requestForwardingCommand ! , [ 'vue:collectExtractProps' , args ] ) ;
85
+ return sendTsRequest ( 'vue:collectExtractProps' , args ) ;
49
86
} ,
50
87
getComponentDirectives ( ...args ) {
51
- return connection . sendRequest ( options . typescript . requestForwardingCommand ! , [ 'vue:getComponentDirectives' , args ] ) ;
88
+ return sendTsRequest ( 'vue:getComponentDirectives' , args ) ;
52
89
} ,
53
90
getComponentEvents ( ...args ) {
54
- return connection . sendRequest ( options . typescript . requestForwardingCommand ! , [ 'vue:getComponentEvents' , args ] ) ;
91
+ return sendTsRequest ( 'vue:getComponentEvents' , args ) ;
55
92
} ,
56
93
getComponentNames ( ...args ) {
57
- return connection . sendRequest ( options . typescript . requestForwardingCommand ! , [ 'vue:getComponentNames' , args ] ) ;
94
+ return sendTsRequest ( 'vue:getComponentNames' , args ) ;
58
95
} ,
59
96
getComponentProps ( ...args ) {
60
- return connection . sendRequest ( options . typescript . requestForwardingCommand ! , [ 'vue:getComponentProps' , args ] ) ;
97
+ return sendTsRequest ( 'vue:getComponentProps' , args ) ;
61
98
} ,
62
99
getElementAttrs ( ...args ) {
63
- return connection . sendRequest ( options . typescript . requestForwardingCommand ! , [ 'vue:getElementAttrs' , args ] ) ;
100
+ return sendTsRequest ( 'vue:getElementAttrs' , args ) ;
64
101
} ,
65
102
getElementNames ( ...args ) {
66
- return connection . sendRequest ( options . typescript . requestForwardingCommand ! , [ 'vue:getElementNames' , args ] ) ;
103
+ return sendTsRequest ( 'vue:getElementNames' , args ) ;
67
104
} ,
68
105
getImportPathForFile ( ...args ) {
69
- return connection . sendRequest ( options . typescript . requestForwardingCommand ! , [ 'vue:getImportPathForFile' , args ] ) ;
106
+ return sendTsRequest ( 'vue:getImportPathForFile' , args ) ;
70
107
} ,
71
108
getPropertiesAtLocation ( ...args ) {
72
- return connection . sendRequest ( options . typescript . requestForwardingCommand ! , [ 'vue:getPropertiesAtLocation' , args ] ) ;
109
+ return sendTsRequest ( 'vue:getPropertiesAtLocation' , args ) ;
73
110
} ,
74
111
getQuickInfoAtPosition ( ...args ) {
75
- return connection . sendRequest ( options . typescript . requestForwardingCommand ! , [ 'vue:getQuickInfoAtPosition' , args ] ) ;
112
+ return sendTsRequest ( 'vue:getQuickInfoAtPosition' , args ) ;
76
113
} ,
77
114
} : undefined )
78
115
) ;
116
+
117
+ function sendTsRequest < T > ( command : string , args : any ) : Promise < T | null > {
118
+ return connection . sendRequest < T > ( options . typescript . requestForwardingCommand ! , [ command , args ] ) ;
119
+ }
120
+
121
+ function createLs ( server : LanguageServer , tsconfig : string | undefined ) {
122
+ const commonLine = tsconfig
123
+ ? createParsedCommandLine ( ts , ts . sys , tsconfig )
124
+ : {
125
+ options : ts . getDefaultCompilerOptions ( ) ,
126
+ vueOptions : getDefaultCompilerOptions ( ) ,
127
+ } ;
128
+ const language = createLanguage < URI > (
129
+ [
130
+ {
131
+ getLanguageId : uri => server . documents . get ( uri ) ?. languageId ,
132
+ } ,
133
+ createVueLanguagePlugin (
134
+ ts ,
135
+ commonLine . options ,
136
+ commonLine . vueOptions ,
137
+ uri => uri . fsPath . replace ( / \\ / g, '/' )
138
+ ) ,
139
+ ] ,
140
+ createUriMap ( ) ,
141
+ uri => {
142
+ const document = server . documents . get ( uri ) ;
143
+ if ( document ) {
144
+ language . scripts . set ( uri , document . getSnapshot ( ) , document . languageId ) ;
145
+ }
146
+ else {
147
+ language . scripts . delete ( uri ) ;
148
+ }
149
+ }
150
+ ) ;
151
+ return createLanguageService (
152
+ language ,
153
+ server . languageServicePlugins ,
154
+ createLanguageServiceEnvironment ( server , [ ...server . workspaceFolders . all ] ) ,
155
+ { vue : { compilerOptions : commonLine . vueOptions } }
156
+ ) ;
157
+ }
79
158
} ) ;
80
159
81
160
connection . onInitialized ( server . initialized ) ;
0 commit comments