Skip to content

Instantly share code, notes, and snippets.

@lucasecdb
Last active May 4, 2024 22:11
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lucasecdb/2baf6d328a10d7fea9ec085d868923a0 to your computer and use it in GitHub Desktop.
Save lucasecdb/2baf6d328a10d7fea9ec085d868923a0 to your computer and use it in GitHub Desktop.
Godot LSP with Neovim inside WSL

Godot workflow with Neovim and WSL

Note

Since writing this guide, me and a few other users have found that using the Godot LSP directly from Neovim wasn't working as well as it could be. This is mostly due to the Godot LSP running on Windows expecting Windows paths in the LSP messages (such as C:\Users\user\MyGame\script.gd), but our LSP client sends (and also expects to receive) paths on a Linux format (e.g. /mnt/c/Users/user/MyGame/script.gd).

This caused several functionalities from the LSP to not work, and some errors to appear in the editor that weren't actual issues. To fix this, I published a very small LSP server, godot-wsl-lsp, that serves to bridge this gap and provide a better DX. It is highly recommended to use it instead of the setup described here, although it is still important to follow some of the steps. Please check the README on the LSP repository for more information.

There already are a few good blogposts regarding integrating Neovim and Godot, including how to setup its LSP server with Neovim builtin client. But none of these blogposts goes into detail into how integrate them if you are running Neovim inside Windows Subsystem for Linux, which some people would prefer, myself included.

I just wanted to provide some additional guidance on these already good posts, and how to integrate them with Windows.

The posts that were most relevant to accomplish this final results were:

The first guide will only teach how to setup Neovim in WSL to work as an external editor to Godot. Although it uses a obsolete program called nvr, which features have already been integrated in Neovim itself, check the remote docs. You can use the nvim.cmd batch program below, which has been updated to use the builtin flags on nvim. On Godot, you need to set the Exec flags as {file} {line} {col}.

The second post will explain how to integrate Godot LSP with Neovim, but they assume you are running both on the same environment (either Neovim running on Windows, or both running on Linux, for example).

The third one one explains how to access Window's localhost network from within WSL.

And the last one will explain how to bypass Windows firewall for connections coming from WSL.

The major issue is that localhost inside WSL is a separate network from Windows localhost, and we need to create a firewall rule to enable Neovim inside WSL to communicate with Godot LSP (which runs on Windows).

Note

This should only be necessary if you are not using WSL 2, in which you can set mirrored networking and would be able to access Windows' localhost without any of these firewall rules.

