Skip to content

Instantly share code, notes, and snippets.

@Inom-Turdikulov
Forked from chillpert/vim-unreal.md
Created January 19, 2023 19:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Inom-Turdikulov/0fc6630bdc33232af3df35adce8df2a2 to your computer and use it in GitHub Desktop.
Save Inom-Turdikulov/0fc6630bdc33232af3df35adce8df2a2 to your computer and use it in GitHub Desktop.
Debugging and autocompletion for Unreal Engine 4 and 5 projects in (Neo)vim

Debugging and autocompletion for Unreal Engine 4 and 5 projects in (Neo)Vim

+++ Updated for UE 5.1 (see bottom)

Autocompletion

For autocompletion there are two options:

  1. coc (Vim, Neovim)
  2. LSP (Neovim only)

In my experience, coc is easier to set up. If you want to get started as quickly as possible, this is your best bet.

Coc

  1. Install coc.vim, e.g. with vim-plug inside .vimrc: Plug 'neoclide/coc.nvim'
  2. Run :CocInstall coc-clangd

You will likely have to familiarize yourself with coc's shortcuts first or customize it as you see fit.

LSP

Setting up LSP takes a bit of time but for those that already have it up and running, there is probably not much to do. To keep this short, I will not explain the setup process. However, feel free to copy my Neovim config from my dotfiles. I have also set up some extra features for clangd in my config, if you are interested. If you wish to start from sratch, check out the server configuration for clangd and the completion plugin nvim-cmp as a starting point.

Creating the compile database

No matter which option you pick, you must do the following steps first:

  1. In UE
    • go to Edit - Editor Preferences - General - Source Code - Source Code Editor and select Visual Studio Code
    • go to File - Refresh Visual Studio Code Project for UE4 or Tools - Refresh Visual Studio Code Project for UE5
  2. Create a symlink from your project's root directory to myProject/.vscode/compileCommands_myProject.json ln -s .vscode/compileCommands_myProject.json compile_commands.json

If you open your project in (Neo)vim and you are greeted by tons of error messages after you have successfully set up either coc or lsp, you might have to modify your compile_commands.json. For this purpose, I have created a file called clang-flags.txt that contains a bunch of compile flags which improve the experience drastically. The files look like this:

-std=c++20
-ferror-limit=0
-Wall
-Wextra
-Wpedantic
-Wshadow-all
-Wno-unused-parameter

Yes, C++20 is not even supported by UE, yet this got rid of many false error messages.

Now you need to modify your compile_commands.json to use clang-flags.txt. Using (Neo)vim to modify the file is easy but making a little script to automate this process is probably worth it in the long run. Add @/path/to/my/clang-flags.txt after clang++.

{
	"file": "/home/User/MyGame/Plugins/UEGitPlugin/Source/GitSourceControl/Private/GitSourceControlMenu.cpp",
	"command": "clang++ @/path/to/my/clang-flags.txt /home/User/MyGame/Plugins/UEGitPlugin/Source/GitSourceControl/Private/GitSourceControlMenu.cpp @/home/User/MyGame/.vscode/compileCommands_MyGame/GitSourceControl.4.rsp",
	"directory": "/home/User/unrealengine/Engine/Source"
},

Make sure that all command entries also contain the respective file that is supposed to be compiled. Above we are trying to compile GitSourceControlMenu.cpp, so its path should be added after clang-flags.txt and before the rsp file. This procedure has to be done for all .h and .cpp files. I highly recommend any type of automation for this process via some simple scripts. At least under UE5, I don't need to do this manually anymore. I regenerate this file regularly using ue4-cli's ue4 gen command.

Another option to eliminate false error messages is using your own clang instead of the one that UE ships with by default. Clang 14 is working well for me. All you need to do is to remove the path before clang++ as shown in the code snippet above. To check if everything is working, simply run the command entry of some arbitrary file in your terminal. You might still get some errors but for the most part you should see a relatively lengthy compilation process. This is also equivalent to the time it will take until you get any completion results inside of (Neo)vim.

Feel free to use this little code snippet to fix-up your compile database (it requires ue4-cli, more about it down below):

#!/usr/bin/env bash

