Note
WORK IN PROGRESS PROJECT!
CGNvim is a simple and modern Neovim >= 0.11 configuration for game and computer graphics development environment (e.g., Unity game engine, C++ game development, development using low-level graphics API etc.).
This configuration tries to provide a minimal set of plugins to approximate the usual game/graphics IDEs (e.g., Visual Studio).
- LSP completion/hints and linting support for: C# (Roslyn LS), C/C++ (clangd), Lua (lua-language-server), GLSL (glsl-analyzer). LSP is implemented using Neovim's >= 0.11 core LSP module.
- Detailed guide for integration with the Unity game engine
- Fast and lightweight default configuration
- Syntax highlighting using nvim-treesitter for: C#, C/C++, GLSL, HLSL, XML, YAML, etc.
- Formatting (and autoformatting on save) using conform.nvim for: C# (csharpier), Lua (stylua), C++, etc.
- Integrated terminal using toggleterm.nvim (togglable using:
<Space>tt) - Git integration using gitsigns.nvim
- Mnemonic keymaps that make sense (e.g.,
<Space>ttfor (t)oggle (t)erminal) - Lazy Neovim plugin management (i.e., plugins are only loaded when needed)
- 3rd party LSPs/formatters/DAPs are automatically handled by mason.nvim using lazy.nvim
- Clear project structure that is highly customizable and easily extensible:
- To add/edit a plugin, see Adding or Editing Plugins
- To add/edit a LSP, see Adding or Editing LSPs
- To add/edit a formatter, see Adding or Editing Formatters
- To add/edit a DAP, see Adding or Editing DAPs
- Debugging support using [nvim-dap][nvim-dap] with sensible default configurations for: C/C++ (codelldb), Python (debugpy), C# (Unity)
- Attaching to the Unity debugger to debug editor/player instances (see neovim-unity for details)
For integration with the Unity game engine see this detailed guide: neovim-unity
.
├── init.lua --> loads the config: require("cgnvim")
├── LICENSE.txt
├── lua
│  └── cgnvim
│  ├── configs --> configs for plugins (maps 1-to-1 with ./plugins/)
│  │  ├── bufferline.lua
│  │  ├── ...
│  │  └── trouble.lua
│  ├── daps --> DAPs are added/configured here
│  │  ├── python.lua
│  │  ├── ...
│  │  └── unity.lua
│  ├── gautocmds.lua --> set of non-plugin-specific autocmd calls
│  ├── gmappings.lua --> set of non-plugin-specific mappings
│  ├── gsettings.lua --> set of non-buffer-specific settings and options
│  ├── gusercmds.lua --> set of non-plugin-specific user commands
│  ├── init.lua --> lsps setup, daps setups, lazynvim bootstrapping
│  ├── lsps --> LSPs are added/configured here
│  │  ├── clangd.lua
│  │  ├── ...
│  │  └── roslyn_ls.lua
│  └── plugins --> plugins to be managed by LazyNvim are added here
│  ├── bufferline.lua
│  ├── ...
│  └── trouble.lua
├── README.md
└── stylua.toml --> for lua formatting using StyLua
lua/cgnvim/plugins/ and lua/cgnvim/configs/ have a one-to-one
association where each file in lua/cgnvim/plugins/ denotes a plugin name
(usually <plugin-name>.lua) and describes how it should be fetched and loaded
by the LazeNvim plugin manager. The options passed to its LSP setup are defined
in a file of similar name in lua/cgnvim/configs/.
Each entry in lua/cgnvim/lsps/ denotes a specific LSP configuration that is usually copied from nvim-lspconfig/lsp and is loaded and enabled in lua/cgnvim/lspconfig.lua.
To list all the defined and default keymaps, enter the command :map
(or for a better output :Telescope keymaps ). CGNvim tries to simplify
the memorization of keymaps by relying on mnemonics.
The main keymaps that contribute the most at simplifying the usual workflow are
listed below (<leader> is <Space> unless the default configuration is changed).
| Keymap | Mode | Short Description | Detailed Description |
|---|---|---|---|
<leader>tt |
n | (t)oggle (t)erminal | toggle the integrated terminal |
<leader>ex |
n | toggle file (ex)plorer | toggle the NvimTree file explorer |
<leader>rw |
n | (r)ename (w)ord | rename all occurences of the word under cursor in current buffer |
<leader>ts |
n,v,x | (t)oggle (s)pell | toggle spell checking for current buffer |
<leader>ss |
n | (s)pell (s)uggest | show Telescope's spell suggestion for the word under the cursor |
<leader>tr |
n,v,x | (t)oggle (r)elative line numbering | toggle relative line numbering (default: off) |
<leader>d |
n,v,x | (d)elete to void register | (d)elete to void register (without copying). Vim's default delete overwrites the content of the register |
<leader>y |
n,v,x | (y)ank to system clipboard | copy (yank) to system clipboard. Might require an external package for support on Wayland |
J |
x | move selected line(s) down(J) | |
K |
x | move selected line(s) up(K) | |
J |
n | append line below to current line |
| Keymap | Mode | Short Description |
|---|---|---|
<C-h> |
n | move to left(h) window |
<C-j> |
n | move to down(j) window |
<C-k> |
n | move to up(k) window |
<C-l> |
n | move to right(l) window |
<C-Up> |
n | resize window size (Up)wards |
<C-Down> |
n | resize window size (Down)wards |
<C-Left> |
n | resize window size (Left)wards |
<C-Right> |
n | resize window size (Right)wards |
<S-l> |
n | navigate to right(l) buffer |
<S-h> |
n | navigate to left(h) buffer |
Only keymaps that are considered important are listed
| Keymap | Mode | Short Description |
|---|---|---|
g? |
n | (g)o to help(?) window to show keymaps |
v<CR> |
n | open() (v)ertically |
a |
n | (a)ppend/create file/folder |
y |
n | (y)ank basename to system clipboard |
Y |
n | (Y)ank file/directory absolute path |
c |
n | copy file (c)ontent |
d |
n | (d)elete file/directory |
r |
n | (r)ename file/directory |
Tab |
n | preview file/expand directory |
K |
n | show file metadata info |
. |
n | run command on current(.) entry |
<leader>tg |
n | (t)oggle (g)itignore filter |
<leader>td |
n | ((t)oggle (d)otfiles filter |
| Keymap | Mode | Short Description | Detailed Description |
|---|---|---|---|
fb |
n | (f)ormat (b)uffer | format current buffer according to conform.nvim config |
<C-I> |
n | same as fb |
VSCode formatting shortcut |
| Keymap | Short Description | Detailed Description |
|---|---|---|
<leader>gsb |
(g)it (s)tage (b)uffer | stage the whole current buffer |
<leader>grb |
(g)it (r)eset (b)uffer | reset the whole current buffer |
<leader>gph |
(g)it (p)review (h)unk | highlight hunk under cursor if a hunk is present |
<leader>gsh |
(g)it (s)tage (h)unk | stage hunk under cursor if a hunk is present |
<leader>grh |
(g)it (r)eset (h)unk | reset hunk under cursor if a hunk is present |
]h |
next (h)unk ([ on the right) | navigate to next hunk |
[h |
prev (h)unk ([ on the left) | navigate to previous hunk |
<leader>gdv |
(g)it (d)iff (v)iew | show git diff view for current buffer |
<leader>gtb |
(g)it (t)oggle (b)lame | toggle git blame for current line under cursor |
| Keymap | Short Description | Detailed Description |
|---|---|---|
]d |
next(]) (d)iagnostics | navigate to next diagnostics |
[d |
prev([) (d)iagnostics | navigate to previous diagnostics |
<leader>vt |
toggle (v)irtual (t)ext diagnostics | toggle virtual text diagnostics. Shows diagnostics at the right of the corresponding line using virtual text |
<leader>vl |
toggle (v)irtual (l)ines diagnostics | toggle virtual lines diagnostics. Uses multiple virtual lines under the corresponding line to show diagnostics. Better than virtual text diagnostics but consumes more visual space (i.e., adds a lot of lines on hover). |
<leader>ed |
(e)xplain (d)iagnostics | explain diagnostics under cursor |
<leader>ql |
(q)uickfix (l)ist | toggles the quickfix list window |
<leader>bd |
(b)uffer (d)iagnostics | toggle Trouble's buffer (local) diagnostics window |
<leader>gd |
(g)lobal (d)iagnostics | toggle Trouble's global diagnostics window |
| Keymap | Short Description | Detailed Description |
|---|---|---|
K |
show LSP (K)ownledge | LSP hover information about symbol under cursor |
KK |
(K)indly jump to LSP (K)ownledge | jumps to LSP hover information window about symbol under cursor |
gd |
(g)o (d)efinition | go to the definition of the symbol under cursor |
gi |
(g)o (i)mplementation | go to the implmentation of the symbol under cursor |
gD |
(g)o (D)eclaration | go to the declaration of the symbol under cursor |
gr |
(g)o (r)eferences | go to the references of symbol under cursor |
<leader>ih |
(i)nlay (h)ints | toggle LSP inlay hints. E.g., uses virtual text to show parameter names) |
<leader>ca |
(c)ode (a)ction | list code actions available for symbol under cursor |
<leader>rs |
(r)ename (s)ymbol | rename symbol under the cursor and all of its references using LSP |
| Keymap | Short Description |
|---|---|
<leader>dc |
(d)ebugger (c)ontinue/start |
<leader>db |
(d)ebugger toggle (b)reakpoint |
<leader>do |
(d)ebugger step (o)ver |
<leader>di |
(d)ebugger step (i)nto |
<leader>dO |
(d)ebugger step (O)ut |
F5 |
same as <leader>dc |
F10 |
same as <leader>do |
F11 |
same as <leader>di |
F12 |
same as <leader>dO |
Getting a LSP to work properly (especially in the case of C# with Unity) can be a daunting task. I have spent a significant amount of time tinkering with different LSPs for C# on Linux. Omnisharp is simply not usable, any mid-sized Unity projects can cause memory consumption of up-to 20GBs or more (very probably due to a severe memory leakage problem).
The relatively new Roslyn Language Server seems to perform better, but there are a couple of caveats that one has to be aware of.
In case you get System.IO.IOException: The configured user limit (128) on the number of inotify instances has been reached. in the LSP log (accessible
in the filesystem at :lua =require('vim.lsp.log').get_filename()) then
you have to increase the maximum number of file descriptors that can be opened
by a process:
echo fs.inotify.max_user_instances=4096 | sudo tee -a /etc/sysctl.conf && sudo sysctl -pIn case you get Undefined reference warnings/errors in the LSP log, you have
to run dotnet restore in your solution/project root directory:
dotnet restore "<unity-project-name>.sln"It is important to note that LSPs can be quite verbose and a lot of errors and
warnings can be safely ignored. This is the case with Roslyn LS, a lot of
Unresolved references can be simply ignored (hence why its logger level is set
to ERROR).
Plugins are managed by lazy.nvim and are automatically loaded from lua/cgnvim/plugins/ where each file corresponds to a plugin.
To add a new plugin:
-
create a new lua file at
lua/cgnvim/plugins/<plugin-name>.luaand add the LazyNvim configuration for it. For example:return { "<plugin-github-repo>/<plugin-name>.nvim", lazy = false, version = "*", -- plugin's setup options are loaded from a file of similar name in lua/cgnvim/configs/ -- this makes plugin configurations that are frequently changed in a single -- convenient location opts = function() return require("cgnvim.configs.<plugin-name>") end, ..., -- other LazyNvim configurations }
-
for a consistent configuration, create a new lua file (with same name as in lua/cgnvim/plugins/) at
lua/cgnvim/configs/<plugin-name>.luaand define your plugin setup options there:return { -- here goes plugin setup options }
-
restart Neovim and check the command
:Lazyto see if your plugin has successfully been added
LSPs are enabled in lua/cgnvim/init.lua and their configurations live in lua/cgnvim/lsps/
To add a new LSP, say a LSP for Python files (e.g., ruff):
-
create a new lua file under the path lua/cgnvim/lsps/ruff.lua and define the LSP client configuration in it as follows (it is usually copied from: nvim-lspconfig/lsps):
return { -- LSP client configuration following: https://neovim.io/doc/user/lsp.html#vim.lsp.ClientConfig -- you usually copy this configuration from https://github.com/neovim/nvim-lspconfig/tree/master/lsp -- and adjust it accordingly (e.g., by changing the LSP cmd) cmd = { "ruff" }, filetypes = { "py" }, root_markers = { ".git" } -- etc ... }
-
(optionally) for automatic installation and management of your LSP by Mason, if it is available in Mason (check command
:Mason), then navigate to lua/cgnvim/configs/mason-tool-installer.lua:-- a list of all tools you want to ensure are installed upon start by Mason ensure_installed = { ..., -- other lsps/formatters/linters { "ruff", auto_update = true }, }
-
restart Neovim and open a file that can trigger the LSP (in this example, a Python file). Check
:LspInfoto see if your LSP configuration is there and a LSP client is successfully attached. Check:LspLogfor LSP logs.
To remove a LSP, navigate to lua/cgnvim/lsps/ and remove the
corresponding LSP entry. Navigate to lua/cgnvim/configs/mason-tool-installer.lua
and remove the plugin from ensure_installed table in case it is there.
To disable a LSP without removing its configuration, navigate to lua/cgnvim/init.lua
then add the lsp name (same as in lua/cgnvim/lsps/ but without the lua extension) to
the lsp_ignore table.
Formatting is managed by the conform.nvim plugin in addition to mason-tool-installer.nvim for automatic installation by mason.nvim.
To add a new formatter, say a formatter (e.g., prettierd) for Javascript files:
-
navigate to lua/cgnvim/configs/conform.lua and add the formatter to
formatters_by_ft:-- add new formatters here (also add them in ./mason-tool-installer.lua for automatic installation by Mason) formatters_by_ft = { ..., -- other formatters javascript = { "prettierd", stop_after_first = true }, },
Here we are assuming the command
prettierdis globally accessible (see next point) -
(optionally) for automatic installation and management of your formatter by Mason, if it is listed in Mason (check command
:Mason), then navigate to lua/cgnvim/configs/mason-tool-installer.lua and add it as an entry inensure_installed:-- a list of all tools you want to ensure are installed upon start by Mason ensure_installed = { ..., -- other lsps/formatters/linters { "prettierd", auto_update = true }, }
-
restart Neovim. Check
:ConformInfoand see if your formatter is ready. Otherwise check:Masonto see if your formatter is installed. Try to open a file with the right extension and format it (either by format on write using:w, or using:lua require("conform").format({ async = true }))
Debug adapters (DA)s are enabled in lua/cgnvim/init.lua and their configurations live in lua/cgnvim/daps/.
To add a new DA, say a DA for javascript/Firefox:
-
install the DA and the debugger (both may reside in the same executable). In this case, the debugger is already integrated within Firefox. You just have to install the DA (e.g., vscode-firefox-debug).
-
create a new lua file under the path
lua/cgnvim/lsps/firefox.luaand define the DA configuration in it as follows (it is usually copied and adjusted from: nvim-dap configs):local dap = require('dap') dap.adapters.firefox = { type = 'executable', command = 'node', -- command to launch the DA -- path to the DA node package (and other optional args) args = {os.getenv('HOME') .. '/path/to/vscode-firefox-debug/dist/adapter.bundle.js'}, } -- make sure not to override other typescript DAP configs if dap.configurations.python == nil then dap.configurations.python = {} end -- do NOT overwrite the Language configuration as multiple DAs may add multiple configurations for the same -- ft (e.g., Chrome debug adapter may already have an entry in the table dap.configurations.typescript) table.insert(dap.configurations.typescript, { -- mandatory options expected by nvim-dap name = 'Debug with Firefox', type = 'firefox', request = 'launch', -- options below are debug-adapter specific reAttach = true, url = 'http://localhost:3000', webRoot = '${workspaceFolder}', -- adjust Firefox path accordingly if necessary firefoxExecutable = '/usr/bin/firefox' })
-
(optionally) for automatic installation and management of your DA by Mason, if it is available in Mason (check command
:Mason), then navigate to lua/cgnvim/configs/mason-tool-installer.lua-- a list of all tools you want to ensure are installed upon start by Mason ensure_installed = { ..., -- other lsps/formatters/linters { "<your-da-name>", auto_update = true }, }
in the case of vscode-firefox-debug, it is not available in Mason (at least officially) and has to be installed manually.
-
restart Neovim and start debugging a Javascript file. Check the
:DapShowLogcommand output for any potential issues.
- Add Godot game engine integration (IMPORTANT)
- Add Unreal Engine integration (IMPORTANT)
- Support large files (usually JSON files that are too large completely
crash Neovim because of treesitter and/or LSP)
- Disable Treesitter for large files (e.g., >= 128KBs)
- Disable LSP for large files (e.g., 32KBs)
- Add script for the generation of single-page PDF overview of keymaps (IMPORTANT)
- Add Yaml highlighting and formatting support (IMPORTANT)
- Add snippets completion (OPTIONAL)
- Add a minimal spell checker (OPTIONAL)
- Add OpenGL completion (from https://github.com/vurentjie/cmp-gl)
MIT License. Read license.txt file.