@@ -14,7 +14,6 @@ signal on_publish_diagnostics(diagnostics: DiagnosticList_Diagnostic.Pack)
1414signal on_jsonrpc_error (error : Dictionary )
1515
1616
17- const ENABLE_DEBUG_LOG : bool = false
1817const TICK_INTERVAL_SECONDS_MIN : float = 0.05
1918const TICK_INTERVAL_SECONDS_MAX : float = 30.0
2019
@@ -26,7 +25,14 @@ var _jsonrpc := JSONRPC.new()
2625var _client := StreamPeerTCP .new ()
2726var _id : int = 0
2827var _timer : Timer
29- var _lsp_project_path : String = "" # Absolute project path reported by LS
28+
29+ ## Absolute project path reported by LS.
30+ ## Will be initialized when the LSP connection is initialized ( _initialize()) and changed by
31+ ## changeWorkspace events.
32+ var _ls_project_path : String = ""
33+
34+ ## Absolute path of the loaded project.
35+ var _project_path : String = ProjectSettings .globalize_path ("res://" ).simplify_path ()
3036
3137
3238func _init (root : Node ) -> void :
@@ -41,7 +47,7 @@ func _init(root: Node) -> void:
4147
4248
4349func disconnect_lsp () -> void :
44- log_debug ("Disconnecting from LSP" )
50+ DiagnosticList_Utils . log_debug ("Disconnecting from LSP" )
4551 _timer .stop ()
4652 _client .disconnect_from_host ()
4753
@@ -56,10 +62,11 @@ func connect_lsp() -> bool:
5662
5763## Connect to the LSP server at the given host and port.
5864func connect_lsp_at (host : String , port : int ) -> bool :
65+ DiagnosticList_Utils .log_debug ("Connecting to LSP at %s :%d " % [ host , port ])
5966 var err := _client .connect_to_host (host , port )
6067
6168 if err != OK :
62- log_error ("Failed to connect to LSP server: %s " % err )
69+ DiagnosticList_Utils . log_error ("Failed to connect to LSP server: %s " % err )
6370 return false
6471
6572 # Enable processing
@@ -96,9 +103,11 @@ func update_diagnostics(res_path: String, content: String) -> void:
96103 })
97104
98105
99- ## Returns the absolute project path as reported by the LS.
100- func get_project_path () -> String :
101- return _lsp_project_path
106+ ## Returns whether the root directory reported by the LS is the same as the project root.
107+ ## If there is a mismatch, the LSP client is very likely connected to a different Godot instance
108+ ## with a different project opened.
109+ func lsp_root_matches_project_root () -> bool :
110+ return _ls_project_path == _project_path
102111
103112
104113func _reset_tick_interval () -> void :
@@ -118,10 +127,11 @@ func _on_tick() -> void:
118127 _update_tick_interval ()
119128
120129 while _client .get_available_bytes ():
130+ DiagnosticList_Utils .log_debug ("Bytes available: %d " % _client .get_available_bytes ())
121131 var json := _read_data ()
122132
123133 if json :
124- log_debug ("Received message:\n %s " % json )
134+ DiagnosticList_Utils . log_debug ("Received message:\n %s " % json )
125135
126136 _handle_response (json )
127137 _reset_tick_interval () # Reset timer interval whenever data arrived as there will likely be more data coming
@@ -139,14 +149,14 @@ func _update_status() -> bool:
139149 StreamPeerTCP .STATUS_NONE :
140150 return false
141151 StreamPeerTCP .STATUS_ERROR :
142- log_error ("StreamPeerTCP error" )
152+ DiagnosticList_Utils . log_error ("StreamPeerTCP error" )
143153 return false
144154 StreamPeerTCP .STATUS_CONNECTING :
145155 pass
146156 StreamPeerTCP .STATUS_CONNECTED :
147157 # First time connected -> run initialization
148158 if last_status != status :
149- log_debug ("Connected to LSP" )
159+ DiagnosticList_Utils . log_debug ("Connected to LSP" )
150160 on_connected .emit ()
151161 _initialize ()
152162
@@ -166,7 +176,7 @@ func _read_data() -> Dictionary:
166176 var json : Dictionary = JSON .parse_string (content )
167177
168178 if not json :
169- log_error ("Failed to parse JSON: %s " % content )
179+ DiagnosticList_Utils . log_error ("Failed to parse JSON: %s " % content )
170180 return {}
171181
172182 return json
@@ -176,14 +186,16 @@ func _read_content(length: int) -> String:
176186 var data := _client .get_data (length )
177187
178188 if data [0 ] != OK :
179- log_error ("Failed to read content: %s " % error_string (data [0 ]))
189+ DiagnosticList_Utils . log_error ("Failed to read content: %s " % error_string (data [0 ]))
180190 return ""
181191 else :
182192 var buf : PackedByteArray = data [1 ]
183193 return buf .get_string_from_utf8 ()
184194
185195
186196func _read_header () -> String :
197+ DiagnosticList_Utils .log_debug ("reading header" )
198+
187199 var buf := PackedByteArray ()
188200 var char_r := "\r " .unicode_at (0 )
189201 var char_n := "\n " .unicode_at (0 )
@@ -192,7 +204,7 @@ func _read_header() -> String:
192204 var data := _client .get_data (1 )
193205
194206 if data [0 ] != OK :
195- log_error ("Failed to read header: %s " % error_string (data [0 ]))
207+ DiagnosticList_Utils . log_error ("Failed to read header: %s " % error_string (data [0 ]))
196208 return ""
197209 else :
198210 buf .push_back (data [1 ][0 ])
@@ -221,20 +233,22 @@ func _handle_response(json: Dictionary) -> void:
221233
222234 # Project path
223235 "gdscript_client/changeWorkspace" :
224- _lsp_project_path = str (json ["params" ]["path" ]).simplify_path ()
236+ _ls_project_path = str (json ["params" ]["path" ]).simplify_path ()
237+ DiagnosticList_Utils .log_debug ("Change Workspace: %s " % _ls_project_path )
225238 return
226239
227240 # Initialization response
228241 if json .get ("id" ) == 0 :
242+ DiagnosticList_Utils .log_debug ("LSP initialized" )
229243 _send_notification ("initialized" , {})
230244 on_initialized .emit ()
231245 return
232246
233247 # JSON-RPC error
234248 if json .has ("error" ):
235249 var error : Dictionary = json ["error" ]
236- log_error ("JSON-RPC Error: %s " % error )
237- log_error ("This is likely a bug in the plugin. Consider submitting a bug report on GitHub." )
250+ DiagnosticList_Utils . log_error ("JSON-RPC Error: %s " % error )
251+ DiagnosticList_Utils . log_error ("This is likely a bug in the plugin. Consider submitting a bug report on GitHub." )
238252 on_jsonrpc_error .emit (error )
239253
240254
@@ -275,14 +289,24 @@ func _send(json: Dictionary) -> void:
275289 var content_bytes := content .to_utf8_buffer ()
276290 var header := "Content-Length: %s \r\n\r\n " % len (content_bytes )
277291 var header_bytes := header .to_ascii_buffer ()
278- log_debug ("Sending message (length: %s ): %s " % [ len (content_bytes ), content ])
279- _client .put_data (header_bytes + content_bytes )
292+ DiagnosticList_Utils .log_debug ("Sending message (length: %s ): %s " % [ len (content_bytes ), content ])
293+ var err := _client .put_data (header_bytes + content_bytes )
294+
295+ if err != OK :
296+ DiagnosticList_Utils .log_error ("Failed to send: %s " % error_string (err ))
297+
280298 _reset_tick_interval () # Reset the timer interval because we are expecting a response
281299
282300
283301func _initialize () -> void :
302+ # Comply with LSP, try to initialize root directory to the current project root.
303+ # Godot will likely ignore it and send a changeWorkspace event anyway.
304+ _ls_project_path = _project_path
305+
284306 _send_request ("initialize" , {
285307 "processId" : null ,
308+ "rootPath" : _ls_project_path ,
309+ "rootUri" : _res_path_to_lsp_uri ("res://" ),
286310 "capabilities" : {
287311 "textDocument" : {
288312 "publishDiagnostics" : {},
@@ -292,16 +316,10 @@ func _initialize() -> void:
292316
293317
294318func _res_path_to_lsp_uri (res_path : String ) -> String :
319+ # NOTE: No need to manually call uri_encode() as JSONRPC seems to do that automatically
320+ # TODO: Consider using the builtin parsing functionality of JSONRPC instead of doing it manually
295321 return URI_PREFIX + ProjectSettings .globalize_path (res_path ).simplify_path ()
296322
297323
298324func _lsp_uri_to_res_path (lsp_uri : String ) -> String :
299- return ProjectSettings .localize_path (lsp_uri .replace (URI_PREFIX , "" ))
300-
301-
302- func log_debug (text : String ) -> void :
303- if ENABLE_DEBUG_LOG :
304- print ("[DiagnosticList] " , text )
305-
306- func log_error (text : String ) -> void :
307- push_error ("[DiagnosticList] " , text )
325+ return ProjectSettings .localize_path (lsp_uri .trim_prefix (URI_PREFIX ).uri_decode ())
0 commit comments