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