ue_path=$(ue4 root | tail -1)
target="clang++ @'$(pwd)/clang-flags.txt'"

sed -i -e "s,$ue_path\(.*\)clang++,$target,g" compile_commands.json

However, this code won't add headers and source files to your compile database in case they are missing.

Debugging

  1. Install vimspector, e.g. Plug 'puremourning/vimspector'
  2. Configure debugging vimspector keybinds or simply add let g:vimspector_enable_mappings = 'HUMAN' to your .vimrc.
  3. Create a .vimspector.json inside of your project's root directory (simply refer to the myProjectEditor (DebugGame) entry in myProject/.vscode/launch.json), e.g.
{
  "configurations": {
    "Launch": {
      "adapter": "vscode-cpptools",
      "configuration": {
        "request": "launch",
        "program": "/path/to/UnrealEngine/Engine/Binaries/Linux/UE4Editor-Linux-DebugGame",
        "args": [ "/path/to/myProject/myProject.uproject" ],
        "cwd": "/path/to/UnrealEngine",
        "externalConsole": true,
        "MIDebuggerPath": "/usr/bin/gdb",
        "MIMode": "gdb"
      }
    }
  }
}
  1. Compile a debug build path/to/UnrealEngine/Engine/Build/BatchFiles/Linux/Build.sh myProject Linux DebugGame '/path/to/myProject/myProject.uproject' -waitmutex
  2. Set a breakpoint (F9) and start debugging (F5) in Vim and your project will open in a new instance for debugging.

If you just wish to compile your project refer to the following entries in myProject/.vscode/tasks.json: path/to/UnrealEngine/Engine/Build/BatchFiles/Linux/Build.sh myProject Linux Development '/path/to/myProject/myProject.uproject' -waitmutex For anything else building and debugging-related simply refer to tasks.json and launch.json in myProject/.vscode.

I am currently trying to set up nvim-dap for Neovim. As soon as it's working, I will add the instructions here.

ue4-cli

I highly recommend using ue4-cli. This application is primarily used for compiling your project from the terminal but it can do a bunch of other neat things like running automation tests, generating IDE project files, cleaning your build files, and even packaging your build. It also works for UE5 if you set the engine path manually (at the time of writing, this is still a bug). I have also defined a function in my .zshrc to act as a wrapper for ue4-cli. Feel free to use this for some more inspiration.

# Expand ue4cli
ue() {
	ue4cli=$HOME/.local/bin/ue4
	engine_path=$($ue4cli root)

    	# cd to ue location
	if [[ "$1" == "engine" ]]; then
		cd $engine_path
   	# combine clean and build in one command
	elif [[ "$1" == "rebuild" ]]; then
		$ue4cli clean
		$ue4cli build 
		if [[ "$2" == "run" ]]; then
			$ue4cli run
		fi
    	# build and optionally run while respecting build flags
	elif [[ "$1" == "build" ]]; then
		if [[ "${@: -1}" == "run" ]]; then
			length="$(($# - 2))" # Get length without last param because of 'run'
			$ue4cli build ${@:2:$length}
			$ue4cli run
		else
			shift 1
			$ue4cli build "$@"
		fi
    	# Run project files generation, create a symlink for the compile database and fix-up the compile database
	elif [[ "$1" == "gen" ]]; then
		$ue4cli gen
		project=${PWD##*/}
		ln -s ".vscode/compileCommands_${project}.json" compile_commands.json	
        	replace_string="clang++ @'$(pwd)/clang-flags.txt'"
        	sed -i -e "s,$engine_path\(.*\)clang++,$replace_string,g" compile_commands.json
    	# Generate ctags for project and optionally the engine itself with `ue ctags` and `ue ctags engine` respectively.
	elif [[ "$1" == "ctags" ]]; then
	        if [[ "$2" == "engine" ]]; then
            		echo "Generating ctags database for unreal engine"
            		ctags -R --c++-kinds=+p --fields=+iaS --extras=+q --languages=C++ "$engine_path/Engine/Source"
			echo "Generation completed."
        	fi

		echo "Generating ctags database in current directory ..."
		ctags -R Source
		echo "Generation completed."
    	# Pass through all other commands to ue4
	else
		$ue4cli "$@"
	fi
}

alias ue4='echo Please use ue instead.'
alias ue5='echo Please use ue instead.'

Ctags

Navigating engine source code is not possible with coc or lsp because it would require a compile database of the engine. While I was able to generate one with the command below, I couldn't get it to work after all. Any help with this would be highly appreciated.

./Engine/Build/BatchFiles/Linux/Build.sh MyProjectName Linux Development '/path/to/myProject/myProject.uproject' -mode=GenerateClangDatabase

However, there is another solution: ctags. Simply install universal-ctags (e.g. with your package manager) and run the following command:

ctags -R --c++-kinds=+p --fields=+iaS --extras=+q --languages=C++ /path/to/your/unrealengine/Engine/Source

When the cursor is over a symbol that was found by ctags, Ctrl+] will let you jump to the tag using the newly generated tags file. I usually run the command above in my project's root directory, so that no additional configuration is required for (N)vim to find the file.

