I'm trying to use nvim-dap to debugger, but there is only option to gdscript, with c# the breakpoints don't pause the program, so I'm trying to use netcoredbg, someone know how can I configure the netcoredbg to debug source on godot using nvim-dap?

11 days later

I have found a solution that runs my main scene through trial and error,

return {
  'mfussenegger/nvim-dap',
  dependencies = {
    'rcarriga/nvim-dap-ui',
    'nvim-neotest/nvim-nio',
    'theHamsta/nvim-dap-virtual-text',
    'nvim-treesitter/nvim-treesitter',
  },
  lazy = false,
  keys = {
    {
      '<F5>',
      mode = { 'n' },
      function()
        require('dap').continue()
      end,
      desc = 'F5 dap ~ [c]ontinue',
    },
    {
      '<F10>',
      mode = { 'n' },
      function()
        require('dap').step_over()
      end,
      desc = 'dap ~ step [o]ver',
    },
    {
      '<F11>',
      mode = { 'n' },
      function()
        require('dap').step_into()
      end,
      desc = 'F11 dap ~ step into',
    },
    {
      '<S-F11>',
      mode = { 'n' },
      function()
        require('dap').step_out()
      end,
      desc = 'Shift F11 dap ~ Step [O]ut',
    },
    {
      '<Leader>b',
      mode = { 'n' },
      function()
        require('dap').toggle_breakpoint()
      end,
      desc = 'dap ~ toggle [b]reakpoint',
    },
  },
  config = function()
    local dap = require 'dap'
    local dapui = require 'dapui'

    -- Configure dapui
    dapui.setup {
      layouts = {
        {
          elements = {
            { id = 'scopes', size = 0.25 },
            'breakpoints',
            'stacks',
            'watches',
          },
          size = 40,
          position = 'left',
        },
        {
          elements = {
            'repl',
            'console',
          },
          size = 0.25,
          position = 'bottom',
        },
      },
    }

    -- Automatically open/close dapui
    dap.listeners.before.attach.dapui_config = function()
      dapui.open()
    end
    dap.listeners.before.launch.dapui_config = function()
      dapui.open()
    end
    dap.listeners.before.event_terminated.dapui_config = function()
      dapui.close()
    end
    dap.listeners.before.event_exited.dapui_config = function()
      dapui.close()
    end

    -- Helper function to find Godot project file and directory
    local function find_godot_project()
      -- Create cache to avoid repeated lookups
      if not _G.godot_project_cache then
        _G.godot_project_cache = {}
      end

      -- Start with current working directory
      local current_dir = vim.fn.getcwd()

      -- Check cache first
      if _G.godot_project_cache[current_dir] then
        return _G.godot_project_cache[current_dir].file_path, _G.godot_project_cache[current_dir].dir_path
      end

      -- Function to check if a directory contains a project.godot file
      local function has_project_file(dir)
        local project_file = dir .. '/project.godot'
        local stat = vim.uv.fs_stat(project_file)
        if stat and stat.type == 'file' then
          return project_file, dir
        else
          return nil, nil
        end
      end

      -- Check current directory first
      local project_file, project_dir = has_project_file(current_dir)
      if project_file then
        _G.godot_project_cache[current_dir] = { file_path = project_file, dir_path = project_dir }
        vim.notify('Found Godot project at: ' .. project_file, vim.log.levels.INFO)
        return project_file, project_dir
      end

      -- Search in parent directories up to a reasonable limit
      local max_depth = 5
      local dir = current_dir

      for i = 1, max_depth do
        -- Get parent directory
        local parent = vim.fn.fnamemodify(dir, ':h')

        -- Stop if we've reached the root
        if parent == dir then
          break
        end

        dir = parent

        -- Check if this directory has a project.godot file
        local project_file, project_dir = has_project_file(dir)
        if project_file then
          _G.godot_project_cache[current_dir] = { file_path = project_file, dir_path = project_dir }
          vim.notify('Found Godot project in parent directory: ' .. project_file, vim.log.levels.INFO)
          return project_file, project_dir
        end
      end

      -- Search in immediate subdirectories (first level only)
      local handle = vim.uv.fs_scandir(current_dir)
      if handle then
        while true do
          local name, type = vim.uv.fs_scandir_next(handle)
          if not name then
            break
          end

          -- Only check directories
          if type == 'directory' then
            local subdir = current_dir .. '/' .. name
            local project_file, project_dir = has_project_file(subdir)
            if project_file then
              _G.godot_project_cache[current_dir] = { file_path = project_file, dir_path = project_dir }
              vim.notify('Found Godot project in subdirectory: ' .. project_file, vim.log.levels.INFO)
              return project_file, project_dir
            end
          end
        end
      end

      -- If still not found, ask the user
      local input_dir = vim.fn.input('Godot project directory: ', current_dir, 'dir')

      -- Validate the input path
      if input_dir ~= '' then
        local project_file, project_dir = has_project_file(input_dir)
        if project_file then
          _G.godot_project_cache[current_dir] = { file_path = project_file, dir_path = project_dir }
          return project_file, project_dir
        end
      end

      vim.notify('No valid Godot project found. Using current directory.', vim.log.levels.WARN)
      return current_dir .. '/project.godot', current_dir
    end

    -- Function to debug and print the full command that will be executed
    local function debug_command(executable, args)
      local full_command = executable
      for _, arg in ipairs(args) do
        -- Properly quote arguments with spaces
        if arg:find ' ' then
          full_command = full_command .. ' "' .. arg .. '"'
        else
          full_command = full_command .. ' ' .. arg
        end
      end

      local debug_msg = 'Executing: ' .. full_command
      vim.notify(debug_msg, vim.log.levels.INFO)
      vim.notify(debug_msg)

      -- Debug environment info
      vim.notify('Current working directory: ' .. vim.fn.getcwd())
      vim.notify('HOME env: ' .. (os.getenv 'HOME' or 'not set'))
      vim.notify('DISPLAY env: ' .. (os.getenv 'DISPLAY' or 'not set'))
      vim.notify('XDG_SESSION_TYPE env: ' .. (os.getenv 'XDG_SESSION_TYPE' or 'not set'))

      return args
    end

    -- Path to the Godot executable
    local godot_executable = '/usr/lib/godot-mono/godot.linuxbsd.editor.x86_64.mono'

    -- Get important environment variables
    local function get_env_vars()
      return {
        -- Graphics-related variables (crucial for GUI apps)
        DISPLAY = os.getenv 'DISPLAY' or ':0',
        WAYLAND_DISPLAY = os.getenv 'WAYLAND_DISPLAY',
        XDG_SESSION_TYPE = os.getenv 'XDG_SESSION_TYPE',
        XAUTHORITY = os.getenv 'XAUTHORITY',

        -- Audio-related variables
        PULSE_SERVER = os.getenv 'PULSE_SERVER',

        -- User-related variables
        HOME = os.getenv 'HOME',
        USER = os.getenv 'USER',
        LOGNAME = os.getenv 'LOGNAME',

        -- Path-related variables
        PATH = os.getenv 'PATH',
        LD_LIBRARY_PATH = os.getenv 'LD_LIBRARY_PATH',

        -- Locale variables
        LANG = os.getenv 'LANG' or 'en_US.UTF-8',
        LC_ALL = os.getenv 'LC_ALL',

        -- XDG variables
        XDG_RUNTIME_DIR = os.getenv 'XDG_RUNTIME_DIR',
        XDG_DATA_HOME = os.getenv 'XDG_DATA_HOME',
        XDG_CONFIG_HOME = os.getenv 'XDG_CONFIG_HOME',

        -- Other potentially relevant variables
        SHELL = os.getenv 'SHELL',
        TERM = os.getenv 'TERM',
        DBUS_SESSION_BUS_ADDRESS = os.getenv 'DBUS_SESSION_BUS_ADDRESS',
      }
    end

    -- Standard GDScript adapter for non-C# projects
    dap.adapters.godot = {
      type = 'server',
      host = '127.0.0.1',
      port = 6006,
    }

    dap.configurations.gdscript = {
      {
        type = 'godot',
        request = 'launch',
        name = 'Launch Scene',
        project = '${workspaceFolder}',
        launch_scene = true,
      },
    }

    -- Direct launch approach using netcoredbg to start Godot-Mono
    dap.adapters.coreclr = {
      type = 'executable',
      command = '/home/jamie/.local/share/nvim/mason/bin/netcoredbg',
      args = {
        '--interpreter=vscode',
        '--',
        godot_executable,
      },
    }

    dap.configurations.cs = {
      -- Launch Godot editor with project - simple approach
      {
        type = 'coreclr',
        request = 'launch',
        name = 'Simple Editor Launch',
        cwd = function()
			local project_file,project_dir = find_godot_project()
			vim.notify("cwd " .. project_dir)
			return project_dir
        end,
        env = get_env_vars(), -- Pass environment variables
        args = function()
					local project_file,project_dir = find_godot_project()
					return {" --editor " .. project_file}
				end,
      },
    }

    -- Add visual indicators for breakpoints
    vim.fn.sign_define('DapBreakpoint', { text = '●', texthl = 'DapBreakpoint', linehl = '', numhl = '' })
    vim.fn.sign_define('DapBreakpointCondition', { text = '◆', texthl = 'DapBreakpointCondition', linehl = '', numhl = '' })
    vim.fn.sign_define('DapLogPoint', { text = '◆', texthl = 'DapLogPoint', linehl = '', numhl = '' })
    vim.fn.sign_define('DapStopped', { text = '→', texthl = 'DapStopped', linehl = 'DapStopped', numhl = 'DapStopped' })
    vim.fn.sign_define('DapBreakpointRejected', { text = '●', texthl = 'DapBreakpointRejected', linehl = '', numhl = '' })
  end,
}

