Advanced Patterns
June 5, 2026 ยท View on GitHub
This guide shows a more complete setup for users who want an IDE-like .NET workflow in Neovim. It focuses on external run/debug windows, the built-in terminal panel, lualine status, and project-file language features.
Visual Studio-style run and debug
external_terminal controls where easy-dotnet launches applications for :Dotnet run. When debugger.console = "externalTerminal" is also set, debug sessions use the same external terminal style.
easy-dotnet runs external applications through AppWrapper. AppWrapper keeps an external terminal process around and reuses idle windows for later run/debug sessions, so repeated launches do not create a pile of terminal windows.
Choose the terminal command for the OS and terminal you use:
-- Windows Terminal
local external_terminal = {
command = "wt",
args = { "-w", "-1", "nt", "--" },
}
-- Kitty
-- local external_terminal = {
-- command = "kitty",
-- args = { "--hold", "--" },
-- }
Full setup example
-- lazy.nvim
local external_terminal = {
command = "wt",
args = { "-w", "-1", "nt", "--" },
}
return {
"GustavEikaas/easy-dotnet.nvim",
dependencies = {
"nvim-lua/plenary.nvim",
"folke/snacks.nvim",
"mfussenegger/nvim-dap",
},
config = function()
local dotnet = require("easy-dotnet")
dotnet.setup({
external_terminal = external_terminal,
debugger = {
console = "externalTerminal",
},
notifications = {
handler = false,
},
lsp = {
restart_roslyn_on_branch_change = true,
},
auto_bootstrap_namespace = {
type = "file_scoped",
enabled = true,
},
})
vim.keymap.set("n", "<A-t>", function()
vim.cmd("Dotnet testrunner")
end, { nowait = true, desc = "Toggle testrunner" })
vim.keymap.set("n", "<C-A-p>", function()
vim.cmd("Dotnet debug profile default")
end, { nowait = true, desc = "Debug with default launchprofile" })
vim.keymap.set("n", "<C-p>", function()
vim.cmd("Dotnet run profile default")
end, { nowait = true, desc = "Run with default launchprofile" })
vim.keymap.set("n", "<C-b>", function()
dotnet.build_default_quickfix()
end, { nowait = true, desc = "Build default project/solution" })
vim.keymap.set({ "n", "t" }, "<A-i>", function()
vim.cmd("Dotnet terminal toggle")
end, { noremap = true, silent = true, desc = "Toggle terminal" })
end,
}
With this setup:
:Dotnet runand:Dotnet run profile defaultlaunch in the configured external terminal.:Dotnet debug profile defaultlaunches the application in the external terminal and attaches the debugger.- Finished external applications leave the AppWrapper window available for reuse.
- easy-dotnet notifications are suppressed because lualine shows job and run state.
Built-in terminal panel
The :Dotnet terminal commands open a VS Code-inspired terminal panel inside Neovim:
:Dotnet terminal toggle
:Dotnet terminal show
:Dotnet terminal hide
The panel is separate from external_terminal. Use it for editor-owned terminal tabs and plugin command output you want to keep inside Neovim. The default mappings support switching tabs, creating a user terminal, closing the current tab, and hiding the panel.
When managed_terminal.auto_hide = true, successful plugin-owned commands hide the panel after managed_terminal.auto_hide_delay milliseconds. User-created terminals stay available as normal terminal tabs.
Quick create menu
The quick create menu creates contextual C# items without leaving the current explorer location. It is designed for file explorer integrations where the selected directory becomes the output path.
Call the Lua helper from explorer mappings:
require("easy-dotnet").create_item(path)
The menu is backed by the easy-dotnet server and currently creates C# enums, records, interfaces, and classes using Roslyn. It also respects auto_bootstrap_namespace.type: when that option is set to "file_scoped", generated C# items prefer file-scoped namespaces.
When integrating with file explorers, pass the selected directory as path. If the cursor is on a file, use the file's parent directory:
local target_dir = node.type == "directory" and node.absolute_path or vim.fs.dirname(node.absolute_path)
require("easy-dotnet").create_item(target_dir)
Lualine status
Disabling notifications.handler when lualine shows active jobs and run/debug state:
local dotnet = require("easy-dotnet")
require("lualine").setup({
sections = {
lualine_a = {
"mode",
dotnet.lualine.jobs,
},
lualine_x = {
dotnet.lualine.active_project,
{
dotnet.lualine.run_status,
color = dotnet.lualine.run_status_color,
on_click = dotnet.lualine.run_status_click,
},
},
},
})
dotnet.lualine.jobs reports server and command progress. dotnet.lualine.active_project shows the selected startup project and launch profile. dotnet.lualine.run_status can run, debug, or stop the active project through its click handler.
ProjX LSP
ProjX LSP is enabled by default and improves .csproj editing. It is separate from Roslyn C# LSP and focuses on project-file intelligence.
Useful ProjX features include:
- Package and version autocomplete in
PackageReferenceelements. - Formatting for project XML.
- Code actions for sorting package references, converting target framework elements, removing package or project references, opening user secrets, and expanding or collapsing XML elements.
- Definitions for project references, imports, and user secrets where supported.