@@ -112,24 +112,35 @@ class Server {
112112 /**
113113 * Handle incoming JSON-RPC message
114114 */
115- public function handleMessage (msg : Message ): Null <ResponseMessage > {
115+ /**
116+ * Synchronous wrapper for handleMessage. Only safe when handleFile is synchronous.
117+ * Used by the VS Code extension (Node.js) where file access is sync.
118+ */
119+ public function handleMessageSync (msg : Message ): Null <ResponseMessage > {
120+ var result : Null <ResponseMessage > = null ;
121+ handleMessage (msg , (response ) -> { result = response ; });
122+ return result ;
123+ }
124+
125+ public function handleMessage (msg : Message , ? onResponse : (response : Null <ResponseMessage >)-> Void ): Void {
116126 try {
117127 // Check message type and dispatch accordingly
118128 if (Reflect .hasField (msg , " method" )) {
119129 if (Reflect .hasField (msg , " id" )) {
120- // Request message
121- return handleRequest (cast msg );
130+ // Request message (may be async)
131+ handleRequest (cast msg , onResponse );
122132 } else {
123- // Notification message
133+ // Notification message (no response)
124134 handleNotification (cast msg );
125- return null ;
135+ if ( onResponse != null ) onResponse ( null ) ;
126136 }
137+ return ;
127138 }
128139
129- return createErrorResponse (null , ErrorCodes . InvalidRequest , " Invalid message" );
140+ if ( onResponse != null ) onResponse ( createErrorResponse (null , ErrorCodes . InvalidRequest , " Invalid message" ) );
130141
131142 } catch (e : Dynamic ) {
132- return createErrorResponse (null , ErrorCodes . ParseError , Std .string (e ));
143+ if ( onResponse != null ) onResponse ( createErrorResponse (null , ErrorCodes . ParseError , Std .string (e ) ));
133144 }
134145 }
135146
@@ -164,60 +175,88 @@ class Server {
164175 /**
165176 * Handle a request message
166177 */
167- function handleRequest (request : RequestMessage ) : ResponseMessage {
178+ function handleRequest (request : RequestMessage , ? onResponse : ( response : ResponseMessage ) -> Void ) : Void {
168179 try {
169180 if (! initialized && request .method != " initialize" ) {
170181 throw { code : ErrorCodes . ServerNotInitialized , message : " Server not initialized" };
171182 }
172183
173- final result = resolveRequest (request );
174- return createResponse (request .id , result );
184+ resolveRequest (request , (result ) -> {
185+ if (onResponse != null ) onResponse (createResponse (request .id , result ));
186+ });
175187
176188 } catch (e : Any ) {
177- if (Reflect .hasField (e , ' code' ) && Reflect .hasField (e , ' message' )) {
178- final err : ResponseError = e ;
179- return createErrorResponse (request .id , err .code , err .message );
180- }
181- else {
182- return createErrorResponse (request .id , ErrorCodes . InternalError , Std .string (e ));
189+ if (onResponse != null ) {
190+ if (Reflect .hasField (e , ' code' ) && Reflect .hasField (e , ' message' )) {
191+ final err : ResponseError = e ;
192+ onResponse (createErrorResponse (request .id , err .code , err .message ));
193+ }
194+ else {
195+ onResponse (createErrorResponse (request .id , ErrorCodes . InternalError , Std .string (e )));
196+ }
183197 }
184198 }
185- return null ;
186199 }
187200
188- function resolveRequest (request : RequestMessage ) : Any {
201+ function resolveRequest (request : RequestMessage , callback : ( result : Any ) -> Void ) : Void {
189202
190- return switch (request .method ) {
191- case " initialize" :
192- handleInitialize (cast request .params );
203+ // Determine which URI needs to be up-to-date before handling
204+ final uri = getRequestDocumentUri (request );
193205
194- case " shutdown" :
195- handleShutdown ();
206+ // Ensure the document is up-to-date (async-safe), then dispatch
207+ updateDocumentIfNeeded (uri , () -> {
208+ try {
209+ final result : Any = switch (request .method ) {
210+ case " initialize" :
211+ handleInitialize (cast request .params );
196212
197- case " textDocument/completion " :
198- handleCompletion ( cast request . params );
213+ case " shutdown " :
214+ handleShutdown ( );
199215
200- case " textDocument/definition " :
201- handleDefinition (cast request .params );
216+ case " textDocument/completion " :
217+ handleCompletion (cast request .params );
202218
203- case " textDocument/hover " :
204- handleHover (cast request .params );
219+ case " textDocument/definition " :
220+ handleDefinition (cast request .params );
205221
206- case " textDocument/documentSymbol " :
207- handleDocumentSymbol (cast request .params );
222+ case " textDocument/hover " :
223+ handleHover (cast request .params );
208224
209- case " textDocument/references " :
210- null ; // TODO
225+ case " textDocument/documentSymbol " :
226+ handleDocumentSymbol ( cast request . params );
211227
212- case " textDocument/formatting " :
213- handleDocumentFormatting ( cast request . params );
228+ case " textDocument/references " :
229+ null ; // TODO
214230
215- case _ :
216- throw { code : ErrorCodes . MethodNotFound , message : ' Method not found: ${request .method }' };
217- }
231+ case " textDocument/formatting" :
232+ handleDocumentFormatting (cast request .params );
233+
234+ case _ :
235+ throw { code : ErrorCodes . MethodNotFound , message : ' Method not found: ${request .method }' };
236+ }
237+ callback (result );
238+ } catch (e : Any ) {
239+ if (Reflect .hasField (e , ' code' ) && Reflect .hasField (e , ' message' )) {
240+ throw e ;
241+ } else {
242+ throw { code : ErrorCodes . InternalError , message : Std .string (e ) };
243+ }
244+ }
245+ });
218246
219247 }
220248
249+ /** Extracts the textDocument.uri from a request's params, or null if not applicable. */
250+ function getRequestDocumentUri (request : RequestMessage ): Null <String > {
251+ try {
252+ final params : Dynamic = request .params ;
253+ if (params != null && params .textDocument != null && params .textDocument .uri != null ) {
254+ return params .textDocument .uri ;
255+ }
256+ } catch (_ : Dynamic ) {}
257+ return null ;
258+ }
259+
221260 /**
222261 * Handle a notification message
223262 */
@@ -545,14 +584,17 @@ class Server {
545584 return components .join (" /" );
546585 }
547586
548- function updateDocumentIfNeeded (uri : String ) {
587+ function updateDocumentIfNeeded (uri : String , ? callback : () -> Void ) {
549588
550589 if (dirtyDocuments .exists (uri )) {
551590 handleFile (pathFromUri (uri ), content -> {
552591 if (content != null ) {
553592 updateDocument (uri , content , true );
554593 }
594+ if (callback != null ) callback ();
555595 });
596+ } else {
597+ if (callback != null ) callback ();
556598 }
557599
558600 }
@@ -715,7 +757,6 @@ class Server {
715757 }): Array <CompletionItem > {
716758
717759 final uri = params .textDocument .uri ;
718- updateDocumentIfNeeded (uri );
719760
720761 final ast = documents .get (uri );
721762 if (ast == null ) return [];
@@ -1122,7 +1163,6 @@ class Server {
11221163 final result : Array <LocationLink > = [];
11231164
11241165 final uri = params .textDocument .uri ;
1125- updateDocumentIfNeeded (uri );
11261166
11271167 final ast = documents .get (uri );
11281168 if (ast == null ) return result ;
@@ -1405,7 +1445,6 @@ class Server {
14051445 position : Position
14061446 }): Null <Hover > {
14071447 final uri = params .textDocument .uri ;
1408- updateDocumentIfNeeded (uri );
14091448
14101449 final ast = documents .get (uri );
14111450 if (ast == null ) return null ;
@@ -1858,7 +1897,11 @@ class Server {
18581897 for (ref in incomingBeats ) {
18591898 final incoming = lens .getFirstParentOfType (ref .origin , NBeatDecl );
18601899 if (incoming != null ) {
1861- targets .push (makePositionLink (incoming .name , uri , incoming ));
1900+ if (incoming .name == " _" ) {
1901+ targets .push (" *top level*" );
1902+ } else {
1903+ targets .push (makePositionLink (incoming .name , uri , incoming ));
1904+ }
18621905 }
18631906 }
18641907 description .push (" - From: " + targets .join (' , ' ));
@@ -2107,8 +2150,6 @@ class Server {
21072150 textDocument : TextDocumentIdentifier
21082151 }): Array <DocumentSymbol > {
21092152
2110- updateDocumentIfNeeded (params .textDocument .uri );
2111-
21122153 final ast = documents .get (params .textDocument .uri );
21132154 if (ast == null ) return [];
21142155
0 commit comments