Clang-tidy

Yes, you can even get static analysis in your code. All you need is a file named .clang-tidy in your project's root directory. Creating this file was quite the hassle since a lot of the tests that clang-tidy runs just don't make any sense when working with Unreal Engine and trying to follow the guidelines. As a starting point, you can use my version:

---
Checks:          'clang-diagnostic-*,misc-*,readability-*,bugprone-*,clang-analyzer-*,cppcoreguidelines-*,modernize-*,-modernize-use-trailing-return-type,-cppcoreguidelines-pro-type-vararg,-cppcoreguidelines-special-member-functions,-readability-avoid-const-params-in-decls,-misc-unused-parameters,-readability-named-parameter,-readability-magic-numbers,-misc-non-private-member-variables-in-classes,-cppcoreguidelines-non-private-member-variables-in-classes,-cppcoreguidelines-avoid-magic-numbers,-modernize-use-auto,-cppcoreguidelines-pro-type-union-access,-bugprone-easily-swappable-parameters'
WarningsAsErrors: false
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
FormatStyle:     google
CheckOptions:
  - key:             cert-dcl16-c.NewSuffixes
    value:           'L;LL;LU;LLU'
  - key:             cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField
    value:           '0'
  - key:             cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors
    value:           '1'
  - key:             cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
    value:           '1'
  - key:             google-readability-braces-around-statements.ShortStatementLines
    value:           '1'
  - key:             google-readability-function-size.StatementThreshold
    value:           '800'
  - key:             google-readability-namespace-comments.ShortNamespaceLines
    value:           '10'
  - key:             google-readability-namespace-comments.SpacesBeforeComments
    value:           '2'
  - key:             modernize-loop-convert.MaxCopySize
    value:           '16'
  - key:             modernize-loop-convert.MinConfidence
    value:           reasonable
  - key:             modernize-loop-convert.NamingStyle
    value:           CamelCase
  - key:             modernize-pass-by-value.IncludeStyle
    value:           llvm
  - key:             modernize-replace-auto-ptr.IncludeStyle
    value:           llvm
  - key:             modernize-use-nullptr.NullMacros
    value:           'NULL'
...

Clang-format

Consider using a .clang-format to automatically format your code in all of your projects. This one is probably the best you can find online. Simply place it in your project's root directory. One thing I disliked about having .clang-format in the root directory is that it would format everything, including the Plugins folder. As a quick workaround, I put the following .clang-format in the Plugins folder:

DisableFormat: true
SortIncludes: false

UE 5.1

The steps above still work after recompiling the engine. However, you have to clean build and delete both .vscode and compile_commands.json before running my custom ue gen override. Also make sure to adjust the respective line with the sed command to replace_string="clang++\"\,\n\t\t\t\"@$(pwd)/clang-flags.txt\"\,".

Misc

If you are on Arch and experience long loading times when starting the editor (UE4 only) follow the instructions on the Arch Wiki. It really makes a difference.

Let me know if you have any suggestions or ideas to make UE development in (Neo)vim even better.

Tested on

OS - Arch Linux VIM - Vi IMproved 8.2 NVIM - 0.7.2 coc.nvim - 0.0.80 clangd - 12.0.1 Unreal - 4.28 Vimspector - Build 1284234985

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