gdshader support (highlighting, tree-sitter, LSP)

This commit is contained in:
mathijs-bakker
2025-08-21 16:37:05 +02:00
parent 5e81e7f228
commit 4d7d1097ac
6 changed files with 172 additions and 67 deletions

125
README.md
View File

@@ -1,93 +1,84 @@
# godotdev.nvim
A Neovim plugin for connecting to the Godot editor LSP server to provide code navigation, diagnostics, and LSP features for GDScript projects. Supports Windows, macOS, and Linux.
Batteries-included Neovim plugin for **Godot game development** (Godot 4.3+), using Neovim as an external editor. Provides LSP support for GDScript and Godot shaders, DAP debugging, and Treesitter syntax highlighting.
## Features
- Connect to a running Godot editor's TCP LSP server.
- LSP-based code navigation for GDScript (`gd`/`gdscript`).
- Diagnostics, hover documentation, workspace symbols, and more.
- Healthcheck to validate editor LSP and required tools.
- OS-aware handling for TCP connection (`ncat` required on Windows).
- Connect to Godot editor LSP over TCP (`127.0.0.1:6005` by default)
- Full GDScript language support
- `.gdshader` syntax highlighting via Treesitter
- Debug GDScript with `nvim-dap` (`127.0.0.1:6006` by default)
- Keymaps for common LSP actions
- Batteries included: everything you need for Godot development in Neovim
## Requirements
- Neovim 0.11+
- Godot editor with TCP LSP server enabled (Editor Settings → Network → Enable TCP LSP server).
- **Windows:** `ncat` must be installed (via Scoop or Chocolatey).
- **macOS/Linux:** optional `nc` for port check; otherwise assumed reachable.
- Neovim 0.9+
- Godot 4.3+ with TCP LSP enabled
- `nvim-lspconfig`
- `nvim-dap` and `nvim-dap-ui` for debugging
- `nvim-treesitter`
- Windows users must have [`ncat`](https://nmap.org/ncat/) in PATH
## Installation
## Installation (Lazy.nvim)
Using [Lazy.nvim](https://github.com/folke/lazy.nvim):
```lua
`return {
{
'Mathijs-Bakker/godotdev.nvim',
lazy = false,
dependencies = { 'nvim-lspconfig', 'nvim-dap', 'nvim-dap-ui', 'nvim-treesitter' },
config = function()
require("godotdev").setup {
editor_port = 6005, -- optional, default is 6005
editor_host = "127.0.0.1" -- optional, default is localhost
}
end
}
```
## Quickstart
```lua
-- Lazy.nvim
return {
'Mathijs-Bakker/godotdev.nvim',
config = function()
require("godotdev").setup({
editor_host = "127.0.0.1", -- Default: 127.0.0.1
editor_port = 6005, -- GDScript language server, default: 6005
debug_port = 6006, -- Debug adapter server, default: 6006
})
require("godotdev").setup()
end,
}
```
## Quickstart
1. Open your Godot project in Neovim
1. Start Godot editor with TCP LSP enabled (Editor Settings → Network → Enable TCP LSP server)
1. Open a .gd or .gdshader file
1. LSP will automatically attach
1. Use <leader>rn to rename, gd to go to definition, gr for references, etc.
1. Start debugging with DAP (Launch scene configuration)
## Configuration
```lua
require("godotdev").setup({
editor_host = "127.0.0.1", -- Godot editor host
editor_port = 6005, -- LSP port
debug_port = 6006, -- DAP port
})
```
# Keymaps
### LSP
- Open any `.gd` or `.gdscript` file in your Godot project.
- The plugin connects automatically to the running Godot editor LSP.
- Keymaps for LSP features (definitions, references, symbols, etc.) are attached per buffer.
`gd` → Go to definition
`gD` → Go to declaration
`gy` → Type definition
`gi` → Go to implementation
`gr` → List references
`K` → Hover
`<C-k>` → Signature help
`<leader>rn` → Rename symbol
`<leader>ca` → Code action
`<leader>f` → Format buffer
`gl` → Show diagnostics
`[d` / `]d` → Previous/next diagnostic
### DAP
`F5` -> Continue/Start
`F10` -> Step over
`F11` -> Step into
`F12` -> Step out
`<leader>db` -> Toggle Breakpoint
`<leader>dB` -> Conditional breakpoint
- Requires `nvim-dap` and `nvim-dap-ui`.
- Launch your game or scene directly from Neovim:
1. Make sure Godot is running with the debugger server enabled.
1. Open a `.gd` or `.gdscript` file.
1. Use DAP commands:
- `:DapContinue` Start/Continue debugging
- `:DapStepOver`, `:DapStepInto`, `:DapStepOut`
1. The DAP UI automatically opens when debugging starts.
### DAP UI
`<leader>du` -> , Toggle UI
`<leader>dr` -> , Open REPL
### Healthcheck
```
:checkhealth godotdev
```
- Checks if the Godot editor LSP port is reachable.
- On Windows, also checks if `ncat` is installed.
- Gives clear instructions to fix missing dependencies or misconfigured ports.
## Configuration Options
```lua
`require("godotdev").setup {
editor_host = "127.0.0.1", -- default
editor_port = 6005, -- default
}`
```
## Notes
- The plugin does **not** start a Godot instance automatically; the Godot editor must be running with TCP LSP enabled.
- On Windows, `ncat` is required for the TCP LSP connection. On macOS/Linux, the plugin assumes the port is reachable.
## License
MIT License
MI

5
ftdetect/gdshader.lua Normal file
View File

@@ -0,0 +1,5 @@
vim.filetype.add({
extension = {
gdshader = "gdshader",
},
})

View File

@@ -23,6 +23,8 @@ function M.setup(opts)
host = M.opts.editor_host,
port = M.opts.debug_port,
})
require("godotdev.tree-sitter")
end
return M

View File

@@ -0,0 +1,32 @@
local ok, ts_configs = pcall(require, "nvim-treesitter.configs")
if not ok then
return
end
ts_configs.setup({
ensure_installed = { "gdscript" },
highlight = {
enable = true,
additional_vim_regex_highlighting = false,
},
})
vim.filetype.add({
extension = {
gdshader = "gdshader",
},
})
local parsers_ok, ts_parsers = pcall(require, "nvim-treesitter.parsers")
if parsers_ok then
local parser_configs = ts_parsers.get_parser_configs()
parser_configs.gdshader = {
used_by = { "gdshader" }, -- filetype
install_info = {
url = "", -- no external parser
files = {}, -- no parser files
generate_requires_npm = false,
requires_generate_from_grammar = false,
},
}
end

View File

@@ -0,0 +1,55 @@
;; ===========================
;; Godot Shader (GDShader) Highlights
;; Extended: multi-line comments + preprocessor
;; ===========================
;; --- Shader types ---
((identifier) @type
(#match? @type "^(spatial|canvas_item|particles|sky)$"))
;; --- Shader stages ---
((identifier) @keyword
(#match? @keyword "^(vertex|fragment|light|start)$"))
;; --- Built-in functions ---
((identifier) @function.builtin
(#match? @function.builtin
"^(abs|acos|asin|atan|ceil|clamp|cos|cross|cross2d|distance|dot|exp|floor|fract|inverse|length|lerp|max|min|normalize|pow|reflect|refract|round|sign|sin|sqrt|step|tan|transpose)$"))
;; --- Godot-specific built-in functions ---
((identifier) @function.builtin
(#match? @function.builtin
"^(NODE_POSITION_WORLD|VIEWPORT_SIZE|VIEWPORT_TEXTURE|INV_CAMERA_MATRIX|CAMERA_MATRIX|NODE_MATRIX|NODE_MATRIX_INVERSE|WORLD_MATRIX|WORLD_MATRIX_INVERSE|CAMERA_DIRECTION|SCREEN_TEXTURE|TIME)$"))
;; --- Built-in constants / vars ---
((identifier) @constant.builtin
(#match? @constant.builtin
"^(TIME|PI|TAU|E|FRAGCOORD|VERTEX|NORMAL|UV|UV2|COLOR|INSTANCE_ID|POINT_COORD|SCREEN_UV|SCREEN_PIXEL_SIZE|FRONT_FACING)$"))
;; --- Keywords ---
((identifier) @keyword
(#match? @keyword "^(uniform|varying|const|if|else|elif|for|while|break|continue|return|discard|void|in|out|inout)$"))
;; --- Types ---
((identifier) @type
(#match? @type "^(bool|int|float|vec2|vec3|vec4|mat3|mat4|sampler2D|samplerCube)$"))
;; --- Operators ---
((operator) @operator
(#match? @operator "^[+\\-*/%=!<>&|^~]+$"))
;; --- Numbers ---
((number) @number
(#match? @number "^[0-9]+(\\.[0-9]+)?$"))
;; --- Single-line comments ---
((comment) @comment
(#match? @comment "^//.*$"))
;; --- Multi-line comments ---
((comment) @comment
(#match? @comment "^/\\*.*\\*/$"))
;; --- Preprocessor / directives ---
((preproc) @keyword
(#match? @preproc "^#(version|ifdef|ifndef|else|elif|endif|define|undef|extension|error|pragma)$"))

View File

@@ -0,0 +1,20 @@
;; ===========================
;; GDShader Injections
;; ===========================
;; Highlight expressions inside parentheses as code
((call_expression
function: (identifier) @function
arguments: (argument_list) @parameter))
;; Highlight numbers inside function calls
((number) @number
(#match? @number "^[0-9]+(\\.[0-9]+)?$"))
;; Highlight vector constructors like vec2, vec3, vec4
((identifier) @type
(#match? @type "^(vec2|vec3|vec4|mat3|mat4)$"))
;; Highlight boolean literals inside expressions
((true) @boolean)
((false) @boolean)