@@ -23,6 +23,7 @@ import {
2323 ServerOptions ,
2424 State ,
2525 TransportKind ,
26+ DidChangeConfigurationNotification
2627} from "vscode-languageclient/node" ;
2728
2829import * as customCommands from "./commands" ;
@@ -90,6 +91,75 @@ export function activate(context: ExtensionContext) {
9091 "rescript" ,
9192 ) ;
9293
94+ const useExperimentalServer = workspace . getConfiguration ( "rescript" ) . get < boolean > ( "useExperimentalServer" , false ) ;
95+
96+ function createExperimentalLanguageClient ( ) {
97+ const binaryPathFromNodeModules = Uri . joinPath (
98+ context . extensionUri ,
99+ "node_modules" ,
100+ ".bin" ,
101+ "rescript-language-server" ,
102+ ) . fsPath ;
103+
104+ const userServerPath = workspace . getConfiguration ( "rescript" ) . get < string | null > ( "experimentalServerPath" , null ) ;
105+
106+ const binaryPath = userServerPath ? userServerPath : binaryPathFromNodeModules ;
107+
108+ let serverOptions : ServerOptions = {
109+ run : {
110+ command : binaryPath ,
111+ transport : TransportKind . stdio ,
112+ } ,
113+ debug : {
114+ command : binaryPath ,
115+ transport : TransportKind . stdio ,
116+ } ,
117+ } ;
118+
119+ // Options to control the language client
120+ let clientOptions : LanguageClientOptions = {
121+ documentSelector : [ { scheme : "file" , language : "rescript" } ] ,
122+ outputChannel,
123+ markdown : {
124+ isTrusted : true ,
125+ } ,
126+ } ;
127+
128+ const client = new LanguageClient (
129+ "ReScriptLSP" ,
130+ "ReScript Experimental Language Server" ,
131+ serverOptions ,
132+ clientOptions ,
133+ ) ;
134+
135+ // This sets up a listener that, if we're in code analysis mode, triggers
136+ // code analysis as the LS server reports that ReScript compilation has
137+ // finished. This is needed because code analysis must wait until
138+ // compilation has finished, and the most reliable source for that is the LS
139+ // server, that already keeps track of when the compiler finishes in order to
140+ // other provide fresh diagnostics.
141+ context . subscriptions . push (
142+ client . onDidChangeState ( ( { newState } ) => {
143+ if ( newState === State . Running ) {
144+ context . subscriptions . push (
145+ client . onNotification ( "rescript/compilationFinished" , ( ) => {
146+ if ( inCodeAnalysisState . active === true ) {
147+ customCommands . codeAnalysisWithReanalyze (
148+ diagnosticsCollection ,
149+ diagnosticsResultCodeActions ,
150+ outputChannel ,
151+ codeAnalysisRunningStatusBarItem ,
152+ ) ;
153+ }
154+ } ) ,
155+ ) ;
156+ }
157+ } ) ,
158+ ) ;
159+
160+ return client ;
161+ }
162+
93163 function createLanguageClient ( ) {
94164 // The server is implemented in node
95165 let serverModule = context . asAbsolutePath (
@@ -172,7 +242,7 @@ export function activate(context: ExtensionContext) {
172242 }
173243
174244 // Create the language client and start the client.
175- client = createLanguageClient ( ) ;
245+ client = useExperimentalServer ? createExperimentalLanguageClient ( ) : createLanguageClient ( ) ;
176246
177247 // Create a custom diagnostics collection, for cases where we want to report
178248 // diagnostics programatically from inside of the extension. The reason this
@@ -263,7 +333,7 @@ export function activate(context: ExtensionContext) {
263333 // Compact success display: project label plus a green check emoji
264334 compilationStatusBarItem . text = `$(check) ReScript: Ok` ;
265335 compilationStatusBarItem . backgroundColor = undefined ;
266- compilationStatusBarItem . color = null ;
336+ compilationStatusBarItem . color = undefined ;
267337 compilationStatusBarItem . command = undefined ;
268338 const projects = successes . map ( ( e ) => e . project ) . join ( ", " ) ;
269339 compilationStatusBarItem . tooltip = projects
@@ -333,7 +403,7 @@ export function activate(context: ExtensionContext) {
333403 const removeAllCodeAction = new CodeAction ( "Remove all unused in file" ) ;
334404 const edit = new WorkspaceEdit ( ) ;
335405 allRemoveActionEdits . forEach ( ( subEdit ) => {
336- subEdit . codeAction . edit . entries ( ) . forEach ( ( [ uri , [ textEdit ] ] ) => {
406+ subEdit . codeAction . edit ? .entries ( ) . forEach ( ( [ uri , [ textEdit ] ] ) => {
337407 edit . replace ( uri , textEdit . range , textEdit . newText ) ;
338408 } ) ;
339409 } ) ;
@@ -361,7 +431,7 @@ export function activate(context: ExtensionContext) {
361431
362432 const document = editor . document ;
363433 const diagnostics = diagnosticsCollection . get ( document . uri ) ;
364- const newDiagnostics = diagnostics . filter ( ( d ) => d !== diagnostic ) ;
434+ const newDiagnostics = diagnostics ? .filter ( ( d ) => d !== diagnostic ) ;
365435 diagnosticsCollection . set ( document . uri , newDiagnostics ) ;
366436 } ,
367437 ) ;
@@ -513,7 +583,7 @@ export function activate(context: ExtensionContext) {
513583
514584 commands . registerCommand ( "rescript-vscode.restart_language_server" , ( ) => {
515585 client . stop ( ) . then ( ( ) => {
516- client = createLanguageClient ( ) ;
586+ client = useExperimentalServer ? createExperimentalLanguageClient ( ) : createLanguageClient ( ) ; ;
517587 client . start ( ) ;
518588 } ) ;
519589 } ) ;
@@ -548,8 +618,12 @@ export function activate(context: ExtensionContext) {
548618 }
549619 // Send a general message that configuration has updated. Clients
550620 // interested can then pull the new configuration as they see fit.
621+ // The spec say params DidChangeConfigurationParams can contain the settings fields
622+ // https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#didChangeConfigurationParams
551623 client
552- . sendNotification ( "workspace/didChangeConfiguration" )
624+ . sendNotification ( DidChangeConfigurationNotification . type , {
625+ settings : null ,
626+ } )
553627 . catch ( ( err ) => {
554628 window . showErrorMessage ( String ( err ) ) ;
555629 } ) ;
0 commit comments