@@ -12,7 +12,6 @@ import * as lsp from 'vscode-languageserver';
12
12
import { tsCompletionEntryToLspCompletionItem } from './completion' ;
13
13
import { tsDiagnosticToLspDiagnostic } from './diagnostic' ;
14
14
import { Logger } from './logger' ;
15
- import { ProjectService } from './project_service' ;
16
15
import { projectLoadingNotification } from './protocol' ;
17
16
import { ServerHost } from './server_host' ;
18
17
import { filePathToUri , lspPositionToTsPosition , lspRangeToTsPositions , tsTextSpanToLspRange , uriToFilePath } from './utils' ;
@@ -37,15 +36,19 @@ const EMPTY_RANGE = lsp.Range.create(0, 0, 0, 0);
37
36
*/
38
37
export class Session {
39
38
private readonly connection : lsp . IConnection ;
40
- private readonly projectService : ProjectService ;
39
+ private readonly projectService : ts . server . ProjectService ;
41
40
private diagnosticsTimeout : NodeJS . Timeout | null = null ;
42
41
private isProjectLoading = false ;
43
42
44
43
constructor ( options : SessionOptions ) {
45
44
// Create a connection for the server. The connection uses Node's IPC as a transport.
46
45
this . connection = lsp . createConnection ( ) ;
47
46
this . addProtocolHandlers ( this . connection ) ;
48
- this . projectService = new ProjectService ( {
47
+ this . projectService = this . createProjectService ( options ) ;
48
+ }
49
+
50
+ private createProjectService ( options : SessionOptions ) : ts . server . ProjectService {
51
+ const projSvc = new ts . server . ProjectService ( {
49
52
host : options . host ,
50
53
logger : options . logger ,
51
54
cancellationToken : ts . server . nullCancellationToken ,
@@ -63,6 +66,26 @@ export class Session {
63
66
pluginProbeLocations : [ options . ngProbeLocation ] ,
64
67
allowLocalPluginLoads : false , // do not load plugins from tsconfig.json
65
68
} ) ;
69
+
70
+ projSvc . setHostConfiguration ( {
71
+ formatOptions : projSvc . getHostFormatCodeOptions ( ) ,
72
+ extraFileExtensions : [
73
+ {
74
+ extension : '.html' ,
75
+ isMixedContent : false ,
76
+ scriptKind : ts . ScriptKind . External ,
77
+ } ,
78
+ ] ,
79
+ } ) ;
80
+
81
+ projSvc . configurePlugin ( {
82
+ pluginName : '@angular/language-service' ,
83
+ configuration : {
84
+ angularOnly : true ,
85
+ } ,
86
+ } ) ;
87
+
88
+ return projSvc ;
66
89
}
67
90
68
91
private addProtocolHandlers ( conn : lsp . IConnection ) {
@@ -142,7 +165,7 @@ export class Session {
142
165
continue ;
143
166
}
144
167
145
- const ngLS = this . projectService . getDefaultLanguageService ( scriptInfo ) ;
168
+ const ngLS = this . getDefaultLanguageService ( scriptInfo ) ;
146
169
if ( ! ngLS ) {
147
170
continue ;
148
171
}
@@ -157,6 +180,53 @@ export class Session {
157
180
}
158
181
}
159
182
183
+ /**
184
+ * Return the default project for the specified `scriptInfo` if it is already
185
+ * a configured project. If not, attempt to find a relevant config file and
186
+ * make that project its default. This method is to ensure HTML files always
187
+ * belong to a configured project instead of the default behavior of being in
188
+ * an inferred project.
189
+ * @param scriptInfo
190
+ */
191
+ getDefaultProjectForScriptInfo ( scriptInfo : ts . server . ScriptInfo ) : ts . server . Project | undefined {
192
+ let project = this . projectService . getDefaultProjectForFile (
193
+ scriptInfo . fileName ,
194
+ // ensureProject tries to find a default project for the scriptInfo if
195
+ // it does not already have one. It is not needed here because we are
196
+ // going to assign it a project below if it does not have one.
197
+ false // ensureProject
198
+ ) ;
199
+
200
+ // TODO: verify that HTML files are attached to Inferred project by default.
201
+ // If they are already part of a ConfiguredProject then the following is
202
+ // not needed.
203
+ if ( ! project || project . projectKind !== ts . server . ProjectKind . Configured ) {
204
+ const { configFileName} = this . projectService . openClientFile ( scriptInfo . fileName ) ;
205
+ if ( ! configFileName ) {
206
+ // Failed to find a config file. There is nothing we could do.
207
+ return ;
208
+ }
209
+ project = this . projectService . findProject ( configFileName ) ;
210
+ if ( ! project ) {
211
+ return ;
212
+ }
213
+ scriptInfo . detachAllProjects ( ) ;
214
+ scriptInfo . attachToProject ( project ) ;
215
+ }
216
+
217
+ return project ;
218
+ }
219
+
220
+ /**
221
+ * Returns a language service for a default project created for the specified `scriptInfo`. If the
222
+ * project does not support a language service, nothing is returned.
223
+ */
224
+ getDefaultLanguageService ( scriptInfo : ts . server . ScriptInfo ) : ts . LanguageService | undefined {
225
+ const project = this . getDefaultProjectForScriptInfo ( scriptInfo ) ;
226
+ if ( ! project ?. languageServiceEnabled ) return ;
227
+ return project . getLanguageService ( ) ;
228
+ }
229
+
160
230
private onInitialize ( params : lsp . InitializeParams ) : lsp . InitializeResult {
161
231
return {
162
232
capabilities : {
@@ -245,7 +315,7 @@ export class Session {
245
315
}
246
316
}
247
317
248
- const project = this . projectService . getDefaultProjectForScriptInfo ( scriptInfo ) ;
318
+ const project = this . getDefaultProjectForScriptInfo ( scriptInfo ) ;
249
319
if ( ! project || ! project . languageServiceEnabled ) {
250
320
return ;
251
321
}
@@ -276,7 +346,7 @@ export class Session {
276
346
}
277
347
278
348
const { fileName} = scriptInfo ;
279
- const langSvc = this . projectService . getDefaultLanguageService ( scriptInfo ) ;
349
+ const langSvc = this . getDefaultLanguageService ( scriptInfo ) ;
280
350
if ( ! langSvc ) {
281
351
return ;
282
352
}
@@ -322,7 +392,7 @@ export class Session {
322
392
if ( ! scriptInfo ) {
323
393
return ;
324
394
}
325
- const langSvc = this . projectService . getDefaultLanguageService ( scriptInfo ) ;
395
+ const langSvc = this . getDefaultLanguageService ( scriptInfo ) ;
326
396
if ( ! langSvc ) {
327
397
return ;
328
398
}
@@ -366,7 +436,7 @@ export class Session {
366
436
return ;
367
437
}
368
438
const { fileName} = scriptInfo ;
369
- const langSvc = this . projectService . getDefaultLanguageService ( scriptInfo ) ;
439
+ const langSvc = this . getDefaultLanguageService ( scriptInfo ) ;
370
440
if ( ! langSvc ) {
371
441
return ;
372
442
}
0 commit comments