Using Godots LSP in GDScript

Godot Version

4.4

Question

i want to send godots LSP some file with some content and then i want to retrieve any errors or warnings which would usually show up in the script editor.

yea… that’s it.

i wanna to retrieve the errors and warnings which occur when writing a script with a certain content!

(i am building a plugin, if there is any way to get those errors without interacting with the LSP, and using some internal functions instead, that would be great too!)

I’m not aware of a way to do that without writing your own LSP client and connect to it. Maybe try searching/opening a proposal here GitHub - godotengine/godot-proposals: Godot Improvement Proposals (GIPs)

I started a few days ago to do that but I’m far from done. Here’s the script:

@tool
extends EditorPlugin



func _enable_plugin() -> void:
	# Add autoloads here.
	pass


func _disable_plugin() -> void:
	# Remove autoloads here.
	pass

signal connected()
signal packet_received(packet:PackedByteArray)

var stream:StreamPeerTCP
var is_connected:bool = false
var json_rpc := JSONRPC.new()


func _enter_tree() -> void:
	connected.connect(func():
		print("CONNECTED")
		var request = json_rpc.make_request("initialize", {
			processId = null,
			rootUri = ABSOLUTE_PATH_TO_PROJECT, # starting with file:/// 
			capabilities = {}
		}, 0)
		print(JSON.stringify(request))
		make_request(request)
	)
	packet_received.connect(func(packet:PackedByteArray):
		print("Packet!")
		print(packet.get_string_from_utf8())
	)
	is_connected = false
	var editor_settings = EditorInterface.get_editor_settings()
	var host = editor_settings.get_setting("network/language_server/remote_host")
	var port = editor_settings.get_setting("network/language_server/remote_port")
	stream = StreamPeerTCP.new()
	stream.connect_to_host(host, port)


func _exit_tree() -> void:
	if stream:
		stream.disconnect_from_host()
		stream = null

	for connection in connected.get_connections():
		connected.disconnect(connection.callable)

	for connection in packet_received.get_connections():
		packet_received.disconnect(connection.callable)


func _process(delta: float) -> void:
	var status = stream.get_status()
	match status:
		StreamPeerTCP.STATUS_NONE:
			return
		StreamPeerTCP.STATUS_ERROR:
			print("ERROR!")
		_:
			if stream.poll() == OK:
				var available_bytes = stream.get_available_bytes()
				if available_bytes > 0:
					var data = stream.get_data(available_bytes)
					if data[0] == OK:
						packet_received.emit(data[1])
					else:
						print("Error when getting data: %s" % error_string(data[0]))
			else:
				print("Failed to poll()")

			if not is_connected and status == StreamPeerTCP.STATUS_CONNECTED:
				is_connected = true
				connected.emit()


func make_request(request:Dictionary) -> void:
	var json = JSON.stringify(request)
	var length = json.to_utf8_buffer().size()
	var content = """Content-Length: {length}\r\n\r\n{json}""".format({
		length = length,
		json = json
	})
	print(content)
	var packet = content.to_utf8_buffer()
	var result = stream.put_data(packet)

1 Like

Do you believe this is important enough to be a Godot Proposal?

I feel that only very few usecases or plugins could take adventage of this…

Also, like you said, this can essentially be accomplished with a TcpClient, even if it is weird that we have to access the LSP like that, since we are in the program with the LSP already.

Thank you very much for the code! I tried to do this myself and only got… towards the very first jsonrpc reply.. But you seem to know much more about the matter, so I will try to iterate on the code and see where I can take it!