@@ -14,7 +14,6 @@ import { UriConverter } from './uriConverter';
14
14
import {
15
15
DidChangeTextDocumentNotification ,
16
16
DidCloseTextDocumentNotification ,
17
- LanguageClient ,
18
17
LanguageClientOptions ,
19
18
ServerOptions ,
20
19
DidCloseTextDocumentParams ,
@@ -53,17 +52,16 @@ import { Options } from '../shared/options';
53
52
import { ServerStateChange } from './ServerStateChange' ;
54
53
import TelemetryReporter from '@vscode/extension-telemetry' ;
55
54
import CSharpIntelliCodeExports from '../CSharpIntelliCodeExports' ;
56
- import { csharpDevkitExtensionId , getCSharpDevKit } from '../utils/getCSharpDevKit' ;
55
+ import { csharpDevkitExtensionId , csharpDevkitIntelliCodeExtensionId , getCSharpDevKit } from '../utils/getCSharpDevKit' ;
57
56
import { randomUUID } from 'crypto' ;
58
57
import { DotnetRuntimeExtensionResolver } from './dotnetRuntimeExtensionResolver' ;
59
58
import { IHostExecutableResolver } from '../shared/constants/IHostExecutableResolver' ;
59
+ import { RoslynLanguageClient } from './roslynLanguageClient' ;
60
60
61
61
let _languageServer : RoslynLanguageServer ;
62
62
let _channel : vscode . OutputChannel ;
63
63
let _traceChannel : vscode . OutputChannel ;
64
64
65
- const csharpDevkitIntelliCodeExtensionId = "ms-dotnettools.vscodeintellicode-csharp" ;
66
-
67
65
export class RoslynLanguageServer {
68
66
69
67
// These are commands that are invoked by the Razor extension, and are used to send LSP requests to the Roslyn LSP server
@@ -90,7 +88,7 @@ export class RoslynLanguageServer {
90
88
* The timeout for stopping the language server (in ms).
91
89
*/
92
90
private static _stopTimeout : number = 10000 ;
93
- private _languageClient : LanguageClient | undefined ;
91
+ private _languageClient : RoslynLanguageClient | undefined ;
94
92
95
93
/**
96
94
* Flag indicating if C# Devkit was installed the last time we activated.
@@ -119,38 +117,7 @@ export class RoslynLanguageServer {
119
117
private optionProvider : OptionProvider ,
120
118
private context : vscode . ExtensionContext ,
121
119
private telemetryReporter : TelemetryReporter
122
- ) {
123
- // subscribe to extension change events so that we can get notified if C# Dev Kit is added/removed later.
124
- this . context . subscriptions . push ( vscode . extensions . onDidChange ( async ( ) => {
125
- let csharpDevkitExtension = getCSharpDevKit ( ) ;
126
-
127
- if ( this . _wasActivatedWithCSharpDevkit === undefined ) {
128
- // Haven't activated yet.
129
- return ;
130
- }
131
-
132
- const title = 'Restart Language Server' ;
133
- const command = 'dotnet.restartServer' ;
134
- if ( csharpDevkitExtension && ! this . _wasActivatedWithCSharpDevkit ) {
135
- // We previously started without C# Dev Kit and its now installed.
136
- // Offer a prompt to restart the server to use C# Dev Kit.
137
- _channel . appendLine ( `Detected new installation of ${ csharpDevkitExtensionId } ` ) ;
138
- let message = `Detected installation of ${ csharpDevkitExtensionId } . Would you like to relaunch the language server for added features?` ;
139
- ShowInformationMessage ( vscode , message , { title, command } ) ;
140
- } else {
141
- // Any other change to extensions is irrelevant - an uninstall requires a reload of the window
142
- // which will automatically restart this extension too.
143
- }
144
- } ) ) ;
145
-
146
- // Subscribe to telemetry events so we can enable/disable as needed
147
- this . context . subscriptions . push ( vscode . env . onDidChangeTelemetryEnabled ( ( isEnabled : boolean ) => {
148
- const title = 'Restart Language Server' ;
149
- const command = 'dotnet.restartServer' ;
150
- const message = 'Detected change in telemetry settings. These will not take effect until the language server is restarted, would you like to restart?' ;
151
- ShowInformationMessage ( vscode , message , { title, command } ) ;
152
- } ) ) ;
153
- }
120
+ ) { }
154
121
155
122
/**
156
123
* Resolves server options and starts the dotnet language server process. The process is started asynchronously and this method will not wait until
@@ -194,7 +161,7 @@ export class RoslynLanguageServer {
194
161
} ;
195
162
196
163
// Create the language client and start the client.
197
- let client = new LanguageClient (
164
+ let client = new RoslynLanguageClient (
198
165
'microsoft-codeanalysis-languageserver' ,
199
166
'Microsoft.CodeAnalysis.LanguageServer' ,
200
167
serverOptions ,
@@ -225,11 +192,14 @@ export class RoslynLanguageServer {
225
192
this . _eventBus . emit ( RoslynLanguageServer . serverStateChangeEvent , ServerStateChange . ProjectInitializationComplete ) ;
226
193
} ) ;
227
194
195
+ this . registerExtensionsChanged ( this . _languageClient ) ;
196
+ this . registerTelemetryChanged ( this . _languageClient ) ;
197
+
228
198
// Start the client. This will also launch the server
229
199
this . _languageClient . start ( ) ;
230
200
231
201
// Register Razor dynamic file info handling
232
- this . registerRazor ( this . _languageClient ) ;
202
+ this . registerDynamicFileInfo ( this . _languageClient ) ;
233
203
}
234
204
235
205
public async stop ( ) : Promise < void > {
@@ -288,6 +258,18 @@ export class RoslynLanguageServer {
288
258
return response ;
289
259
}
290
260
261
+ /**
262
+ * Sends an LSP notification to the server with a given method and parameters.
263
+ */
264
+ public async sendNotification < Params > ( method : string , params : Params ) : Promise < any > {
265
+ if ( ! this . isRunning ( ) ) {
266
+ throw new Error ( 'Tried to send request while server is not started.' ) ;
267
+ }
268
+
269
+ let response = await this . _languageClient ! . sendNotification ( method , params ) ;
270
+ return response ;
271
+ }
272
+
291
273
public async registerSolutionSnapshot ( token : vscode . CancellationToken ) : Promise < SolutionSnapshotId > {
292
274
let response = await _languageServer . sendRequest0 ( RegisterSolutionSnapshotRequest . type , token ) ;
293
275
if ( response )
@@ -437,7 +419,7 @@ export class RoslynLanguageServer {
437
419
return childProcess ;
438
420
}
439
421
440
- private registerRazor ( client : LanguageClient ) {
422
+ private registerDynamicFileInfo ( client : RoslynLanguageClient ) {
441
423
// When the Roslyn language server sends a request for Razor dynamic file info, we forward that request along to Razor via
442
424
// a command.
443
425
client . onRequest (
@@ -446,43 +428,41 @@ export class RoslynLanguageServer {
446
428
client . onNotification (
447
429
RoslynLanguageServer . removeRazorDynamicFileInfoMethodName ,
448
430
async notification => vscode . commands . executeCommand ( DynamicFileInfoHandler . removeDynamicFileInfoCommand , notification ) ) ;
431
+ }
449
432
450
- // Razor will call into us (via command) for generated file didChange/didClose notifications. We'll then forward these
451
- // notifications along to Roslyn. didOpen notifications are handled separately via the vscode.openTextDocument method.
452
- vscode . commands . registerCommand ( RoslynLanguageServer . roslynDidChangeCommand , ( notification : DidChangeTextDocumentParams ) => {
453
- client . sendNotification ( DidChangeTextDocumentNotification . method , notification ) ;
454
- } ) ;
455
- vscode . commands . registerCommand ( RoslynLanguageServer . roslynDidCloseCommand , ( notification : DidCloseTextDocumentParams ) => {
456
- client . sendNotification ( DidCloseTextDocumentNotification . method , notification ) ;
457
- } ) ;
458
- vscode . commands . registerCommand ( RoslynLanguageServer . roslynPullDiagnosticCommand , async ( request : DocumentDiagnosticParams ) => {
459
- let diagnosticRequestType = new RequestType < DocumentDiagnosticParams , DocumentDiagnosticReport , any > ( DocumentDiagnosticRequest . method ) ;
460
- return await this . sendRequest ( diagnosticRequestType , request , CancellationToken . None ) ;
461
- } ) ;
433
+ private registerExtensionsChanged ( languageClient : RoslynLanguageClient ) {
434
+ // subscribe to extension change events so that we can get notified if C# Dev Kit is added/removed later.
435
+ languageClient . addDisposable ( vscode . extensions . onDidChange ( async ( ) => {
436
+ let csharpDevkitExtension = getCSharpDevKit ( ) ;
462
437
463
- // The VS Code API for code actions (and the vscode.CodeAction type) doesn't support everything that LSP supports,
464
- // namely the data property, which Razor needs to identify which code actions are on their allow list, so we need
465
- // to expose a command for them to directly invoke our code actions LSP endpoints, rather than use built-in commands.
466
- vscode . commands . registerCommand ( RoslynLanguageServer . provideCodeActionsCommand , async ( request : CodeActionParams ) => {
467
- return await this . sendRequest ( CodeActionRequest . type , request , CancellationToken . None ) ;
468
- } ) ;
469
- vscode . commands . registerCommand ( RoslynLanguageServer . resolveCodeActionCommand , async ( request : CodeAction ) => {
470
- return await this . sendRequest ( CodeActionResolveRequest . type , request , CancellationToken . None ) ;
471
- } ) ;
438
+ if ( this . _wasActivatedWithCSharpDevkit === undefined ) {
439
+ // Haven't activated yet.
440
+ return ;
441
+ }
472
442
473
- vscode . commands . registerCommand ( RoslynLanguageServer . provideCompletionsCommand , async ( request : CompletionParams ) => {
474
- return await this . sendRequest ( CompletionRequest . type , request , CancellationToken . None ) ;
475
- } ) ;
476
- vscode . commands . registerCommand ( RoslynLanguageServer . resolveCompletionsCommand , async ( request : CompletionItem ) => {
477
- return await this . sendRequest ( CompletionResolveRequest . type , request , CancellationToken . None ) ;
478
- } ) ;
443
+ const title = 'Restart Language Server' ;
444
+ const command = 'dotnet.restartServer' ;
445
+ if ( csharpDevkitExtension && ! this . _wasActivatedWithCSharpDevkit ) {
446
+ // We previously started without C# Dev Kit and its now installed.
447
+ // Offer a prompt to restart the server to use C# Dev Kit.
448
+ _channel . appendLine ( `Detected new installation of ${ csharpDevkitExtensionId } ` ) ;
449
+ let message = `Detected installation of ${ csharpDevkitExtensionId } . Would you like to relaunch the language server for added features?` ;
450
+ ShowInformationMessage ( vscode , message , { title, command } ) ;
451
+ } else {
452
+ // Any other change to extensions is irrelevant - an uninstall requires a reload of the window
453
+ // which will automatically restart this extension too.
454
+ }
455
+ } ) ) ;
456
+ }
479
457
480
- // Roslyn is responsible for producing a json file containing information for Razor, that comes from the compilation for
481
- // a project. We want to defer this work until necessary, so this command is called by the Razor document manager to tell
482
- // us when they need us to initialize the Razor things.
483
- vscode . commands . registerCommand ( RoslynLanguageServer . razorInitializeCommand , ( ) => {
484
- client . sendNotification ( "razor/initialize" , { } ) ;
485
- } ) ;
458
+ private registerTelemetryChanged ( languageClient : RoslynLanguageClient ) {
459
+ // Subscribe to telemetry events so we can enable/disable as needed
460
+ languageClient . addDisposable ( vscode . env . onDidChangeTelemetryEnabled ( ( isEnabled : boolean ) => {
461
+ const title = 'Restart Language Server' ;
462
+ const command = 'dotnet.restartServer' ;
463
+ const message = 'Detected change in telemetry settings. These will not take effect until the language server is restarted, would you like to restart?' ;
464
+ ShowInformationMessage ( vscode , message , { title, command } ) ;
465
+ } ) ) ;
486
466
}
487
467
488
468
private async getCSharpDevkitExportArgs ( csharpDevkitExtension : vscode . Extension < CSharpDevKitExports > , options : Options ) : Promise < string [ ] > {
@@ -564,6 +544,8 @@ export async function activateRoslynLanguageServer(context: vscode.ExtensionCont
564
544
// Register any commands that need to be handled by the extension.
565
545
registerCommands ( context , _languageServer , optionProvider , hostExecutableResolver ) ;
566
546
547
+ registerRazorCommands ( context , _languageServer ) ;
548
+
567
549
// Register any needed debugger components that need to communicate with the language server.
568
550
registerDebugger ( context , _languageServer , platformInfo , optionProvider , _channel ) ;
569
551
@@ -635,6 +617,45 @@ function getServerFileName(platformInfo: PlatformInformation) {
635
617
return `${ serverFileName } ${ extension } ` ;
636
618
}
637
619
620
+ function registerRazorCommands ( context : vscode . ExtensionContext , languageServer : RoslynLanguageServer ) {
621
+ // Razor will call into us (via command) for generated file didChange/didClose notifications. We'll then forward these
622
+ // notifications along to Roslyn. didOpen notifications are handled separately via the vscode.openTextDocument method.
623
+ context . subscriptions . push ( vscode . commands . registerCommand ( RoslynLanguageServer . roslynDidChangeCommand , async ( notification : DidChangeTextDocumentParams ) => {
624
+ await languageServer . sendNotification ( DidChangeTextDocumentNotification . method , notification ) ;
625
+ } ) ) ;
626
+ context . subscriptions . push ( vscode . commands . registerCommand ( RoslynLanguageServer . roslynDidCloseCommand , async ( notification : DidCloseTextDocumentParams ) => {
627
+ await languageServer . sendNotification ( DidCloseTextDocumentNotification . method , notification ) ;
628
+ } ) ) ;
629
+ context . subscriptions . push ( vscode . commands . registerCommand ( RoslynLanguageServer . roslynPullDiagnosticCommand , async ( request : DocumentDiagnosticParams ) => {
630
+ let diagnosticRequestType = new RequestType < DocumentDiagnosticParams , DocumentDiagnosticReport , any > ( DocumentDiagnosticRequest . method ) ;
631
+ return await languageServer . sendRequest ( diagnosticRequestType , request , CancellationToken . None ) ;
632
+ } ) ) ;
633
+
634
+ // The VS Code API for code actions (and the vscode.CodeAction type) doesn't support everything that LSP supports,
635
+ // namely the data property, which Razor needs to identify which code actions are on their allow list, so we need
636
+ // to expose a command for them to directly invoke our code actions LSP endpoints, rather than use built-in commands.
637
+ context . subscriptions . push ( vscode . commands . registerCommand ( RoslynLanguageServer . provideCodeActionsCommand , async ( request : CodeActionParams ) => {
638
+ return await languageServer . sendRequest ( CodeActionRequest . type , request , CancellationToken . None ) ;
639
+ } ) ) ;
640
+ context . subscriptions . push ( vscode . commands . registerCommand ( RoslynLanguageServer . resolveCodeActionCommand , async ( request : CodeAction ) => {
641
+ return await languageServer . sendRequest ( CodeActionResolveRequest . type , request , CancellationToken . None ) ;
642
+ } ) ) ;
643
+
644
+ context . subscriptions . push ( vscode . commands . registerCommand ( RoslynLanguageServer . provideCompletionsCommand , async ( request : CompletionParams ) => {
645
+ return await languageServer . sendRequest ( CompletionRequest . type , request , CancellationToken . None ) ;
646
+ } ) ) ;
647
+ context . subscriptions . push ( vscode . commands . registerCommand ( RoslynLanguageServer . resolveCompletionsCommand , async ( request : CompletionItem ) => {
648
+ return await languageServer . sendRequest ( CompletionResolveRequest . type , request , CancellationToken . None ) ;
649
+ } ) ) ;
650
+
651
+ // Roslyn is responsible for producing a json file containing information for Razor, that comes from the compilation for
652
+ // a project. We want to defer this work until necessary, so this command is called by the Razor document manager to tell
653
+ // us when they need us to initialize the Razor things.
654
+ context . subscriptions . push ( vscode . commands . registerCommand ( RoslynLanguageServer . razorInitializeCommand , async ( ) => {
655
+ await languageServer . sendNotification ( "razor/initialize" , { } ) ;
656
+ } ) ) ;
657
+ }
658
+
638
659
async function applyAutoInsertEdit ( e : vscode . TextDocumentChangeEvent , token : vscode . CancellationToken ) {
639
660
const change = e . contentChanges [ 0 ] ;
640
661
0 commit comments