you want to find either your godot-mono command or the executable. and replace the paths as specified. If you find out how to connect to a already running instance please share it.

    budtard I can confirm this works with Godot-Mono on an Arch-based Linux environment. Just make sure your paths are correct. Make sure to replace "jamie" in command = '/home/jamie/.local/share/nvim/mason/bin/netcoredbg' with your username at a minimum. Other than that, I didn't have to change anything to make this work. Thank you! I've been trying to get this working on and off for a while now.

    17 days later

    did anybody using lazyvim here?
    i'm config based on the default implementation of lazyvim, but the Snacks.picker keep say No results found for select

    -- Helper function to find Godot project file and directory
    local function find_godot_project()
    	-- Create cache to avoid repeated lookups
    	if not _G.godot_project_cache then
    		_G.godot_project_cache = {}
    	end
    
    	-- Start with current working directory
    	local current_dir = vim.fn.getcwd()
    
    	-- Check cache first
    	if _G.godot_project_cache[current_dir] then
    		return _G.godot_project_cache[current_dir].file_path, _G.godot_project_cache[current_dir].dir_path
    	end
    
    	-- Function to check if a directory contains a project.godot file
    	local function has_project_file(dir)
    		local project_file = dir .. "/project.godot"
    		local stat = vim.uv.fs_stat(project_file)
    		if stat and stat.type == "file" then
    			return project_file, dir
    		else
    			return nil, nil
    		end
    	end
    
    	-- Check current directory first
    	local project_file, project_dir = has_project_file(current_dir)
    	if project_file then
    		_G.godot_project_cache[current_dir] = { file_path = project_file, dir_path = project_dir }
    		Snacks.notifier.notify("Found Godot project at: " .. project_file, vim.log.levels.INFO)
    		return project_file, project_dir
    	end
    
    	-- Search in parent directories up to a reasonable limit
    	local max_depth = 5
    	local dir = current_dir
    
    	for _ = 1, max_depth do
    		-- Get parent directory
    		local parent = vim.fn.fnamemodify(dir, ":h")
    
    		-- Stop if we've reached the root
    		if parent == dir then
    			break
    		end
    
    		dir = parent
    
    		-- Check if this directory has a project.godot file
    		local project_file, project_dir = has_project_file(dir)
    		if project_file then
    			_G.godot_project_cache[current_dir] = { file_path = project_file, dir_path = project_dir }
    			Snacks.notifier.notify("Found Godot project in parent directory: " .. project_file, vim.log.levels.INFO)
    			return project_file, project_dir
    		end
    	end
    
    	-- Search in immediate subdirectories (first level only)
    	local handle = vim.uv.fs_scandir(current_dir)
    	if handle then
    		while true do
    			local name, type = vim.uv.fs_scandir_next(handle)
    			if not name then
    				break
    			end
    
    			-- Only check directories
    			if type == "directory" then
    				local subdir = current_dir .. "/" .. name
    				local project_file, project_dir = has_project_file(subdir)
    				if project_file then
    					_G.godot_project_cache[current_dir] = { file_path = project_file, dir_path = project_dir }
    					Snacks.notifier.notify("Found Godot project in subdirectory: " .. project_file, vim.log.levels.INFO)
    					return project_file, project_dir
    				end
    			end
    		end
    	end
    
    	-- If still not found, ask the user
    	local input_dir = vim.fn.input("Godot project directory: ", current_dir, "dir")
    
    	-- Validate the input path
    	if input_dir ~= "" then
    		local project_file, project_dir = has_project_file(input_dir)
    		if project_file then
    			_G.godot_project_cache[current_dir] = { file_path = project_file, dir_path = project_dir }
    			return project_file, project_dir
    		end
    	end
    
    	Snacks.notifier.notify("No valid Godot project found. Using current directory.", vim.log.levels.WARN)
    	return current_dir .. "/project.godot", current_dir
    end
    
    -- Get important environment variables for Godot
    local function get_env_vars()
    	return {
    		-- Graphics-related variables
    		DISPLAY = os.getenv("DISPLAY") or ":0",
    		WAYLAND_DISPLAY = os.getenv("WAYLAND_DISPLAY"),
    		XDG_SESSION_TYPE = os.getenv("XDG_SESSION_TYPE"),
    		XAUTHORITY = os.getenv("XAUTHORITY"),
    
    		-- User-related variables
    		HOME = os.getenv("HOME"),
    		USER = os.getenv("USER"),
    		LOGNAME = os.getenv("LOGNAME"),
    
    		-- Path-related variables
    		PATH = os.getenv("PATH"),
    		LD_LIBRARY_PATH = os.getenv("LD_LIBRARY_PATH"),
    
    		-- Locale variables
    		LANG = os.getenv("LANG") or "en_US.UTF-8",
    		LC_ALL = os.getenv("LC_ALL"),
    
    		-- XDG variables
    		XDG_RUNTIME_DIR = os.getenv("XDG_RUNTIME_DIR"),
    		XDG_DATA_HOME = os.getenv("XDG_DATA_HOME"),
    		XDG_CONFIG_HOME = os.getenv("XDG_CONFIG_HOME"),
    
    		-- Other potentially relevant variables
    		DBUS_SESSION_BUS_ADDRESS = os.getenv("DBUS_SESSION_BUS_ADDRESS"),
    	}
    end
    return {
    	{
    		"Saghen/blink.cmp",
    		opts = {
    			keymap = {
    				preset = "super-tab",
    			},
    			signature = { window = { border = "rounded" } },
    			completion = {
    				menu = { border = "rounded" },
    				documentation = { window = { border = "rounded" } },
    				trigger = {
    					show_on_insert_on_trigger_character = false,
    				},
    			},
    		},
    	},
    	{
    		"neovim/nvim-lspconfig",
    		opts = {
    			servers = {
    				omnisharp = {
    					enable_editor_config_support = true,
    					settings = {
    						EnableEditorConfigSupport = true,
    					},
    				},
    			},
    		},
    	},
    	{
    		"stevearc/conform.nvim",
    		opts = {
    			formatters_by_ft = {
    				cs = {},
    			},
    			formatters = {
    				csharpier = {
    					command = "",
    				},
    			},
    		},
    	},
    	{
    		"mfussenegger/nvim-dap",
    		opts = {
    			adapters = {
    				coreclr = {
    					type = "executable",
    					command = vim.fn.exepath("netcoredbg") or "/home/fm39hz/.local/share/nvim/mason/bin/netcoredbg",
    					args = {
    						"--interpreter=vscode",
    						"--",
    						os.getenv("GODOT") or "/home/fm39hz/.config/godotenv/godot/bin/godot",
    					},
    				},
    			},
    			configurations = {
    				cs = {
    					{
    						type = "coreclr",
    						request = "launch",
    						name = "Simple Editor Launch",
    						cwd = function()
    							local project_file, project_dir = find_godot_project()
    							Snacks.notifier.notify("cwd " .. project_dir, "info")
    							Snacks.notifier.notify("file " .. project_file, "info")
    							return project_dir
    						end,
    						env = get_env_vars(),
    						args = function()
    							local project_file, project_dir = find_godot_project()
    							Snacks.notifier.notify("cwd " .. project_dir, "info")
    							return { " --editor " .. project_file }
    						end,
    					},
    				},
    			},
    		},
    	},
    	{
    		"rcarriga/nvim-dap-ui",
    		opts = {
    			expand_lines = true,
    			icons = { expanded = "", collapsed = "", circular = "" },
    			layouts = {
    				{
    					elements = {
    						{ id = "watches", size = 0.24 },
    						{ id = "scopes", size = 0.24 },
    						{ id = "breakpoints", size = 0.24 },
    						{ id = "stacks", size = 0.28 },
    					},
    					size = 0.23,
    					position = "right",
    				},
    				{
    					elements = {
    						{ id = "repl", size = 0.55 },
    						{ id = "console", size = 0.45 },
    					},
    					size = 0.27,
    					position = "bottom",
    				},
    			},
    			floating = {
    				max_height = 0.9,
    				max_width = 0.5,
    				border = "rounded",
    			},
    		},
    	},
    }

    why do people stray away from gdscript? C# is unreadable 🙁

    9 days later

    budtard
    Hi, ive tried your config but i keep getting this error:

    This is he trace logs:

    
    [INFO] 2025-03-27 21:33:46 dap/session.lua:1593	"Process exit"	"/usr/bin/netcoredbg"	0	15814
    [DEBUG] 2025-03-27 21:34:06 dap/session.lua:1533	"Spawning debug adapter"	{
      args = { "--interpreter=vscode", "--", "/home/shaka/.config/godotenv/godot/bin/godot" },
      command = "/usr/bin/netcoredbg",
      type = "executable"
    }
    [DEBUG] 2025-03-27 21:34:06 dap/session.lua:1872	"request"	{
      arguments = {
        adapterID = "nvim-dap",
        clientID = "neovim",
        clientName = "neovim",
        columnsStartAt1 = true,
        linesStartAt1 = true,
        locale = "en_US.UTF-8",
        pathFormat = "path",
        supportsProgressReporting = true,
        supportsRunInTerminalRequest = true,
        supportsStartDebuggingRequest = true,
        supportsVariableType = true
      },
      command = "initialize",
      seq = 1,
      type = "request"
    }
    [DEBUG] 2025-03-27 21:34:06 dap/session.lua:1068	2	{
      body = {
        capabilities = {
          exceptionBreakpointFilters = { {
              filter = "user-unhandled",
              label = "user-unhandled"
            }, {
              filter = "all",
              label = "all"
            } },
          supportTerminateDebuggee = true,
          supportsCancelRequest = true,
          supportsConditionalBreakpoints = true,
          supportsConfigurationDoneRequest = true,
          supportsExceptionFilterOptions = true,
          supportsExceptionInfoRequest = true,
          supportsExceptionOptions = false,
          supportsFunctionBreakpoints = true,
          supportsSetExpression = true,
          supportsSetVariable = true,
          supportsTerminateRequest = true
        }
      },
      event = "capabilities",
      seq = "1",
      type = "event"
    }
    [DEBUG] 2025-03-27 21:34:06 dap/session.lua:1068	2	{
      body = {
        exceptionBreakpointFilters = { {
            filter = "user-unhandled",
            label = "user-unhandled"
          }, {
            filter = "all",
            label = "all"
          } },
        supportTerminateDebuggee = true,
        supportsCancelRequest = true,
        supportsConditionalBreakpoints = true,
        supportsConfigurationDoneRequest = true,
        supportsExceptionFilterOptions = true,
        supportsExceptionInfoRequest = true,
        supportsExceptionOptions = false,
        supportsFunctionBreakpoints = true,
        supportsSetExpression = true,
        supportsSetVariable = true,
        supportsTerminateRequest = true
      },
      command = "initialize",
      request_seq = 1,
      seq = "2",
      success = true,
      type = "response"
    }
    [DEBUG] 2025-03-27 21:34:06 dap/session.lua:1068	2	{
      body = vim.empty_dict(),
      event = "initialized",
      seq = "3",
      type = "event"
    }
    [DEBUG] 2025-03-27 21:34:06 dap/session.lua:1872	"request"	{
      arguments = {
        args = { " --editor /home/shaka/Projects/Nova/Nova.Game/project.godot" },
        cwd = "/home/shaka/Projects/Nova/Nova.Game",
        env = {
          DBUS_SESSION_BUS_ADDRESS = "unix:path=/run/user/1000/bus",
          DISPLAY = ":0",
          HOME = "/home/shaka",
          LANG = "en_US.UTF-8",
          LOGNAME = "shaka",
          PATH = "/home/shaka/.nvm/versions/node/v22.14.0/bin:/home/shaka/.config/godotenv/godot/bin:/home/shaka/.local/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/opt/nvim:/home/shaka/.dotnet:/home/shaka/.dotnet/tools",
          SHELL = "/bin/bash",
          TERM = "xterm-256color",
          USER = "shaka",
          XAUTHORITY = "/tmp/xauth_ZxyWEi",
          XDG_RUNTIME_DIR = "/run/user/1000",
          XDG_SESSION_TYPE = "x11"
        },
        name = "Simple Editor Launch",
        request = "launch",
        type = "coreclr"
      },
      command = "launch",
      seq = 2,
      type = "request"
    }
    [DEBUG] 2025-03-27 21:34:06 dap/session.lua:1872	"request"	{
      arguments = {
        filters = {}
      },
      command = "setExceptionBreakpoints",
      seq = 3,
      type = "request"
    }
    [DEBUG] 2025-03-27 21:34:06 dap/session.lua:1068	2	{
      body = vim.empty_dict(),
      command = "launch",
      request_seq = 2,
      seq = "4",
      success = true,
      type = "response"
    }
    [DEBUG] 2025-03-27 21:34:06 dap/session.lua:1068	2	{
      body = vim.empty_dict(),
      command = "setExceptionBreakpoints",
      request_seq = 3,
      seq = "5",
      success = true,
      type = "response"
    }
    [DEBUG] 2025-03-27 21:34:06 dap/session.lua:1872	"request"	{
      command = "co