You can run this command (found in the superuser post's answer) in Windows to create this firewall rule:

New-NetFirewallRule -DisplayName "Godot LSP" -Direction Inbound -Protocol TCP -LocalPort 6005 -Action Allow 

I removed the -InterfaceAlias to allow any network interface to communicate with Godot LSP. You can also create another rule to access Godot's DAP as well

New-NetFirewallRule -DisplayName "Godot DAP" -Direction Inbound -Protocol TCP -LocalPort 6006 -Action Allow

These ports are configurable inside Godot, if you want to assign them to a different value. Look into Editor > Editor Settings > Network > Language/Debug Adapter. You will also need to set the "Remote Host" in the Language Server to 0.0.0.0.

Then, you should restart your computer for the firewall rules to take an effect, and then add the gdscript.lua inside this Gist inside your ~/.local/nvim/after/ftplugin/gdscript.lua file (inside WSL).

Now, you should have the server start everytime you open a GDScript file inside your Neovim instance in WSL, while Godot is running on Windows. Remember to always run neovim with the following command nvim --listen /tmp/godot.pipe.

-- after/ftplugin/gdscript.lua
local uv = require("luv")
local hostname = vim.fn.hostname() .. '.local'
local ip_address = uv.getaddrinfo(hostname)[1]['addr']
local port = os.getenv('GDScript_Port') or '6005'
local cmd = vim.lsp.rpc.connect(ip_address, port)
local pipe = '/tmp/godot.pipe'
vim.lsp.start({
name = 'Godot',
cmd = cmd,
filetypes = {'gdscript'},
root_dir = vim.fs.dirname(vim.fs.find({'project.godot', '.git'}, {
upward = true,
path = vim.fs.dirname(vim.api.nvim_buf_get_name(0))
})[1]),
on_attach = function(client, bufnr)
vim.api.nvim_command('echo serverstart("' .. pipe .. '")')
end
})
@echo off
wsl wslpath "%1" > tmpfile
set /p filepath= < tmpfile
del tmpfile
wsl nvim --server "/tmp/godot.pipe" --remote-send "<esc>:n %filepath%<CR>:call cursor(%2,%3)<CR>"
@imposis
Copy link

imposis commented Jan 18, 2024

Thank you very much for the helpful guide.

I'll just add some advice on issues i encountered while trying to set this up.

  1. Check if any paths you use anywhere contain spaces, this causes issues in nvim.cmd in %filepath% for example.
  2. Connecting from WSL to host machine. I couldn't get it working based on the link provided. I fixed this by changing the IP where the LSP server is hosted from localhost to 0.0.0.0 in Godot config, https://docs.godotengine.org/en/stable/tutorials/io/data_paths.html#editor-data-paths. This is can be also fixed if you're on WIN11 22H2 build and later where you can put networkingMode=mirrored into .wslconfig and you should be able to access host localhost, but i didn't test this

@General-Mudkip
Copy link

  1. Check if any paths you use anywhere contain spaces, this causes issues in nvim.cmd in %filepath% for example.

For this issue, you can change
wsl wslpath "%1" > tmpfile
To
wsl wslpath "%~f1" > tmpfile

This helped me resolve any problems with having a space in the path name.

@KK01101011
Copy link

I didn't quite get the godot-wsl-lsp. How can I implement it, should I just change the gdscript.lua?

@General-Mudkip
Copy link

General-Mudkip commented Apr 27, 2024

You need to install it globally using npm i -g godot-wsl-lsp, and then (if you're using LSPConfig) included it in the gdscript LSP setup, like this:

local lspconfig = require("lspconfig")

lspconfig.gdscript.setup({
   cmd = { "godot-wsl-lsp", "--useMirroredNetworking" }
})

Additionally, you should set up Mirrored Networking on WSL2, this article is good: https://learn.microsoft.com/en-us/windows/wsl/wsl-config

@KK01101011
Copy link

You need to install it globally using npm i -g godot-wsl-lsp, and then (if you're using LSPConfig) included it in the gdscript LSP setup, like this:

local lspconfig = require("lspconfig")

lspconfig.gdscript.setup({
   cmd = { "godot-wsl-lsp", "--useMirroredNetworking" }
})

Additionally, you should set up Mirrored Networking on WSL2, this article is good: https://learn.microsoft.com/en-us/windows/wsl/wsl-config

Unfortunately it gives an error that says "Client 1 quit with exit code 127 and signal 0". I've tried a lot of things but I couldn't get the GDScript LSP to work. The scripts that I open from Godot are immediately sent to Nvim and there is no problem with that but I can't get the LSP and the documentation to work. I am an amateur in Godot, Nvim, Linux, WSL and all things software so I couldn't find what is wrong. :(

@General-Mudkip
Copy link

You need to install it globally using npm i -g godot-wsl-lsp, and then (if you're using LSPConfig) included it in the gdscript LSP setup, like this:

local lspconfig = require("lspconfig")

lspconfig.gdscript.setup({
   cmd = { "godot-wsl-lsp", "--useMirroredNetworking" }
})

Additionally, you should set up Mirrored Networking on WSL2, this article is good: https://learn.microsoft.com/en-us/windows/wsl/wsl-config

Unfortunately it gives an error that says "Client 1 quit with exit code 127 and signal 0". I've tried a lot of things but I couldn't get the GDScript LSP to work. The scripts that I open from Godot are immediately sent to Nvim and there is no problem with that but I can't get the LSP and the documentation to work. I am an amateur in Godot, Nvim, Linux, WSL and all things software so I couldn't find what is wrong. :(

Godot needs to be open in order for the LSP to work, and the port for the Language Server (which can be found in Editor Settings) needs to be correct (6005 if I recall correctly). Are you sure that mirrored networking has been enabled? That's an important part.

@KK01101011
Copy link

You need to install it globally using npm i -g godot-wsl-lsp, and then (if you're using LSPConfig) included it in the gdscript LSP setup, like this:

local lspconfig = require("lspconfig")

lspconfig.gdscript.setup({
   cmd = { "godot-wsl-lsp", "--useMirroredNetworking" }
})

Additionally, you should set up Mirrored Networking on WSL2, this article is good: https://learn.microsoft.com/en-us/windows/wsl/wsl-config

Unfortunately it gives an error that says "Client 1 quit with exit code 127 and signal 0". I've tried a lot of things but I couldn't get the GDScript LSP to work. The scripts that I open from Godot are immediately sent to Nvim and there is no problem with that but I can't get the LSP and the documentation to work. I am an amateur in Godot, Nvim, Linux, WSL and all things software so I couldn't find what is wrong. :(

Godot needs to be open in order for the LSP to work, and the port for the Language Server (which can be found in Editor Settings) needs to be correct (6005 if I recall correctly). Are you sure that mirrored networking has been enabled? That's an important part.

All of this seems to correct but the issue still persist. I've created a file called .wslconfig.txt and the files includes this:
[wsl2] networkingMode=mirrored

Godot is open and the port is set to 6005. What else can be the problem? I am going crazy.

@General-Mudkip
Copy link

General-Mudkip commented Apr 27, 2024

You need to install it globally using npm i -g godot-wsl-lsp, and then (if you're using LSPConfig) included it in the gdscript LSP setup, like this:

local lspconfig = require("lspconfig")

lspconfig.gdscript.setup({
   cmd = { "godot-wsl-lsp", "--useMirroredNetworking" }
})

Additionally, you should set up Mirrored Networking on WSL2, this article is good: https://learn.microsoft.com/en-us/windows/wsl/wsl-config

Unfortunately it gives an error that says "Client 1 quit with exit code 127 and signal 0". I've tried a lot of things but I couldn't get the GDScript LSP to work. The scripts that I open from Godot are immediately sent to Nvim and there is no problem with that but I can't get the LSP and the documentation to work. I am an amateur in Godot, Nvim, Linux, WSL and all things software so I couldn't find what is wrong. :(

Godot needs to be open in order for the LSP to work, and the port for the Language Server (which can be found in Editor Settings) needs to be correct (6005 if I recall correctly). Are you sure that mirrored networking has been enabled? That's an important part.

All of this seems to correct but the issue still persist. I've created a file called .wslconfig.txt and the files includes this: [wsl2] networkingMode=mirrored

Godot is open and the port is set to 6005. What else can be the problem? I am going crazy.

I believe that the file has to be simply “.wslconfig” (no .txt) and placed in your Windows C:/Users/YourUsername/ directory. This page has some more info: https://learn.microsoft.com/en-us/windows/wsl/wsl-config#wslconfig

Also, maybe checking your NeoVim log files could help identify the issue.

@KK01101011
Copy link

Now I have changed the .wslconfig.txt to .wslconfig . It still does not work unfortunately. These are my logs:

[START][2024-04-28 10:05:54] LSP logging initiated
[ERROR][2024-04-28 10:05:54] .../vim/lsp/rpc.lua:734	"rpc"	"/mnt/c/Users/orsimsek/AppData/Roaming/npm/godot-wsl-lsp"	"stderr"	"node:internal/dns/promises:275\n    this.reject(new DNSException(err, this.bindingName, this.hostname));\n                ^\n\nError: queryAny ENOTIMP localhost\n    at QueryReqWrap.onresolve [as oncomplete] (node:internal/dns/promises:275:17) {\n  errno: undefined,\n  code: 'ENOTIMP',\n  syscall: 'queryAny',\n  hostname: 'localhost'\n}\n\nNode.js v20.12.2\n"

From what I understand there is a problem with dns maybe?

@General-Mudkip
Copy link

Now I have changed the .wslconfig.txt to .wslconfig . It still does not work unfortunately. These are my logs:

[START][2024-04-28 10:05:54] LSP logging initiated
[ERROR][2024-04-28 10:05:54] .../vim/lsp/rpc.lua:734	"rpc"	"/mnt/c/Users/orsimsek/AppData/Roaming/npm/godot-wsl-lsp"	"stderr"	"node:internal/dns/promises:275\n    this.reject(new DNSException(err, this.bindingName, this.hostname));\n                ^\n\nError: queryAny ENOTIMP localhost\n    at QueryReqWrap.onresolve [as oncomplete] (node:internal/dns/promises:275:17) {\n  errno: undefined,\n  code: 'ENOTIMP',\n  syscall: 'queryAny',\n  hostname: 'localhost'\n}\n\nNode.js v20.12.2\n"

From what I understand there is a problem with dns maybe?

Maybe try having [wsl2] and networkingMode=mirrored on different lines? Other than that I’m really not sure, sorry. When I had the same issue turning on mirrored networking resolved it.

@KK01101011
Copy link

Now I have changed the .wslconfig.txt to .wslconfig . It still does not work unfortunately. These are my logs:

[START][2024-04-28 10:05:54] LSP logging initiated
[ERROR][2024-04-28 10:05:54] .../vim/lsp/rpc.lua:734	"rpc"	"/mnt/c/Users/orsimsek/AppData/Roaming/npm/godot-wsl-lsp"	"stderr"	"node:internal/dns/promises:275\n    this.reject(new DNSException(err, this.bindingName, this.hostname));\n                ^\n\nError: queryAny ENOTIMP localhost\n    at QueryReqWrap.onresolve [as oncomplete] (node:internal/dns/promises:275:17) {\n  errno: undefined,\n  code: 'ENOTIMP',\n  syscall: 'queryAny',\n  hostname: 'localhost'\n}\n\nNode.js v20.12.2\n"

From what I understand there is a problem with dns maybe?

Maybe try having [wsl2] and networkingMode=mirrored on different lines? Other than that I’m really not sure, sorry. When I had the same issue turning on mirrored networking resolved it.

They are actually on different lines, I've checked it now again. It doesn't work unfortunately. Thank you so much for your help anyways.

@General-Mudkip
Copy link

Now I have changed the .wslconfig.txt to .wslconfig . It still does not work unfortunately. These are my logs:

[START][2024-04-28 10:05:54] LSP logging initiated
[ERROR][2024-04-28 10:05:54] .../vim/lsp/rpc.lua:734	"rpc"	"/mnt/c/Users/orsimsek/AppData/Roaming/npm/godot-wsl-lsp"	"stderr"	"node:internal/dns/promises:275\n    this.reject(new DNSException(err, this.bindingName, this.hostname));\n                ^\n\nError: queryAny ENOTIMP localhost\n    at QueryReqWrap.onresolve [as oncomplete] (node:internal/dns/promises:275:17) {\n  errno: undefined,\n  code: 'ENOTIMP',\n  syscall: 'queryAny',\n  hostname: 'localhost'\n}\n\nNode.js v20.12.2\n"

From what I understand there is a problem with dns maybe?

Maybe try having [wsl2] and networkingMode=mirrored on different lines? Other than that I’m really not sure, sorry. When I had the same issue turning on mirrored networking resolved it.

They are actually on different lines, I've checked it now again. It doesn't work unfortunately. Thank you so much for your help anyways.

If it gives you any consolation, I eventually ended up using VS Code with Vim emulation as the LSP experience in NeoVim wasn’t amazing. Not perfect, but does the job

@lucasecdb
Copy link
Author

@KK01101011 did you install godot-wsl-lsp on Windows or Linux? from your stacktrace it suggests that you installed it globally on Windows, and that won't work "/mnt/c/Users/orsimsek/AppData/Roaming/npm/godot-wsl-lsp"

This guide is for users who want to run Neovim inside of Windows Subsystem for Linux. If you are running Neovim on Windows you don't need this guide, and can use the Godot LSP directly.

@KK01101011
Copy link

@KK01101011 did you install godot-wsl-lsp on Windows or Linux? from your stacktrace it suggests that you installed it globally on Windows, and that won't work "/mnt/c/Users/orsimsek/AppData/Roaming/npm/godot-wsl-lsp"

This guide is for users who want to run Neovim inside of Windows Subsystem for Linux. If you are running Neovim on Windows you don't need this guide, and can use the Godot LSP directly.

I did both. I also use WSL. It didn't work for me no matter what I did. Now I am using Helix through WSL with Godot and I have no problems.

@KK01101011
Copy link

[ERROR][2024-05-05 01:09:42] .../vim/lsp/rpc.lua:770 "rpc" "/usr/bin/godot-wsl-lsp" "stderr" "node:internal/dns/promises:293\n this.reject(new DNSException(err, this.bindingName, this.hostname));\n ^\n\nError: queryAny ENOTIMP localhost\n at QueryReqWrap.onresolve [as oncomplete] (node:internal/dns/promises:293:17) {\n errno: undefined,\n code: 'ENOTIMP',\n syscall: 'queryAny',\n hostname: 'localhost'\n}\n\nNode.js v22.1.0\n"
This was also a log which shows the WSL path btw. I think there is a DNS problem that is beyond my knowledge

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment